Skip to content

Commit 528ed08

Browse files
palantjoseluisq
andauthored
fix: Accept-Encoding handling to work correctly if only two compression schemes are available (#361)
* Fix Accept-Encoding handling to work correctly if only two compression schemes are available * Fixed typo and slightly extended test --------- Co-authored-by: Jose Quintana <1700322+joseluisq@users.noreply.github.com>
1 parent cfd1390 commit 528ed08

File tree

3 files changed

+33
-51
lines changed

3 files changed

+33
-51
lines changed

src/compression.rs

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ pub const TEXT_MIME_TYPES: [&str; 24] = [
6464
"application/wasm",
6565
];
6666

67+
/// List of encodings that can be handled given enabled features.
68+
const AVAILABLE_ENCODINGS: &[ContentCoding] = &[
69+
#[cfg(any(feature = "compression", feature = "compression-deflate"))]
70+
ContentCoding::DEFLATE,
71+
#[cfg(any(feature = "compression", feature = "compression-gzip"))]
72+
ContentCoding::GZIP,
73+
#[cfg(any(feature = "compression", feature = "compression-brotli"))]
74+
ContentCoding::BROTLI,
75+
#[cfg(any(feature = "compression", feature = "compression-zstd"))]
76+
ContentCoding::ZSTD,
77+
];
78+
6779
/// Create a wrapping handler that compresses the Body of a [`hyper::Response`]
6880
/// using gzip, `deflate`, `brotli` or `zstd` if is specified in the `Accept-Encoding` header, adding
6981
/// `content-encoding: <coding>` to the Response's [`HeaderMap`].
@@ -226,45 +238,12 @@ pub fn create_encoding_header(existing: Option<HeaderValue>, coding: ContentCodi
226238
#[inline(always)]
227239
pub fn get_preferred_encoding(headers: &HeaderMap<HeaderValue>) -> Option<ContentCoding> {
228240
if let Some(ref accept_encoding) = headers.typed_get::<AcceptEncoding>() {
229-
tracing::trace!("request with accept-ecoding header: {:?}", accept_encoding);
230-
231-
let encoding = accept_encoding.preferred_encoding();
232-
if let Some(preferred_enc) = encoding {
233-
let mut feature_formats = Vec::<ContentCoding>::with_capacity(5);
234-
if cfg!(feature = "compression-deflate") {
235-
feature_formats.push(ContentCoding::DEFLATE);
236-
}
237-
if cfg!(feature = "compression-gzip") {
238-
feature_formats.push(ContentCoding::GZIP);
239-
}
240-
if cfg!(feature = "compression-brotli") {
241-
feature_formats.push(ContentCoding::BROTLI);
242-
}
243-
if cfg!(feature = "compression-zstd") {
244-
feature_formats.push(ContentCoding::ZSTD);
245-
}
241+
tracing::trace!("request with accept-encoding header: {:?}", accept_encoding);
246242

247-
// If there is only one Cargo compression feature enabled
248-
// then re-evaluate the preferred encoding and return
249-
// that feature compression algorithm as `ContentCoding` only
250-
// if was contained in the `AcceptEncoding` value.
251-
if feature_formats.len() == 1 {
252-
let feature_enc = *feature_formats.first().unwrap();
253-
if feature_enc != preferred_enc
254-
&& accept_encoding
255-
.sorted_encodings()
256-
.any(|enc| enc == feature_enc)
257-
{
258-
tracing::trace!(
259-
"preferred encoding {:?} is re-evalated to {:?} because is the only compression feature enabled that matches the `accept-encoding` header",
260-
preferred_enc,
261-
feature_enc
262-
);
263-
return Some(feature_enc);
264-
}
243+
for encoding in accept_encoding.sorted_encodings() {
244+
if AVAILABLE_ENCODINGS.contains(&encoding) {
245+
return Some(encoding);
265246
}
266-
267-
return encoding;
268247
}
269248
}
270249
None

src/headers_ext/accept_encoding.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,6 @@ impl Header for AcceptEncoding {
4848
}
4949

5050
impl AcceptEncoding {
51-
/// Returns the most preferred encoding that is specified by the header,
52-
/// if one is specified.
53-
pub(crate) fn preferred_encoding(&self) -> Option<ContentCoding> {
54-
self.0.iter().next().map(ContentCoding::from)
55-
}
56-
5751
/// Returns a quality sorted iterator of the `ContentCoding`
5852
pub(crate) fn sorted_encodings(&self) -> impl Iterator<Item = ContentCoding> + '_ {
5953
self.0.iter().map(ContentCoding::from)
@@ -66,18 +60,14 @@ mod tests {
6660

6761
#[test]
6862
fn from_static() {
69-
let val = HeaderValue::from_static("deflate, gzip;q=1.0, br;q=0.9");
63+
let val = HeaderValue::from_static("deflate, zstd;q=0.7, gzip;q=1.0, br;q=0.9");
7064
let accept_enc = AcceptEncoding(val.into());
7165

72-
assert_eq!(
73-
accept_enc.preferred_encoding(),
74-
Some(ContentCoding::DEFLATE)
75-
);
76-
7766
let mut encodings = accept_enc.sorted_encodings();
7867
assert_eq!(encodings.next(), Some(ContentCoding::DEFLATE));
7968
assert_eq!(encodings.next(), Some(ContentCoding::GZIP));
8069
assert_eq!(encodings.next(), Some(ContentCoding::BROTLI));
70+
assert_eq!(encodings.next(), Some(ContentCoding::ZSTD));
8171
assert_eq!(encodings.next(), None);
8272
}
8373
}

tests/static_files.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -671,12 +671,25 @@ mod tests {
671671
))]
672672
#[tokio::test]
673673
async fn handle_file_compressions() {
674-
let encodings = ["gzip", "deflate", "br", "zstd", "xyz"];
674+
let encodings = [
675+
#[cfg(any(feature = "compression", feature = "compression-gzip"))]
676+
"gzip",
677+
#[cfg(any(feature = "compression", feature = "compression-deflate"))]
678+
"deflate",
679+
#[cfg(any(feature = "compression", feature = "compression-brotli"))]
680+
"br",
681+
#[cfg(any(feature = "compression", feature = "compression-zstd"))]
682+
"zstd",
683+
"xyz",
684+
];
675685
let method = &Method::GET;
676686

677687
for enc in encodings {
678688
let mut headers = HeaderMap::new();
679-
headers.insert(http::header::ACCEPT_ENCODING, enc.parse().unwrap());
689+
headers.insert(
690+
http::header::ACCEPT_ENCODING,
691+
format!("identity, {enc}").parse().unwrap(),
692+
);
680693

681694
match static_files::handle(&HandleOpts {
682695
method,

0 commit comments

Comments
 (0)