From 68924d148174beab37eced32065ab15781493907 Mon Sep 17 00:00:00 2001 From: bearcage Date: Sat, 15 Jun 2024 15:51:02 -0700 Subject: [PATCH 1/3] Add regression tests covering this bug --- src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 7a21227..5b5c0c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1547,6 +1547,34 @@ mod tests { } } + // A single byte which is part of a method is not invalid + req! { + test_request_one_byte_method, + b"G", Ok(Status::Partial), + |_req| {} + } + + // A subset of a method is a partial method, not invalid + req! { + test_request_partial_method, + b"GE", Ok(Status::Partial), + |_req| {} + } + + // A method, without the delimiting space, is a partial request + req! { + test_request_method_no_delimiter, + b"GET", Ok(Status::Partial), + |_req| {} + } + + // Regression test: assert that a partial read with just the method and + // space results in a partial, rather than a token error from uri parsing. + req! { + test_request_method_only, + b"GET ", Ok(Status::Partial), + |_req| {} + } req! { test_request_partial, @@ -1560,6 +1588,18 @@ mod tests { |_req| {} } + req! { + test_request_method_path_no_delimiter, + b"GET /", Ok(Status::Partial), + |_req| {} + } + + req! { + test_request_method_path_only, + b"GET / ", Ok(Status::Partial), + |_req| {} + } + req! { test_request_partial_parses_headers_as_much_as_it_can, b"GET / HTTP/1.1\r\nHost: yolo\r\n", From c356fc05eeb0dfa63dae5ae67cf123e819ed1ff3 Mon Sep 17 00:00:00 2001 From: bearcage Date: Sat, 15 Jun 2024 15:52:08 -0700 Subject: [PATCH 2/3] Add a test covering all similar permutations --- src/lib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 5b5c0c8..461bdd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1834,6 +1834,24 @@ mod tests { } } + /// Check all subset permutations of a partial request line with no headers + #[test] + fn partial_permutations() { + let req_str = "GET / HTTP/1.1\r\n\r\n"; + let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; + let mut req = Request::new(&mut headers[..]); + for i in 0..req_str.len() { + let status = req.parse(req_str[..i].as_bytes()); + assert_eq!( + status, + Ok(Status::Partial), + "partial request line should return partial. \ + Portion which failed: '{seg}' (below {i})", + seg = &req_str[..i] + ); + } + } + static RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n"; From c162ec062c2b04aa202b1334b277f3166e215f67 Mon Sep 17 00:00:00 2001 From: bearcage Date: Sat, 15 Jun 2024 16:22:52 -0700 Subject: [PATCH 3/3] Fix parse_uri to pass the new tests --- src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 461bdd2..4ccd783 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -955,12 +955,14 @@ fn parse_token<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { pub fn parse_uri<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { let start = bytes.pos(); simd::match_uri_vectored(bytes); - // URI must have at least one char - if bytes.pos() == start { - return Err(Error::Token); - } + let end = bytes.pos(); if next!(bytes) == b' ' { + // URI must have at least one char + if end == start { + return Err(Error::Token); + } + return Ok(Status::Complete( // SAFETY: all bytes up till `i` must have been `is_token` and therefore also utf-8. unsafe { str::from_utf8_unchecked(bytes.slice_skip(1)) },