diff options
| author | Damien Neil <dneil@google.com> | 2022-05-18 16:23:28 -0700 |
|---|---|---|
| committer | Damien Neil <dneil@google.com> | 2022-08-16 20:01:36 +0000 |
| commit | a55793835f16d0242be18aff4ec0bd13494175bd (patch) | |
| tree | d176c1b120a56875f1f33cf2c0b81f91f0084ff4 /src/net/http/httputil/reverseproxy_test.go | |
| parent | 68005592b38027490a08972f13269406b2556a07 (diff) | |
| download | go-a55793835f16d0242be18aff4ec0bd13494175bd.tar.xz | |
net/http/httputil: add ReverseProxy.Rewrite
Add a new Rewrite hook to ReverseProxy, superseding the Director hook.
Director does not distinguish between the inbound and outbound request,
which makes it possible for headers added by Director to be inadvertently
removed before forwarding if they are listed in the inbound request's
Connection header. Rewrite accepts a value containing the inbound
and outbound requests, with hop-by-hop headers already removed from
the outbound request, avoiding this problem.
ReverseProxy's appends the client IP to the inbound X-Forwarded-For
header by default. Users must manually delete untrusted X-Forwarded-For
values. When used with a Rewrite hook, ReverseProxy now strips
X-Forwarded-* headers by default.
NewSingleHostReverseProxy creates a proxy that does not rewrite the
Host header of inbound requests. Changing this behavior is
cumbersome, as it requires wrapping the Director function created
by NewSingleHostReverseProxy. The Rewrite hook's ProxyRequest
parameter provides a SetURL method that provides equivalent
functionality to NewSingleHostReverseProxy, rewrites the Host
header by default, and can be more easily extended with additional
customizations.
Fixes #28168.
Fixes #50580.
Fixes #53002.
Change-Id: Ib84e2fdd1d52c610e3887af66f517d4a74e594d0
Reviewed-on: https://go-review.googlesource.com/c/go/+/407214
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Damien Neil <dneil@google.com>
Diffstat (limited to 'src/net/http/httputil/reverseproxy_test.go')
| -rw-r--r-- | src/net/http/httputil/reverseproxy_test.go | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go index 3090e37582..f8157e9435 100644 --- a/src/net/http/httputil/reverseproxy_test.go +++ b/src/net/http/httputil/reverseproxy_test.go @@ -409,6 +409,46 @@ func TestXForwardedFor_Omit(t *testing.T) { res.Body.Close() } +func TestReverseProxyRewriteStripsForwarded(t *testing.T) { + headers := []string{ + "Forwarded", + "X-Forwarded-For", + "X-Forwarded-Host", + "X-Forwarded-Proto", + } + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + for _, h := range headers { + if v := r.Header.Get(h); v != "" { + t.Errorf("got %v header: %q", h, v) + } + } + })) + defer backend.Close() + backendURL, err := url.Parse(backend.URL) + if err != nil { + t.Fatal(err) + } + proxyHandler := &ReverseProxy{ + Rewrite: func(r *ProxyRequest) { + r.SetURL(backendURL) + }, + } + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + + getReq, _ := http.NewRequest("GET", frontend.URL, nil) + getReq.Host = "some-name" + getReq.Close = true + for _, h := range headers { + getReq.Header.Set(h, "x") + } + res, err := frontend.Client().Do(getReq) + if err != nil { + t.Fatalf("Get: %v", err) + } + res.Body.Close() +} + var proxyQueryTests = []struct { baseSuffix string // suffix to add to backend URL reqSuffix string // suffix to add to frontend's request URL @@ -1523,6 +1563,40 @@ func TestUnannouncedTrailer(t *testing.T) { } +func TestSetURL(t *testing.T) { + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(r.Host)) + })) + defer backend.Close() + backendURL, err := url.Parse(backend.URL) + if err != nil { + t.Fatal(err) + } + proxyHandler := &ReverseProxy{ + Rewrite: func(r *ProxyRequest) { + r.SetURL(backendURL) + }, + } + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + frontendClient := frontend.Client() + + res, err := frontendClient.Get(frontend.URL) + if err != nil { + t.Fatalf("Get: %v", err) + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + t.Fatalf("Reading body: %v", err) + } + + if got, want := string(body), backendURL.Host; got != want { + t.Errorf("backend got Host %q, want %q", got, want) + } +} + func TestSingleJoinSlash(t *testing.T) { tests := []struct { slasha string @@ -1572,3 +1646,28 @@ func TestJoinURLPath(t *testing.T) { } } } + +func TestReverseProxyRewriteReplacesOut(t *testing.T) { + const content = "response_content" + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(content)) + })) + defer backend.Close() + proxyHandler := &ReverseProxy{ + Rewrite: func(r *ProxyRequest) { + r.Out, _ = http.NewRequest("GET", backend.URL, nil) + }, + } + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + + res, err := frontend.Client().Get(frontend.URL) + if err != nil { + t.Fatalf("Get: %v", err) + } + defer res.Body.Close() + body, _ := io.ReadAll(res.Body) + if got, want := string(body), content; got != want { + t.Errorf("got response %q, want %q", got, want) + } +} |
