@@ -0,0 +1,176 @@
1+package main
2+
3+import (
4+ "net"
5+ "net/http"
6+ "reflect"
7+ "testing"
8+
9+ "github.com/stretchr/testify/assert"
10+)
11+
12+func TestGetRealClientIPParser(t *testing.T) {
13+ forwardedForType := reflect.TypeOf((*xForwardedForClientIPParser)(nil))
14+
15+ tests := []struct {
16+ header string
17+ errString string
18+ parserType reflect.Type
19+ }{
20+ {"X-Forwarded-For", "", forwardedForType},
21+ {"X-REAL-IP", "", forwardedForType},
22+ {"x-proxyuser-ip", "", forwardedForType},
23+ {"", "the http header key () is either invalid or unsupported", nil},
24+ {"Forwarded", "the http header key (Forwarded) is either invalid or unsupported", nil},
25+ {"2#* @##$$:kd", "the http header key (2#* @##$$:kd) is either invalid or unsupported", nil},
26+ }
27+
28+ for _, test := range tests {
29+ p, err := getRealClientIPParser(test.header)
30+
31+ if test.errString == "" {
32+ assert.Nil(t, err)
33+ } else {
34+ assert.NotNil(t, err)
35+ assert.Equal(t, test.errString, err.Error())
36+ }
37+
38+ if test.parserType == nil {
39+ assert.Nil(t, p)
40+ } else {
41+ assert.NotNil(t, p)
42+ assert.Equal(t, test.parserType, reflect.TypeOf(p))
43+ }
44+
45+ if xp, ok := p.(*xForwardedForClientIPParser); ok {
46+ assert.Equal(t, http.CanonicalHeaderKey(test.header), xp.header)
47+ }
48+ }
49+}
50+
51+func TestXForwardedForClientIPParser(t *testing.T) {
52+ p := &xForwardedForClientIPParser{header: http.CanonicalHeaderKey("X-Forwarded-For")}
53+
54+ tests := []struct {
55+ headerValue string
56+ errString string
57+ expectedIP net.IP
58+ }{
59+ {"", "", nil},
60+ {"1.2.3.4", "", net.ParseIP("1.2.3.4")},
61+ {"10::23", "", net.ParseIP("10::23")},
62+ {"::1", "", net.ParseIP("::1")},
63+ {"[::1]:1234", "", net.ParseIP("::1")},
64+ {"10.0.10.11:1234", "", net.ParseIP("10.0.10.11")},
65+ {"192.168.10.50, 10.0.0.1, 1.2.3.4", "", net.ParseIP("192.168.10.50")},
66+ {"nil", "unable to parse ip (nil) from X-Forwarded-For header", nil},
67+ {"10000.10000.10000.10000", "unable to parse ip (10000.10000.10000.10000) from X-Forwarded-For header", nil},
68+ }
69+
70+ for _, test := range tests {
71+ h := http.Header{}
72+ h.Add("X-Forwarded-For", test.headerValue)
73+
74+ ip, err := p.GetRealClientIP(h)
75+
76+ if test.errString == "" {
77+ assert.Nil(t, err)
78+ } else {
79+ assert.NotNil(t, err)
80+ assert.Equal(t, test.errString, err.Error())
81+ }
82+
83+ if test.expectedIP == nil {
84+ assert.Nil(t, ip)
85+ } else {
86+ assert.NotNil(t, ip)
87+ assert.Equal(t, test.expectedIP, ip)
88+ }
89+ }
90+}
91+
92+func TestXForwardedForClientIPParserIgnoresOthers(t *testing.T) {
93+ p := &xForwardedForClientIPParser{header: http.CanonicalHeaderKey("X-Forwarded-For")}
94+
95+ h := http.Header{}
96+ expectedIPString := "192.168.10.50"
97+ h.Add("X-Real-IP", "10.0.0.1")
98+ h.Add("X-ProxyUser-IP", "10.0.0.1")
99+ h.Add("X-Forwarded-For", expectedIPString)
100+ ip, err := p.GetRealClientIP(h)
101+ assert.Nil(t, err)
102+ assert.NotNil(t, ip)
103+ assert.Equal(t, ip, net.ParseIP(expectedIPString))
104+}
105+
106+func TestGetRemoteIP(t *testing.T) {
107+ tests := []struct {
108+ remoteAddr string
109+ errString string
110+ expectedIP net.IP
111+ }{
112+ {"", "unable to get ip and port from http.RemoteAddr ()", nil},
113+ {"nil", "unable to get ip and port from http.RemoteAddr (nil)", nil},
114+ {"235.28.129.186", "unable to get ip and port from http.RemoteAddr (235.28.129.186)", nil},
115+ {"90::45", "unable to get ip and port from http.RemoteAddr (90::45)", nil},
116+ {"192.168.73.165:14976, 10.4.201.15:18453", "unable to get ip and port from http.RemoteAddr (192.168.73.165:14976, 10.4.201.15:18453)", nil},
117+ {"10000.10000.10000.10000:8080", "unable to parse ip (10000.10000.10000.10000)", nil},
118+ {"[::1]:48290", "", net.ParseIP("::1")},
119+ {"10.254.244.165:62750", "", net.ParseIP("10.254.244.165")},
120+ }
121+
122+ for _, test := range tests {
123+ req := &http.Request{RemoteAddr: test.remoteAddr}
124+
125+ ip, err := getRemoteIP(req)
126+
127+ if test.errString == "" {
128+ assert.Nil(t, err)
129+ } else {
130+ assert.NotNil(t, err)
131+ assert.Equal(t, test.errString, err.Error())
132+ }
133+
134+ if test.expectedIP == nil {
135+ assert.Nil(t, ip)
136+ } else {
137+ assert.NotNil(t, ip)
138+ assert.Equal(t, test.expectedIP, ip)
139+ }
140+ }
141+}
142+
143+func TestGetClientString(t *testing.T) {
144+ p := &xForwardedForClientIPParser{header: http.CanonicalHeaderKey("X-Forwarded-For")}
145+
146+ tests := []struct {
147+ parser realClientIPParser
148+ remoteAddr string
149+ headerValue string
150+ expectedClient string
151+ expectedClientFull string
152+ }{
153+ // Should fail quietly, only printing warnings to the log
154+ {nil, "", "", "", ""},
155+ {p, "127.0.0.1:11950", "", "127.0.0.1", "127.0.0.1"},
156+ {p, "[::1]:28660", "99.103.56.12", "99.103.56.12", "::1 (99.103.56.12)"},
157+ {nil, "10.254.244.165:62750", "", "10.254.244.165", "10.254.244.165"},
158+ // Parser is nil, the contents of X-Forwarded-For should be ignored in all cases.
159+ {nil, "[2001:470:26:307:a5a1:1177:2ae3:e9c3]:48290", "127.0.0.1", "2001:470:26:307:a5a1:1177:2ae3:e9c3", "2001:470:26:307:a5a1:1177:2ae3:e9c3"},
160+ }
161+
162+ for _, test := range tests {
163+ h := http.Header{}
164+ h.Add("X-Forwarded-For", test.headerValue)
165+ req := &http.Request{
166+ Header: h,
167+ RemoteAddr: test.remoteAddr,
168+ }
169+
170+ client := getClientString(test.parser, req, false)
171+ assert.Equal(t, test.expectedClient, client)
172+
173+ clientFull := getClientString(test.parser, req, true)
174+ assert.Equal(t, test.expectedClientFull, clientFull)
175+ }
176+}