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:
parent
fcdf096c2c
commit
bc37a3a792
1
echo.go
1
echo.go
@ -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")
|
||||||
|
@ -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"
|
||||||
@ -38,10 +37,14 @@ type (
|
|||||||
// "/users/*/orders/*": "/user/$1/order/$2",
|
// "/users/*/orders/*": "/user/$1/order/$2",
|
||||||
Rewrite map[string]string
|
Rewrite map[string]string
|
||||||
|
|
||||||
// Context key to store selected ProxyTarget into context.
|
// Context key to store selected ProxyTarget into context.
|
||||||
// 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
24
middleware/proxy_1_11.go
Normal 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
|
||||||
|
}
|
13
middleware/proxy_1_11_n.go
Normal file
13
middleware/proxy_1_11_n.go
Normal 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)
|
||||||
|
}
|
52
middleware/proxy_1_11_test.go
Normal file
52
middleware/proxy_1_11_test.go
Normal 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)
|
||||||
|
}
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user