From 2fe518995491da430c17316f3b9a2741cacf22cd Mon Sep 17 00:00:00 2001 From: Paul <135009186+phooijenga@users.noreply.github.com> Date: Sun, 26 Nov 2023 21:57:19 +0100 Subject: [PATCH] Do not encode zero-sized response bodies (#3199) * Do not encode zero-sized response bodies * Test empty response remains empty after compression --- actix-http/CHANGES.md | 4 +++ actix-http/src/encoding/encoder.rs | 2 +- actix-web/src/middleware/compress.rs | 45 +++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 318df5a4d..fc49bc931 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -6,6 +6,10 @@ - Updated `zstd` dependency to `0.13`. +### Fixed + +- Do not encode zero-sized response bodies + ## 3.4.0 ### Added diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 527bfebaa..e084aa564 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -52,7 +52,7 @@ impl Encoder { pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, body: B) -> Self { // no need to compress an empty body - if matches!(body.size(), BodySize::None) { + if matches!(body.size(), BodySize::None | BodySize::Sized(0)) { return Self::none(); } diff --git a/actix-web/src/middleware/compress.rs b/actix-web/src/middleware/compress.rs index a55b46264..8ff518cd3 100644 --- a/actix-web/src/middleware/compress.rs +++ b/actix-web/src/middleware/compress.rs @@ -373,7 +373,7 @@ mod tests { .default_service(web::to(move || { HttpResponse::Ok() .insert_header((header::VARY, "x-test")) - .finish() + .body(TEXT_DATA) })) }) .await; @@ -429,4 +429,47 @@ mod tests { assert_successful_identity_res_with_content_type(&res, "image/jpeg"); assert_eq!(test::read_body(res).await, TEXT_DATA.as_bytes()); } + + #[actix_rt::test] + async fn prevents_compression_empty() { + let app = test::init_service({ + App::new() + .wrap(Compress::default()) + .default_service(web::to(move || HttpResponse::Ok().finish())) + }) + .await; + + let req = test::TestRequest::default() + .insert_header((header::ACCEPT_ENCODING, "gzip")) + .to_request(); + let res = test::call_service(&app, req).await; + assert_eq!(res.status(), StatusCode::OK); + assert!(!res.headers().contains_key(header::CONTENT_ENCODING)); + assert!(test::read_body(res).await.is_empty()); + } +} + +#[cfg(feature = "compress-brotli")] +#[cfg(test)] +mod tests_brotli { + use super::*; + use crate::{test, web, App}; + + #[actix_rt::test] + async fn prevents_compression_empty() { + let app = test::init_service({ + App::new() + .wrap(Compress::default()) + .default_service(web::to(move || HttpResponse::Ok().finish())) + }) + .await; + + let req = test::TestRequest::default() + .insert_header((header::ACCEPT_ENCODING, "br")) + .to_request(); + let res = test::call_service(&app, req).await; + assert_eq!(res.status(), StatusCode::OK); + assert!(!res.headers().contains_key(header::CONTENT_ENCODING)); + assert!(test::read_body(res).await.is_empty()); + } }