Skip to content

Commit 67a2898

Browse files
committed
Allow ignoring invalid header lines (fixes #61, #83)
1 parent 7e97ef1 commit 67a2898

File tree

1 file changed

+271
-15
lines changed

1 file changed

+271
-15
lines changed

src/lib.rs

Lines changed: 271 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,11 @@ pub struct ParserConfig {
247247
allow_obsolete_multiline_headers_in_responses: bool,
248248
allow_multiple_spaces_in_request_line_delimiters: bool,
249249
allow_multiple_spaces_in_response_status_delimiters: bool,
250+
ignore_invalid_headers_in_responses: bool,
250251
}
251252

252253
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.
254255
pub fn allow_spaces_after_header_name_in_responses(
255256
&mut self,
256257
value: bool,
@@ -361,6 +362,26 @@ impl ParserConfig {
361362
request.parse_with_config_and_uninit_headers(buf, self, headers)
362363
}
363364

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+
364385
/// Parses a response with the given config.
365386
pub fn parse_response<'headers, 'buf>(
366387
&self,
@@ -947,8 +968,30 @@ fn parse_headers_iter_uninit<'a, 'b>(
947968
}
948969

949970
'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+
950993
// a newline here means the head is over!
951-
let mut b = next!(bytes);
994+
let b = next!(bytes);
952995
if b == b'\r' {
953996
expect!(bytes.next() == b'\n' => Err(Error::NewLine));
954997
result = Ok(Status::Complete(count + bytes.pos()));
@@ -959,14 +1002,9 @@ fn parse_headers_iter_uninit<'a, 'b>(
9591002
break;
9601003
}
9611004
if !is_header_name_token(b) {
962-
return Err(Error::HeaderName);
1005+
continue_with_new_header_on_next_line!(bytes, b, HeaderName);
9631006
}
9641007

965-
let uninit_header = match iter.next() {
966-
Some(header) => header,
967-
None => break 'headers
968-
};
969-
9701008
// parse header name until colon
9711009
let header_name: &str = 'name: loop {
9721010
let mut b = next!(bytes);
@@ -996,7 +1034,7 @@ fn parse_headers_iter_uninit<'a, 'b>(
9961034
}
9971035
}
9981036

999-
return Err(Error::HeaderName);
1037+
continue_with_new_header_on_next_line!(bytes, b, HeaderName);
10001038
};
10011039

10021040
let mut b;
@@ -1017,7 +1055,7 @@ fn parse_headers_iter_uninit<'a, 'b>(
10171055
b = next!(bytes);
10181056
}
10191057
if b != b'\n' {
1020-
return Err(Error::HeaderValue);
1058+
continue_with_new_header_on_next_line!(bytes, b, HeaderValue);
10211059
}
10221060

10231061
maybe_continue_after_obsolete_line_folding!(bytes, 'whitespace_after_colon);
@@ -1071,7 +1109,7 @@ fn parse_headers_iter_uninit<'a, 'b>(
10711109
} else if b == b'\n' {
10721110
1
10731111
} else {
1074-
return Err(Error::HeaderValue);
1112+
continue_with_new_header_on_next_line!(bytes, b, HeaderValue);
10751113
};
10761114

10771115
maybe_continue_after_obsolete_line_folding!(bytes, 'value_lines);
@@ -1084,6 +1122,11 @@ fn parse_headers_iter_uninit<'a, 'b>(
10841122
}
10851123
};
10861124

1125+
let uninit_header = match iter.next() {
1126+
Some(header) => header,
1127+
None => break 'headers
1128+
};
1129+
10871130
// trim trailing whitespace in the header
10881131
let header_value = if let Some(last_visible) = value_slice
10891132
.iter()
@@ -1616,6 +1659,23 @@ mod tests {
16161659
assert_eq!(response.headers[1].value, &b"baguette"[..]);
16171660
}
16181661

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+
16191679
static REQUEST_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON: &'static [u8] =
16201680
b"GET / HTTP/1.1\r\nHost : localhost\r\n\r\n";
16211681

@@ -1888,17 +1948,213 @@ mod tests {
18881948
assert_eq!(result, Err(::Error::Status));
18891949
}
18901950

1891-
static RESPONSE_WITH_INVALID_CHAR_BETWEEN_HEADER_NAME_AND_COLON: &'static [u8] =
1892-
b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials\xFF: true\r\nBread: 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\nBread: 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\nAccess-Control-Allow-Credentials\xFF : true\r\nBread: 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\nAccess-Control-Allow-Credentials\r\nBread: 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\nAccess-Control-Allow-Credentials \r\n hello\r\nBread: 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\nAccess-Control-Allow-Cred\0entials: hello\r\nBread: 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+
}
18932072

18942073
#[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\nAccess-Control-Allow-Credentials \0: hello\r\nBread: baguette\r\n\r\n";
2077+
18962078
let mut headers = [EMPTY_HEADER; 2];
18972079
let mut response = Response::new(&mut headers[..]);
2080+
18982081
let result = ::ParserConfig::default()
18992082
.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));
19012085

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);
19022090
assert_eq!(result, Err(::Error::HeaderName));
19032091
}
2092+
2093+
#[test]
2094+
fn test_header_with_nul_in_value() {
2095+
const RESPONSE: &[u8] =
2096+
b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials: hell\0o\r\nBread: 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\nAccess-Control-Allow-Credentials: hell\x01o\r\nBread: 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\nAccess-Control-Allow-Credentials: hell\x01o \n world!\r\nBread: 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+
}
19042160
}

0 commit comments

Comments
 (0)