Repository navigation
fix(serve): negotiate Content-Encoding from Accept-Encoding #60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
cf6c303
eb5d490
ad93ff2
ab45bf8
b25e5a4
cb7ea4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| use std::io::Write; | ||
|
|
||
| use brotli as brotli_rs; | ||
|
|
||
| pub fn brotli(input: &[u8]) -> Vec<u8> { | ||
| let mut writer = brotli_rs::CompressorWriter::new(Vec::new(), 4096, 11, 22); | ||
| writer.write_all(input).unwrap(); | ||
| writer.into_inner() | ||
| } |
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @alshdavid-machine read-only. What is the purpose of this file?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Consumed by Read-only request — no code changed, nothing committed or pushed.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Despite offering the ability to negotiate the use of gzip, it appears only brotli is supported for actual compression. Is there a mechanism to compress with gzip?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No — nothing produces gzip at request time. The only gzip path is pre-compressed sidecar files.
So negotiation advertises |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| /// The content codings the server can produce, in preference order. | ||
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| pub enum Encoding { | ||
| Brotli, | ||
| Gzip, | ||
| } | ||
|
|
||
| impl Encoding { | ||
| pub fn name(self) -> &'static str { | ||
| match self { | ||
| Encoding::Brotli => "br", | ||
| Encoding::Gzip => "gzip", | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Pick the most preferred supported coding from an `Accept-Encoding` header. | ||
| pub fn negotiate(accept_encoding: Option<&str>) -> Option<Encoding> { | ||
| let header = accept_encoding?; | ||
|
|
||
| let mut best: Option<(Encoding, f32)> = None; | ||
|
|
||
| for encoding in [Encoding::Brotli, Encoding::Gzip] { | ||
| let Some(quality) = quality(header, encoding.name()) else { | ||
| continue; | ||
| }; | ||
|
|
||
| if quality <= 0.0 { | ||
| continue; | ||
| } | ||
|
|
||
| match best { | ||
| Some((_, best_quality)) if best_quality >= quality => {} | ||
| _ => best = Some((encoding, quality)), | ||
| } | ||
| } | ||
|
|
||
| best.map(|(encoding, _)| encoding) | ||
| } | ||
|
|
||
| fn quality( | ||
| header: &str, | ||
| name: &str, | ||
| ) -> Option<f32> { | ||
| let mut wildcard = None; | ||
|
|
||
| for part in header.split(',') { | ||
| let mut params = part.split(';'); | ||
| let token = params.next().unwrap_or_default().trim(); | ||
|
|
||
| let quality = params | ||
| .find_map(|param| param.trim().strip_prefix("q=")?.trim().parse::<f32>().ok()) | ||
| .unwrap_or(1.0); | ||
|
|
||
| if token.eq_ignore_ascii_case(name) { | ||
| return Some(quality); | ||
| } | ||
|
|
||
| if token == "*" { | ||
| wildcard = Some(quality); | ||
| } | ||
| } | ||
|
|
||
| wildcard | ||
| } |
|
alshdavid marked this conversation as resolved.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| use super::Encoding; | ||
| use super::negotiate; | ||
|
|
||
| fn accepted(header: &str) -> Option<&'static str> { | ||
| negotiate(Some(header)).map(Encoding::name) | ||
| } | ||
|
|
||
| #[test] | ||
| fn prefers_brotli_when_accepted() { | ||
| assert_eq!(accepted("gzip, deflate, br"), Some("br")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn honours_quality_values() { | ||
| assert_eq!(accepted("br;q=0.5, gzip"), Some("gzip")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn falls_back_to_gzip() { | ||
| assert_eq!(accepted("gzip"), Some("gzip")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn returns_none_when_nothing_supported() { | ||
| assert_eq!(accepted("deflate"), None); | ||
| assert_eq!(accepted("br;q=0"), None); | ||
| assert_eq!(accepted("identity"), None); | ||
| assert_eq!(negotiate(None), None); | ||
| } | ||
|
|
||
| #[test] | ||
| fn honours_wildcard() { | ||
| assert_eq!(accepted("*"), Some("br")); | ||
| assert_eq!(accepted("br;q=0, *"), Some("gzip")); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,8 @@ | ||
| use std::io::Write; | ||
| mod brotli; | ||
| mod encoding; | ||
|
|
||
| use brotli as brotli_rs; | ||
| #[cfg(test)] | ||
|
alshdavid marked this conversation as resolved.
|
||
| mod encoding_test; | ||
|
|
||
| pub fn brotli(input: &[u8]) -> Vec<u8> { | ||
| let mut writer = brotli_rs::CompressorWriter::new(Vec::new(), 4096, 11, 22); | ||
| writer.write_all(input).unwrap(); | ||
| writer.into_inner() | ||
| } | ||
| pub use self::brotli::*; | ||
| pub use self::encoding::*; | ||
Uh oh!
There was an error while loading. Please reload this page.