1
0
mirror of https://github.com/labstack/echo.git synced 2024-12-24 20:14:31 +02:00

Proxy: Better errors + remote custom TLS (#1197)

Proxy will be more verbose on errors + possibility to configure custom transport (example: for custom TLS certificates)
This commit is contained in:
Gregor Noczinski 2018-10-13 08:10:19 +02:00 committed by Vishal Rana
parent fcdf096c2c
commit bc37a3a792
6 changed files with 97 additions and 7 deletions

View File

@ -260,6 +260,7 @@ var (
ErrBadGateway = NewHTTPError(http.StatusBadGateway) ErrBadGateway = NewHTTPError(http.StatusBadGateway)
ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) ErrInternalServerError = NewHTTPError(http.StatusInternalServerError)
ErrRequestTimeout = NewHTTPError(http.StatusRequestTimeout) ErrRequestTimeout = NewHTTPError(http.StatusRequestTimeout)
ErrServiceUnavailable = NewHTTPError(http.StatusServiceUnavailable)
ErrValidatorNotRegistered = errors.New("validator not registered") ErrValidatorNotRegistered = errors.New("validator not registered")
ErrRendererNotRegistered = errors.New("renderer not registered") ErrRendererNotRegistered = errors.New("renderer not registered")
ErrInvalidRedirectCode = errors.New("invalid redirect status code") ErrInvalidRedirectCode = errors.New("invalid redirect status code")

View File

@ -6,7 +6,6 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"net/http/httputil"
"net/url" "net/url"
"regexp" "regexp"
"strings" "strings"
@ -42,6 +41,10 @@ type (
// Optional. Default value "target". // Optional. Default value "target".
ContextKey string ContextKey string
// To customize the transport to remote.
// Examples: If custom TLS certificates are required.
Transport http.RoundTripper
rewriteRegex map[*regexp.Regexp]string rewriteRegex map[*regexp.Regexp]string
} }
@ -85,10 +88,6 @@ var (
} }
) )
func proxyHTTP(t *ProxyTarget) http.Handler {
return httputil.NewSingleHostReverseProxy(t.URL)
}
func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
in, _, err := c.Response().Hijack() in, _, err := c.Response().Hijack()
@ -250,7 +249,7 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
proxyRaw(tgt, c).ServeHTTP(res, req) proxyRaw(tgt, c).ServeHTTP(res, req)
case req.Header.Get(echo.HeaderAccept) == "text/event-stream": case req.Header.Get(echo.HeaderAccept) == "text/event-stream":
default: default:
proxyHTTP(tgt).ServeHTTP(res, req) proxyHTTP(tgt, c, config).ServeHTTP(res, req)
} }
return return

24
middleware/proxy_1_11.go Normal file
View File

@ -0,0 +1,24 @@
// +build go1.11
package middleware
import (
"fmt"
"github.com/labstack/echo"
"net/http"
"net/http/httputil"
)
func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
proxy := httputil.NewSingleHostReverseProxy(tgt.URL)
proxy.ErrorHandler = func(resp http.ResponseWriter, req *http.Request, err error) {
descr := tgt.URL.String()
if tgt.Name != "" {
descr = fmt.Sprintf("%s(%s)", tgt.Name, tgt.URL.String())
}
c.Logger().Errorf("remote %s unreachable, could not forward: %v", descr, err)
c.Error(echo.ErrServiceUnavailable)
}
proxy.Transport = config.Transport
return proxy
}

View File

@ -0,0 +1,13 @@
// +build !go1.11
package middleware
import (
"github.com/labstack/echo"
"net/http"
"net/http/httputil"
)
func proxyHTTP(t *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
return httputil.NewSingleHostReverseProxy(t.URL)
}

View File

@ -0,0 +1,52 @@
// +build go1.11
package middleware
import (
"github.com/labstack/echo"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"net/url"
"testing"
)
func TestProxy_1_11(t *testing.T) {
// Setup
url1, _ := url.Parse("http://127.0.0.1:27121")
url2, _ := url.Parse("http://127.0.0.1:27122")
targets := []*ProxyTarget{
{
Name: "target 1",
URL: url1,
},
{
Name: "target 2",
URL: url2,
},
}
rb := NewRandomBalancer(nil)
// must add targets:
for _, target := range targets {
assert.True(t, rb.AddTarget(target))
}
// must ignore duplicates:
for _, target := range targets {
assert.False(t, rb.AddTarget(target))
}
// Random
e := echo.New()
e.Use(Proxy(rb))
req := httptest.NewRequest(echo.GET, "/", nil)
rec := newCloseNotifyRecorder()
// Remote unreachable
rec = newCloseNotifyRecorder()
req.URL.Path = "/api/users"
e.ServeHTTP(rec, req)
assert.Equal(t, "/api/users", req.URL.Path)
assert.Equal(t, http.StatusServiceUnavailable, rec.Code)
}

View File

@ -124,6 +124,7 @@ func TestProxy(t *testing.T) {
req.URL.Path = "/users/jack/orders/1" req.URL.Path = "/users/jack/orders/1"
e.ServeHTTP(rec, req) e.ServeHTTP(rec, req)
assert.Equal(t, "/user/jack/order/1", req.URL.Path) assert.Equal(t, "/user/jack/order/1", req.URL.Path)
assert.Equal(t, http.StatusOK, rec.Code)
// ProxyTarget is set in context // ProxyTarget is set in context
contextObserver := func(next echo.HandlerFunc) echo.HandlerFunc { contextObserver := func(next echo.HandlerFunc) echo.HandlerFunc {