Add an option to preserve server path
This commit is contained in:
parent
6e1f5dc071
commit
83871f27dd
19 changed files with 251 additions and 44 deletions
|
@ -59,10 +59,12 @@
|
|||
[[http.services.Service02.loadBalancer.servers]]
|
||||
url = "foobar"
|
||||
weight = 42
|
||||
preservePath = true
|
||||
|
||||
[[http.services.Service02.loadBalancer.servers]]
|
||||
url = "foobar"
|
||||
weight = 42
|
||||
preservePath = true
|
||||
[http.services.Service02.loadBalancer.healthCheck]
|
||||
scheme = "foobar"
|
||||
mode = "foobar"
|
||||
|
|
|
@ -66,8 +66,10 @@ http:
|
|||
servers:
|
||||
- url: foobar
|
||||
weight: 42
|
||||
preservePath: true
|
||||
- url: foobar
|
||||
weight: 42
|
||||
preservePath: true
|
||||
healthCheck:
|
||||
scheme: foobar
|
||||
mode: foobar
|
||||
|
|
|
@ -256,8 +256,10 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
|||
| `traefik/http/services/Service02/loadBalancer/healthCheck/timeout` | `42s` |
|
||||
| `traefik/http/services/Service02/loadBalancer/passHostHeader` | `true` |
|
||||
| `traefik/http/services/Service02/loadBalancer/responseForwarding/flushInterval` | `42s` |
|
||||
| `traefik/http/services/Service02/loadBalancer/servers/0/preservePath` | `true` |
|
||||
| `traefik/http/services/Service02/loadBalancer/servers/0/url` | `foobar` |
|
||||
| `traefik/http/services/Service02/loadBalancer/servers/0/weight` | `42` |
|
||||
| `traefik/http/services/Service02/loadBalancer/servers/1/preservePath` | `true` |
|
||||
| `traefik/http/services/Service02/loadBalancer/servers/1/url` | `foobar` |
|
||||
| `traefik/http/services/Service02/loadBalancer/servers/1/weight` | `42` |
|
||||
| `traefik/http/services/Service02/loadBalancer/serversTransport` | `foobar` |
|
||||
|
|
|
@ -116,12 +116,8 @@ Each service has a load-balancer, even if there is only one server to forward tr
|
|||
#### Servers
|
||||
|
||||
Servers declare a single instance of your program.
|
||||
The `url` option point to a specific instance.
|
||||
|
||||
!!! info ""
|
||||
Paths in the servers' `url` have no effect.
|
||||
If you want the requests to be sent to a specific path on your servers,
|
||||
configure your [`routers`](../routers/index.md) to use a corresponding [middleware](../../middlewares/overview.md) (e.g. the [AddPrefix](../../middlewares/http/addprefix.md) or [ReplacePath](../../middlewares/http/replacepath.md)) middlewares.
|
||||
The `url` option point to a specific instance.
|
||||
|
||||
??? example "A Service with One Server -- Using the [File Provider](../../providers/file.md)"
|
||||
|
||||
|
@ -173,6 +169,34 @@ The `weight` option allows for weighted load balancing on the servers.
|
|||
weight = 1
|
||||
```
|
||||
|
||||
The `preservePath` option allows to preserve the URL path.
|
||||
|
||||
!!! info "Health Check"
|
||||
|
||||
When a [health check](#health-check) is configured for the server, the path is not preserved.
|
||||
|
||||
??? example "A Service with One Server and PreservePath -- Using the [File Provider](../../providers/file.md)"
|
||||
|
||||
```yaml tab="YAML"
|
||||
## Dynamic configuration
|
||||
http:
|
||||
services:
|
||||
my-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://private-ip-server-1/base"
|
||||
preservePath: true
|
||||
```
|
||||
|
||||
```toml tab="TOML"
|
||||
## Dynamic configuration
|
||||
[http.services]
|
||||
[http.services.my-service.loadBalancer]
|
||||
[[http.services.my-service.loadBalancer.servers]]
|
||||
url = "http://private-ip-server-1/base"
|
||||
preservePath = true
|
||||
```
|
||||
|
||||
#### Load-balancing
|
||||
|
||||
For now, only round robin load balancing is supported:
|
||||
|
|
|
@ -245,7 +245,8 @@ func (r *ResponseForwarding) SetDefaults() {
|
|||
// Server holds the server configuration.
|
||||
type Server struct {
|
||||
URL string `json:"url,omitempty" toml:"url,omitempty" yaml:"url,omitempty" label:"-"`
|
||||
Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty" label:"weight"`
|
||||
Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty" label:"weight" export:"true"`
|
||||
PreservePath bool `json:"preservePath,omitempty" toml:"preservePath,omitempty" yaml:"preservePath,omitempty" label:"-" export:"true"`
|
||||
Scheme string `json:"-" toml:"-" yaml:"-" file:"-"`
|
||||
Port string `json:"-" toml:"-" yaml:"-" file:"-"`
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ func (r *ProxyBuilder) Update(newConfigs map[string]*dynamic.ServersTransport) {
|
|||
}
|
||||
|
||||
// Build builds a new ReverseProxy with the given configuration.
|
||||
func (r *ProxyBuilder) Build(cfgName string, targetURL *url.URL, passHostHeader bool) (http.Handler, error) {
|
||||
func (r *ProxyBuilder) Build(cfgName string, targetURL *url.URL, passHostHeader, preservePath bool) (http.Handler, error) {
|
||||
proxyURL, err := r.proxy(&http.Request{URL: targetURL})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting proxy: %w", err)
|
||||
|
@ -90,7 +90,7 @@ func (r *ProxyBuilder) Build(cfgName string, targetURL *url.URL, passHostHeader
|
|||
}
|
||||
|
||||
pool := r.getPool(cfgName, cfg, tlsConfig, targetURL, proxyURL)
|
||||
return NewReverseProxy(targetURL, proxyURL, r.debug, passHostHeader, responseHeaderTimeout, pool)
|
||||
return NewReverseProxy(targetURL, proxyURL, r.debug, passHostHeader, preservePath, responseHeaderTimeout, pool)
|
||||
}
|
||||
|
||||
func (r *ProxyBuilder) getPool(cfgName string, config *dynamic.ServersTransport, tlsConfig *tls.Config, targetURL *url.URL, proxyURL *url.URL) *connPool {
|
||||
|
|
|
@ -121,11 +121,12 @@ type ReverseProxy struct {
|
|||
|
||||
targetURL *url.URL
|
||||
passHostHeader bool
|
||||
preservePath bool
|
||||
responseHeaderTimeout time.Duration
|
||||
}
|
||||
|
||||
// NewReverseProxy creates a new ReverseProxy.
|
||||
func NewReverseProxy(targetURL *url.URL, proxyURL *url.URL, debug, passHostHeader bool, responseHeaderTimeout time.Duration, connPool *connPool) (*ReverseProxy, error) {
|
||||
func NewReverseProxy(targetURL, proxyURL *url.URL, debug, passHostHeader, preservePath bool, responseHeaderTimeout time.Duration, connPool *connPool) (*ReverseProxy, error) {
|
||||
var proxyAuth string
|
||||
if proxyURL != nil && proxyURL.User != nil && targetURL.Scheme == "http" {
|
||||
username := proxyURL.User.Username()
|
||||
|
@ -136,6 +137,7 @@ func NewReverseProxy(targetURL *url.URL, proxyURL *url.URL, debug, passHostHeade
|
|||
return &ReverseProxy{
|
||||
debug: debug,
|
||||
passHostHeader: passHostHeader,
|
||||
preservePath: preservePath,
|
||||
targetURL: targetURL,
|
||||
proxyAuth: proxyAuth,
|
||||
connPool: connPool,
|
||||
|
@ -207,6 +209,11 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
|
||||
u2.Path = u.Path
|
||||
u2.RawPath = u.RawPath
|
||||
|
||||
if p.preservePath {
|
||||
u2.Path, u2.RawPath = proxyhttputil.JoinURLPath(p.targetURL, u)
|
||||
}
|
||||
|
||||
u2.RawQuery = strings.ReplaceAll(u.RawQuery, ";", "&")
|
||||
|
||||
outReq.SetHost(u2.Host)
|
||||
|
|
|
@ -230,7 +230,7 @@ func TestProxyFromEnvironment(t *testing.T) {
|
|||
return u, nil
|
||||
}
|
||||
|
||||
reverseProxy, err := builder.Build("foo", testhelpers.MustParseURL(backendURL), false)
|
||||
reverseProxy, err := builder.Build("foo", testhelpers.MustParseURL(backendURL), false, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
reverseProxyServer := httptest.NewServer(reverseProxy)
|
||||
|
@ -252,6 +252,32 @@ func TestProxyFromEnvironment(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPreservePath(t *testing.T) {
|
||||
var callCount int
|
||||
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
callCount++
|
||||
assert.Equal(t, "/base/foo/bar", req.URL.Path)
|
||||
assert.Equal(t, "/base/foo%2Fbar", req.URL.RawPath)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
builder := NewProxyBuilder(&transportManagerMock{}, static.FastProxyConfig{})
|
||||
|
||||
serverURL, err := url.JoinPath(server.URL, "base")
|
||||
require.NoError(t, err)
|
||||
|
||||
proxyHandler, err := builder.Build("", testhelpers.MustParseURL(serverURL), true, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/foo%2Fbar", http.NoBody)
|
||||
res := httptest.NewRecorder()
|
||||
|
||||
proxyHandler.ServeHTTP(res, req)
|
||||
|
||||
assert.Equal(t, 1, callCount)
|
||||
assert.Equal(t, http.StatusOK, res.Code)
|
||||
}
|
||||
|
||||
func newCertificate(t *testing.T, domain string) *tls.Certificate {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
@ -362,7 +362,7 @@ func TestWebSocketRequestWithHeadersInResponseWriter(t *testing.T) {
|
|||
|
||||
u := parseURI(t, srv.URL)
|
||||
|
||||
f, err := NewReverseProxy(u, nil, true, false, 0, newConnPool(1, 0, func() (net.Conn, error) {
|
||||
f, err := NewReverseProxy(u, nil, true, false, false, 0, newConnPool(1, 0, func() (net.Conn, error) {
|
||||
return net.Dial("tcp", u.Host)
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
|
@ -434,7 +434,7 @@ func TestWebSocketUpgradeFailed(t *testing.T) {
|
|||
defer srv.Close()
|
||||
|
||||
u := parseURI(t, srv.URL)
|
||||
f, err := NewReverseProxy(u, nil, true, false, 0, newConnPool(1, 0, func() (net.Conn, error) {
|
||||
f, err := NewReverseProxy(u, nil, true, false, false, 0, newConnPool(1, 0, func() (net.Conn, error) {
|
||||
return net.Dial("tcp", u.Host)
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
|
@ -676,7 +676,7 @@ func createProxyWithForwarder(t *testing.T, uri string, pool *connPool) *httptes
|
|||
t.Helper()
|
||||
|
||||
u := parseURI(t, uri)
|
||||
proxy, err := NewReverseProxy(u, nil, false, true, 0, pool)
|
||||
proxy, err := NewReverseProxy(u, nil, false, true, false, 0, pool)
|
||||
require.NoError(t, err)
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
@ -38,7 +38,7 @@ func NewProxyBuilder(transportManager TransportManager, semConvMetricsRegistry *
|
|||
func (r *ProxyBuilder) Update(_ map[string]*dynamic.ServersTransport) {}
|
||||
|
||||
// Build builds a new httputil.ReverseProxy with the given configuration.
|
||||
func (r *ProxyBuilder) Build(cfgName string, targetURL *url.URL, shouldObserve, passHostHeader bool, flushInterval time.Duration) (http.Handler, error) {
|
||||
func (r *ProxyBuilder) Build(cfgName string, targetURL *url.URL, shouldObserve, passHostHeader, preservePath bool, flushInterval time.Duration) (http.Handler, error) {
|
||||
roundTripper, err := r.transportManager.GetRoundTripper(cfgName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting RoundTripper: %w", err)
|
||||
|
@ -50,5 +50,5 @@ func (r *ProxyBuilder) Build(cfgName string, targetURL *url.URL, shouldObserve,
|
|||
roundTripper = newObservabilityRoundTripper(r.semConvMetricsRegistry, roundTripper)
|
||||
}
|
||||
|
||||
return buildSingleHostProxy(targetURL, passHostHeader, flushInterval, roundTripper, r.bufferPool), nil
|
||||
return buildSingleHostProxy(targetURL, passHostHeader, preservePath, flushInterval, roundTripper, r.bufferPool), nil
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ func TestEscapedPath(t *testing.T) {
|
|||
roundTrippers: map[string]http.RoundTripper{"default": &http.Transport{}},
|
||||
}
|
||||
|
||||
p, err := NewProxyBuilder(transportManager, nil).Build("default", testhelpers.MustParseURL(srv.URL), false, true, 0)
|
||||
p, err := NewProxyBuilder(transportManager, nil).Build("default", testhelpers.MustParseURL(srv.URL), false, true, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy := httptest.NewServer(http.HandlerFunc(p.ServeHTTP))
|
||||
|
|
|
@ -15,15 +15,17 @@ import (
|
|||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
|
||||
const (
|
||||
// StatusClientClosedRequest non-standard HTTP status code for client disconnection.
|
||||
const StatusClientClosedRequest = 499
|
||||
StatusClientClosedRequest = 499
|
||||
|
||||
// StatusClientClosedRequestText non-standard HTTP status for client disconnection.
|
||||
const StatusClientClosedRequestText = "Client Closed Request"
|
||||
StatusClientClosedRequestText = "Client Closed Request"
|
||||
)
|
||||
|
||||
func buildSingleHostProxy(target *url.URL, passHostHeader bool, flushInterval time.Duration, roundTripper http.RoundTripper, bufferPool httputil.BufferPool) http.Handler {
|
||||
func buildSingleHostProxy(target *url.URL, passHostHeader bool, preservePath bool, flushInterval time.Duration, roundTripper http.RoundTripper, bufferPool httputil.BufferPool) http.Handler {
|
||||
return &httputil.ReverseProxy{
|
||||
Director: directorBuilder(target, passHostHeader),
|
||||
Director: directorBuilder(target, passHostHeader, preservePath),
|
||||
Transport: roundTripper,
|
||||
FlushInterval: flushInterval,
|
||||
BufferPool: bufferPool,
|
||||
|
@ -31,7 +33,7 @@ func buildSingleHostProxy(target *url.URL, passHostHeader bool, flushInterval ti
|
|||
}
|
||||
}
|
||||
|
||||
func directorBuilder(target *url.URL, passHostHeader bool) func(req *http.Request) {
|
||||
func directorBuilder(target *url.URL, passHostHeader bool, preservePath bool) func(req *http.Request) {
|
||||
return func(outReq *http.Request) {
|
||||
outReq.URL.Scheme = target.Scheme
|
||||
outReq.URL.Host = target.Host
|
||||
|
@ -46,6 +48,11 @@ func directorBuilder(target *url.URL, passHostHeader bool) func(req *http.Reques
|
|||
|
||||
outReq.URL.Path = u.Path
|
||||
outReq.URL.RawPath = u.RawPath
|
||||
|
||||
if preservePath {
|
||||
outReq.URL.Path, outReq.URL.RawPath = JoinURLPath(target, u)
|
||||
}
|
||||
|
||||
// If a plugin/middleware adds semicolons in query params, they should be urlEncoded.
|
||||
outReq.URL.RawQuery = strings.ReplaceAll(u.RawQuery, ";", "&")
|
||||
outReq.RequestURI = "" // Outgoing request should not have RequestURI
|
||||
|
@ -54,7 +61,7 @@ func directorBuilder(target *url.URL, passHostHeader bool) func(req *http.Reques
|
|||
outReq.ProtoMajor = 1
|
||||
outReq.ProtoMinor = 1
|
||||
|
||||
// Do not pass client Host header unless optsetter PassHostHeader is set.
|
||||
// Do not pass client Host header unless option PassHostHeader is set.
|
||||
if !passHostHeader {
|
||||
outReq.Host = outReq.URL.Host
|
||||
}
|
||||
|
@ -106,6 +113,13 @@ func ErrorHandler(w http.ResponseWriter, req *http.Request, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
func statusText(statusCode int) string {
|
||||
if statusCode == StatusClientClosedRequest {
|
||||
return StatusClientClosedRequestText
|
||||
}
|
||||
return http.StatusText(statusCode)
|
||||
}
|
||||
|
||||
// ComputeStatusCode computes the HTTP status code according to the given error.
|
||||
func ComputeStatusCode(err error) int {
|
||||
switch {
|
||||
|
@ -127,9 +141,38 @@ func ComputeStatusCode(err error) int {
|
|||
return http.StatusInternalServerError
|
||||
}
|
||||
|
||||
func statusText(statusCode int) string {
|
||||
if statusCode == StatusClientClosedRequest {
|
||||
return StatusClientClosedRequestText
|
||||
// JoinURLPath computes the joined path and raw path of the given URLs.
|
||||
// From https://github.com/golang/go/blob/b521ebb55a9b26c8824b219376c7f91f7cda6ec2/src/net/http/httputil/reverseproxy.go#L221
|
||||
func JoinURLPath(a, b *url.URL) (path, rawpath string) {
|
||||
if a.RawPath == "" && b.RawPath == "" {
|
||||
return singleJoiningSlash(a.Path, b.Path), ""
|
||||
}
|
||||
return http.StatusText(statusCode)
|
||||
|
||||
// Same as singleJoiningSlash, but uses EscapedPath to determine
|
||||
// whether a slash should be added
|
||||
apath := a.EscapedPath()
|
||||
bpath := b.EscapedPath()
|
||||
|
||||
aslash := strings.HasSuffix(apath, "/")
|
||||
bslash := strings.HasPrefix(bpath, "/")
|
||||
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a.Path + b.Path[1:], apath + bpath[1:]
|
||||
case !aslash && !bslash:
|
||||
return a.Path + "/" + b.Path, apath + "/" + bpath
|
||||
}
|
||||
return a.Path + b.Path, apath + bpath
|
||||
}
|
||||
|
||||
func singleJoiningSlash(a, b string) string {
|
||||
aslash := strings.HasSuffix(a, "/")
|
||||
bslash := strings.HasPrefix(b, "/")
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a + b[1:]
|
||||
case !aslash && !bslash:
|
||||
return a + "/" + b
|
||||
}
|
||||
return a + b
|
||||
}
|
||||
|
|
102
pkg/proxy/httputil/proxy_test.go
Normal file
102
pkg/proxy/httputil/proxy_test.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package httputil
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/traefik/traefik/v3/pkg/testhelpers"
|
||||
)
|
||||
|
||||
func Test_directorBuilder(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
target *url.URL
|
||||
passHostHeader bool
|
||||
preservePath bool
|
||||
incomingURL string
|
||||
expectedScheme string
|
||||
expectedHost string
|
||||
expectedPath string
|
||||
expectedRawPath string
|
||||
expectedQuery string
|
||||
}{
|
||||
{
|
||||
name: "Basic proxy",
|
||||
target: testhelpers.MustParseURL("http://example.com"),
|
||||
passHostHeader: false,
|
||||
preservePath: false,
|
||||
incomingURL: "http://localhost/test?param=value",
|
||||
expectedScheme: "http",
|
||||
expectedHost: "example.com",
|
||||
expectedPath: "/test",
|
||||
expectedQuery: "param=value",
|
||||
},
|
||||
{
|
||||
name: "HTTPS target",
|
||||
target: testhelpers.MustParseURL("https://secure.example.com"),
|
||||
passHostHeader: false,
|
||||
preservePath: false,
|
||||
incomingURL: "http://localhost/secure",
|
||||
expectedScheme: "https",
|
||||
expectedHost: "secure.example.com",
|
||||
expectedPath: "/secure",
|
||||
},
|
||||
{
|
||||
name: "PassHostHeader",
|
||||
target: testhelpers.MustParseURL("http://example.com"),
|
||||
passHostHeader: true,
|
||||
preservePath: false,
|
||||
incomingURL: "http://original.host/test",
|
||||
expectedScheme: "http",
|
||||
expectedHost: "original.host",
|
||||
expectedPath: "/test",
|
||||
},
|
||||
{
|
||||
name: "Preserve path",
|
||||
target: testhelpers.MustParseURL("http://example.com/base"),
|
||||
passHostHeader: false,
|
||||
preservePath: true,
|
||||
incomingURL: "http://localhost/foo%2Fbar",
|
||||
expectedScheme: "http",
|
||||
expectedHost: "example.com",
|
||||
expectedPath: "/base/foo/bar",
|
||||
expectedRawPath: "/base/foo%2Fbar",
|
||||
},
|
||||
{
|
||||
name: "Handle semicolons in query",
|
||||
target: testhelpers.MustParseURL("http://example.com"),
|
||||
passHostHeader: false,
|
||||
preservePath: false,
|
||||
incomingURL: "http://localhost/test?param1=value1;param2=value2",
|
||||
expectedScheme: "http",
|
||||
expectedHost: "example.com",
|
||||
expectedPath: "/test",
|
||||
expectedQuery: "param1=value1¶m2=value2",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
director := directorBuilder(test.target, test.passHostHeader, test.preservePath)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, test.incomingURL, http.NoBody)
|
||||
director(req)
|
||||
|
||||
assert.Equal(t, test.expectedScheme, req.URL.Scheme)
|
||||
assert.Equal(t, test.expectedHost, req.Host)
|
||||
assert.Equal(t, test.expectedPath, req.URL.Path)
|
||||
assert.Equal(t, test.expectedRawPath, req.URL.RawPath)
|
||||
assert.Equal(t, test.expectedQuery, req.URL.RawQuery)
|
||||
assert.Empty(t, req.RequestURI)
|
||||
assert.Equal(t, "HTTP/1.1", req.Proto)
|
||||
assert.Equal(t, 1, req.ProtoMajor)
|
||||
assert.Equal(t, 1, req.ProtoMinor)
|
||||
assert.False(t, !test.passHostHeader && req.Host != req.URL.Host)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -298,9 +298,8 @@ func TestWebSocketRequestWithHeadersInResponseWriter(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
p, err := NewProxyBuilder(transportManager, nil).Build("default@internal", testhelpers.MustParseURL(srv.URL), false, true, 0)
|
||||
p, err := NewProxyBuilder(transportManager, nil).Build("default@internal", testhelpers.MustParseURL(srv.URL), false, true, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
req.URL = testhelpers.MustParseURL(srv.URL)
|
||||
w.Header().Set("HEADER-KEY", "HEADER-VALUE")
|
||||
|
@ -355,9 +354,8 @@ func TestWebSocketUpgradeFailed(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
p, err := NewProxyBuilder(transportManager, nil).Build("default@internal", testhelpers.MustParseURL(srv.URL), false, true, 0)
|
||||
p, err := NewProxyBuilder(transportManager, nil).Build("default@internal", testhelpers.MustParseURL(srv.URL), false, true, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
path := req.URL.Path // keep the original path
|
||||
|
||||
|
@ -588,7 +586,7 @@ func createProxyWithForwarder(t *testing.T, uri string, transport http.RoundTrip
|
|||
roundTrippers: map[string]http.RoundTripper{"fwd": transport},
|
||||
}
|
||||
|
||||
p, err := NewProxyBuilder(transportManager, nil).Build("fwd", u, false, true, 0)
|
||||
p, err := NewProxyBuilder(transportManager, nil).Build("fwd", u, false, true, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
@ -45,7 +45,7 @@ func (b *SmartBuilder) Update(newConfigs map[string]*dynamic.ServersTransport) {
|
|||
}
|
||||
|
||||
// Build builds an HTTP proxy for the given URL using the ServersTransport with the given name.
|
||||
func (b *SmartBuilder) Build(configName string, targetURL *url.URL, shouldObserve, passHostHeader bool, flushInterval time.Duration) (http.Handler, error) {
|
||||
func (b *SmartBuilder) Build(configName string, targetURL *url.URL, shouldObserve, passHostHeader, preservePath bool, flushInterval time.Duration) (http.Handler, error) {
|
||||
serversTransport, err := b.transportManager.Get(configName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting ServersTransport: %w", err)
|
||||
|
@ -55,7 +55,7 @@ func (b *SmartBuilder) Build(configName string, targetURL *url.URL, shouldObserv
|
|||
// For the https scheme we cannot guess if the backend communication will use HTTP2,
|
||||
// thus we check if HTTP/2 is disabled to use the fast proxy implementation when this is possible.
|
||||
if targetURL.Scheme == "h2c" || (targetURL.Scheme == "https" && !serversTransport.DisableHTTP2) {
|
||||
return b.proxyBuilder.Build(configName, targetURL, shouldObserve, passHostHeader, flushInterval)
|
||||
return b.proxyBuilder.Build(configName, targetURL, shouldObserve, passHostHeader, preservePath, flushInterval)
|
||||
}
|
||||
return b.fastProxyBuilder.Build(configName, targetURL, passHostHeader)
|
||||
return b.fastProxyBuilder.Build(configName, targetURL, passHostHeader, preservePath)
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ func TestSmartBuilder_Build(t *testing.T) {
|
|||
httpProxyBuilder := httputil.NewProxyBuilder(transportManager, nil)
|
||||
proxyBuilder := NewSmartBuilder(transportManager, httpProxyBuilder, test.fastProxyConfig)
|
||||
|
||||
proxyHandler, err := proxyBuilder.Build("test", targetURL, false, false, time.Second)
|
||||
proxyHandler, err := proxyBuilder.Build("test", targetURL, false, false, false, time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
|
|
|
@ -897,7 +897,7 @@ func BenchmarkService(b *testing.B) {
|
|||
|
||||
type proxyBuilderMock struct{}
|
||||
|
||||
func (p proxyBuilderMock) Build(_ string, _ *url.URL, _, _ bool, _ time.Duration) (http.Handler, error) {
|
||||
func (p proxyBuilderMock) Build(_ string, _ *url.URL, _, _, _ bool, _ time.Duration) (http.Handler, error) {
|
||||
return http.HandlerFunc(func(responseWriter http.ResponseWriter, req *http.Request) {}), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -254,7 +254,7 @@ func TestInternalServices(t *testing.T) {
|
|||
|
||||
type proxyBuilderMock struct{}
|
||||
|
||||
func (p proxyBuilderMock) Build(_ string, _ *url.URL, _, _ bool, _ time.Duration) (http.Handler, error) {
|
||||
func (p proxyBuilderMock) Build(_ string, _ *url.URL, _, _, _ bool, _ time.Duration) (http.Handler, error) {
|
||||
return http.HandlerFunc(func(responseWriter http.ResponseWriter, req *http.Request) {}), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ const (
|
|||
|
||||
// ProxyBuilder builds reverse proxy handlers.
|
||||
type ProxyBuilder interface {
|
||||
Build(cfgName string, targetURL *url.URL, shouldObserve, passHostHeader bool, flushInterval time.Duration) (http.Handler, error)
|
||||
Build(cfgName string, targetURL *url.URL, shouldObserve, passHostHeader, preservePath bool, flushInterval time.Duration) (http.Handler, error)
|
||||
Update(configs map[string]*dynamic.ServersTransport)
|
||||
}
|
||||
|
||||
|
@ -338,7 +338,7 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
|
|||
qualifiedSvcName := provider.GetQualifiedName(ctx, serviceName)
|
||||
|
||||
shouldObserve := m.observabilityMgr.ShouldAddTracing(qualifiedSvcName) || m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName)
|
||||
proxy, err := m.proxyBuilder.Build(service.ServersTransport, target, shouldObserve, passHostHeader, flushInterval)
|
||||
proxy, err := m.proxyBuilder.Build(service.ServersTransport, target, shouldObserve, passHostHeader, server.PreservePath, flushInterval)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building proxy for server URL %s: %w", server.URL, err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue