Skip to content

Commit

Permalink
httputil: accumulate X-Forwarded-For header info
Browse files Browse the repository at this point in the history
If the X-Forwarded-For header already exists on a request, we
should append our client's IP to it after a comma+space instead
of overwriting it.

Fixes #3846.

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6448053
  • Loading branch information
bpowers authored and bradfitz committed Jul 30, 2012
1 parent ad058ca commit 7520f0b
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 2 deletions.
10 changes: 8 additions & 2 deletions src/pkg/net/http/httputil/reverseproxy.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -106,8 +106,14 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
outreq.Header.Del("Connection") outreq.Header.Del("Connection")
} }


if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
outreq.Header.Set("X-Forwarded-For", clientIp) // If we aren't the first proxy retain prior
// X-Forwarded-For information as a comma+space
// separated list and fold multiple headers into one.
if prior, ok := outreq.Header["X-Forwarded-For"]; ok {
clientIP = strings.Join(prior, ", ") + ", " + clientIP
}
outreq.Header.Set("X-Forwarded-For", clientIP)
} }


res, err := transport.RoundTrip(outreq) res, err := transport.RoundTrip(outreq)
Expand Down
42 changes: 42 additions & 0 deletions src/pkg/net/http/httputil/reverseproxy_test.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"strings"
"testing" "testing"
"time" "time"
) )
Expand Down Expand Up @@ -71,6 +72,47 @@ func TestReverseProxy(t *testing.T) {
} }
} }


func TestXForwardedFor(t *testing.T) {
const prevForwardedFor = "client ip"
const backendResponse = "I am the backend"
const backendStatus = 404
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Forwarded-For") == "" {
t.Errorf("didn't get X-Forwarded-For header")
}
if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) {
t.Errorf("X-Forwarded-For didn't contain prior data")
}
w.WriteHeader(backendStatus)
w.Write([]byte(backendResponse))
}))
defer backend.Close()
backendURL, err := url.Parse(backend.URL)
if err != nil {
t.Fatal(err)
}
proxyHandler := NewSingleHostReverseProxy(backendURL)
frontend := httptest.NewServer(proxyHandler)
defer frontend.Close()

getReq, _ := http.NewRequest("GET", frontend.URL, nil)
getReq.Host = "some-name"
getReq.Header.Set("Connection", "close")
getReq.Header.Set("X-Forwarded-For", prevForwardedFor)
getReq.Close = true
res, err := http.DefaultClient.Do(getReq)
if err != nil {
t.Fatalf("Get: %v", err)
}
if g, e := res.StatusCode, backendStatus; g != e {
t.Errorf("got res.StatusCode %d; expected %d", g, e)
}
bodyBytes, _ := ioutil.ReadAll(res.Body)
if g, e := string(bodyBytes), backendResponse; g != e {
t.Errorf("got body %q; expected %q", g, e)
}
}

var proxyQueryTests = []struct { var proxyQueryTests = []struct {
baseSuffix string // suffix to add to backend URL baseSuffix string // suffix to add to backend URL
reqSuffix string // suffix to add to frontend's request URL reqSuffix string // suffix to add to frontend's request URL
Expand Down

0 comments on commit 7520f0b

Please sign in to comment.