From 26e0de349779809901c4250e6519ffba2966de46 Mon Sep 17 00:00:00 2001 From: Alex Ellis Date: Sat, 3 Mar 2018 10:44:54 +0000 Subject: [PATCH] Remove Golang reverseproxy and use http Client Signed-off-by: Alex Ellis --- gateway/handlers/forwarding_proxy.go | 52 ++++++++++++++++++++++------ gateway/metrics/add_metrics.go | 2 +- gateway/server.go | 3 +- gateway/types/http_adapter.go | 48 ------------------------- gateway/types/proxy_client.go | 32 +++++++++++++++++ 5 files changed, 75 insertions(+), 62 deletions(-) delete mode 100644 gateway/types/http_adapter.go create mode 100644 gateway/types/proxy_client.go diff --git a/gateway/handlers/forwarding_proxy.go b/gateway/handlers/forwarding_proxy.go index 66ebee47..ff2546a9 100644 --- a/gateway/handlers/forwarding_proxy.go +++ b/gateway/handlers/forwarding_proxy.go @@ -1,9 +1,9 @@ package handlers import ( + "io" "log" "net/http" - "net/http/httputil" "strconv" "strings" "time" @@ -14,36 +14,66 @@ import ( ) // MakeForwardingProxyHandler create a handler which forwards HTTP requests -func MakeForwardingProxyHandler(proxy *httputil.ReverseProxy, metrics *metrics.MetricOptions) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - uri := r.URL.String() +func MakeForwardingProxyHandler(proxy *types.HttpClientReverseProxy, metrics *metrics.MetricOptions) http.HandlerFunc { + baseURL := proxy.BaseURL.String() + if strings.HasSuffix(baseURL, "/") { + baseURL = baseURL[0 : len(baseURL)-1] + } - log.Printf("> Forwarding [%s] to %s", r.Method, r.URL.String()) + return func(w http.ResponseWriter, r *http.Request) { + + requestURL := r.URL.String() + + log.Printf("> Forwarding [%s] to %s", r.Method, requestURL) start := time.Now() - writeAdapter := types.NewWriteAdapter(w) - proxy.ServeHTTP(writeAdapter, r) + upstreamReq, _ := http.NewRequest(r.Method, baseURL+requestURL, nil) + + upstreamReq.Header["X-Forwarded-For"] = []string{r.RequestURI} + + if r.Body != nil { + defer r.Body.Close() + upstreamReq.Body = r.Body + + } + + res, resErr := proxy.Client.Do(upstreamReq) + if resErr != nil { + log.Printf("upstream client error: %s\n", resErr) + return + } + + if res.Body != nil { + defer res.Body.Close() + } + + for k, v := range res.Header { + w.Header()[k] = v + } + + io.CopyBuffer(w, res.Body, nil) seconds := time.Since(start).Seconds() log.Printf("< [%s] - %d took %f seconds\n", r.URL.String(), - writeAdapter.GetHeaderCode(), seconds) + res.StatusCode, seconds) forward := "/function/" - if startsWith(uri, forward) { + if startsWith(requestURL, forward) { // log.Printf("function=%s", uri[len(forward):]) - service := uri[len(forward):] + service := requestURL[len(forward):] metrics.GatewayFunctionsHistogram. WithLabelValues(service). Observe(seconds) - code := strconv.Itoa(writeAdapter.GetHeaderCode()) + code := strconv.Itoa(res.StatusCode) metrics.GatewayFunctionInvocation. With(prometheus.Labels{"function_name": service, "code": code}). Inc() } + } } diff --git a/gateway/metrics/add_metrics.go b/gateway/metrics/add_metrics.go index bc45ab30..02437a94 100644 --- a/gateway/metrics/add_metrics.go +++ b/gateway/metrics/add_metrics.go @@ -48,7 +48,7 @@ func AddMetricsHandler(handler http.HandlerFunc, prometheusQuery PrometheusQuery err := json.Unmarshal(upstreamBody, &functions) if err != nil { - log.Println(err) + log.Printf("Metrics upstream error: %s", err) w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusInternalServerError) diff --git a/gateway/server.go b/gateway/server.go index 62f7b425..bd0fd3cf 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -7,7 +7,6 @@ import ( "fmt" "log" "net/http" - "net/http/httputil" "time" "github.com/gorilla/mux" @@ -41,7 +40,7 @@ func main() { servicePollInterval := time.Second * 5 - reverseProxy := httputil.NewSingleHostReverseProxy(config.FunctionsProviderURL) + reverseProxy := types.NewHttpClientReverseProxy(config.FunctionsProviderURL, config.ReadTimeout) faasHandlers.Proxy = internalHandlers.MakeForwardingProxyHandler(reverseProxy, &metricsOptions) faasHandlers.RoutelessProxy = internalHandlers.MakeForwardingProxyHandler(reverseProxy, &metricsOptions) diff --git a/gateway/types/http_adapter.go b/gateway/types/http_adapter.go deleted file mode 100644 index f0cc0ccc..00000000 --- a/gateway/types/http_adapter.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Alex Ellis 2017. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -package types - -import ( - "log" - "net/http" -) - -// WriteAdapter adapts a ResponseWriter -type WriteAdapter struct { - Writer http.ResponseWriter - HTTPResult *HTTPResult -} - -// HTTPResult captures data from forwarded HTTP call -type HTTPResult struct { - HeaderCode int // HeaderCode is the result of WriteHeader(int) -} - -//NewWriteAdapter create a new NewWriteAdapter -func NewWriteAdapter(w http.ResponseWriter) WriteAdapter { - return WriteAdapter{Writer: w, HTTPResult: &HTTPResult{}} -} - -//Header adapts Header -func (w WriteAdapter) Header() http.Header { - return w.Writer.Header() -} - -// Write adapts Write for a straight pass-through -func (w WriteAdapter) Write(data []byte) (int, error) { - return w.Writer.Write(data) -} - -// WriteHeader adapts WriteHeader -func (w WriteAdapter) WriteHeader(statusCode int) { - w.Writer.WriteHeader(statusCode) - w.HTTPResult.HeaderCode = statusCode - - log.Printf("GetHeaderCode %d", w.HTTPResult.HeaderCode) -} - -// GetHeaderCode result from WriteHeader -func (w *WriteAdapter) GetHeaderCode() int { - return w.HTTPResult.HeaderCode -} diff --git a/gateway/types/proxy_client.go b/gateway/types/proxy_client.go new file mode 100644 index 00000000..faafdeae --- /dev/null +++ b/gateway/types/proxy_client.go @@ -0,0 +1,32 @@ +package types + +import ( + "net" + "net/http" + "net/url" + "time" +) + +func NewHttpClientReverseProxy(baseURL *url.URL, timeout time.Duration) *HttpClientReverseProxy { + h := HttpClientReverseProxy{ + BaseURL: baseURL, + } + + h.Client = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: timeout, + KeepAlive: 1 * time.Second, + }).DialContext, + IdleConnTimeout: 120 * time.Millisecond, + ExpectContinueTimeout: 1500 * time.Millisecond, + }, + } + return &h +} + +type HttpClientReverseProxy struct { + BaseURL *url.URL + Client *http.Client +}