-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add forwarding headers on incoming requests, add travis (#6)
This change injects the X-Forwarded-Proto and X-Forwarded-Port headers to all incoming requests to the proxy. Useful for applications like Jenkins to behave well behind SSL proxies. This change also adds a Travis CI build.
- Loading branch information
1 parent
93013be
commit a4f05cd
Showing
7 changed files
with
141 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
language: go | ||
go: | ||
- "1.10.x" | ||
- "1.11.x" | ||
install: | ||
- go get -u github.com/golang/dep/cmd/dep | ||
script: | ||
- make | ||
- make test |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ build: | |
|
||
.PHONY: test | ||
test: | ||
go test ./... | ||
go test -v ./... | ||
|
||
.PHONY: run | ||
run: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package reverseproxy | ||
|
||
import ( | ||
"net/http" | ||
"net/http/httputil" | ||
"net/url" | ||
"strings" | ||
) | ||
|
||
// Build initializes and returns a new ReverseProxy instance suitable for SSL proxying | ||
func Build(toURL *url.URL) *httputil.ReverseProxy { | ||
localProxy := &httputil.ReverseProxy{} | ||
addProxyHeaders := func(req *http.Request) { | ||
req.Header.Set(http.CanonicalHeaderKey("X-Forwarded-Proto"), "https") | ||
req.Header.Set(http.CanonicalHeaderKey("X-Forwarded-Port"), "443") // TODO: inherit another port if needed | ||
} | ||
localProxy.Director = newDirector(toURL, addProxyHeaders) | ||
|
||
return localProxy | ||
} | ||
|
||
// newDirector creates a base director that should be exactly what http.NewSingleHostReverseProxy() creates, but allows | ||
// for the caller to supply and extraDirector function to decorate to request to the downstream server | ||
func newDirector(target *url.URL, extraDirector func(*http.Request)) func(*http.Request) { | ||
targetQuery := target.RawQuery | ||
return func(req *http.Request) { | ||
req.URL.Scheme = target.Scheme | ||
req.URL.Host = target.Host | ||
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path) | ||
if targetQuery == "" || req.URL.RawQuery == "" { | ||
req.URL.RawQuery = targetQuery + req.URL.RawQuery | ||
} else { | ||
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery | ||
} | ||
if _, ok := req.Header["User-Agent"]; !ok { | ||
// explicitly disable User-Agent so it's not set to default value | ||
req.Header.Set("User-Agent", "") | ||
} | ||
|
||
if extraDirector != nil { | ||
extraDirector(req) | ||
} | ||
} | ||
} | ||
|
||
// singleJoiningSlash is a utility function that adds a single slash to a URL where appropriate, copied from | ||
// the httputil package | ||
// TODO: add test to ensure behavior does not diverge from httputil's implementation, as per Rob Pike's proverbs | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package reverseproxy | ||
|
||
import ( | ||
"net/http" | ||
"net/http/httptest" | ||
"net/http/httputil" | ||
"net/url" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
// TestBuild_AddHeaders tests that Build's returned ReverseProxy Director adds the proper request headers | ||
func TestBuild_AddHeaders(t *testing.T) { | ||
u, err := url.Parse("http://127.0.0.1") | ||
assert.Nil(t, err, "error should be nil") | ||
proxy := Build(u) | ||
assert.NotNil(t, proxy, "Build should not return nil") | ||
|
||
req := httptest.NewRequest("GET", "/test", nil) | ||
proxy.Director(req) | ||
|
||
// Check that headers were added to req | ||
assert.Equal(t, req.Header.Get(http.CanonicalHeaderKey("X-Forwarded-Proto")), "https", | ||
"X-Forwarded-Proto should be present") | ||
assert.Equal(t, req.Header.Get(http.CanonicalHeaderKey("X-Forwarded-Port")), "443", | ||
"X-Forwarded-Port should be present") | ||
|
||
} | ||
|
||
func TestNewDirector(t *testing.T) { | ||
u, err := url.Parse("http://127.0.0.1") | ||
assert.Nil(t, err, "error should be nil") | ||
director := newDirector(u, nil) | ||
|
||
defaultProxy := httputil.NewSingleHostReverseProxy(u) | ||
defaultDirector := defaultProxy.Director | ||
|
||
expectedReq := httptest.NewRequest("GET", "/test", nil) | ||
testReq := httptest.NewRequest("GET", "/test", nil) | ||
|
||
defaultDirector(expectedReq) | ||
director(testReq) | ||
|
||
assert.EqualValues(t, expectedReq, testReq, | ||
"default proxy and package directors should modify the request in the same way") | ||
// TODO: add more test cases | ||
} |