Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/util/get-client-ip-address.test.ts
5818 views
1
import { getClientIpAddress } from "./get-client-ip-address";
2
3
describe("getClientIpAddress()", () => {
4
const createRequest = (headers: Record<string, string>) => ({ headers });
5
6
describe("Standard Headers Supported by request-ip", () => {
7
it("should handle CF-Connecting-IP (highest priority)", () => {
8
const req = createRequest({
9
"x-client-ip": "203.0.113.1",
10
"x-forwarded-for": "192.168.1.1",
11
"cf-connecting-ip": "198.51.100.1",
12
});
13
14
const result = getClientIpAddress(req);
15
expect(result).toBe("198.51.100.1");
16
});
17
18
it("should handle X-Forwarded-For with multiple IPs", () => {
19
const req = createRequest({
20
"x-forwarded-for": "203.0.113.1, 192.168.1.1, 10.0.0.1",
21
});
22
23
const result = getClientIpAddress(req);
24
expect(result).toBe("203.0.113.1");
25
});
26
27
it("should handle CF-Connecting-IP from Cloudflare", () => {
28
const req = createRequest({
29
"cf-connecting-ip": "203.0.113.1",
30
});
31
32
const result = getClientIpAddress(req);
33
expect(result).toBe("203.0.113.1");
34
});
35
36
it("should handle Fastly-Client-Ip from Fastly", () => {
37
const req = createRequest({
38
"fastly-client-ip": "203.0.113.1",
39
});
40
41
const result = getClientIpAddress(req);
42
expect(result).toBe("203.0.113.1");
43
});
44
45
it("should handle True-Client-Ip from Akamai/Cloudflare", () => {
46
const req = createRequest({
47
"true-client-ip": "203.0.113.1",
48
});
49
50
const result = getClientIpAddress(req);
51
expect(result).toBe("203.0.113.1");
52
});
53
54
it("should handle X-Real-IP from nginx", () => {
55
const req = createRequest({
56
"x-real-ip": "203.0.113.1",
57
});
58
59
const result = getClientIpAddress(req);
60
expect(result).toBe("203.0.113.1");
61
});
62
63
it("should handle X-Cluster-Client-IP from Rackspace", () => {
64
const req = createRequest({
65
"x-cluster-client-ip": "203.0.113.1",
66
});
67
68
const result = getClientIpAddress(req);
69
expect(result).toBe("203.0.113.1");
70
});
71
72
it("should handle appengine-user-ip from Google App Engine", () => {
73
const req = createRequest({
74
"appengine-user-ip": "203.0.113.1",
75
});
76
77
const result = getClientIpAddress(req);
78
expect(result).toBe("203.0.113.1");
79
});
80
});
81
82
describe("Header Priority Order", () => {
83
it("should prioritize X-Client-IP over X-Forwarded-For", () => {
84
const req = createRequest({
85
"x-client-ip": "203.0.113.1",
86
"x-forwarded-for": "192.168.1.1",
87
});
88
89
const result = getClientIpAddress(req);
90
expect(result).toBe("203.0.113.1");
91
});
92
93
it("should prioritize CF-Connecting-IP over X-Forwarded-For", () => {
94
const req = createRequest({
95
"x-forwarded-for": "203.0.113.1",
96
"cf-connecting-ip": "192.168.1.1",
97
});
98
99
const result = getClientIpAddress(req);
100
expect(result).toBe("192.168.1.1");
101
});
102
103
it("should prioritize CF-Connecting-IP over Fastly-Client-Ip", () => {
104
const req = createRequest({
105
"cf-connecting-ip": "203.0.113.1",
106
"fastly-client-ip": "192.168.1.1",
107
});
108
109
const result = getClientIpAddress(req);
110
expect(result).toBe("203.0.113.1");
111
});
112
113
it("should prioritize Fastly-Client-Ip over True-Client-Ip", () => {
114
const req = createRequest({
115
"fastly-client-ip": "203.0.113.1",
116
"true-client-ip": "192.168.1.1",
117
});
118
119
const result = getClientIpAddress(req);
120
expect(result).toBe("203.0.113.1");
121
});
122
123
it("should prioritize True-Client-Ip over X-Real-IP", () => {
124
const req = createRequest({
125
"true-client-ip": "203.0.113.1",
126
"x-real-ip": "192.168.1.1",
127
});
128
129
const result = getClientIpAddress(req);
130
expect(result).toBe("203.0.113.1");
131
});
132
});
133
134
describe("Case Sensitivity (Headers are lowercase in Node.js)", () => {
135
it("should handle uppercase headers (converted to lowercase by Node.js)", () => {
136
const req = createRequest({
137
"X-CLIENT-IP": "203.0.113.1", // This would be lowercase in real Node.js
138
});
139
140
const result = getClientIpAddress(req);
141
expect(result).toBe("203.0.113.1");
142
});
143
144
it("should handle mixed case headers", () => {
145
const req = createRequest({
146
"X-Forwarded-For": "203.0.113.1, 192.168.1.1",
147
});
148
149
const result = getClientIpAddress(req);
150
expect(result).toBe("203.0.113.1");
151
});
152
});
153
154
describe("Forwarded Header Fallback (when request-ip fails)", () => {
155
it("should parse simple Forwarded header", () => {
156
const req = createRequest({
157
forwarded: "for=192.0.2.60",
158
});
159
160
const result = getClientIpAddress(req);
161
expect(result).toBe("192.0.2.60");
162
});
163
164
it("should parse quoted Forwarded header", () => {
165
const req = createRequest({
166
forwarded: 'for="192.0.2.60"',
167
});
168
169
const result = getClientIpAddress(req);
170
expect(result).toBe("192.0.2.60");
171
});
172
173
it("should parse Forwarded header with IPv6 brackets", () => {
174
const req = createRequest({
175
forwarded: 'for="[2001:db8:cafe::17]"',
176
});
177
178
const result = getClientIpAddress(req);
179
expect(result).toBe("2001:db8:cafe::17");
180
});
181
182
it("should handle port stripping for IPv4", () => {
183
const req = createRequest({
184
forwarded: "for=192.0.2.60:4711",
185
});
186
187
const result = getClientIpAddress(req);
188
expect(result).toBe("192.0.2.60");
189
});
190
191
it("should handle IPv4 addresses with ports in X-Forwarded-For", () => {
192
const req = createRequest({
193
"x-forwarded-for": "192.168.1.1:8080",
194
});
195
196
const result = getClientIpAddress(req);
197
expect(result).toBe("192.168.1.1");
198
});
199
200
it("should handle multiple parameters in Forwarded header", () => {
201
const req = createRequest({
202
forwarded: "for=192.0.2.60;proto=http;by=203.0.113.43",
203
});
204
205
const result = getClientIpAddress(req);
206
expect(result).toBe("192.0.2.60");
207
});
208
209
it("should handle case-insensitive FOR parameter", () => {
210
const req = createRequest({
211
forwarded: "For=192.0.2.60",
212
});
213
214
const result = getClientIpAddress(req);
215
expect(result).toBe("192.0.2.60");
216
});
217
218
it("should skip invalid entries and use first valid IP", () => {
219
const req = createRequest({
220
forwarded: "for=_gazonk, for=192.0.2.60",
221
});
222
223
const result = getClientIpAddress(req);
224
expect(result).toBe("192.0.2.60");
225
});
226
227
it("should return undefined when no valid for= parameter exists", () => {
228
const req = createRequest({
229
forwarded: "proto=http;by=203.0.113.43",
230
});
231
232
const result = getClientIpAddress(req);
233
expect(result).toBeUndefined();
234
});
235
236
it("should handle Forwarded header with spaces around commas", () => {
237
const req = createRequest({
238
forwarded: "for=192.0.2.60 , for=203.0.113.43",
239
});
240
241
const result = getClientIpAddress(req);
242
expect(result).toBe("192.0.2.60");
243
});
244
245
it("should handle Forwarded header with spaces around semicolons", () => {
246
const req = createRequest({
247
forwarded: "for=192.0.2.60 ; proto=http ; by=203.0.113.43",
248
});
249
250
const result = getClientIpAddress(req);
251
expect(result).toBe("192.0.2.60");
252
});
253
254
it("should handle Forwarded header with mixed spacing", () => {
255
const req = createRequest({
256
forwarded: " for=192.0.2.60 ; proto=http ; by=203.0.113.43 ",
257
});
258
259
const result = getClientIpAddress(req);
260
expect(result).toBe("192.0.2.60");
261
});
262
});
263
264
describe("IPv6 Support", () => {
265
it("should handle IPv6 addresses in X-Forwarded-For", () => {
266
const req = createRequest({
267
"x-forwarded-for": "2001:db8:85a3:8d3:1319:8a2e:370:7348",
268
});
269
270
const result = getClientIpAddress(req);
271
expect(result).toBe("2001:db8:85a3:8d3:1319:8a2e:370:7348");
272
});
273
274
it("should handle compressed IPv6 addresses", () => {
275
const req = createRequest({
276
"x-forwarded-for": "2001:db8::1",
277
});
278
279
const result = getClientIpAddress(req);
280
expect(result).toBe("2001:db8::1");
281
});
282
283
it("should handle IPv6 loopback", () => {
284
const req = createRequest({
285
"x-forwarded-for": "::1",
286
});
287
288
const result = getClientIpAddress(req);
289
expect(result).toBe("::1");
290
});
291
292
it("should handle IPv6 addresses with ports", () => {
293
const req = createRequest({
294
"x-forwarded-for": "[2001:db8::1]:8080",
295
});
296
297
const result = getClientIpAddress(req);
298
expect(result).toBe("2001:db8::1");
299
});
300
301
it("should handle compressed IPv6 addresses with ports", () => {
302
const req = createRequest({
303
"x-forwarded-for": "[::1]:9000",
304
});
305
306
const result = getClientIpAddress(req);
307
expect(result).toBe("::1");
308
});
309
});
310
311
describe("Edge Cases", () => {
312
it("should return undefined for empty headers", () => {
313
const req = createRequest({});
314
315
const result = getClientIpAddress(req);
316
expect(result).toBeUndefined();
317
});
318
319
it("should return undefined for invalid IP addresses", () => {
320
const req = createRequest({
321
"x-forwarded-for": "not.an.ip.address",
322
});
323
324
const result = getClientIpAddress(req);
325
expect(result).toBeUndefined();
326
});
327
328
it("should handle localhost addresses", () => {
329
const req = createRequest({
330
"x-forwarded-for": "127.0.0.1",
331
});
332
333
const result = getClientIpAddress(req);
334
expect(result).toBe("127.0.0.1");
335
});
336
337
it("should handle private IP addresses", () => {
338
const req = createRequest({
339
"x-forwarded-for": "192.168.1.1",
340
});
341
342
const result = getClientIpAddress(req);
343
expect(result).toBe("192.168.1.1");
344
});
345
346
it("should handle IP addresses with whitespace", () => {
347
const req = createRequest({
348
"x-forwarded-for": " 192.168.1.1 ",
349
});
350
351
const result = getClientIpAddress(req);
352
expect(result).toBe("192.168.1.1");
353
});
354
355
it("should handle IPv6 addresses with whitespace and ports", () => {
356
const req = createRequest({
357
"x-forwarded-for": " [2001:db8::1]:8080 ",
358
});
359
360
const result = getClientIpAddress(req);
361
expect(result).toBe("2001:db8::1");
362
});
363
});
364
});
365
366