@@ -70,6 +70,35 @@ impl Request {
70
70
self . local_addr
71
71
}
72
72
73
+ /// Get the remote address for this request.
74
+ pub fn remote ( & self ) -> Option < String > {
75
+ self . forwarded_for ( )
76
+ . or_else ( || self . peer_addr . map ( |peer| peer. to_string ( ) ) )
77
+ }
78
+
79
+ /// Parses the Forwarded or X-Forwarded-For headers.
80
+ /// The returned String will either be an IP address or a domain and an optional port.
81
+ pub fn forwarded_for ( & self ) -> Option < String > {
82
+ if let Some ( header) = self . header ( & "Forwarded" . parse ( ) . unwrap ( ) ) {
83
+ header. as_str ( ) . split ( ";" ) . find_map ( |key_equals_value| {
84
+ let parts = key_equals_value. split ( "=" ) . collect :: < Vec < _ > > ( ) ;
85
+ if parts. len ( ) == 2 && parts[ 0 ] . eq_ignore_ascii_case ( "for" ) {
86
+ Some ( String :: from ( parts[ 1 ] ) )
87
+ } else {
88
+ None
89
+ }
90
+ } )
91
+ } else if let Some ( header) = self . header ( & "X-Forwarded-For" . parse ( ) . unwrap ( ) ) {
92
+ header
93
+ . as_str ( )
94
+ . split ( "," )
95
+ . next ( )
96
+ . map ( |client| String :: from ( client) )
97
+ } else {
98
+ None
99
+ }
100
+ }
101
+
73
102
/// Get the HTTP method
74
103
pub fn method ( & self ) -> Method {
75
104
self . method
@@ -556,3 +585,97 @@ impl<'a> IntoIterator for &'a mut Request {
556
585
self . headers . iter_mut ( )
557
586
}
558
587
}
588
+
589
+ #[ cfg( test) ]
590
+ mod tests {
591
+ use super :: * ;
592
+
593
+ fn build_test_request ( ) -> Request {
594
+ Request :: new ( Method :: Get , "http://irrelevant/" . parse ( ) . unwrap ( ) )
595
+ }
596
+
597
+ fn set_x_forwarded_for ( request : & mut Request , client : & ' static str ) {
598
+ request
599
+ . insert_header (
600
+ "x-forwarded-for" ,
601
+ format ! ( "{},proxy.com,other-proxy.com" , client) ,
602
+ )
603
+ . unwrap ( ) ;
604
+ }
605
+
606
+ fn set_forwarded ( request : & mut Request , client : & ' static str ) {
607
+ request
608
+ . insert_header (
609
+ "Forwarded" ,
610
+ format ! ( "by=something.com;for={};host=host.com;proto=http" , client) ,
611
+ )
612
+ . unwrap ( ) ;
613
+ }
614
+
615
+ #[ test]
616
+ fn test_remote_and_forwarded_for_when_forwarded_is_properly_formatted ( ) {
617
+ let mut request = build_test_request ( ) ;
618
+ request. peer_addr = Some ( "127.0.0.1:8000" . parse ( ) . unwrap ( ) ) ;
619
+ set_forwarded ( & mut request, "127.0.0.1:8001" ) ;
620
+
621
+ assert_eq ! (
622
+ request. forwarded_for( ) ,
623
+ Some ( String :: from( "127.0.0.1:8001" ) )
624
+ ) ;
625
+ assert_eq ! ( request. remote( ) , Some ( String :: from( "127.0.0.1:8001" ) ) ) ;
626
+ }
627
+
628
+ #[ test]
629
+ fn test_remote_and_forwarded_for_when_forwarded_is_improperly_formatted ( ) {
630
+ let mut request = build_test_request ( ) ;
631
+ request. peer_addr = Some ( "127.0.0.1:8000" . parse ( ) . unwrap ( ) ) ;
632
+
633
+ request
634
+ . insert_header ( "Forwarded" , "this is an improperly ;;; formatted header" )
635
+ . unwrap ( ) ;
636
+
637
+ assert_eq ! ( request. forwarded_for( ) , None ) ;
638
+ assert_eq ! ( request. remote( ) , Some ( String :: from( "127.0.0.1:8000" ) ) ) ;
639
+ }
640
+
641
+ #[ test]
642
+ fn test_remote_and_forwarded_for_when_x_forwarded_for_is_set ( ) {
643
+ let mut request = build_test_request ( ) ;
644
+ request. peer_addr = Some ( "127.0.0.1:8000" . parse ( ) . unwrap ( ) ) ;
645
+ set_x_forwarded_for ( & mut request, "forwarded-host.com" ) ;
646
+
647
+ assert_eq ! (
648
+ request. forwarded_for( ) ,
649
+ Some ( String :: from( "forwarded-host.com" ) )
650
+ ) ;
651
+
652
+ assert_eq ! ( request. remote( ) , Some ( String :: from( "forwarded-host.com" ) ) ) ;
653
+ }
654
+
655
+ #[ test]
656
+ fn test_remote_and_forwarded_for_when_both_forwarding_headers_are_set ( ) {
657
+ let mut request = build_test_request ( ) ;
658
+ set_forwarded ( & mut request, "forwarded.com" ) ;
659
+ set_x_forwarded_for ( & mut request, "forwarded-for-client.com" ) ;
660
+ request. peer_addr = Some ( "127.0.0.1:8000" . parse ( ) . unwrap ( ) ) ;
661
+
662
+ assert_eq ! ( request. forwarded_for( ) , Some ( "forwarded.com" . into( ) ) ) ;
663
+ assert_eq ! ( request. remote( ) , Some ( "forwarded.com" . into( ) ) ) ;
664
+ }
665
+
666
+ #[ test]
667
+ fn test_remote_and_forwarded_for_falling_back_to_peer_addr ( ) {
668
+ let mut request = build_test_request ( ) ;
669
+ request. peer_addr = Some ( "127.0.0.1:8000" . parse ( ) . unwrap ( ) ) ;
670
+
671
+ assert_eq ! ( request. forwarded_for( ) , None ) ;
672
+ assert_eq ! ( request. remote( ) , Some ( "127.0.0.1:8000" . into( ) ) ) ;
673
+ }
674
+
675
+ #[ test]
676
+ fn test_remote_and_forwarded_for_when_no_remote_available ( ) {
677
+ let request = build_test_request ( ) ;
678
+ assert_eq ! ( request. forwarded_for( ) , None ) ;
679
+ assert_eq ! ( request. remote( ) , None ) ;
680
+ }
681
+ }
0 commit comments