diff --git a/src/proto/h1/role.rs b/src/proto/h1/role.rs index f5c8db5ed0..29bcd44b0d 100644 --- a/src/proto/h1/role.rs +++ b/src/proto/h1/role.rs @@ -106,7 +106,10 @@ fn is_complete_fast(bytes: &[u8], prev_len: usize) -> bool { if bytes[i + 1..].chunks(3).next() == Some(&b"\n\r\n"[..]) { return true; } - } else if b == b'\n' && bytes.get(i + 1) == Some(&b'\n') { + } else if b == b'\n' + && (bytes.get(i + 1) == Some(&b'\n') + || bytes[i + 1..].chunks(2).next() == Some(&b"\r\n"[..])) + { return true; } } @@ -2977,6 +2980,10 @@ mod tests { for n in 0..s.len() { assert!(is_complete_fast(s, n)); } + let s = b"GET / HTTP/1.1\r\na: b\n\r\n"; + for n in 0..s.len() { + assert!(is_complete_fast(s, n), "{:?}; {}", s, n); + } // Not let s = b"GET / HTTP/1.1\r\na: b\r\n\r"; @@ -2987,6 +2994,36 @@ mod tests { for n in 0..s.len() { assert!(!is_complete_fast(s, n)); } + let s = b"GET / HTTP/1.1\r\na: b\n\r"; + for n in 0..s.len() { + assert!(!is_complete_fast(s, n)); + } + } + + #[cfg(feature = "server")] + #[test] + fn test_parse_accepts_lf_crlf_terminator() { + // The full parser (httparse) accepts a bare-LF line ending followed + // by a CRLF blank line as the end of the head, so the partial-read + // fast path must recognize it too. + let mut bytes = BytesMut::from("GET / HTTP/1.1\r\na: b\n\r\n"); + Server::parse( + &mut bytes, + ParseContext { + cached_headers: &mut None, + req_method: &mut None, + h1_parser_config: Default::default(), + h1_max_headers: None, + preserve_header_case: false, + #[cfg(feature = "ffi")] + preserve_header_order: false, + h09_responses: false, + #[cfg(feature = "client")] + on_informational: &mut None, + }, + ) + .expect("parse ok") + .expect("parse complete"); } #[test]