@@ -247,10 +247,11 @@ pub struct ParserConfig {
247
247
allow_obsolete_multiline_headers_in_responses : bool ,
248
248
allow_multiple_spaces_in_request_line_delimiters : bool ,
249
249
allow_multiple_spaces_in_response_status_delimiters : bool ,
250
+ ignore_invalid_headers_in_responses : bool ,
250
251
}
251
252
252
253
impl ParserConfig {
253
- /// Sets whether spaces should be allowed after header name .
254
+ /// Sets whether spaces and tabs should be allowed after header names in responses .
254
255
pub fn allow_spaces_after_header_name_in_responses (
255
256
& mut self ,
256
257
value : bool ,
@@ -361,6 +362,26 @@ impl ParserConfig {
361
362
request. parse_with_config_and_uninit_headers ( buf, self , headers)
362
363
}
363
364
365
+ /// Sets whether invalid header lines should be silently ignored in responses.
366
+ ///
367
+ /// This mimicks the behaviour of major browsers. You probably don't want this.
368
+ /// You should only want this if you are implementing a proxy whose main
369
+ /// purpose is to sit in front of browsers whose users access arbitrary content
370
+ /// which may be malformed, and they expect everything that works without
371
+ /// the proxy to keep working with the proxy.
372
+ ///
373
+ /// This option will prevent `ParserConfig::parse_response` from returning
374
+ /// an error encountered when parsing a header, except if the error was caused
375
+ /// by the character NUL (ASCII code 0), as Chrome specifically always reject
376
+ /// those.
377
+ pub fn ignore_invalid_headers_in_responses (
378
+ & mut self ,
379
+ value : bool ,
380
+ ) -> & mut Self {
381
+ self . ignore_invalid_headers_in_responses = value;
382
+ self
383
+ }
384
+
364
385
/// Parses a response with the given config.
365
386
pub fn parse_response < ' headers , ' buf > (
366
387
& self ,
@@ -947,8 +968,30 @@ fn parse_headers_iter_uninit<'a, 'b>(
947
968
}
948
969
949
970
' headers: loop {
971
+ macro_rules! continue_with_new_header_on_next_line {
972
+ ( $bytes: ident, $b: ident, $err: ident) => {
973
+ if !config. ignore_invalid_headers_in_responses {
974
+ return Err ( Error :: $err) ;
975
+ }
976
+
977
+ let mut b = $b;
978
+
979
+ while b != b'\n' {
980
+ if b == b'\0' {
981
+ return Err ( Error :: $err) ;
982
+ }
983
+ b = next!( $bytes) ;
984
+ }
985
+
986
+ count += $bytes. pos( ) ;
987
+ $bytes. slice( ) ;
988
+
989
+ continue ' headers;
990
+ } ;
991
+ }
992
+
950
993
// a newline here means the head is over!
951
- let mut b = next ! ( bytes) ;
994
+ let b = next ! ( bytes) ;
952
995
if b == b'\r' {
953
996
expect ! ( bytes. next( ) == b'\n' => Err ( Error :: NewLine ) ) ;
954
997
result = Ok ( Status :: Complete ( count + bytes. pos ( ) ) ) ;
@@ -959,14 +1002,9 @@ fn parse_headers_iter_uninit<'a, 'b>(
959
1002
break ;
960
1003
}
961
1004
if !is_header_name_token ( b) {
962
- return Err ( Error :: HeaderName ) ;
1005
+ continue_with_new_header_on_next_line ! ( bytes , b , HeaderName ) ;
963
1006
}
964
1007
965
- let uninit_header = match iter. next ( ) {
966
- Some ( header) => header,
967
- None => break ' headers
968
- } ;
969
-
970
1008
// parse header name until colon
971
1009
let header_name: & str = ' name: loop {
972
1010
let mut b = next ! ( bytes) ;
@@ -996,7 +1034,7 @@ fn parse_headers_iter_uninit<'a, 'b>(
996
1034
}
997
1035
}
998
1036
999
- return Err ( Error :: HeaderName ) ;
1037
+ continue_with_new_header_on_next_line ! ( bytes , b , HeaderName ) ;
1000
1038
} ;
1001
1039
1002
1040
let mut b;
@@ -1017,7 +1055,7 @@ fn parse_headers_iter_uninit<'a, 'b>(
1017
1055
b = next ! ( bytes) ;
1018
1056
}
1019
1057
if b != b'\n' {
1020
- return Err ( Error :: HeaderValue ) ;
1058
+ continue_with_new_header_on_next_line ! ( bytes , b , HeaderValue ) ;
1021
1059
}
1022
1060
1023
1061
maybe_continue_after_obsolete_line_folding ! ( bytes, ' whitespace_after_colon) ;
@@ -1071,7 +1109,7 @@ fn parse_headers_iter_uninit<'a, 'b>(
1071
1109
} else if b == b'\n' {
1072
1110
1
1073
1111
} else {
1074
- return Err ( Error :: HeaderValue ) ;
1112
+ continue_with_new_header_on_next_line ! ( bytes , b , HeaderValue ) ;
1075
1113
} ;
1076
1114
1077
1115
maybe_continue_after_obsolete_line_folding ! ( bytes, ' value_lines) ;
@@ -1084,6 +1122,11 @@ fn parse_headers_iter_uninit<'a, 'b>(
1084
1122
}
1085
1123
} ;
1086
1124
1125
+ let uninit_header = match iter. next ( ) {
1126
+ Some ( header) => header,
1127
+ None => break ' headers
1128
+ } ;
1129
+
1087
1130
// trim trailing whitespace in the header
1088
1131
let header_value = if let Some ( last_visible) = value_slice
1089
1132
. iter ( )
@@ -1616,6 +1659,23 @@ mod tests {
1616
1659
assert_eq ! ( response. headers[ 1 ] . value, & b"baguette" [ ..] ) ;
1617
1660
}
1618
1661
1662
+ #[ test]
1663
+ fn test_ignore_header_line_with_whitespaces_after_header_name ( ) {
1664
+ let mut headers = [ EMPTY_HEADER ; 2 ] ;
1665
+ let mut response = Response :: new ( & mut headers[ ..] ) ;
1666
+ let result = :: ParserConfig :: default ( )
1667
+ . ignore_invalid_headers_in_responses ( true )
1668
+ . parse_response ( & mut response, RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON ) ;
1669
+
1670
+ assert_eq ! ( result, Ok ( Status :: Complete ( 77 ) ) ) ;
1671
+ assert_eq ! ( response. version. unwrap( ) , 1 ) ;
1672
+ assert_eq ! ( response. code. unwrap( ) , 200 ) ;
1673
+ assert_eq ! ( response. reason. unwrap( ) , "OK" ) ;
1674
+ assert_eq ! ( response. headers. len( ) , 1 ) ;
1675
+ assert_eq ! ( response. headers[ 0 ] . name, "Bread" ) ;
1676
+ assert_eq ! ( response. headers[ 0 ] . value, & b"baguette" [ ..] ) ;
1677
+ }
1678
+
1619
1679
static REQUEST_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON : & ' static [ u8 ] =
1620
1680
b"GET / HTTP/1.1\r \n Host : localhost\r \n \r \n " ;
1621
1681
@@ -1888,17 +1948,213 @@ mod tests {
1888
1948
assert_eq ! ( result, Err ( :: Error :: Status ) ) ;
1889
1949
}
1890
1950
1891
- static RESPONSE_WITH_INVALID_CHAR_BETWEEN_HEADER_NAME_AND_COLON : & ' static [ u8 ] =
1892
- b"HTTP/1.1 200 OK\r \n Access-Control-Allow-Credentials\xFF : true\r \n Bread: baguette\r \n \r \n " ;
1951
+ #[ test]
1952
+ fn test_response_with_empty_header_name ( ) {
1953
+ const RESPONSE : & [ u8 ] =
1954
+ b"HTTP/1.1 200 OK\r \n : hello\r \n Bread: baguette\r \n \r \n " ;
1955
+
1956
+ let mut headers = [ EMPTY_HEADER ; 2 ] ;
1957
+ let mut response = Response :: new ( & mut headers[ ..] ) ;
1958
+
1959
+ let result = :: ParserConfig :: default ( )
1960
+ . allow_spaces_after_header_name_in_responses ( true )
1961
+ . parse_response ( & mut response, RESPONSE ) ;
1962
+ assert_eq ! ( result, Err ( :: Error :: HeaderName ) ) ;
1963
+
1964
+ let result = :: ParserConfig :: default ( )
1965
+ . ignore_invalid_headers_in_responses ( true )
1966
+ . parse_response ( & mut response, RESPONSE ) ;
1967
+ assert_eq ! ( result, Ok ( Status :: Complete ( 45 ) ) ) ;
1968
+
1969
+ assert_eq ! ( response. version. unwrap( ) , 1 ) ;
1970
+ assert_eq ! ( response. code. unwrap( ) , 200 ) ;
1971
+ assert_eq ! ( response. reason. unwrap( ) , "OK" ) ;
1972
+ assert_eq ! ( response. headers. len( ) , 1 ) ;
1973
+ assert_eq ! ( response. headers[ 0 ] . name, "Bread" ) ;
1974
+ assert_eq ! ( response. headers[ 0 ] . value, & b"baguette" [ ..] ) ;
1975
+ }
1976
+
1977
+ #[ test]
1978
+ fn test_response_with_invalid_char_between_header_name_and_colon ( ) {
1979
+ const RESPONSE : & [ u8 ] =
1980
+ b"HTTP/1.1 200 OK\r \n Access-Control-Allow-Credentials\xFF : true\r \n Bread: baguette\r \n \r \n " ;
1981
+
1982
+ let mut headers = [ EMPTY_HEADER ; 2 ] ;
1983
+ let mut response = Response :: new ( & mut headers[ ..] ) ;
1984
+
1985
+ let result = :: ParserConfig :: default ( )
1986
+ . allow_spaces_after_header_name_in_responses ( true )
1987
+ . parse_response ( & mut response, RESPONSE ) ;
1988
+ assert_eq ! ( result, Err ( :: Error :: HeaderName ) ) ;
1989
+
1990
+ let result = :: ParserConfig :: default ( )
1991
+ . ignore_invalid_headers_in_responses ( true )
1992
+ . parse_response ( & mut response, RESPONSE ) ;
1993
+
1994
+ assert_eq ! ( result, Ok ( Status :: Complete ( 79 ) ) ) ;
1995
+ assert_eq ! ( response. version. unwrap( ) , 1 ) ;
1996
+ assert_eq ! ( response. code. unwrap( ) , 200 ) ;
1997
+ assert_eq ! ( response. reason. unwrap( ) , "OK" ) ;
1998
+ assert_eq ! ( response. headers. len( ) , 1 ) ;
1999
+ assert_eq ! ( response. headers[ 0 ] . name, "Bread" ) ;
2000
+ assert_eq ! ( response. headers[ 0 ] . value, & b"baguette" [ ..] ) ;
2001
+ }
2002
+
2003
+ #[ test]
2004
+ fn test_ignore_header_line_with_missing_colon ( ) {
2005
+ const RESPONSE : & [ u8 ] =
2006
+ b"HTTP/1.1 200 OK\r \n Access-Control-Allow-Credentials\r \n Bread: baguette\r \n \r \n " ;
2007
+
2008
+ let mut headers = [ EMPTY_HEADER ; 2 ] ;
2009
+ let mut response = Response :: new ( & mut headers[ ..] ) ;
2010
+
2011
+ let result = :: ParserConfig :: default ( )
2012
+ . parse_response ( & mut response, RESPONSE ) ;
2013
+ assert_eq ! ( result, Err ( :: Error :: HeaderName ) ) ;
2014
+
2015
+ let result = :: ParserConfig :: default ( )
2016
+ . ignore_invalid_headers_in_responses ( true )
2017
+ . parse_response ( & mut response, RESPONSE ) ;
2018
+ assert_eq ! ( result, Ok ( Status :: Complete ( 70 ) ) ) ;
2019
+
2020
+ assert_eq ! ( response. version. unwrap( ) , 1 ) ;
2021
+ assert_eq ! ( response. code. unwrap( ) , 200 ) ;
2022
+ assert_eq ! ( response. reason. unwrap( ) , "OK" ) ;
2023
+ assert_eq ! ( response. headers. len( ) , 1 ) ;
2024
+ assert_eq ! ( response. headers[ 0 ] . name, "Bread" ) ;
2025
+ assert_eq ! ( response. headers[ 0 ] . value, & b"baguette" [ ..] ) ;
2026
+ }
2027
+
2028
+ #[ test]
2029
+ fn test_header_with_missing_colon_with_folding ( ) {
2030
+ const RESPONSE : & [ u8 ] =
2031
+ b"HTTP/1.1 200 OK\r \n Access-Control-Allow-Credentials \r \n hello\r \n Bread: baguette\r \n \r \n " ;
2032
+
2033
+ let mut headers = [ EMPTY_HEADER ; 2 ] ;
2034
+ let mut response = Response :: new ( & mut headers[ ..] ) ;
2035
+
2036
+ let result = :: ParserConfig :: default ( )
2037
+ . allow_obsolete_multiline_headers_in_responses ( true )
2038
+ . allow_spaces_after_header_name_in_responses ( true )
2039
+ . parse_response ( & mut response, RESPONSE ) ;
2040
+ assert_eq ! ( result, Err ( :: Error :: HeaderName ) ) ;
2041
+
2042
+ let result = :: ParserConfig :: default ( )
2043
+ . ignore_invalid_headers_in_responses ( true )
2044
+ . parse_response ( & mut response, RESPONSE ) ;
2045
+ assert_eq ! ( result, Ok ( Status :: Complete ( 81 ) ) ) ;
2046
+
2047
+ assert_eq ! ( response. version. unwrap( ) , 1 ) ;
2048
+ assert_eq ! ( response. code. unwrap( ) , 200 ) ;
2049
+ assert_eq ! ( response. reason. unwrap( ) , "OK" ) ;
2050
+ assert_eq ! ( response. headers. len( ) , 1 ) ;
2051
+ assert_eq ! ( response. headers[ 0 ] . name, "Bread" ) ;
2052
+ assert_eq ! ( response. headers[ 0 ] . value, & b"baguette" [ ..] ) ;
2053
+ }
2054
+
2055
+ #[ test]
2056
+ fn test_header_with_nul_in_header_name ( ) {
2057
+ const RESPONSE : & [ u8 ] =
2058
+ b"HTTP/1.1 200 OK\r \n Access-Control-Allow-Cred\0 entials: hello\r \n Bread: baguette\r \n \r \n " ;
2059
+
2060
+ let mut headers = [ EMPTY_HEADER ; 2 ] ;
2061
+ let mut response = Response :: new ( & mut headers[ ..] ) ;
2062
+
2063
+ let result = :: ParserConfig :: default ( )
2064
+ . parse_response ( & mut response, RESPONSE ) ;
2065
+ assert_eq ! ( result, Err ( :: Error :: HeaderName ) ) ;
2066
+
2067
+ let result = :: ParserConfig :: default ( )
2068
+ . ignore_invalid_headers_in_responses ( true )
2069
+ . parse_response ( & mut response, RESPONSE ) ;
2070
+ assert_eq ! ( result, Err ( :: Error :: HeaderName ) ) ;
2071
+ }
1893
2072
1894
2073
#[ test]
1895
- fn test_forbid_response_with_invalid_char_between_header_name_and_colon ( ) {
2074
+ fn test_header_with_nul_in_whitespace_before_colon ( ) {
2075
+ const RESPONSE : & [ u8 ] =
2076
+ b"HTTP/1.1 200 OK\r \n Access-Control-Allow-Credentials \0 : hello\r \n Bread: baguette\r \n \r \n " ;
2077
+
1896
2078
let mut headers = [ EMPTY_HEADER ; 2 ] ;
1897
2079
let mut response = Response :: new ( & mut headers[ ..] ) ;
2080
+
1898
2081
let result = :: ParserConfig :: default ( )
1899
2082
. allow_spaces_after_header_name_in_responses ( true )
1900
- . parse_response ( & mut response, RESPONSE_WITH_INVALID_CHAR_BETWEEN_HEADER_NAME_AND_COLON ) ;
2083
+ . parse_response ( & mut response, RESPONSE ) ;
2084
+ assert_eq ! ( result, Err ( :: Error :: HeaderName ) ) ;
1901
2085
2086
+ let result = :: ParserConfig :: default ( )
2087
+ . allow_spaces_after_header_name_in_responses ( true )
2088
+ . ignore_invalid_headers_in_responses ( true )
2089
+ . parse_response ( & mut response, RESPONSE ) ;
1902
2090
assert_eq ! ( result, Err ( :: Error :: HeaderName ) ) ;
1903
2091
}
2092
+
2093
+ #[ test]
2094
+ fn test_header_with_nul_in_value ( ) {
2095
+ const RESPONSE : & [ u8 ] =
2096
+ b"HTTP/1.1 200 OK\r \n Access-Control-Allow-Credentials: hell\0 o\r \n Bread: baguette\r \n \r \n " ;
2097
+
2098
+ let mut headers = [ EMPTY_HEADER ; 2 ] ;
2099
+ let mut response = Response :: new ( & mut headers[ ..] ) ;
2100
+
2101
+ let result = :: ParserConfig :: default ( )
2102
+ . parse_response ( & mut response, RESPONSE ) ;
2103
+ assert_eq ! ( result, Err ( :: Error :: HeaderValue ) ) ;
2104
+
2105
+ let result = :: ParserConfig :: default ( )
2106
+ . ignore_invalid_headers_in_responses ( true )
2107
+ . parse_response ( & mut response, RESPONSE ) ;
2108
+ assert_eq ! ( result, Err ( :: Error :: HeaderValue ) ) ;
2109
+ }
2110
+
2111
+ #[ test]
2112
+ fn test_header_with_invalid_char_in_value ( ) {
2113
+ const RESPONSE : & [ u8 ] =
2114
+ b"HTTP/1.1 200 OK\r \n Access-Control-Allow-Credentials: hell\x01 o\r \n Bread: baguette\r \n \r \n " ;
2115
+
2116
+ let mut headers = [ EMPTY_HEADER ; 2 ] ;
2117
+ let mut response = Response :: new ( & mut headers[ ..] ) ;
2118
+
2119
+ let result = :: ParserConfig :: default ( )
2120
+ . parse_response ( & mut response, RESPONSE ) ;
2121
+ assert_eq ! ( result, Err ( :: Error :: HeaderValue ) ) ;
2122
+
2123
+ let result = :: ParserConfig :: default ( )
2124
+ . ignore_invalid_headers_in_responses ( true )
2125
+ . parse_response ( & mut response, RESPONSE ) ;
2126
+ assert_eq ! ( result, Ok ( Status :: Complete ( 78 ) ) ) ;
2127
+
2128
+ assert_eq ! ( response. version. unwrap( ) , 1 ) ;
2129
+ assert_eq ! ( response. code. unwrap( ) , 200 ) ;
2130
+ assert_eq ! ( response. reason. unwrap( ) , "OK" ) ;
2131
+ assert_eq ! ( response. headers. len( ) , 1 ) ;
2132
+ assert_eq ! ( response. headers[ 0 ] . name, "Bread" ) ;
2133
+ assert_eq ! ( response. headers[ 0 ] . value, & b"baguette" [ ..] ) ;
2134
+ }
2135
+
2136
+ #[ test]
2137
+ fn test_header_with_invalid_char_in_value_with_folding ( ) {
2138
+ const RESPONSE : & [ u8 ] =
2139
+ b"HTTP/1.1 200 OK\r \n Access-Control-Allow-Credentials: hell\x01 o \n world!\r \n Bread: baguette\r \n \r \n " ;
2140
+
2141
+ let mut headers = [ EMPTY_HEADER ; 2 ] ;
2142
+ let mut response = Response :: new ( & mut headers[ ..] ) ;
2143
+
2144
+ let result = :: ParserConfig :: default ( )
2145
+ . parse_response ( & mut response, RESPONSE ) ;
2146
+ assert_eq ! ( result, Err ( :: Error :: HeaderValue ) ) ;
2147
+
2148
+ let result = :: ParserConfig :: default ( )
2149
+ . ignore_invalid_headers_in_responses ( true )
2150
+ . parse_response ( & mut response, RESPONSE ) ;
2151
+ assert_eq ! ( result, Ok ( Status :: Complete ( 88 ) ) ) ;
2152
+
2153
+ assert_eq ! ( response. version. unwrap( ) , 1 ) ;
2154
+ assert_eq ! ( response. code. unwrap( ) , 200 ) ;
2155
+ assert_eq ! ( response. reason. unwrap( ) , "OK" ) ;
2156
+ assert_eq ! ( response. headers. len( ) , 1 ) ;
2157
+ assert_eq ! ( response. headers[ 0 ] . name, "Bread" ) ;
2158
+ assert_eq ! ( response. headers[ 0 ] . value, & b"baguette" [ ..] ) ;
2159
+ }
1904
2160
}
0 commit comments