diff --git a/src/proto/h1/decode.rs b/src/proto/h1/decode.rs index 275d6a9b8a..2e196c36a5 100644 --- a/src/proto/h1/decode.rs +++ b/src/proto/h1/decode.rs @@ -517,7 +517,7 @@ impl ChunkedState { rdr: &mut R, ) -> Poll> { match byte!(rdr, cx) { - b'\n' => Poll::Ready(Ok(ChunkedState::Size)), + b'\n' => Poll::Ready(Ok(ChunkedState::Start)), _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk body LF", @@ -902,6 +902,26 @@ mod tests { assert_eq!("1234567890abcdef", &result); } + #[tokio::test] + async fn test_read_chunked_with_missing_zero_digit() { + // After reading a valid chunk, the ending is missing a zero. + let mut mock_buf = &b"1\r\nZ\r\n\r\n\r\n"[..]; + let mut decoder = Decoder::chunked(None, None); + let buf = decoder + .decode_fut(&mut mock_buf) + .await + .expect("decode") + .into_data() + .expect("unknown frame type"); + assert_eq!("Z", buf); + + let err = decoder + .decode_fut(&mut mock_buf) + .await + .expect_err("decode 2"); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + } + #[tokio::test] async fn test_read_chunked_extensions_over_limit() { // construct a chunked body where each individual chunked extension diff --git a/tests/server.rs b/tests/server.rs index 82bc80669c..5120ad776f 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -572,6 +572,29 @@ fn post_with_incomplete_body() { req.read(&mut [0; 256]).expect("read"); } +#[test] +fn post_with_chunked_missing_final_digit() { + let _ = pretty_env_logger::try_init(); + let server = serve(); + let mut req = connect(server.addr()); + req.write_all( + b"\ + POST / HTTP/1.1\r\n\ + Host: example.domain\r\n\ + transfer-encoding: chunked\r\n\ + \r\n\ + 1\r\n\ + Z\r\n\ + \r\n\r\n\ + ", + ) + .expect("write"); + + server.body_err(); + + req.read(&mut [0; 256]).expect("read"); +} + #[test] fn head_response_can_send_content_length() { let _ = pretty_env_logger::try_init();