Skip to content

Commit eca3979

Browse files
authored
Add test for presence of X-Forwarded-For and X-Forwarded-Proto HTTP headers (#299)
* add test for presence of X-Forwarded-For and X-Forwarded-Proto HTTP headers Presence and content of `X-Forwarded-For` and `X-Forwarded-Proto` is checked in requests made to the origin server. * add test for X-Forwarded-Proto for HTTP requests * set X-Forwarded-Proto for requests forwarded to the origin server * use insecure HTTP client for performing test requests Generated cert is not trusted by the test runner. * add comment for parseDumpedHTTPHeadersFromBody * accept ::1 as valid X-Forwarded-For value in tests
1 parent 6a7372b commit eca3979

File tree

5 files changed

+63
-3
lines changed

5 files changed

+63
-3
lines changed

cmd/puma-dev/main_test.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"crypto/sha1"
5+
"crypto/tls"
56
"encoding/json"
67
"errors"
78
"fmt"
@@ -144,7 +145,14 @@ func getURLWithHost(t *testing.T, url string, host string) string {
144145
req, _ := http.NewRequest("GET", url, nil)
145146
req.Host = host
146147

147-
resp, err := http.DefaultClient.Do(req)
148+
// we don't care about checking certs in tests
149+
// the generated cert will not be trusted by the test runner
150+
insecureTransport := &http.Transport{
151+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
152+
}
153+
insecureClient := &http.Client{Transport: insecureTransport}
154+
155+
resp, err := insecureClient.Do(req)
148156

149157
if err != nil {
150158
assert.FailNow(t, err.Error())
@@ -157,6 +165,20 @@ func getURLWithHost(t *testing.T, url string, host string) string {
157165
return strings.TrimSpace(string(bodyBytes))
158166
}
159167

168+
// function to parse dumped request headers sent by puma-dev to the origin server
169+
// for details see etc/rack-request-headers-dump/config.ru
170+
func parseDumpedHTTPHeadersFromBody(body string) map[string]string {
171+
dumpedHeadersLines := strings.Split(body, "\n")
172+
173+
dumpedHeaders := make(map[string]string)
174+
for _, pairString := range dumpedHeadersLines {
175+
pair := strings.SplitN(pairString, " ", 2)
176+
dumpedHeaders[pair[0]] = pair[1]
177+
}
178+
179+
return dumpedHeaders
180+
}
181+
160182
func pollForEvent(t *testing.T, app string, event string, reason string) error {
161183
return retry.Do(
162184
func() error {
@@ -293,4 +315,24 @@ func runPlatformAgnosticTestScenarios(t *testing.T) {
293315

294316
assert.Equal(t, "rack wuz here", getURLWithHost(t, reqURL, statusHost))
295317
})
318+
319+
t.Run("request-headers-dump contains X-Forwarded-* headers with http", func(t *testing.T) {
320+
reqURL := fmt.Sprintf("http://localhost:%d/", *fHTTPPort)
321+
statusHost := "request-headers-dump"
322+
323+
dumpedHeaders := parseDumpedHTTPHeadersFromBody(getURLWithHost(t, reqURL, statusHost))
324+
325+
assert.Regexp(t, `127\.0\.0\.1|::1`, dumpedHeaders["HTTP_X_FORWARDED_FOR"])
326+
assert.Equal(t, "http", dumpedHeaders["HTTP_X_FORWARDED_PROTO"])
327+
})
328+
329+
t.Run("request-headers-dump contains X-Forwarded-* headers with https", func(t *testing.T) {
330+
reqURL := fmt.Sprintf("https://localhost:%d/", *fTLSPort)
331+
statusHost := "request-headers-dump"
332+
333+
dumpedHeaders := parseDumpedHTTPHeadersFromBody(getURLWithHost(t, reqURL, statusHost))
334+
335+
assert.Regexp(t, `^127\.0\.0\.1|::1`, dumpedHeaders["HTTP_X_FORWARDED_FOR"])
336+
assert.Equal(t, "https", dumpedHeaders["HTTP_X_FORWARDED_PROTO"])
337+
})
296338
}

dev/devtest/testutils.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,9 @@ func LinkTestApps(t *testing.T, workingDirPath string, testAppsToLink map[string
220220

221221
func LinkAllTestApps(t *testing.T, workingDirPath string) func() {
222222
testAppsToLink := map[string]string{
223-
"hipuma": "rack-hi-puma",
224-
"static-site": "static-hi-puma",
223+
"hipuma": "rack-hi-puma",
224+
"static-site": "static-hi-puma",
225+
"request-headers-dump": "rack-request-headers-dump",
225226
}
226227

227228
return LinkTestApps(t, workingDirPath, testAppsToLink)

dev/http.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ func (h *HTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
144144
}
145145
}
146146

147+
if req.TLS == nil {
148+
req.Header.Set("X-Forwarded-Proto", "http")
149+
} else {
150+
req.Header.Set("X-Forwarded-Proto", "https")
151+
}
152+
147153
req.URL.Scheme, req.URL.Host = app.Scheme, app.Address()
148154
h.proxy.ServeHTTP(w, req)
149155
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class Application
2+
def call(env)
3+
status = 200
4+
headers = { "Content-Type" => "application/json" }
5+
body = [ env.select { |k, v| k.start_with?('HTTP_') }.map { |(k, v)| [k, v].join(' ') }.join("\n") ]
6+
7+
[status, headers, body]
8+
end
9+
end
10+
11+
run Application.new

etc/rack-request-headers-dump/tmp/restart.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)