Skip to content

Commit 8e5de1b

Browse files
committedJul 9, 2024·
fix(http1): reject final chunked if missing 0
If a chunked body had valid chunks, but ended without a `0` in the final chunk (so, just `\r\n\r\n`), it would be parsed as a valid end. Now it will be rejected as the final chunk MUST be `0\r\n\r\n`. This was partially done before, but only if there were no chunks before the final. This fixes both paths.
1 parent fdc1916 commit 8e5de1b

File tree

2 files changed

+44
-1
lines changed

2 files changed

+44
-1
lines changed
 

‎src/proto/h1/decode.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ impl ChunkedState {
517517
rdr: &mut R,
518518
) -> Poll<Result<ChunkedState, io::Error>> {
519519
match byte!(rdr, cx) {
520-
b'\n' => Poll::Ready(Ok(ChunkedState::Size)),
520+
b'\n' => Poll::Ready(Ok(ChunkedState::Start)),
521521
_ => Poll::Ready(Err(io::Error::new(
522522
io::ErrorKind::InvalidInput,
523523
"Invalid chunk body LF",
@@ -902,6 +902,26 @@ mod tests {
902902
assert_eq!("1234567890abcdef", &result);
903903
}
904904

905+
#[tokio::test]
906+
async fn test_read_chunked_with_missing_zero_digit() {
907+
// After reading a valid chunk, the ending is missing a zero.
908+
let mut mock_buf = &b"1\r\nZ\r\n\r\n\r\n"[..];
909+
let mut decoder = Decoder::chunked(None, None);
910+
let buf = decoder
911+
.decode_fut(&mut mock_buf)
912+
.await
913+
.expect("decode")
914+
.into_data()
915+
.expect("unknown frame type");
916+
assert_eq!("Z", buf);
917+
918+
let err = decoder
919+
.decode_fut(&mut mock_buf)
920+
.await
921+
.expect_err("decode 2");
922+
assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
923+
}
924+
905925
#[tokio::test]
906926
async fn test_read_chunked_extensions_over_limit() {
907927
// construct a chunked body where each individual chunked extension

‎tests/server.rs

+23
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,29 @@ fn post_with_incomplete_body() {
572572
req.read(&mut [0; 256]).expect("read");
573573
}
574574

575+
#[test]
576+
fn post_with_chunked_missing_final_digit() {
577+
let _ = pretty_env_logger::try_init();
578+
let server = serve();
579+
let mut req = connect(server.addr());
580+
req.write_all(
581+
b"\
582+
POST / HTTP/1.1\r\n\
583+
Host: example.domain\r\n\
584+
transfer-encoding: chunked\r\n\
585+
\r\n\
586+
1\r\n\
587+
Z\r\n\
588+
\r\n\r\n\
589+
",
590+
)
591+
.expect("write");
592+
593+
server.body_err();
594+
595+
req.read(&mut [0; 256]).expect("read");
596+
}
597+
575598
#[test]
576599
fn head_response_can_send_content_length() {
577600
let _ = pretty_env_logger::try_init();

0 commit comments

Comments
 (0)