Skip to content

Commit a3c10fd

Browse files
authored
Add option to ignore invalid headers for requests (#145)
1 parent 6b5d4d9 commit a3c10fd

File tree

2 files changed

+224
-13
lines changed

2 files changed

+224
-13
lines changed

src/lib.rs

Lines changed: 223 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ pub struct ParserConfig {
260260
allow_multiple_spaces_in_request_line_delimiters: bool,
261261
allow_multiple_spaces_in_response_status_delimiters: bool,
262262
ignore_invalid_headers_in_responses: bool,
263+
ignore_invalid_headers_in_requests: bool,
263264
}
264265

265266
impl ParserConfig {
@@ -412,6 +413,15 @@ impl ParserConfig {
412413
self
413414
}
414415

416+
/// Sets whether invalid header lines should be silently ignored in requests.
417+
pub fn ignore_invalid_headers_in_requests(
418+
&mut self,
419+
value: bool,
420+
) -> &mut Self {
421+
self.ignore_invalid_headers_in_requests = value;
422+
self
423+
}
424+
415425
/// Parses a response with the given config.
416426
pub fn parse_response<'buf>(
417427
&self,
@@ -506,7 +516,11 @@ impl<'h, 'b> Request<'h, 'b> {
506516
let headers_len = complete!(parse_headers_iter_uninit(
507517
&mut headers,
508518
&mut bytes,
509-
&ParserConfig::default(),
519+
&HeaderParserConfig {
520+
allow_spaces_after_header_name: false,
521+
allow_obsolete_multiline_headers: false,
522+
ignore_invalid_headers: config.ignore_invalid_headers_in_requests
523+
},
510524
));
511525
/* SAFETY: see `parse_headers_iter_uninit` guarantees */
512526
self.headers = unsafe { assume_init_slice(headers) };
@@ -699,7 +713,11 @@ impl<'h, 'b> Response<'h, 'b> {
699713
let headers_len = complete!(parse_headers_iter_uninit(
700714
&mut headers,
701715
&mut bytes,
702-
config
716+
&HeaderParserConfig {
717+
allow_spaces_after_header_name: config.allow_spaces_after_header_name_in_responses,
718+
allow_obsolete_multiline_headers: config.allow_obsolete_multiline_headers_in_responses,
719+
ignore_invalid_headers: config.ignore_invalid_headers_in_responses
720+
}
703721
));
704722
/* SAFETY: see `parse_headers_iter_uninit` guarantees */
705723
self.headers = unsafe { assume_init_slice(headers) };
@@ -950,15 +968,15 @@ pub fn parse_headers<'b: 'h, 'h>(
950968
mut dst: &'h mut [Header<'b>],
951969
) -> Result<(usize, &'h [Header<'b>])> {
952970
let mut iter = Bytes::new(src);
953-
let pos = complete!(parse_headers_iter(&mut dst, &mut iter, &ParserConfig::default()));
971+
let pos = complete!(parse_headers_iter(&mut dst, &mut iter, &HeaderParserConfig::default()));
954972
Ok(Status::Complete((pos, dst)))
955973
}
956974

957975
#[inline]
958976
fn parse_headers_iter<'a>(
959977
headers: &mut &mut [Header<'a>],
960978
bytes: &mut Bytes<'a>,
961-
config: &ParserConfig,
979+
config: &HeaderParserConfig,
962980
) -> Result<usize> {
963981
parse_headers_iter_uninit(
964982
/* SAFETY: see `parse_headers_iter_uninit` guarantees */
@@ -979,6 +997,13 @@ unsafe fn assume_init_slice<T>(s: &mut [MaybeUninit<T>]) -> &mut [T] {
979997
&mut *s
980998
}
981999

1000+
#[derive(Clone, Debug, Default)]
1001+
struct HeaderParserConfig {
1002+
allow_spaces_after_header_name: bool,
1003+
allow_obsolete_multiline_headers: bool,
1004+
ignore_invalid_headers: bool,
1005+
}
1006+
9821007
/* Function which parsers headers into uninitialized buffer.
9831008
*
9841009
* Guarantees that it doesn't write garbage, so casting
@@ -991,7 +1016,7 @@ unsafe fn assume_init_slice<T>(s: &mut [MaybeUninit<T>]) -> &mut [T] {
9911016
fn parse_headers_iter_uninit<'a>(
9921017
headers: &mut &mut [MaybeUninit<Header<'a>>],
9931018
bytes: &mut Bytes<'a>,
994-
config: &ParserConfig,
1019+
config: &HeaderParserConfig
9951020
) -> Result<usize> {
9961021

9971022
/* Flow of this function is pretty complex, especially with macros,
@@ -1026,7 +1051,7 @@ fn parse_headers_iter_uninit<'a>(
10261051

10271052
macro_rules! maybe_continue_after_obsolete_line_folding {
10281053
($bytes:ident, $label:lifetime) => {
1029-
if config.allow_obsolete_multiline_headers_in_responses {
1054+
if config.allow_obsolete_multiline_headers {
10301055
match $bytes.peek() {
10311056
None => {
10321057
// Next byte may be a space, in which case that header
@@ -1055,7 +1080,7 @@ fn parse_headers_iter_uninit<'a>(
10551080
// parsing on the next one.
10561081
macro_rules! handle_invalid_char {
10571082
($bytes:ident, $b:ident, $err:ident) => {
1058-
if !config.ignore_invalid_headers_in_responses {
1083+
if !config.ignore_invalid_headers {
10591084
return Err(Error::$err);
10601085
}
10611086

@@ -1114,7 +1139,7 @@ fn parse_headers_iter_uninit<'a>(
11141139
break 'name name;
11151140
}
11161141

1117-
if config.allow_spaces_after_header_name_in_responses {
1142+
if config.allow_spaces_after_header_name {
11181143
while b == b' ' || b == b'\t' {
11191144
b = next!(bytes);
11201145

@@ -1733,7 +1758,7 @@ mod tests {
17331758
}
17341759

17351760
#[test]
1736-
fn test_ignore_header_line_with_whitespaces_after_header_name() {
1761+
fn test_ignore_header_line_with_whitespaces_after_header_name_in_response() {
17371762
let mut headers = [EMPTY_HEADER; 2];
17381763
let mut response = Response::new(&mut headers[..]);
17391764
let result = crate::ParserConfig::default()
@@ -1761,6 +1786,17 @@ mod tests {
17611786
assert_eq!(result, Err(crate::Error::HeaderName));
17621787
}
17631788

1789+
#[test]
1790+
fn test_ignore_header_line_with_whitespaces_after_header_name_in_request() {
1791+
let mut headers = [EMPTY_HEADER; 2];
1792+
let mut request = Request::new(&mut headers[..]);
1793+
let result = crate::ParserConfig::default()
1794+
.ignore_invalid_headers_in_requests(true)
1795+
.parse_request(&mut request, REQUEST_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON);
1796+
1797+
assert_eq!(result, Ok(Status::Complete(36)));
1798+
}
1799+
17641800
static RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_START: &[u8] =
17651801
b"HTTP/1.1 200 OK\r\nLine-Folded-Header: \r\n \r\n hello there\r\n\r\n";
17661802

@@ -2047,6 +2083,24 @@ mod tests {
20472083
assert_eq!(response.headers[0].value, &b"baguette"[..]);
20482084
}
20492085

2086+
#[test]
2087+
fn test_request_with_empty_header_name() {
2088+
const RESPONSE: &[u8] =
2089+
b"GET / HTTP/1.1\r\n: hello\r\nBread: baguette\r\n\r\n";
2090+
2091+
let mut headers = [EMPTY_HEADER; 2];
2092+
let mut request = Request::new(&mut headers[..]);
2093+
2094+
let result = crate::ParserConfig::default()
2095+
.parse_request(&mut request, RESPONSE);
2096+
assert_eq!(result, Err(crate::Error::HeaderName));
2097+
2098+
let result = crate::ParserConfig::default()
2099+
.ignore_invalid_headers_in_requests(true)
2100+
.parse_request(&mut request, RESPONSE);
2101+
assert_eq!(result, Ok(Status::Complete(44)));
2102+
}
2103+
20502104
#[test]
20512105
fn test_request_with_whitespace_between_header_name_and_colon() {
20522106
const REQUEST: &[u8] =
@@ -2094,7 +2148,25 @@ mod tests {
20942148
}
20952149

20962150
#[test]
2097-
fn test_ignore_header_line_with_missing_colon() {
2151+
fn test_request_with_invalid_char_between_header_name_and_colon() {
2152+
const REQUEST: &[u8] =
2153+
b"GET / HTTP/1.1\r\nAccess-Control-Allow-Credentials\xFF : true\r\nBread: baguette\r\n\r\n";
2154+
2155+
let mut headers = [EMPTY_HEADER; 2];
2156+
let mut request = Request::new(&mut headers[..]);
2157+
2158+
let result = crate::ParserConfig::default()
2159+
.parse_request(&mut request, REQUEST);
2160+
assert_eq!(result, Err(crate::Error::HeaderName));
2161+
2162+
let result = crate::ParserConfig::default()
2163+
.ignore_invalid_headers_in_requests(true)
2164+
.parse_request(&mut request, REQUEST);
2165+
assert_eq!(result, Ok(Status::Complete(78)));
2166+
}
2167+
2168+
#[test]
2169+
fn test_ignore_header_line_with_missing_colon_in_response() {
20982170
const RESPONSE: &[u8] =
20992171
b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials\r\nBread: baguette\r\n\r\n";
21002172

@@ -2119,7 +2191,25 @@ mod tests {
21192191
}
21202192

21212193
#[test]
2122-
fn test_header_with_missing_colon_with_folding() {
2194+
fn test_ignore_header_line_with_missing_colon_in_request() {
2195+
const REQUEST: &[u8] =
2196+
b"GET / HTTP/1.1\r\nAccess-Control-Allow-Credentials\r\nBread: baguette\r\n\r\n";
2197+
2198+
let mut headers = [EMPTY_HEADER; 2];
2199+
let mut request = Request::new(&mut headers[..]);
2200+
2201+
let result = crate::ParserConfig::default()
2202+
.parse_request(&mut request, REQUEST);
2203+
assert_eq!(result, Err(crate::Error::HeaderName));
2204+
2205+
let result = crate::ParserConfig::default()
2206+
.ignore_invalid_headers_in_requests(true)
2207+
.parse_request(&mut request, REQUEST);
2208+
assert_eq!(result, Ok(Status::Complete(69)));
2209+
}
2210+
2211+
#[test]
2212+
fn test_response_header_with_missing_colon_with_folding() {
21232213
const RESPONSE: &[u8] =
21242214
b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials \r\n hello\r\nBread: baguette\r\n\r\n";
21252215

@@ -2146,7 +2236,25 @@ mod tests {
21462236
}
21472237

21482238
#[test]
2149-
fn test_header_with_nul_in_header_name() {
2239+
fn test_request_header_with_missing_colon_with_folding() {
2240+
const REQUEST: &[u8] =
2241+
b"GET / HTTP/1.1\r\nAccess-Control-Allow-Credentials \r\n hello\r\nBread: baguette\r\n\r\n";
2242+
2243+
let mut headers = [EMPTY_HEADER; 2];
2244+
let mut request = Request::new(&mut headers[..]);
2245+
2246+
let result = crate::ParserConfig::default()
2247+
.parse_request(&mut request, REQUEST);
2248+
assert_eq!(result, Err(crate::Error::HeaderName));
2249+
2250+
let result = crate::ParserConfig::default()
2251+
.ignore_invalid_headers_in_requests(true)
2252+
.parse_request(&mut request, REQUEST);
2253+
assert_eq!(result, Ok(Status::Complete(80)));
2254+
}
2255+
2256+
#[test]
2257+
fn test_response_header_with_nul_in_header_name() {
21502258
const RESPONSE: &[u8] =
21512259
b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Cred\0entials: hello\r\nBread: baguette\r\n\r\n";
21522260

@@ -2163,6 +2271,24 @@ mod tests {
21632271
assert_eq!(result, Err(crate::Error::HeaderName));
21642272
}
21652273

2274+
#[test]
2275+
fn test_request_header_with_nul_in_header_name() {
2276+
const REQUEST: &[u8] =
2277+
b"GET / HTTP/1.1\r\nAccess-Control-Allow-Cred\0entials: hello\r\nBread: baguette\r\n\r\n";
2278+
2279+
let mut headers = [EMPTY_HEADER; 2];
2280+
let mut request = Request::new(&mut headers[..]);
2281+
2282+
let result = crate::ParserConfig::default()
2283+
.parse_request(&mut request, REQUEST);
2284+
assert_eq!(result, Err(crate::Error::HeaderName));
2285+
2286+
let result = crate::ParserConfig::default()
2287+
.ignore_invalid_headers_in_requests(true)
2288+
.parse_request(&mut request, REQUEST);
2289+
assert_eq!(result, Err(crate::Error::HeaderName));
2290+
}
2291+
21662292
#[test]
21672293
fn test_header_with_cr_in_header_name() {
21682294
const RESPONSE: &[u8] =
@@ -2179,6 +2305,21 @@ mod tests {
21792305
.ignore_invalid_headers_in_responses(true)
21802306
.parse_response(&mut response, RESPONSE);
21812307
assert_eq!(result, Err(crate::Error::HeaderName));
2308+
2309+
const REQUEST: &[u8] =
2310+
b"GET / HTTP/1.1\r\nAccess-Control-Allow-Cred\rentials: hello\r\nBread: baguette\r\n\r\n";
2311+
2312+
let mut headers = [EMPTY_HEADER; 2];
2313+
let mut request = Request::new(&mut headers[..]);
2314+
2315+
let result = crate::ParserConfig::default()
2316+
.parse_request(&mut request, REQUEST);
2317+
assert_eq!(result, Err(crate::Error::HeaderName));
2318+
2319+
let result = crate::ParserConfig::default()
2320+
.ignore_invalid_headers_in_requests(true)
2321+
.parse_request(&mut request, REQUEST);
2322+
assert_eq!(result, Err(crate::Error::HeaderName));
21822323
}
21832324

21842325
#[test]
@@ -2199,6 +2340,17 @@ mod tests {
21992340
.ignore_invalid_headers_in_responses(true)
22002341
.parse_response(&mut response, RESPONSE);
22012342
assert_eq!(result, Err(crate::Error::HeaderName));
2343+
2344+
const REQUEST: &[u8] =
2345+
b"GET / HTTP/1.1\r\nAccess-Control-Allow-Credentials \0: hello\r\nBread: baguette\r\n\r\n";
2346+
2347+
let mut headers = [EMPTY_HEADER; 2];
2348+
let mut request = Request::new(&mut headers[..]);
2349+
2350+
let result = crate::ParserConfig::default()
2351+
.ignore_invalid_headers_in_requests(true)
2352+
.parse_request(&mut request, REQUEST);
2353+
assert_eq!(result, Err(crate::Error::HeaderName));
22022354
}
22032355

22042356
#[test]
@@ -2217,6 +2369,21 @@ mod tests {
22172369
.ignore_invalid_headers_in_responses(true)
22182370
.parse_response(&mut response, RESPONSE);
22192371
assert_eq!(result, Err(crate::Error::HeaderValue));
2372+
2373+
const REQUEST: &[u8] =
2374+
b"GET / HTTP/1.1\r\nAccess-Control-Allow-Credentials: hell\0o\r\nBread: baguette\r\n\r\n";
2375+
2376+
let mut headers = [EMPTY_HEADER; 2];
2377+
let mut request = Request::new(&mut headers[..]);
2378+
2379+
let result = crate::ParserConfig::default()
2380+
.parse_request(&mut request, REQUEST);
2381+
assert_eq!(result, Err(crate::Error::HeaderValue));
2382+
2383+
let result = crate::ParserConfig::default()
2384+
.ignore_invalid_headers_in_requests(true)
2385+
.parse_request(&mut request, REQUEST);
2386+
assert_eq!(result, Err(crate::Error::HeaderValue));
22202387
}
22212388

22222389
#[test]
@@ -2242,6 +2409,28 @@ mod tests {
22422409
assert_eq!(response.headers.len(), 1);
22432410
assert_eq!(response.headers[0].name, "Bread");
22442411
assert_eq!(response.headers[0].value, &b"baguette"[..]);
2412+
2413+
const REQUEST: &[u8] =
2414+
b"GET / HTTP/1.1\r\nAccess-Control-Allow-Credentials: hell\x01o\r\nBread: baguette\r\n\r\n";
2415+
2416+
let mut headers = [EMPTY_HEADER; 2];
2417+
let mut request = Request::new(&mut headers[..]);
2418+
2419+
let result = crate::ParserConfig::default()
2420+
.parse_request(&mut request, REQUEST);
2421+
assert_eq!(result, Err(crate::Error::HeaderValue));
2422+
2423+
let result = crate::ParserConfig::default()
2424+
.ignore_invalid_headers_in_requests(true)
2425+
.parse_request(&mut request, REQUEST);
2426+
assert_eq!(result, Ok(Status::Complete(77)));
2427+
2428+
assert_eq!(request.version.unwrap(), 1);
2429+
assert_eq!(request.method.unwrap(), "GET");
2430+
assert_eq!(request.path.unwrap(), "/");
2431+
assert_eq!(request.headers.len(), 1);
2432+
assert_eq!(request.headers[0].name, "Bread");
2433+
assert_eq!(request.headers[0].value, &b"baguette"[..]);
22452434
}
22462435

22472436
#[test]
@@ -2267,6 +2456,28 @@ mod tests {
22672456
assert_eq!(response.headers.len(), 1);
22682457
assert_eq!(response.headers[0].name, "Bread");
22692458
assert_eq!(response.headers[0].value, &b"baguette"[..]);
2459+
2460+
const REQUEST: &[u8] =
2461+
b"GET / HTTP/1.1\r\nAccess-Control-Allow-Credentials: hell\x01o \n world!\r\nBread: baguette\r\n\r\n";
2462+
2463+
let mut headers = [EMPTY_HEADER; 2];
2464+
let mut request = Request::new(&mut headers[..]);
2465+
2466+
let result = crate::ParserConfig::default()
2467+
.parse_request(&mut request, REQUEST);
2468+
assert_eq!(result, Err(crate::Error::HeaderValue));
2469+
2470+
let result = crate::ParserConfig::default()
2471+
.ignore_invalid_headers_in_requests(true)
2472+
.parse_request(&mut request, REQUEST);
2473+
assert_eq!(result, Ok(Status::Complete(87)));
2474+
2475+
assert_eq!(request.version.unwrap(), 1);
2476+
assert_eq!(request.method.unwrap(), "GET");
2477+
assert_eq!(request.path.unwrap(), "/");
2478+
assert_eq!(request.headers.len(), 1);
2479+
assert_eq!(request.headers[0].name, "Bread");
2480+
assert_eq!(request.headers[0].value, &b"baguette"[..]);
22702481
}
22712482

22722483
#[test]

0 commit comments

Comments
 (0)