diff --git a/src/async_impl/decoder.rs b/src/async_impl/decoder.rs index 4cffbf529..1fbb94b83 100644 --- a/src/async_impl/decoder.rs +++ b/src/async_impl/decoder.rs @@ -373,7 +373,7 @@ impl HttpBody for Decoder { let stream_reader = gzip_decoder.get_mut(); let peekable_io_stream = stream_reader.get_mut(); match futures_core::ready!(Pin::new(peekable_io_stream).poll_next(cx)) { - Some(Ok(bytes)) => Poll::Ready(Some(Ok(Frame::data(bytes)))), + Some(Ok(bytes)) => Poll::Ready(Some(Err(crate::error::decode("there are extra bytes after body has been decompressed")))), Some(Err(err)) => Poll::Ready(Some(Err(crate::error::decode_io(err)))), None => Poll::Ready(None), } diff --git a/tests/gzip.rs b/tests/gzip.rs index 34ca68820..a4ef7da91 100644 --- a/tests/gzip.rs +++ b/tests/gzip.rs @@ -320,3 +320,64 @@ async fn test_chunked_fragmented_response_2() { ); assert!(start.elapsed() >= DELAY_BETWEEN_RESPONSE_PARTS - DELAY_MARGIN); } + +#[tokio::test] +async fn test_chunked_fragmented_response_with_extra_bytes() { + const DELAY_BETWEEN_RESPONSE_PARTS: tokio::time::Duration = + tokio::time::Duration::from_millis(1000); + const DELAY_MARGIN: tokio::time::Duration = tokio::time::Duration::from_millis(50); + + let server = server::low_level_with_response(|_raw_request, client_socket| { + Box::new(async move { + let response_first_part = b"HTTP/1.1 200 OK\x0d\x0a\ + Content-Type: text/plain\x0d\x0a\ + Transfer-Encoding: chunked\x0d\x0a\ + Connection: keep-alive\x0d\x0a\ + Content-Encoding: gzip\x0d\x0a\ + \x0d\x0a\ + 55\x0d\x0a\ + \x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xabV*\xae\xccM\xca\xcfQ\xb2Rr\x0aq\x0e\x0dv\x09Q\xd2Q\xca/H\xcd\xf3\xcc+I-J-.\x01J\x98\x1b\x18\x98\x9a\xe9\x99\x9a\x18\x03\xa5J2sS\x95\xac\x0c\xcd\x8d\x8cM\x8cLML\x0c---j\x01\xd7Gb;D\x00\x00\x00"; + let response_second_part = b"\x0d\x0a2ab\x0d\x0a0\x0d\x0a\x0d\x0a"; + + client_socket + .write_all(response_first_part) + .await + .expect("response_first_part write_all failed"); + client_socket + .flush() + .await + .expect("response_first_part flush failed"); + + tokio::time::sleep(DELAY_BETWEEN_RESPONSE_PARTS).await; + + client_socket + .write_all(response_second_part) + .await + .expect("response_second_part write_all failed"); + client_socket + .flush() + .await + .expect("response_second_part flush failed"); + }) + }); + + let start = tokio::time::Instant::now(); + + let client = reqwest::Client::builder() + .connection_verbose(true) + .timeout(Duration::from_secs(15)) + .pool_idle_timeout(Some(std::time::Duration::from_secs(300))) + .pool_max_idle_per_host(5) + .build() + .expect("reqwest client init error"); + + let res = client + .get(&format!("http://{}/", server.addr())) + .send() + .await + .expect("response"); + + let err = res.text().await.expect_err("there must be an error"); + assert!(err.is_decode()); + assert!(start.elapsed() >= DELAY_BETWEEN_RESPONSE_PARTS - DELAY_MARGIN); +}