diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index 389faaf9..b21d38fc 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -34,14 +34,15 @@ type UpstreamHostDownFunc func(*UpstreamHost) bool // UpstreamHost represents a single proxy upstream type UpstreamHost struct { // The hostname of this upstream host - Name string - ReverseProxy *ReverseProxy - Conns int64 - Fails int32 - FailTimeout time.Duration - Unhealthy bool - ExtraHeaders http.Header - CheckDown UpstreamHostDownFunc + Name string + ReverseProxy *ReverseProxy + Conns int64 + Fails int32 + FailTimeout time.Duration + Unhealthy bool + ExtraHeaders http.Header + CheckDown UpstreamHostDownFunc + WithoutPathPrefix string } // Down checks whether the upstream host is down or not. @@ -77,7 +78,7 @@ func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { if baseURL, err := url.Parse(host.Name); err == nil { r.Host = baseURL.Host if proxy == nil { - proxy = NewSingleHostReverseProxy(baseURL) + proxy = NewSingleHostReverseProxy(baseURL, host.WithoutPathPrefix) } } else if proxy == nil { return http.StatusInternalServerError, err diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 409df463..6fb6a899 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -120,7 +120,7 @@ func (u *fakeUpstream) Select() *UpstreamHost { uri, _ := url.Parse(u.name) return &UpstreamHost{ Name: u.name, - ReverseProxy: NewSingleHostReverseProxy(uri), + ReverseProxy: NewSingleHostReverseProxy(uri, ""), ExtraHeaders: proxyHeaders, } } diff --git a/middleware/proxy/reverseproxy.go b/middleware/proxy/reverseproxy.go index 0ffe54a1..e5652fa9 100644 --- a/middleware/proxy/reverseproxy.go +++ b/middleware/proxy/reverseproxy.go @@ -62,7 +62,9 @@ func singleJoiningSlash(a, b string) string { // URLs to the scheme, host, and base path provided in target. If the // target's path is "/base" and the incoming request was for "/dir", // the target request will be for /base/dir. -func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy { +// Without logic: target's path is "/", incoming is "/api/messages", +// without is "/api", then the target request will be for /messages. +func NewSingleHostReverseProxy(target *url.URL, without string) *ReverseProxy { targetQuery := target.RawQuery director := func(req *http.Request) { req.URL.Scheme = target.Scheme @@ -73,6 +75,9 @@ func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy { } else { req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery } + if without != "" { + req.URL.Path = strings.TrimPrefix(req.URL.Path, without) + } } return &ReverseProxy{Director: director} } diff --git a/middleware/proxy/upstream.go b/middleware/proxy/upstream.go index 011a58b8..cca5c772 100644 --- a/middleware/proxy/upstream.go +++ b/middleware/proxy/upstream.go @@ -28,13 +28,13 @@ type staticUpstream struct { Path string Interval time.Duration } + WithoutPathPrefix string } // NewStaticUpstreams parses the configuration input and sets up // static upstreams for the proxy middleware. func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) { var upstreams []Upstream - for c.Next() { upstream := &staticUpstream{ from: "", @@ -104,6 +104,11 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) { case "websocket": proxyHeaders.Add("Connection", "{>Connection}") proxyHeaders.Add("Upgrade", "{>Upgrade}") + case "without": + if !c.NextArg() { + return upstreams, c.ArgErr() + } + upstream.WithoutPathPrefix = c.Val() } } @@ -131,9 +136,10 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) { return false } }(upstream), + WithoutPathPrefix: upstream.WithoutPathPrefix, } if baseURL, err := url.Parse(uh.Name); err == nil { - uh.ReverseProxy = NewSingleHostReverseProxy(baseURL) + uh.ReverseProxy = NewSingleHostReverseProxy(baseURL, uh.WithoutPathPrefix) } else { return upstreams, err }