Skip to content

Commit

Permalink
Issue #304: Add X-Forwarded-Prefix header
Browse files Browse the repository at this point in the history
This patch adds the X-Forwarded-Prefix header to
upstream requests which contains the unmodified
url path from the original request.

Fixes #304
  • Loading branch information
magiconair committed Jun 7, 2017
1 parent ee3fc74 commit 641fd2e
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 24 deletions.
6 changes: 5 additions & 1 deletion proxy/http_headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
// * ClientIPHeader != "": Set header with that name to <remote ip>
// * TLS connection: Set header with name from `cfg.TLSHeader` to `cfg.TLSHeaderValue`
//
func addHeaders(r *http.Request, cfg config.Proxy) error {
func addHeaders(r *http.Request, cfg config.Proxy, stripPath string) error {
remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return errors.New("cannot parse " + r.RemoteAddr)
Expand Down Expand Up @@ -66,6 +66,10 @@ func addHeaders(r *http.Request, cfg config.Proxy) error {
r.Header.Set("X-Forwarded-Port", localPort(r))
}

if stripPath != "" {
r.Header.Set("X-Forwarded-Prefix", stripPath)
}

fwd := r.Header.Get("Forwarded")
if fwd == "" {
fwd = "for=" + remoteIP + "; proto=" + proto
Expand Down
43 changes: 33 additions & 10 deletions proxy/http_headers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,39 @@ import (

func TestAddHeaders(t *testing.T) {
tests := []struct {
desc string
r *http.Request
cfg config.Proxy
hdrs http.Header
err string
desc string
r *http.Request
cfg config.Proxy
strip string
hdrs http.Header
err string
}{
{"error",
&http.Request{RemoteAddr: "1.2.3.4"},
config.Proxy{},
"",
http.Header{},
"cannot parse 1.2.3.4",
},

{"http request",
&http.Request{RemoteAddr: "1.2.3.4:5555"},
config.Proxy{},
"/foo",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=http"},
"X-Forwarded-Proto": []string{"http"},
"X-Forwarded-Port": []string{"80"},
"X-Real-Ip": []string{"1.2.3.4"},
"Forwarded": []string{"for=1.2.3.4; proto=http"},
"X-Forwarded-Proto": []string{"http"},
"X-Forwarded-Port": []string{"80"},
"X-Forwarded-Prefix": []string{"/foo"},
"X-Real-Ip": []string{"1.2.3.4"},
},
"",
},

{"https request",
&http.Request{RemoteAddr: "1.2.3.4:5555", TLS: &tls.ConnectionState{}},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=https"},
"X-Forwarded-Proto": []string{"https"},
Expand All @@ -51,6 +56,7 @@ func TestAddHeaders(t *testing.T) {
{"ws request",
&http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Upgrade": {"websocket"}}},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=ws"},
"Upgrade": []string{"websocket"},
Expand All @@ -65,6 +71,7 @@ func TestAddHeaders(t *testing.T) {
{"wss request",
&http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Upgrade": {"websocket"}}, TLS: &tls.ConnectionState{}},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=wss"},
"Upgrade": []string{"websocket"},
Expand All @@ -79,6 +86,7 @@ func TestAddHeaders(t *testing.T) {
{"set client ip header",
&http.Request{RemoteAddr: "1.2.3.4:5555"},
config.Proxy{ClientIPHeader: "Client-IP"},
"",
http.Header{
"Client-Ip": []string{"1.2.3.4"},
"Forwarded": []string{"for=1.2.3.4; proto=http"},
Expand All @@ -92,6 +100,7 @@ func TestAddHeaders(t *testing.T) {
{"set Forwarded with localIP",
&http.Request{RemoteAddr: "1.2.3.4:5555"},
config.Proxy{LocalIP: "5.6.7.8"},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=http; by=5.6.7.8"},
"X-Forwarded-Proto": []string{"http"},
Expand All @@ -104,6 +113,7 @@ func TestAddHeaders(t *testing.T) {
{"set Forwarded with localIP for https",
&http.Request{RemoteAddr: "1.2.3.4:5555", TLS: &tls.ConnectionState{}},
config.Proxy{LocalIP: "5.6.7.8"},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=https; by=5.6.7.8"},
"X-Forwarded-Proto": []string{"https"},
Expand All @@ -116,6 +126,7 @@ func TestAddHeaders(t *testing.T) {
{"extend Forwarded with localIP",
&http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Forwarded": {"for=9.9.9.9; proto=http; by=8.8.8.8"}}},
config.Proxy{LocalIP: "5.6.7.8"},
"",
http.Header{
"Forwarded": []string{"for=9.9.9.9; proto=http; by=8.8.8.8; by=5.6.7.8"},
"X-Forwarded-Proto": []string{"http"},
Expand All @@ -128,6 +139,7 @@ func TestAddHeaders(t *testing.T) {
{"set tls header",
&http.Request{RemoteAddr: "1.2.3.4:5555", TLS: &tls.ConnectionState{}},
config.Proxy{TLSHeader: "Secure"},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=https"},
"Secure": []string{""},
Expand All @@ -141,6 +153,7 @@ func TestAddHeaders(t *testing.T) {
{"set tls header with value",
&http.Request{RemoteAddr: "1.2.3.4:5555", TLS: &tls.ConnectionState{}},
config.Proxy{TLSHeader: "Secure", TLSHeaderValue: "true"},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=https"},
"Secure": []string{"true"},
Expand All @@ -154,6 +167,7 @@ func TestAddHeaders(t *testing.T) {
{"overwrite tls header for https, when set",
&http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Secure": []string{"on"}}, TLS: &tls.ConnectionState{}},
config.Proxy{TLSHeader: "Secure", TLSHeaderValue: "true"},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=https"},
"Secure": []string{"true"},
Expand All @@ -167,6 +181,7 @@ func TestAddHeaders(t *testing.T) {
{"drop tls header for http, when set",
&http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Secure": []string{"on"}}},
config.Proxy{TLSHeader: "Secure", TLSHeaderValue: "true"},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=http"},
"X-Forwarded-Proto": []string{"http"},
Expand All @@ -179,6 +194,7 @@ func TestAddHeaders(t *testing.T) {
{"do not overwrite X-Forwarded-Proto, if present",
&http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"X-Forwarded-Proto": {"some value"}}},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=some value"},
"X-Forwarded-Proto": []string{"some value"},
Expand All @@ -191,6 +207,7 @@ func TestAddHeaders(t *testing.T) {
{"set scheme from X-Forwarded-Proto, if present and Forwarded is missing",
&http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"X-Forwarded-Proto": {"some value"}}},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=some value"},
"X-Forwarded-Proto": []string{"some value"},
Expand All @@ -203,6 +220,7 @@ func TestAddHeaders(t *testing.T) {
{"set scheme from Forwarded, if present and X-Forwarded-Proto is missing",
&http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Forwarded": {"for=1.2.3.4; proto=some value"}}},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=some value"},
"X-Forwarded-Proto": []string{"some value"},
Expand All @@ -221,6 +239,7 @@ func TestAddHeaders(t *testing.T) {
},
},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=some value"},
"X-Forwarded-Proto": []string{"other value"},
Expand All @@ -233,6 +252,7 @@ func TestAddHeaders(t *testing.T) {
{"set X-Forwarded-Port from Host",
&http.Request{RemoteAddr: "1.2.3.4:5555", Host: "5.6.7.8:1234"},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=http"},
"X-Forwarded-Proto": []string{"http"},
Expand All @@ -245,6 +265,7 @@ func TestAddHeaders(t *testing.T) {
{"set X-Forwarded-Port from Host for https",
&http.Request{RemoteAddr: "1.2.3.4:5555", Host: "5.6.7.8:1234", TLS: &tls.ConnectionState{}},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=https"},
"X-Forwarded-Proto": []string{"https"},
Expand All @@ -257,6 +278,7 @@ func TestAddHeaders(t *testing.T) {
{"do not overwrite X-Forwarded-Port header, if present",
&http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"X-Forwarded-Port": {"4444"}}},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=http"},
"X-Forwarded-Proto": []string{"http"},
Expand All @@ -269,6 +291,7 @@ func TestAddHeaders(t *testing.T) {
{"do not overwrite X-Real-Ip, if present",
&http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"X-Real-Ip": {"6.6.6.6"}}},
config.Proxy{},
"",
http.Header{
"Forwarded": []string{"for=1.2.3.4; proto=http"},
"X-Forwarded-Proto": []string{"http"},
Expand All @@ -287,7 +310,7 @@ func TestAddHeaders(t *testing.T) {
tt.r.Header = http.Header{}
}

err := addHeaders(tt.r, tt.cfg)
err := addHeaders(tt.r, tt.cfg, tt.strip)
if err != nil {
if got, want := err.Error(), tt.err; got != want {
t.Fatalf("%d: %s\ngot %q\nwant %q", i, tt.desc, got, want)
Expand Down
26 changes: 13 additions & 13 deletions proxy/http_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,6 @@ func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

if err := addHeaders(r, p.Config); err != nil {
http.Error(w, "cannot parse "+r.RemoteAddr, http.StatusInternalServerError)
return
}

if p.Config.RequestID != "" {
id := p.UUID
if id == nil {
id = uuid.NewUUID
}
r.Header.Set(p.Config.RequestID, id())
}

// build the request url since r.URL will get modified
// by the reverse proxy and contains only the RequestURI anyway
requestURL := &url.URL{
Expand Down Expand Up @@ -112,6 +99,19 @@ func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
targetURL.Path = targetURL.Path[len(t.StripPath):]
}

if err := addHeaders(r, p.Config, t.StripPath); err != nil {
http.Error(w, "cannot parse "+r.RemoteAddr, http.StatusInternalServerError)
return
}

if p.Config.RequestID != "" {
id := p.UUID
if id == nil {
id = uuid.NewUUID
}
r.Header.Set(p.Config.RequestID, id())
}

upgrade, accept := r.Header.Get("Upgrade"), r.Header.Get("Accept")

tr := p.Transport
Expand Down

0 comments on commit 641fd2e

Please sign in to comment.