rename BodyEncoding methods

This commit is contained in:
Rob Ede 2022-01-02 08:46:38 +00:00
parent d981dda09b
commit b540c4c9b8
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
11 changed files with 211 additions and 165 deletions

View File

@ -420,7 +420,7 @@ impl NamedFile {
} }
if let Some(current_encoding) = self.encoding { if let Some(current_encoding) = self.encoding {
res.encoding(current_encoding); res.encode_with(current_encoding);
} }
let reader = chunked::new_chunked_read(self.md.len(), 0, self.file); let reader = chunked::new_chunked_read(self.md.len(), 0, self.file);
@ -494,7 +494,7 @@ impl NamedFile {
// default compressing // default compressing
if let Some(current_encoding) = self.encoding { if let Some(current_encoding) = self.encoding {
res.encoding(current_encoding); res.encode_with(current_encoding);
} }
if let Some(lm) = last_modified { if let Some(lm) = last_modified {
@ -517,7 +517,7 @@ impl NamedFile {
length = ranges[0].length; length = ranges[0].length;
offset = ranges[0].start; offset = ranges[0].start;
res.encoding(ContentEncoding::Identity); res.encode_with(ContentEncoding::Identity);
res.insert_header(( res.insert_header((
header::CONTENT_RANGE, header::CONTENT_RANGE,
format!("bytes {}-{}/{}", offset, offset + length - 1, self.md.len()), format!("bytes {}-{}/{}", offset, offset + length - 1, self.md.len()),

View File

@ -20,7 +20,7 @@ pub struct ContentEncodingParseError;
/// See [IANA HTTP Content Coding Registry]. /// See [IANA HTTP Content Coding Registry].
/// ///
/// [IANA HTTP Content Coding Registry]: https://www.iana.org/assignments/http-parameters/http-parameters.xhtml /// [IANA HTTP Content Coding Registry]: https://www.iana.org/assignments/http-parameters/http-parameters.xhtml
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive] #[non_exhaustive]
pub enum ContentEncoding { pub enum ContentEncoding {
/// Indicates the no-op identity encoding. /// Indicates the no-op identity encoding.
@ -42,12 +42,6 @@ pub enum ContentEncoding {
} }
impl ContentEncoding { impl ContentEncoding {
// /// Is the content compressed?
// #[inline]
// pub const fn is_compression(self) -> bool {
// matches!(self, ContentEncoding::Identity)
// }
/// Convert content encoding to string. /// Convert content encoding to string.
#[inline] #[inline]
pub const fn as_str(self) -> &'static str { pub const fn as_str(self) -> &'static str {
@ -82,16 +76,18 @@ impl Default for ContentEncoding {
impl FromStr for ContentEncoding { impl FromStr for ContentEncoding {
type Err = ContentEncodingParseError; type Err = ContentEncodingParseError;
fn from_str(val: &str) -> Result<Self, Self::Err> { fn from_str(enc: &str) -> Result<Self, Self::Err> {
let val = val.trim(); let enc = enc.trim();
if val.eq_ignore_ascii_case("br") { if enc.eq_ignore_ascii_case("br") {
Ok(ContentEncoding::Brotli) Ok(ContentEncoding::Brotli)
} else if val.eq_ignore_ascii_case("gzip") { } else if enc.eq_ignore_ascii_case("gzip") {
Ok(ContentEncoding::Gzip) Ok(ContentEncoding::Gzip)
} else if val.eq_ignore_ascii_case("deflate") { } else if enc.eq_ignore_ascii_case("deflate") {
Ok(ContentEncoding::Deflate) Ok(ContentEncoding::Deflate)
} else if val.eq_ignore_ascii_case("zstd") { } else if enc.eq_ignore_ascii_case("identity") {
Ok(ContentEncoding::Identity)
} else if enc.eq_ignore_ascii_case("zstd") {
Ok(ContentEncoding::Zstd) Ok(ContentEncoding::Zstd)
} else { } else {
Err(ContentEncodingParseError) Err(ContentEncodingParseError)

View File

@ -473,7 +473,7 @@ async fn test_no_decompress() {
.wrap(actix_web::middleware::Compress::default()) .wrap(actix_web::middleware::Compress::default())
.service(web::resource("/").route(web::to(|| { .service(web::resource("/").route(web::to(|| {
let mut res = HttpResponse::Ok().body(STR); let mut res = HttpResponse::Ok().body(STR);
res.encoding(header::ContentEncoding::Gzip); res.encode_with(header::ContentEncoding::Gzip);
res res
}))) })))
}); });
@ -645,7 +645,7 @@ async fn test_client_deflate_encoding() {
let srv = actix_test::start(|| { let srv = actix_test::start(|| {
App::new().default_service(web::to(|body: Bytes| { App::new().default_service(web::to(|body: Bytes| {
HttpResponse::Ok() HttpResponse::Ok()
.encoding(ContentEncoding::Brotli) .encode_with(ContentEncoding::Brotli)
.body(body) .body(body)
})) }))
}); });
@ -670,7 +670,7 @@ async fn test_client_deflate_encoding_large_random() {
let srv = actix_test::start(|| { let srv = actix_test::start(|| {
App::new().default_service(web::to(|body: Bytes| { App::new().default_service(web::to(|body: Bytes| {
HttpResponse::Ok() HttpResponse::Ok()
.encoding(ContentEncoding::Brotli) .encode_with(ContentEncoding::Brotli)
.body(body) .body(body)
})) }))
}); });
@ -689,7 +689,7 @@ async fn test_client_streaming_explicit() {
let srv = actix_test::start(|| { let srv = actix_test::start(|| {
App::new().default_service(web::to(|body: web::Payload| { App::new().default_service(web::to(|body: web::Payload| {
HttpResponse::Ok() HttpResponse::Ok()
.encoding(ContentEncoding::Identity) .encode_with(ContentEncoding::Identity)
.streaming(body) .streaming(body)
})) }))
}); });
@ -714,7 +714,7 @@ async fn test_body_streaming_implicit() {
}); });
HttpResponse::Ok() HttpResponse::Ok()
.encoding(ContentEncoding::Gzip) .encode_with(ContentEncoding::Gzip)
.streaming(Box::pin(body)) .streaming(Box::pin(body))
})) }))
}); });

View File

@ -12,16 +12,16 @@ save_exit_code() {
[ "$CMD_EXIT" = "0" ] || EXIT=$CMD_EXIT [ "$CMD_EXIT" = "0" ] || EXIT=$CMD_EXIT
} }
save_exit_code cargo test --lib --tests -p=actix-router --all-features save_exit_code cargo test --lib --tests -p=actix-router --all-features -- --nocapture
save_exit_code cargo test --lib --tests -p=actix-http --all-features save_exit_code cargo test --lib --tests -p=actix-http --all-features -- --nocapture
save_exit_code cargo test --lib --tests -p=actix-web --features=rustls,openssl -- --skip=test_reading_deflate_encoding_large_random_rustls save_exit_code cargo test --lib --tests -p=actix-web --features=rustls,openssl -- --nocapture --skip=test_reading_deflate_encoding_large_random_rustls
save_exit_code cargo test --lib --tests -p=actix-web-codegen --all-features save_exit_code cargo test --lib --tests -p=actix-web-codegen --all-features -- --nocapture
save_exit_code cargo test --lib --tests -p=awc --all-features save_exit_code cargo test --lib --tests -p=awc --all-features -- --nocapture
save_exit_code cargo test --lib --tests -p=actix-http-test --all-features save_exit_code cargo test --lib --tests -p=actix-http-test --all-features -- --nocapture
save_exit_code cargo test --lib --tests -p=actix-test --all-features save_exit_code cargo test --lib --tests -p=actix-test --all-features -- --nocapture
save_exit_code cargo test --lib --tests -p=actix-files save_exit_code cargo test --lib --tests -p=actix-files -- --nocapture
save_exit_code cargo test --lib --tests -p=actix-multipart --all-features save_exit_code cargo test --lib --tests -p=actix-multipart --all-features -- --nocapture
save_exit_code cargo test --lib --tests -p=actix-web-actors --all-features save_exit_code cargo test --lib --tests -p=actix-web-actors --all-features -- --nocapture
save_exit_code cargo test --workspace --doc save_exit_code cargo test --workspace --doc

View File

@ -22,7 +22,7 @@ pub use crate::service::{HttpServiceFactory, ServiceRequest, ServiceResponse, We
pub use crate::types::{JsonBody, Readlines, UrlEncoded}; pub use crate::types::{JsonBody, Readlines, UrlEncoded};
use crate::http::header::ContentEncoding; use crate::{http::header::ContentEncoding, HttpMessage as _};
use actix_router::Patterns; use actix_router::Patterns;
@ -47,60 +47,105 @@ pub(crate) fn ensure_leading_slash(mut patterns: Patterns) -> Patterns {
/// Helper trait for managing response encoding. /// Helper trait for managing response encoding.
/// ///
/// Use `encoding` to flag response as already encoded. For example, when serving a Gzip compressed /// Use `pre_encoded_with` to flag response as already encoded. For example, when serving a Gzip
/// file from disk. /// compressed file from disk.
pub trait BodyEncoding { pub trait BodyEncoding {
/// Get content encoding /// Get content encoding
fn get_encoding(&self) -> Option<ContentEncoding>; fn preferred_encoding(&self) -> Option<ContentEncoding>;
/// Set content encoding /// Set content encoding to use.
/// ///
/// Must be used with [`crate::middleware::Compress`] to take effect. /// Must be used with [`Compress`] to take effect.
fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self; ///
/// [`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;
} }
struct Enc(ContentEncoding); struct CompressWith(ContentEncoding);
impl BodyEncoding for actix_http::ResponseBuilder { struct PreCompressed(ContentEncoding);
fn get_encoding(&self) -> Option<ContentEncoding> {
self.extensions().get::<Enc>().map(|enc| enc.0)
}
fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self {
self.extensions_mut().insert(Enc(encoding));
self
}
}
impl<B> BodyEncoding for actix_http::Response<B> {
fn get_encoding(&self) -> Option<ContentEncoding> {
self.extensions().get::<Enc>().map(|enc| enc.0)
}
fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self {
self.extensions_mut().insert(Enc(encoding));
self
}
}
impl BodyEncoding for crate::HttpResponseBuilder { impl BodyEncoding for crate::HttpResponseBuilder {
fn get_encoding(&self) -> Option<ContentEncoding> { fn preferred_encoding(&self) -> Option<ContentEncoding> {
self.extensions().get::<Enc>().map(|enc| enc.0) self.extensions().get::<CompressWith>().map(|enc| enc.0)
} }
fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self { fn encode_with(&mut self, encoding: ContentEncoding) -> &mut Self {
self.extensions_mut().insert(Enc(encoding)); self.extensions_mut().insert(CompressWith(encoding));
self
}
fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self {
self.extensions_mut().insert(PreCompressed(encoding));
self self
} }
} }
impl<B> BodyEncoding for crate::HttpResponse<B> { impl<B> BodyEncoding for crate::HttpResponse<B> {
fn get_encoding(&self) -> Option<ContentEncoding> { fn preferred_encoding(&self) -> Option<ContentEncoding> {
self.extensions().get::<Enc>().map(|enc| enc.0) self.extensions().get::<CompressWith>().map(|enc| enc.0)
} }
fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self { fn encode_with(&mut self, encoding: ContentEncoding) -> &mut Self {
self.extensions_mut().insert(Enc(encoding)); self.extensions_mut().insert(CompressWith(encoding));
self
}
fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self {
self.extensions_mut().insert(PreCompressed(encoding));
self self
} }
} }
impl<B> BodyEncoding for ServiceResponse<B> {
fn preferred_encoding(&self) -> Option<ContentEncoding> {
self.request()
.extensions()
.get::<CompressWith>()
.map(|enc| enc.0)
}
fn encode_with(&mut self, encoding: ContentEncoding) -> &mut Self {
self.request()
.extensions_mut()
.insert(CompressWith(encoding));
self
}
fn pre_encoded_with(&mut self, encoding: ContentEncoding) -> &mut Self {
self.request()
.extensions_mut()
.insert(PreCompressed(encoding));
self
}
}
// impl BodyEncoding for actix_http::ResponseBuilder {
// fn get_encoding(&self) -> Option<ContentEncoding> {
// self.extensions().get::<Enc>().map(|enc| enc.0)
// }
// fn compress_with(&mut self, encoding: ContentEncoding) -> &mut Self {
// self.extensions_mut().insert(Enc(encoding));
// self
// }
// }
// impl<B> BodyEncoding for actix_http::Response<B> {
// fn get_encoding(&self) -> Option<ContentEncoding> {
// self.extensions().get::<Enc>().map(|enc| enc.0)
// }
// fn compress_with(&mut self, encoding: ContentEncoding) -> &mut Self {
// self.extensions_mut().insert(Enc(encoding));
// self
// }
// }

View File

@ -1,6 +1,6 @@
use std::collections::HashSet; use std::collections::HashSet;
use super::{common_header, Encoding, Preference, Quality, QualityItem}; use super::{common_header, ContentEncoding, Encoding, Preference, Quality, QualityItem};
use crate::http::header; use crate::http::header;
common_header! { common_header! {
@ -31,7 +31,7 @@ common_header! {
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// AcceptEncoding(vec![QualityItem::max(Preference::Specific(Encoding::Gzip))]) /// AcceptEncoding(vec![QualityItem::max(Preference::Specific(Encoding::gzip()))])
/// ); /// );
/// ``` /// ```
/// ///
@ -57,8 +57,8 @@ common_header! {
order_of_appearance, order_of_appearance,
vec![b"br, gzip"], vec![b"br, gzip"],
Some(AcceptEncoding(vec![ Some(AcceptEncoding(vec![
QualityItem::max(Preference::Specific(Encoding::Brotli)), QualityItem::max(Preference::Specific(Encoding::brotli())),
QualityItem::max(Preference::Specific(Encoding::Gzip)), QualityItem::max(Preference::Specific(Encoding::gzip())),
])) ]))
); );
@ -76,7 +76,7 @@ common_header! {
only_gzip_no_identity, only_gzip_no_identity,
vec![b"gzip, *; q=0"], vec![b"gzip, *; q=0"],
Some(AcceptEncoding(vec![ Some(AcceptEncoding(vec![
QualityItem::max(Preference::Specific(Encoding::Gzip)), QualityItem::max(Preference::Specific(Encoding::gzip())),
QualityItem::zero(Preference::Any), QualityItem::zero(Preference::Any),
])) ]))
); );
@ -109,7 +109,7 @@ impl AcceptEncoding {
if self.0.is_empty() { if self.0.is_empty() {
// though it is not recommended to encode in this case, return identity encoding // though it is not recommended to encode in this case, return identity encoding
return Some(Encoding::Identity); return Some(Encoding::identity());
} }
// 2. If the representation has no content-coding, then it is acceptable by default unless // 2. If the representation has no content-coding, then it is acceptable by default unless
@ -119,10 +119,10 @@ impl AcceptEncoding {
let acceptable_items = self.ranked_items().collect::<Vec<_>>(); let acceptable_items = self.ranked_items().collect::<Vec<_>>();
let identity_acceptable = is_identity_acceptable(&acceptable_items); let identity_acceptable = is_identity_acceptable(&acceptable_items);
let identity_supported = supported_set.contains(&Encoding::Identity); let identity_supported = supported_set.contains(&Encoding::identity());
if identity_acceptable && identity_supported && supported_set.len() == 1 { if identity_acceptable && identity_supported && supported_set.len() == 1 {
return Some(Encoding::Identity); return Some(Encoding::identity());
} }
// 3. If the representation's content-coding is one of the content-codings listed in the // 3. If the representation's content-coding is one of the content-codings listed in the
@ -144,7 +144,7 @@ impl AcceptEncoding {
match matched { match matched {
Some(Preference::Specific(enc)) => Some(enc), Some(Preference::Specific(enc)) => Some(enc),
_ if identity_acceptable => Some(Encoding::Identity), _ if identity_acceptable => Some(Encoding::identity()),
_ => None, _ => None,
} }
@ -190,14 +190,15 @@ impl AcceptEncoding {
match self.0.iter().find(|pref| { match self.0.iter().find(|pref| {
matches!( matches!(
pref.item, pref.item,
Preference::Any | Preference::Specific(Encoding::Identity) Preference::Any
| Preference::Specific(Encoding::Known(ContentEncoding::Identity))
) )
}) { }) {
// "identity" or "*" found so no representation is acceptable // "identity" or "*" found so no representation is acceptable
Some(_) => None, Some(_) => None,
// implicit "identity" is acceptable // implicit "identity" is acceptable
None => Some(Preference::Specific(Encoding::Identity)), None => Some(Preference::Specific(Encoding::identity())),
} }
}) })
} }
@ -240,7 +241,9 @@ fn is_identity_acceptable(items: &'_ [QualityItem<Preference<Encoding>>]) -> boo
for q in items { for q in items {
match (q.quality, &q.item) { match (q.quality, &q.item) {
// occurrence of "identity;q=n"; return true if quality is non-zero // occurrence of "identity;q=n"; return true if quality is non-zero
(q, Preference::Specific(Encoding::Identity)) => return q > Quality::ZERO, (q, Preference::Specific(Encoding::Known(ContentEncoding::Identity))) => {
return q > Quality::ZERO
}
// occurrence of "*;q=n"; return true if quality is non-zero // occurrence of "*;q=n"; return true if quality is non-zero
(q, Preference::Any) => return q > Quality::ZERO, (q, Preference::Any) => return q > Quality::ZERO,
@ -308,80 +311,82 @@ mod tests {
let test = accept_encoding!(); let test = accept_encoding!();
assert_eq!( assert_eq!(
test.negotiate([Encoding::Identity].iter()), test.negotiate([Encoding::identity()].iter()),
Some(Encoding::Identity), Some(Encoding::identity()),
); );
let test = accept_encoding!("identity;q=0"); let test = accept_encoding!("identity;q=0");
assert_eq!(test.negotiate([Encoding::Identity].iter()), None); assert_eq!(test.negotiate([Encoding::identity()].iter()), None);
let test = accept_encoding!("*;q=0"); let test = accept_encoding!("*;q=0");
assert_eq!(test.negotiate([Encoding::Identity].iter()), None); assert_eq!(test.negotiate([Encoding::identity()].iter()), None);
let test = accept_encoding!(); let test = accept_encoding!();
assert_eq!( assert_eq!(
test.negotiate([Encoding::Gzip, Encoding::Identity].iter()), test.negotiate([Encoding::gzip(), Encoding::identity()].iter()),
Some(Encoding::Identity), Some(Encoding::identity()),
); );
let test = accept_encoding!("gzip"); let test = accept_encoding!("gzip");
assert_eq!( assert_eq!(
test.negotiate([Encoding::Gzip, Encoding::Identity].iter()), test.negotiate([Encoding::gzip(), Encoding::identity()].iter()),
Some(Encoding::Gzip), Some(Encoding::gzip()),
); );
assert_eq!( assert_eq!(
test.negotiate([Encoding::Brotli, Encoding::Identity].iter()), test.negotiate([Encoding::brotli(), Encoding::identity()].iter()),
Some(Encoding::Identity), Some(Encoding::identity()),
); );
assert_eq!( assert_eq!(
test.negotiate([Encoding::Brotli, Encoding::Gzip, Encoding::Identity].iter()), test.negotiate([Encoding::brotli(), Encoding::gzip(), Encoding::identity()].iter()),
Some(Encoding::Gzip), Some(Encoding::gzip()),
); );
let test = accept_encoding!("gzip", "identity;q=0"); let test = accept_encoding!("gzip", "identity;q=0");
assert_eq!( assert_eq!(
test.negotiate([Encoding::Gzip, Encoding::Identity].iter()), test.negotiate([Encoding::gzip(), Encoding::identity()].iter()),
Some(Encoding::Gzip), Some(Encoding::gzip()),
); );
assert_eq!( assert_eq!(
test.negotiate([Encoding::Brotli, Encoding::Identity].iter()), test.negotiate([Encoding::brotli(), Encoding::identity()].iter()),
None None
); );
let test = accept_encoding!("gzip", "*;q=0"); let test = accept_encoding!("gzip", "*;q=0");
assert_eq!( assert_eq!(
test.negotiate([Encoding::Gzip, Encoding::Identity].iter()), test.negotiate([Encoding::gzip(), Encoding::identity()].iter()),
Some(Encoding::Gzip), Some(Encoding::gzip()),
); );
assert_eq!( assert_eq!(
test.negotiate([Encoding::Brotli, Encoding::Identity].iter()), test.negotiate([Encoding::brotli(), Encoding::identity()].iter()),
None None
); );
let test = accept_encoding!("gzip", "deflate", "br"); let test = accept_encoding!("gzip", "deflate", "br");
assert_eq!( assert_eq!(
test.negotiate([Encoding::Gzip, Encoding::Identity].iter()), test.negotiate([Encoding::gzip(), Encoding::identity()].iter()),
Some(Encoding::Gzip), Some(Encoding::gzip()),
); );
assert_eq!( assert_eq!(
test.negotiate([Encoding::Brotli, Encoding::Identity].iter()), test.negotiate([Encoding::brotli(), Encoding::identity()].iter()),
Some(Encoding::Brotli) Some(Encoding::brotli())
); );
assert_eq!( assert_eq!(
test.negotiate([Encoding::Deflate, Encoding::Identity].iter()), test.negotiate([Encoding::deflate(), Encoding::identity()].iter()),
Some(Encoding::Deflate) Some(Encoding::deflate())
); );
assert_eq!( assert_eq!(
test.negotiate([Encoding::Gzip, Encoding::Deflate, Encoding::Identity].iter()), test.negotiate(
Some(Encoding::Gzip) [Encoding::gzip(), Encoding::deflate(), Encoding::identity()].iter()
),
Some(Encoding::gzip())
); );
assert_eq!( assert_eq!(
test.negotiate([Encoding::Gzip, Encoding::Brotli, Encoding::Identity].iter()), test.negotiate([Encoding::gzip(), Encoding::brotli(), Encoding::identity()].iter()),
Some(Encoding::Gzip) Some(Encoding::gzip())
); );
assert_eq!( assert_eq!(
test.negotiate([Encoding::Brotli, Encoding::Gzip, Encoding::Identity].iter()), test.negotiate([Encoding::brotli(), Encoding::gzip(), Encoding::identity()].iter()),
Some(Encoding::Gzip) Some(Encoding::gzip())
); );
} }

View File

@ -1,36 +1,44 @@
use std::{fmt, str}; use std::{fmt, str};
/// A value to represent an encoding used in the `Accept-Encoding` header. use actix_http::ContentEncoding;
/// A value to represent an encoding used in the `Accept-Encoding` and `Content-Encoding` header.
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Encoding { pub enum Encoding {
/// The no-op "identity" encoding. /// A supported content encoding. See [`ContentEncoding`] for variants.
Identity, Known(ContentEncoding),
/// Brotli compression (`br`). /// Some other encoding that is less common, can be any string.
Brotli, Unknown(String),
}
/// Gzip compression. impl Encoding {
Gzip, pub const fn identity() -> Self {
Self::Known(ContentEncoding::Identity)
}
/// Deflate (LZ77) encoding. pub const fn brotli() -> Self {
Deflate, Self::Known(ContentEncoding::Brotli)
}
/// Zstd compression. pub const fn deflate() -> Self {
Zstd, Self::Known(ContentEncoding::Deflate)
}
/// Some other encoding that is less common, can be any String. pub const fn gzip() -> Self {
Other(String), Self::Known(ContentEncoding::Gzip)
}
pub const fn zstd() -> Self {
Self::Known(ContentEncoding::Zstd)
}
} }
impl fmt::Display for Encoding { impl fmt::Display for Encoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self { f.write_str(match self {
Encoding::Identity => "identity", Encoding::Known(enc) => enc.as_str(),
Encoding::Brotli => "br", Encoding::Unknown(enc) => enc.as_str(),
Encoding::Gzip => "gzip",
Encoding::Deflate => "deflate",
Encoding::Zstd => "zstd",
Encoding::Other(ref enc) => enc.as_ref(),
}) })
} }
} }
@ -38,14 +46,10 @@ impl fmt::Display for Encoding {
impl str::FromStr for Encoding { impl str::FromStr for Encoding {
type Err = crate::error::ParseError; type Err = crate::error::ParseError;
fn from_str(enc_str: &str) -> Result<Self, crate::error::ParseError> { fn from_str(enc: &str) -> Result<Self, crate::error::ParseError> {
match enc_str { match enc.parse::<ContentEncoding>() {
"identity" => Ok(Self::Identity), Ok(enc) => Ok(Self::Known(enc)),
"br" => Ok(Self::Brotli), Err(_) => Ok(Self::Unknown(enc.to_owned())),
"gzip" => Ok(Self::Gzip),
"deflate" => Ok(Self::Deflate),
"zstd" => Ok(Self::Zstd),
_ => Ok(Self::Other(enc_str.to_owned())),
} }
} }
} }

View File

@ -16,7 +16,7 @@ use pin_project_lite::pin_project;
use crate::{ use crate::{
body::{EitherBody, MessageBody}, body::{EitherBody, MessageBody},
dev::BodyEncoding, dev::BodyEncoding as _,
http::{ http::{
header::{self, AcceptEncoding, ContentEncoding, Encoding, HeaderValue}, header::{self, AcceptEncoding, ContentEncoding, Encoding, HeaderValue},
StatusCode, StatusCode,
@ -111,22 +111,22 @@ static SUPPORTED_ENCODINGS_STRING: Lazy<String> = Lazy::new(|| {
}); });
static SUPPORTED_ENCODINGS: Lazy<Vec<Encoding>> = Lazy::new(|| { static SUPPORTED_ENCODINGS: Lazy<Vec<Encoding>> = Lazy::new(|| {
let mut encodings = vec![Encoding::Identity]; let mut encodings = vec![Encoding::identity()];
#[cfg(feature = "compress-brotli")] #[cfg(feature = "compress-brotli")]
{ {
encodings.push(Encoding::Brotli); encodings.push(Encoding::brotli());
} }
#[cfg(feature = "compress-gzip")] #[cfg(feature = "compress-gzip")]
{ {
encodings.push(Encoding::Gzip); encodings.push(Encoding::gzip());
encodings.push(Encoding::Deflate); encodings.push(Encoding::deflate());
} }
#[cfg(feature = "compress-zstd")] #[cfg(feature = "compress-zstd")]
{ {
encodings.push(Encoding::Zstd); encodings.push(Encoding::zstd());
} }
assert!( assert!(
@ -158,7 +158,7 @@ where
// missing header; fallback to identity // missing header; fallback to identity
None => { None => {
return Either::left(CompressResponse { return Either::left(CompressResponse {
encoding: Encoding::Identity, encoding: Encoding::identity(),
fut: self.service.call(req), fut: self.service.call(req),
_phantom: PhantomData, _phantom: PhantomData,
}) })
@ -217,16 +217,14 @@ where
match ready!(this.fut.poll(cx)) { match ready!(this.fut.poll(cx)) {
Ok(resp) => { Ok(resp) => {
let enc = if let Some(enc) = resp.response().get_encoding() { let enc = if let Some(enc) = resp.response().preferred_encoding() {
enc enc
} else { } else {
match this.encoding { match this.encoding {
Encoding::Brotli => ContentEncoding::Brotli, Encoding::Known(enc) => *enc,
Encoding::Gzip => ContentEncoding::Gzip, Encoding::Unknown(enc) => {
Encoding::Deflate => ContentEncoding::Deflate, unimplemented!("encoding {} should not be here", enc);
Encoding::Identity => ContentEncoding::Identity, }
Encoding::Zstd => ContentEncoding::Zstd,
enc => unimplemented!("encoding {} should not be here", enc),
} }
}; };

View File

@ -27,7 +27,7 @@ macro_rules! test_server {
web::to(|| { web::to(|| {
HttpResponse::Ok() HttpResponse::Ok()
// signal to compressor that content should not be altered // signal to compressor that content should not be altered
.encoding(ContentEncoding::Identity) .encode_with(ContentEncoding::Identity)
// signal to client that content is encoded // signal to client that content is encoded
.insert_header(ContentEncoding::Gzip) .insert_header(ContentEncoding::Gzip)
.body(LOREM_GZIP) .body(LOREM_GZIP)
@ -38,7 +38,7 @@ macro_rules! test_server {
web::to(|| { web::to(|| {
HttpResponse::Ok() HttpResponse::Ok()
// signal to compressor that content should not be altered // signal to compressor that content should not be altered
.encoding(ContentEncoding::Identity) .encode_with(ContentEncoding::Identity)
// signal to client that content is encoded // signal to client that content is encoded
.insert_header(ContentEncoding::Brotli) .insert_header(ContentEncoding::Brotli)
.body(LOREM_BR) .body(LOREM_BR)
@ -49,7 +49,7 @@ macro_rules! test_server {
web::to(|| { web::to(|| {
HttpResponse::Ok() HttpResponse::Ok()
// signal to compressor that content should not be altered // signal to compressor that content should not be altered
.encoding(ContentEncoding::Identity) .encode_with(ContentEncoding::Identity)
// signal to client that content is encoded // signal to client that content is encoded
.insert_header(ContentEncoding::Zstd) .insert_header(ContentEncoding::Zstd)
.body(LOREM_ZSTD) .body(LOREM_ZSTD)
@ -60,7 +60,7 @@ macro_rules! test_server {
web::to(|| { web::to(|| {
HttpResponse::Ok() HttpResponse::Ok()
// signal to compressor that content should not be altered // signal to compressor that content should not be altered
.encoding(ContentEncoding::Identity) .encode_with(ContentEncoding::Identity)
// signal to client that content is encoded as 7zip // signal to client that content is encoded as 7zip
.insert_header((header::CONTENT_ENCODING, "xz")) .insert_header((header::CONTENT_ENCODING, "xz"))
.body(LOREM_XZ) .body(LOREM_XZ)

View File

@ -136,12 +136,12 @@ async fn test_body_encoding_override() {
.wrap(Compress::new(ContentEncoding::Gzip)) .wrap(Compress::new(ContentEncoding::Gzip))
.service(web::resource("/").route(web::to(|| { .service(web::resource("/").route(web::to(|| {
HttpResponse::Ok() HttpResponse::Ok()
.encoding(ContentEncoding::Deflate) .encode_with(ContentEncoding::Deflate)
.body(STR) .body(STR)
}))) })))
.service(web::resource("/raw").route(web::to(|| { .service(web::resource("/raw").route(web::to(|| {
let mut res = HttpResponse::with_body(actix_web::http::StatusCode::OK, STR); let mut res = HttpResponse::with_body(actix_web::http::StatusCode::OK, STR);
res.encoding(ContentEncoding::Deflate); res.encode_with(ContentEncoding::Deflate);
res.map_into_boxed_body() res.map_into_boxed_body()
}))) })))
}); });
@ -704,7 +704,7 @@ async fn test_brotli_encoding_large_openssl() {
actix_test::start_with(actix_test::config().openssl(openssl_config()), move || { actix_test::start_with(actix_test::config().openssl(openssl_config()), move || {
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { App::new().service(web::resource("/").route(web::to(|bytes: Bytes| {
HttpResponse::Ok() HttpResponse::Ok()
.encoding(ContentEncoding::Identity) .encode_with(ContentEncoding::Identity)
.body(bytes) .body(bytes)
}))) })))
}); });
@ -765,20 +765,15 @@ mod plus_rustls {
let srv = actix_test::start_with(actix_test::config().rustls(tls_config()), || { let srv = actix_test::start_with(actix_test::config().rustls(tls_config()), || {
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { App::new().service(web::resource("/").route(web::to(|bytes: Bytes| {
HttpResponse::Ok() HttpResponse::Ok()
.encoding(ContentEncoding::Identity) .encode_with(ContentEncoding::Identity)
.body(bytes) .body(bytes)
}))) })))
}); });
// encode data
let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap();
let req = srv let req = srv
.post("/") .post("/")
.insert_header((actix_web::http::header::CONTENT_ENCODING, "deflate")) .insert_header((actix_web::http::header::CONTENT_ENCODING, "deflate"))
.send_stream(TestBody::new(Bytes::from(enc), 1024)); .send_stream(TestBody::new(Bytes::from(deflate::encode(&data)), 1024));
let mut res = req.await.unwrap(); let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status(), StatusCode::OK);

View File

@ -1,3 +1,6 @@
// compiling some tests will trigger unused function warnings even though other tests use them
#![allow(dead_code)]
use std::io::{Read as _, Write as _}; use std::io::{Read as _, Write as _};
pub mod gzip { pub mod gzip {