Skip to content

Commit

Permalink
httputil: add ModifyRequest hook
Browse files Browse the repository at this point in the history
Adds a ModifyRequest hook (field) to the ReverseProxy struct.

It aims to be a simplified and more recommended hook that you would want
to use if you plan to retain default reverse proxy behavior from Director,
but further extend it for specific cases a la vhost reverse proxying.
  • Loading branch information
syndbg committed Apr 10, 2019
1 parent c349505 commit f42207e
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 3 deletions.
19 changes: 16 additions & 3 deletions src/net/http/httputil/reverseproxy.go
Expand Up @@ -30,9 +30,17 @@ type ReverseProxy struct {
// using Transport. Its response is then copied
// back to the original client unmodified.
// Director must not access the provided Request
// after returning.
// after returning. Default Director implements standard reverse-proxy
// behavior that retains scheme, host, path and query params from target URL.
// Also retains User-Agent header if set.
Director func(*http.Request)

// ModifyRequest must be a function which is called by Director
// that modifies the request into a new request to be sent using Transport.
// If you plan to retain default reverse proxy behavior from Director,
// but further extend it for specific cases a la vhost reverse proxying.
ModifyRequest func(*http.Request)

// The transport used to perform proxy requests.
// If nil, http.DefaultTransport is used.
Transport http.RoundTripper
Expand Down Expand Up @@ -107,7 +115,8 @@ func singleJoiningSlash(a, b string) string {
// Director policy.
func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {
targetQuery := target.RawQuery
director := func(req *http.Request) {
proxy := &ReverseProxy{}
proxy.Director = func(req *http.Request) {
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
Expand All @@ -120,8 +129,12 @@ func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {
// explicitly disable User-Agent so it's not set to default value
req.Header.Set("User-Agent", "")
}

if proxy.ModifyRequest != nil {
proxy.ModifyRequest(req)
}
}
return &ReverseProxy{Director: director}
return proxy
}

func copyHeader(dst, src http.Header) {
Expand Down
33 changes: 33 additions & 0 deletions src/net/http/httputil/reverseproxy_test.go
Expand Up @@ -896,6 +896,39 @@ func TestClonesRequestHeaders(t *testing.T) {
if req.Header.Get("X-Forwarded-For") != "" {
t.Error("X-Forward-For header mutation modified caller's request")
}
}

func TestModifyRequest(t *testing.T) {
log.SetOutput(ioutil.Discard)
defer log.SetOutput(os.Stderr)
req, _ := http.NewRequest("GET", "http://foo.tld/", nil)
req.RemoteAddr = "1.2.3.4:56789"
url, err := url.Parse("http://foo.tld/")
if err != nil {
t.Error(err)
}

rp := NewSingleHostReverseProxy(url)
rp.ModifyRequest = func(req *http.Request) {
req.Header.Set("From-Modify-Request", "1")
}

rp.Transport = roundTripperFunc(func(req *http.Request) (*http.Response, error) {
if v := req.Header.Get("From-Modify-Request"); v != "1" {
t.Errorf("From-Modify-Request value = %q; want 1", v)
}
return nil, io.EOF
})

rp.ServeHTTP(httptest.NewRecorder(), req)

if req.Header.Get("From-Modify-Request") == "1" {
t.Error("ModifyRequest header mutation modified caller's request")
}

if req.Header.Get("X-Forwarded-For") != "" {
t.Error("X-Forward-For header mutation modified caller's request")
}

}

Expand Down

0 comments on commit f42207e

Please sign in to comment.