Skip to content

Commit ef914ab

Browse files
committed
Update /ip endpoint to return single origin IP
1 parent b30cb3a commit ef914ab

File tree

2 files changed

+56
-17
lines changed

2 files changed

+56
-17
lines changed

httpbin/handlers_test.go

+51-13
Original file line numberDiff line numberDiff line change
@@ -318,22 +318,60 @@ func TestCORS(t *testing.T) {
318318
}
319319

320320
func TestIP(t *testing.T) {
321-
r, _ := http.NewRequest("GET", "/ip", nil)
322-
r.RemoteAddr = "192.168.0.100"
323-
w := httptest.NewRecorder()
324-
handler.ServeHTTP(w, r)
321+
t.Parallel()
325322

326-
assertStatusCode(t, w, http.StatusOK)
327-
assertContentType(t, w, jsonContentType)
328-
329-
var resp *ipResponse
330-
err := json.Unmarshal(w.Body.Bytes(), &resp)
331-
if err != nil {
332-
t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err)
323+
testCases := map[string]struct {
324+
remoteAddr string
325+
headers map[string]string
326+
wantOrigin string
327+
}{
328+
"remote addr used if no x-forwarded-for": {
329+
remoteAddr: "192.168.0.100",
330+
wantOrigin: "192.168.0.100",
331+
},
332+
"remote addr used if x-forwarded-for empty": {
333+
remoteAddr: "192.168.0.100",
334+
headers: map[string]string{"X-Forwarded-For": ""},
335+
wantOrigin: "192.168.0.100",
336+
},
337+
"first entry in x-forwarded-for used if present": {
338+
remoteAddr: "192.168.0.100",
339+
headers: map[string]string{"X-Forwarded-For": "10.1.1.1, 10.2.2.2, 10.3.3.3"},
340+
wantOrigin: "10.1.1.1",
341+
},
342+
"single entry x-forwarded-for ok": {
343+
remoteAddr: "192.168.0.100",
344+
headers: map[string]string{"X-Forwarded-For": "10.1.1.1"},
345+
wantOrigin: "10.1.1.1",
346+
},
333347
}
334348

335-
if resp.Origin != r.RemoteAddr {
336-
t.Fatalf("%#v != %#v", resp.Origin, r.RemoteAddr)
349+
for name, tc := range testCases {
350+
tc := tc
351+
t.Run(name, func(t *testing.T) {
352+
t.Parallel()
353+
354+
r, _ := http.NewRequest("GET", "/ip", nil)
355+
r.RemoteAddr = tc.remoteAddr
356+
for k, v := range tc.headers {
357+
r.Header.Set(k, v)
358+
}
359+
w := httptest.NewRecorder()
360+
handler.ServeHTTP(w, r)
361+
362+
assertStatusCode(t, w, http.StatusOK)
363+
assertContentType(t, w, jsonContentType)
364+
365+
var resp *ipResponse
366+
err := json.Unmarshal(w.Body.Bytes(), &resp)
367+
if err != nil {
368+
t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err)
369+
}
370+
371+
if resp.Origin != tc.wantOrigin {
372+
t.Fatalf("got %q, want %q", resp.Origin, tc.wantOrigin)
373+
}
374+
})
337375
}
338376
}
339377

httpbin/helpers.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ func getRequestHeaders(r *http.Request) http.Header {
3333
}
3434

3535
func getOrigin(r *http.Request) string {
36-
origin := r.Header.Get("X-Forwarded-For")
37-
if origin == "" {
38-
origin = r.RemoteAddr
36+
forwardedFor := r.Header.Get("X-Forwarded-For")
37+
if forwardedFor == "" {
38+
return r.RemoteAddr
3939
}
40-
return origin
40+
// take the first entry in a comma-separated list of IP addrs
41+
return strings.TrimSpace(strings.SplitN(forwardedFor, ",", 2)[0])
4142
}
4243

4344
func getURL(r *http.Request) *url.URL {

0 commit comments

Comments
 (0)