diff --git a/CHANGES.md b/CHANGES.md index 7b84a3f2b..423ea9fdc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,22 @@ # Changes ## Unreleased - 2021-xx-xx +### Added +- `impl Hash` for `http::header::Encoding`. [#2501] +- `AcceptEncoding::negotiate()`. [#2501] + +### Changed +- `AcceptEncoding::preference` now returns `Option>`. [#2501] +- Rename methods `BodyEncoding::{encoding => encode_with, get_encoding => preferred_encoding}`. [#2501] +- `http::header::Encoding` now only represents `Content-Encoding` types. [#2501] + +### Fixed +- Auto-negotiation of content encoding is more fault-tolerant when using the `Compress` middleware. [#2501] + +### Removed +- `Compress::new`; restricting compression algorithm is done through feature flags. [#2501] + +[#2501]: https://github.com/actix/actix-web/pull/2501 ## 4.0.0-beta.18 - 2021-12-29 @@ -19,22 +35,18 @@ ### Added - `guard::GuardContext` for use with the `Guard` trait. [#2552] - `ServiceRequest::guard_ctx` for obtaining a guard context. [#2552] -- `impl Hash` for `http::header::Encoding`. [#2501] -- `AcceptEncoding::negotiate`. [#2501] ### Changed - `Guard` trait now receives a `&GuardContext`. [#2552] - `guard::fn_guard` functions now receives a `&GuardContext`. [#2552] - Some guards now return `impl Guard` and their concrete types are made private: `guard::Header` and all the method guards. [#2552] - The `Not` guard is now generic over the type of guard it wraps. [#2552] -- `AcceptEncoding::preference` now returns `Option>`. [#2501] ### Fixed - Rename `ConnectionInfo::{remote_addr => peer_addr}`, deprecating the old name. [#2554] - `ConnectionInfo::peer_addr` will not return the port number. [#2554] - `ConnectionInfo::realip_remote_addr` will not return the port number if sourcing the IP from the peer's socket address. [#2554] -[#2501]: https://github.com/actix/actix-web/pull/2501 [#2552]: https://github.com/actix/actix-web/pull/2552 [#2554]: https://github.com/actix/actix-web/pull/2554 diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 69ddb1363..cbdccd93c 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -6,12 +6,22 @@ - `impl Copy` for `QualityItem` where `T: Copy`. [#2501] - `Quality::ZERO` equivalent to `q=0`. [#2501] - `QualityItem::zero` that uses `Quality::ZERO`. [#2501] +- `ContentEncoding::to_header_value()`. [#2501] ### Changed - `Quality::MIN` is now the smallest non-zero value. [#2501] -- `QualityItem::min` has different semantics due to the `QualityItem::MIN` change. [#2501] +- `QualityItem::min` semantics changed with `QualityItem::MIN`. [#2501] +- Rename `ContentEncoding::{Br => Brotli}`. [#2501] - Minimum supported Rust version (MSRV) is now 1.54. +### Fixed +- `ContentEncoding::Identity` can now be parsed from a string. [#2501] +- A `Vary` header is now correctly sent along with compressed content. [#2501] + +### Removed +- `ContentEncoding::Auto` variant. [#2501] +- `ContentEncoding::is_compression()`. [#2501] + [#2501]: https://github.com/actix/actix-web/pull/2501 diff --git a/src/dev.rs b/src/dev.rs index 48a5beea5..b15d15747 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -60,18 +60,19 @@ pub trait BodyEncoding { /// [`Compress`]: crate::middleware::Compress fn encode_with(&mut self, encoding: ContentEncoding) -> &mut Self; - /// Flags that a file already is encoded so that [`Compress`] does not modify it. - /// - /// Effectively a shortcut for `compress_with("identity")` - /// plus `insert_header(ContentEncoding, encoding)`. - /// - /// [`Compress`]: crate::middleware::Compress - fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self; + // /// Flags that a file already is encoded so that [`Compress`] does not modify it. + // /// + // /// Effectively a shortcut for `compress_with("identity")` + // /// plus `insert_header(ContentEncoding, encoding)`. + // /// + // /// [`Compress`]: crate::middleware::Compress + // fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self; } struct CompressWith(ContentEncoding); -struct PreCompressed(ContentEncoding); +// TODO: add or delete this +// struct PreCompressed(ContentEncoding); impl BodyEncoding for crate::HttpResponseBuilder { fn preferred_encoding(&self) -> Option { @@ -83,10 +84,10 @@ impl BodyEncoding for crate::HttpResponseBuilder { self } - fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self { - self.extensions_mut().insert(PreCompressed(encoding)); - self - } + // fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self { + // self.extensions_mut().insert(PreCompressed(encoding)); + // self + // } } impl BodyEncoding for crate::HttpResponse { @@ -99,10 +100,10 @@ impl BodyEncoding for crate::HttpResponse { self } - fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self { - self.extensions_mut().insert(PreCompressed(encoding)); - self - } + // fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self { + // self.extensions_mut().insert(PreCompressed(encoding)); + // self + // } } impl BodyEncoding for ServiceResponse { @@ -120,32 +121,33 @@ impl BodyEncoding for ServiceResponse { self } - fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self { - self.request() - .extensions_mut() - .insert(PreCompressed(encoding)); + // fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self { + // self.request() + // .extensions_mut() + // .insert(PreCompressed(encoding)); + // self + // } +} + +// TODO: remove these impls ? +impl BodyEncoding for actix_http::ResponseBuilder { + fn preferred_encoding(&self) -> Option { + self.extensions().get::().map(|enc| enc.0) + } + + fn encode_with(&mut self, encoding: ContentEncoding) -> &mut Self { + self.extensions_mut().insert(CompressWith(encoding)); self } } -// impl BodyEncoding for actix_http::ResponseBuilder { -// fn get_encoding(&self) -> Option { -// self.extensions().get::().map(|enc| enc.0) -// } +impl BodyEncoding for actix_http::Response { + fn preferred_encoding(&self) -> Option { + self.extensions().get::().map(|enc| enc.0) + } -// fn compress_with(&mut self, encoding: ContentEncoding) -> &mut Self { -// self.extensions_mut().insert(Enc(encoding)); -// self -// } -// } - -// impl BodyEncoding for actix_http::Response { -// fn get_encoding(&self) -> Option { -// self.extensions().get::().map(|enc| enc.0) -// } - -// fn compress_with(&mut self, encoding: ContentEncoding) -> &mut Self { -// self.extensions_mut().insert(Enc(encoding)); -// self -// } -// } + fn encode_with(&mut self, encoding: ContentEncoding) -> &mut Self { + self.extensions_mut().insert(CompressWith(encoding)); + self + } +} diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs index 07a4514d9..3a0d5630d 100644 --- a/src/middleware/compress.rs +++ b/src/middleware/compress.rs @@ -18,7 +18,7 @@ use crate::{ body::{EitherBody, MessageBody}, dev::BodyEncoding as _, http::{ - header::{self, AcceptEncoding, ContentEncoding, Encoding, HeaderValue}, + header::{self, AcceptEncoding, Encoding, HeaderValue}, StatusCode, }, service::{ServiceRequest, ServiceResponse}, @@ -38,26 +38,10 @@ use crate::{ /// .wrap(middleware::Compress::default()) /// .default_service(web::to(|| HttpResponse::NotFound())); /// ``` -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] #[non_exhaustive] pub struct Compress; -impl Compress { - /// Create new `Compress` middleware with the specified encoding. - // TODO: remove - pub fn new(_encoding: ContentEncoding) -> Self { - // Compress(encoding) - Compress - } -} - -impl Default for Compress { - fn default() -> Self { - // Compress::new(ContentEncoding::Auto) - Compress - } -} - impl Transform for Compress where B: MessageBody, @@ -70,73 +54,14 @@ where type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { - ok(CompressMiddleware { - service, - // encoding: self.0, - }) + ok(CompressMiddleware { service }) } } pub struct CompressMiddleware { service: S, - // encoding: ContentEncoding, } -static SUPPORTED_ENCODINGS_STRING: Lazy = Lazy::new(|| { - #[allow(unused_mut)] // only unused when no compress features enabled - let mut encoding: Vec<&str> = vec![]; - - #[cfg(feature = "compress-brotli")] - { - encoding.push("br"); - } - - #[cfg(feature = "compress-gzip")] - { - encoding.push("gzip"); - encoding.push("deflate"); - } - - #[cfg(feature = "compress-zstd")] - { - encoding.push("zstd"); - } - - assert!( - !encoding.is_empty(), - "encoding can not be empty unless __compress feature has been explicitly enabled by itself" - ); - - encoding.join(", ") -}); - -static SUPPORTED_ENCODINGS: Lazy> = Lazy::new(|| { - let mut encodings = vec![Encoding::identity()]; - - #[cfg(feature = "compress-brotli")] - { - encodings.push(Encoding::brotli()); - } - - #[cfg(feature = "compress-gzip")] - { - encodings.push(Encoding::gzip()); - encodings.push(Encoding::deflate()); - } - - #[cfg(feature = "compress-zstd")] - { - encodings.push(Encoding::zstd()); - } - - assert!( - !encodings.is_empty(), - "encodings can not be empty unless __compress feature has been explicitly enabled by itself" - ); - - encodings -}); - impl Service for CompressMiddleware where S: Service, Error = Error>, @@ -237,3 +162,58 @@ where } } } + +static SUPPORTED_ENCODINGS_STRING: Lazy = Lazy::new(|| { + #[allow(unused_mut)] // only unused when no compress features enabled + let mut encoding: Vec<&str> = vec![]; + + #[cfg(feature = "compress-brotli")] + { + encoding.push("br"); + } + + #[cfg(feature = "compress-gzip")] + { + encoding.push("gzip"); + encoding.push("deflate"); + } + + #[cfg(feature = "compress-zstd")] + { + encoding.push("zstd"); + } + + assert!( + !encoding.is_empty(), + "encoding can not be empty unless __compress feature has been explicitly enabled by itself" + ); + + encoding.join(", ") +}); + +static SUPPORTED_ENCODINGS: Lazy> = Lazy::new(|| { + let mut encodings = vec![Encoding::identity()]; + + #[cfg(feature = "compress-brotli")] + { + encodings.push(Encoding::brotli()); + } + + #[cfg(feature = "compress-gzip")] + { + encodings.push(Encoding::gzip()); + encodings.push(Encoding::deflate()); + } + + #[cfg(feature = "compress-zstd")] + { + encodings.push(Encoding::zstd()); + } + + assert!( + !encodings.is_empty(), + "encodings can not be empty unless __compress feature has been explicitly enabled by itself" + ); + + encodings +}); diff --git a/tests/compression.rs b/tests/compression.rs index 7de5d8136..c0bf10e4c 100644 --- a/tests/compression.rs +++ b/tests/compression.rs @@ -288,6 +288,7 @@ async fn deny_identity_coding_no_decompress() { } // TODO: fix test +// currently fails because negotiation doesn't consider unknown encoding types #[ignore] #[actix_rt::test] async fn deny_identity_for_manual_coding() { diff --git a/tests/test_server.rs b/tests/test_server.rs index c725ff3a5..e8d514e64 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -133,7 +133,7 @@ async fn test_body() { async fn test_body_encoding_override() { let srv = actix_test::start_with(actix_test::config().h1(), || { App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) + .wrap(Compress::default()) .service(web::resource("/").route(web::to(|| { HttpResponse::Ok() .encode_with(ContentEncoding::Deflate) @@ -183,12 +183,9 @@ async fn body_gzip_large() { let srv = actix_test::start_with(actix_test::config().h1(), move || { let data = srv_data.clone(); - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service( - web::resource("/") - .route(web::to(move || HttpResponse::Ok().body(data.clone()))), - ) + App::new().wrap(Compress::default()).service( + web::resource("/").route(web::to(move || HttpResponse::Ok().body(data.clone()))), + ) }); let mut res = srv @@ -217,12 +214,9 @@ async fn test_body_gzip_large_random() { let srv = actix_test::start_with(actix_test::config().h1(), move || { let data = srv_data.clone(); - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service( - web::resource("/") - .route(web::to(move || HttpResponse::Ok().body(data.clone()))), - ) + App::new().wrap(Compress::default()).service( + web::resource("/").route(web::to(move || HttpResponse::Ok().body(data.clone()))), + ) }); let mut res = srv @@ -244,7 +238,7 @@ async fn test_body_gzip_large_random() { async fn test_body_chunked_implicit() { let srv = actix_test::start_with(actix_test::config().h1(), || { App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) + .wrap(Compress::default()) .service(web::resource("/").route(web::get().to(move || { HttpResponse::Ok() .streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24)) @@ -271,7 +265,7 @@ async fn test_body_chunked_implicit() { async fn test_body_br_streaming() { let srv = actix_test::start_with(actix_test::config().h1(), || { App::new() - .wrap(Compress::new(ContentEncoding::Brotli)) + .wrap(Compress::default()) .service(web::resource("/").route(web::to(move || { HttpResponse::Ok() .streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24)) @@ -337,7 +331,7 @@ async fn test_no_chunking() { async fn test_body_deflate() { let srv = actix_test::start_with(actix_test::config().h1(), || { App::new() - .wrap(Compress::new(ContentEncoding::Deflate)) + .wrap(Compress::default()) .service(web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR)))) }); @@ -360,7 +354,7 @@ async fn test_body_deflate() { async fn test_body_brotli() { let srv = actix_test::start_with(actix_test::config().h1(), || { App::new() - .wrap(Compress::new(ContentEncoding::Brotli)) + .wrap(Compress::default()) .service(web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR)))) }); @@ -383,7 +377,7 @@ async fn test_body_brotli() { async fn test_body_zstd() { let srv = actix_test::start_with(actix_test::config().h1(), || { App::new() - .wrap(Compress::new(ContentEncoding::Zstd)) + .wrap(Compress::default()) .service(web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR)))) }); @@ -406,7 +400,7 @@ async fn test_body_zstd() { async fn test_body_zstd_streaming() { let srv = actix_test::start_with(actix_test::config().h1(), || { App::new() - .wrap(Compress::new(ContentEncoding::Zstd)) + .wrap(Compress::default()) .service(web::resource("/").route(web::to(move || { HttpResponse::Ok() .streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24))