From 296d546ca4b2c7de4a5bcc64d05585e5e38ca115 Mon Sep 17 00:00:00 2001 From: ihciah Date: Mon, 10 Jul 2023 07:37:28 +0000 Subject: [PATCH] fix: set error to Payload before dispatcher disconnect --- actix-http/CHANGES.md | 3 ++ actix-http/src/h1/dispatcher.rs | 1 + actix-http/tests/test_server.rs | 54 +++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index a105c02c1..519942980 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -7,6 +7,9 @@ - Add `body::to_body_limit()` function. - Add `body::BodyLimitExceeded` error type. +### Fixed +- Fix truncated body ending without error when connection closed abnormally. [#3067] + ### Changed - Minimum supported Rust version (MSRV) is now 1.65 due to transitive `time` dependency. diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 270707807..6d332010a 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -1115,6 +1115,7 @@ where let inner = inner.as_mut().project(); inner.flags.insert(Flags::READ_DISCONNECT); if let Some(mut payload) = inner.payload.take() { + payload.set_error(io::Error::from(io::ErrorKind::UnexpectedEof).into()); payload.feed_eof(); } }; diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index f55b1b36c..e953c4726 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -439,6 +439,60 @@ async fn content_length() { srv.stop().await; } +#[actix_rt::test] +async fn content_length_truncated() { + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + + let mut srv = test_server(|| { + HttpService::build() + .h1(|mut req: Request| async move { + let expected_length: usize = req.uri().path()[1..].parse().unwrap(); + let mut payload = req.take_payload(); + + let mut length = 0; + let mut seen_error = false; + while let Some(chunk) = payload.next().await { + match chunk { + Ok(b) => length += b.len(), + Err(_) => { + seen_error = true; + break; + } + } + } + if seen_error { + return Result::<_, Infallible>::Ok(Response::bad_request()); + } + + assert_eq!(length, expected_length, "length must match when no error"); + Result::<_, Infallible>::Ok(Response::ok()) + }) + .tcp() + }) + .await; + + let addr = srv.addr(); + let mut buf = [0; 12]; + + let mut conn = TcpStream::connect(&addr).await.unwrap(); + conn.write_all(b"POST /10000 HTTP/1.1\r\nContent-Length: 10000\r\n\r\ndata_truncated") + .await + .unwrap(); + conn.shutdown().await.unwrap(); + conn.read_exact(&mut buf).await.unwrap(); + assert_eq!(&buf, b"HTTP/1.1 400"); + + let mut conn = TcpStream::connect(&addr).await.unwrap(); + conn.write_all(b"POST /4 HTTP/1.1\r\nContent-Length: 4\r\n\r\ndata") + .await + .unwrap(); + conn.shutdown().await.unwrap(); + conn.read_exact(&mut buf).await.unwrap(); + assert_eq!(&buf, b"HTTP/1.1 200"); + + srv.stop().await; +} + #[actix_rt::test] async fn h1_headers() { let data = STR.repeat(10);