Skip to content

Commit 9914178

Browse files
Nuryssoappleboy
andauthored
fix(context): ClientIP handling for multiple X-Forwarded-For header values (#4472)
* Fix ClientIP calculation by concatenating all RemoteIPHeaders values * test: used http.MethodGet instead constants and fix lints * lint error fixed * Refactor ClientIP X-Forwarded-For tests --------- Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
1 parent 915e4c9 commit 9914178

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

context.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,8 @@ func (c *Context) ClientIP() string {
989989

990990
if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil {
991991
for _, headerName := range c.engine.RemoteIPHeaders {
992-
ip, valid := c.engine.validateHeader(c.requestHeader(headerName))
992+
headerValue := strings.Join(c.Request.Header.Values(headerName), ",")
993+
ip, valid := c.engine.validateHeader(headerValue)
993994
if valid {
994995
return ip
995996
}

context_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,37 @@ func TestContextRenderNoContentIndentedJSON(t *testing.T) {
11431143
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
11441144
}
11451145

1146+
func TestContextClientIPWithMultipleHeaders(t *testing.T) {
1147+
c, _ := CreateTestContext(httptest.NewRecorder())
1148+
c.Request, _ = http.NewRequest(http.MethodGet, "/test", nil)
1149+
1150+
// Multiple X-Forwarded-For headers
1151+
c.Request.Header.Add("X-Forwarded-For", "1.2.3.4, "+localhostIP)
1152+
c.Request.Header.Add("X-Forwarded-For", "5.6.7.8")
1153+
c.Request.RemoteAddr = localhostIP + ":1234"
1154+
1155+
c.engine.ForwardedByClientIP = true
1156+
c.engine.RemoteIPHeaders = []string{"X-Forwarded-For"}
1157+
_ = c.engine.SetTrustedProxies([]string{localhostIP})
1158+
1159+
// Should return 5.6.7.8 (last non-trusted IP)
1160+
assert.Equal(t, "5.6.7.8", c.ClientIP())
1161+
}
1162+
1163+
func TestContextClientIPWithSingleHeader(t *testing.T) {
1164+
c, _ := CreateTestContext(httptest.NewRecorder())
1165+
c.Request, _ = http.NewRequest(http.MethodGet, "/test", nil)
1166+
c.Request.Header.Set("X-Forwarded-For", "1.2.3.4, "+localhostIP)
1167+
c.Request.RemoteAddr = localhostIP + ":1234"
1168+
1169+
c.engine.ForwardedByClientIP = true
1170+
c.engine.RemoteIPHeaders = []string{"X-Forwarded-For"}
1171+
_ = c.engine.SetTrustedProxies([]string{localhostIP})
1172+
1173+
// Should return 1.2.3.4
1174+
assert.Equal(t, "1.2.3.4", c.ClientIP())
1175+
}
1176+
11461177
// Tests that the response is serialized as Secure JSON
11471178
// and Content-Type is set to application/json
11481179
func TestContextRenderSecureJSON(t *testing.T) {

0 commit comments

Comments
 (0)