Path: blob/master/src/packages/util/get-client-ip-address.test.ts
5818 views
import { getClientIpAddress } from "./get-client-ip-address";12describe("getClientIpAddress()", () => {3const createRequest = (headers: Record<string, string>) => ({ headers });45describe("Standard Headers Supported by request-ip", () => {6it("should handle CF-Connecting-IP (highest priority)", () => {7const req = createRequest({8"x-client-ip": "203.0.113.1",9"x-forwarded-for": "192.168.1.1",10"cf-connecting-ip": "198.51.100.1",11});1213const result = getClientIpAddress(req);14expect(result).toBe("198.51.100.1");15});1617it("should handle X-Forwarded-For with multiple IPs", () => {18const req = createRequest({19"x-forwarded-for": "203.0.113.1, 192.168.1.1, 10.0.0.1",20});2122const result = getClientIpAddress(req);23expect(result).toBe("203.0.113.1");24});2526it("should handle CF-Connecting-IP from Cloudflare", () => {27const req = createRequest({28"cf-connecting-ip": "203.0.113.1",29});3031const result = getClientIpAddress(req);32expect(result).toBe("203.0.113.1");33});3435it("should handle Fastly-Client-Ip from Fastly", () => {36const req = createRequest({37"fastly-client-ip": "203.0.113.1",38});3940const result = getClientIpAddress(req);41expect(result).toBe("203.0.113.1");42});4344it("should handle True-Client-Ip from Akamai/Cloudflare", () => {45const req = createRequest({46"true-client-ip": "203.0.113.1",47});4849const result = getClientIpAddress(req);50expect(result).toBe("203.0.113.1");51});5253it("should handle X-Real-IP from nginx", () => {54const req = createRequest({55"x-real-ip": "203.0.113.1",56});5758const result = getClientIpAddress(req);59expect(result).toBe("203.0.113.1");60});6162it("should handle X-Cluster-Client-IP from Rackspace", () => {63const req = createRequest({64"x-cluster-client-ip": "203.0.113.1",65});6667const result = getClientIpAddress(req);68expect(result).toBe("203.0.113.1");69});7071it("should handle appengine-user-ip from Google App Engine", () => {72const req = createRequest({73"appengine-user-ip": "203.0.113.1",74});7576const result = getClientIpAddress(req);77expect(result).toBe("203.0.113.1");78});79});8081describe("Header Priority Order", () => {82it("should prioritize X-Client-IP over X-Forwarded-For", () => {83const req = createRequest({84"x-client-ip": "203.0.113.1",85"x-forwarded-for": "192.168.1.1",86});8788const result = getClientIpAddress(req);89expect(result).toBe("203.0.113.1");90});9192it("should prioritize CF-Connecting-IP over X-Forwarded-For", () => {93const req = createRequest({94"x-forwarded-for": "203.0.113.1",95"cf-connecting-ip": "192.168.1.1",96});9798const result = getClientIpAddress(req);99expect(result).toBe("192.168.1.1");100});101102it("should prioritize CF-Connecting-IP over Fastly-Client-Ip", () => {103const req = createRequest({104"cf-connecting-ip": "203.0.113.1",105"fastly-client-ip": "192.168.1.1",106});107108const result = getClientIpAddress(req);109expect(result).toBe("203.0.113.1");110});111112it("should prioritize Fastly-Client-Ip over True-Client-Ip", () => {113const req = createRequest({114"fastly-client-ip": "203.0.113.1",115"true-client-ip": "192.168.1.1",116});117118const result = getClientIpAddress(req);119expect(result).toBe("203.0.113.1");120});121122it("should prioritize True-Client-Ip over X-Real-IP", () => {123const req = createRequest({124"true-client-ip": "203.0.113.1",125"x-real-ip": "192.168.1.1",126});127128const result = getClientIpAddress(req);129expect(result).toBe("203.0.113.1");130});131});132133describe("Case Sensitivity (Headers are lowercase in Node.js)", () => {134it("should handle uppercase headers (converted to lowercase by Node.js)", () => {135const req = createRequest({136"X-CLIENT-IP": "203.0.113.1", // This would be lowercase in real Node.js137});138139const result = getClientIpAddress(req);140expect(result).toBe("203.0.113.1");141});142143it("should handle mixed case headers", () => {144const req = createRequest({145"X-Forwarded-For": "203.0.113.1, 192.168.1.1",146});147148const result = getClientIpAddress(req);149expect(result).toBe("203.0.113.1");150});151});152153describe("Forwarded Header Fallback (when request-ip fails)", () => {154it("should parse simple Forwarded header", () => {155const req = createRequest({156forwarded: "for=192.0.2.60",157});158159const result = getClientIpAddress(req);160expect(result).toBe("192.0.2.60");161});162163it("should parse quoted Forwarded header", () => {164const req = createRequest({165forwarded: 'for="192.0.2.60"',166});167168const result = getClientIpAddress(req);169expect(result).toBe("192.0.2.60");170});171172it("should parse Forwarded header with IPv6 brackets", () => {173const req = createRequest({174forwarded: 'for="[2001:db8:cafe::17]"',175});176177const result = getClientIpAddress(req);178expect(result).toBe("2001:db8:cafe::17");179});180181it("should handle port stripping for IPv4", () => {182const req = createRequest({183forwarded: "for=192.0.2.60:4711",184});185186const result = getClientIpAddress(req);187expect(result).toBe("192.0.2.60");188});189190it("should handle IPv4 addresses with ports in X-Forwarded-For", () => {191const req = createRequest({192"x-forwarded-for": "192.168.1.1:8080",193});194195const result = getClientIpAddress(req);196expect(result).toBe("192.168.1.1");197});198199it("should handle multiple parameters in Forwarded header", () => {200const req = createRequest({201forwarded: "for=192.0.2.60;proto=http;by=203.0.113.43",202});203204const result = getClientIpAddress(req);205expect(result).toBe("192.0.2.60");206});207208it("should handle case-insensitive FOR parameter", () => {209const req = createRequest({210forwarded: "For=192.0.2.60",211});212213const result = getClientIpAddress(req);214expect(result).toBe("192.0.2.60");215});216217it("should skip invalid entries and use first valid IP", () => {218const req = createRequest({219forwarded: "for=_gazonk, for=192.0.2.60",220});221222const result = getClientIpAddress(req);223expect(result).toBe("192.0.2.60");224});225226it("should return undefined when no valid for= parameter exists", () => {227const req = createRequest({228forwarded: "proto=http;by=203.0.113.43",229});230231const result = getClientIpAddress(req);232expect(result).toBeUndefined();233});234235it("should handle Forwarded header with spaces around commas", () => {236const req = createRequest({237forwarded: "for=192.0.2.60 , for=203.0.113.43",238});239240const result = getClientIpAddress(req);241expect(result).toBe("192.0.2.60");242});243244it("should handle Forwarded header with spaces around semicolons", () => {245const req = createRequest({246forwarded: "for=192.0.2.60 ; proto=http ; by=203.0.113.43",247});248249const result = getClientIpAddress(req);250expect(result).toBe("192.0.2.60");251});252253it("should handle Forwarded header with mixed spacing", () => {254const req = createRequest({255forwarded: " for=192.0.2.60 ; proto=http ; by=203.0.113.43 ",256});257258const result = getClientIpAddress(req);259expect(result).toBe("192.0.2.60");260});261});262263describe("IPv6 Support", () => {264it("should handle IPv6 addresses in X-Forwarded-For", () => {265const req = createRequest({266"x-forwarded-for": "2001:db8:85a3:8d3:1319:8a2e:370:7348",267});268269const result = getClientIpAddress(req);270expect(result).toBe("2001:db8:85a3:8d3:1319:8a2e:370:7348");271});272273it("should handle compressed IPv6 addresses", () => {274const req = createRequest({275"x-forwarded-for": "2001:db8::1",276});277278const result = getClientIpAddress(req);279expect(result).toBe("2001:db8::1");280});281282it("should handle IPv6 loopback", () => {283const req = createRequest({284"x-forwarded-for": "::1",285});286287const result = getClientIpAddress(req);288expect(result).toBe("::1");289});290291it("should handle IPv6 addresses with ports", () => {292const req = createRequest({293"x-forwarded-for": "[2001:db8::1]:8080",294});295296const result = getClientIpAddress(req);297expect(result).toBe("2001:db8::1");298});299300it("should handle compressed IPv6 addresses with ports", () => {301const req = createRequest({302"x-forwarded-for": "[::1]:9000",303});304305const result = getClientIpAddress(req);306expect(result).toBe("::1");307});308});309310describe("Edge Cases", () => {311it("should return undefined for empty headers", () => {312const req = createRequest({});313314const result = getClientIpAddress(req);315expect(result).toBeUndefined();316});317318it("should return undefined for invalid IP addresses", () => {319const req = createRequest({320"x-forwarded-for": "not.an.ip.address",321});322323const result = getClientIpAddress(req);324expect(result).toBeUndefined();325});326327it("should handle localhost addresses", () => {328const req = createRequest({329"x-forwarded-for": "127.0.0.1",330});331332const result = getClientIpAddress(req);333expect(result).toBe("127.0.0.1");334});335336it("should handle private IP addresses", () => {337const req = createRequest({338"x-forwarded-for": "192.168.1.1",339});340341const result = getClientIpAddress(req);342expect(result).toBe("192.168.1.1");343});344345it("should handle IP addresses with whitespace", () => {346const req = createRequest({347"x-forwarded-for": " 192.168.1.1 ",348});349350const result = getClientIpAddress(req);351expect(result).toBe("192.168.1.1");352});353354it("should handle IPv6 addresses with whitespace and ports", () => {355const req = createRequest({356"x-forwarded-for": " [2001:db8::1]:8080 ",357});358359const result = getClientIpAddress(req);360expect(result).toBe("2001:db8::1");361});362});363});364365366