33
44import { isIP } from 'net' ;
55
6- /**
7- * This is the list of headers, in order of preference, that will be used to
8- * determine the client's IP address.
9- */
10- const headerNames = [
11- 'X-Client-IP' ,
12- 'X-Forwarded-For' ,
13- 'Fly-Client-IP' ,
14- 'CF-Connecting-IP' ,
15- 'Fastly-Client-Ip' ,
16- 'True-Client-Ip' ,
17- 'X-Real-IP' ,
18- 'X-Cluster-Client-IP' ,
19- 'X-Forwarded' ,
20- 'Forwarded-For' ,
21- 'Forwarded' ,
22- ] ;
236/**
247 * Get the IP address of the client sending a request.
258 *
@@ -45,35 +28,58 @@ const headerNames = [
4528 * will be returned.
4629 */
4730export function getClientIPAddress ( headers : Headers ) : string | null {
48- const ipAddress = headerNames
49- . map ( ( headerName : string ) => {
50- const value = headers . get ( headerName ) ;
51- if ( headerName === 'Forwarded' ) {
52- return parseForwardedHeader ( value ) ;
53- }
54- if ( ! value ?. includes ( ', ' ) ) return value ;
55- return value . split ( ', ' ) ;
56- } )
57- . reduce ( ( acc : string [ ] , val ) => {
58- if ( ! val ) {
59- return acc ;
60- }
31+ // The headers to check, in priority order
32+ const headerNames = [
33+ "X-Client-IP" ,
34+ "X-Forwarded-For" ,
35+ "Fly-Client-IP" ,
36+ "CF-Connecting-IP" ,
37+ "Fastly-Client-Ip" ,
38+ "True-Client-Ip" ,
39+ "X-Real-IP" ,
40+ "X-Cluster-Client-IP" ,
41+ "X-Forwarded" ,
42+ "Forwarded-For" ,
43+ "Forwarded" ,
44+ ] ;
45+
46+ // This will end up being Array<string | string[] | undefined | null> because of the various possible values a header
47+ // can take
48+ const headerValues = headerNames . map ( ( headerName : string ) => {
49+ const value = headers . get ( headerName ) ;
50+
51+ if ( headerName === "Forwarded" ) {
52+ return parseForwardedHeader ( value ) ;
53+ }
54+
55+ return value ?. split ( ", " ) ;
56+ } ) ;
57+
58+ // Flatten the array and filter out any falsy entries
59+ const flattenedHeaderValues = headerValues . reduce ( ( acc : string [ ] , val ) => {
60+ if ( ! val ) {
61+ return acc ;
62+ }
6163
62- return acc . concat ( val ) ;
63- } , [ ] )
64- . find ( ip => {
65- if ( ip === null ) return false ;
66- return isIP ( ip ) ;
67- } ) ;
64+ return acc . concat ( val ) ;
65+ } , [ ] ) ;
6866
69- return ipAddress ?? null ;
67+ // Find the first value which is a valid IP address, if any
68+ const ipAddress = flattenedHeaderValues . find ( ( ip ) => ip !== null && isIP ( ip ) ) ;
69+
70+ return ipAddress || null ;
7071}
7172
7273function parseForwardedHeader ( value : string | null ) : string | null {
73- if ( ! value ) return null ;
74- for ( const part of value . split ( ';' ) ) {
75- if ( part . startsWith ( 'for=' ) ) return part . slice ( 4 ) ;
76- continue ;
74+ if ( ! value ) {
75+ return null ;
76+ }
77+
78+ for ( const part of value . split ( ";" ) ) {
79+ if ( part . startsWith ( "for=" ) ) {
80+ return part . slice ( 4 ) ;
81+ }
7782 }
83+
7884 return null ;
7985}
0 commit comments