Skip to content

Commit

Permalink
Add support for zstd
Browse files Browse the repository at this point in the history
  • Loading branch information
stephank committed Jul 1, 2024
1 parent 26a8d04 commit 963eab5
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 2 deletions.
25 changes: 23 additions & 2 deletions src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub struct ResolvedFile<F = File> {
pub handle: F,
/// The resolved and sanitized path to the file.
/// For directory indexes, this includes `index.html`.
/// For pre-encoded files, this will include the compressed extension. (`.gz` or `.br`)
/// For pre-encoded files, this will include the compressed extension. (`.gz`, `.br`, or `.zst`)
pub path: PathBuf,
/// Size in bytes.
pub size: u64,
Expand Down Expand Up @@ -68,7 +68,7 @@ pub struct Resolver<O = TokioFileOpener> {
/// Encodings the client is allowed to request with `Accept-Encoding`.
///
/// This only supports pre-encoded files, that exist adjacent to the original file, but with an
/// additional `.br` or `.gz` suffix (after the original extension).
/// additional `.gz`, `.br`, or `.zst` suffix (after the original extension).
///
/// Typically initialized with `AcceptEncoding::all()` or `AcceptEncoding::none()`.
pub allowed_encodings: AcceptEncoding,
Expand Down Expand Up @@ -288,6 +288,18 @@ impl<O: FileOpener> Resolver<O> {
.map(|mime| mime.to_string());

// Resolve pre-encoded files.
if accept_encoding.zstd {
let mut zstd_path = path.clone().into_os_string();
zstd_path.push(".zst");
if let Ok(file) = self.opener.open(zstd_path.as_ref()).await {
return Ok(ResolveResult::Found(ResolvedFile::new(
file,
zstd_path.into(),
mime,
Some(Encoding::Zstd),
)));
}
}
if accept_encoding.br {
let mut br_path = path.clone().into_os_string();
br_path.push(".br");
Expand Down Expand Up @@ -337,6 +349,8 @@ pub enum Encoding {
Gzip,
/// Response body is encoded with brotli.
Br,
/// Response body is encoded with zstd.
Zstd,
}

impl Encoding {
Expand All @@ -345,6 +359,7 @@ impl Encoding {
HeaderValue::from_static(match self {
Encoding::Gzip => "gzip",
Encoding::Br => "br",
Encoding::Zstd => "zstd",
})
}
}
Expand All @@ -356,6 +371,8 @@ pub struct AcceptEncoding {
pub gzip: bool,
/// Look for `.br` files.
pub br: bool,
/// Look for `.zst` files.
pub zstd: bool,
}

impl AcceptEncoding {
Expand All @@ -364,6 +381,7 @@ impl AcceptEncoding {
Self {
gzip: true,
br: true,
zstd: true,
}
}

Expand All @@ -372,6 +390,7 @@ impl AcceptEncoding {
Self {
gzip: false,
br: false,
zstd: false,
}
}

Expand All @@ -384,6 +403,7 @@ impl AcceptEncoding {
match enc.split(';').next().unwrap().trim() {
"gzip" => res.gzip = true,
"br" => res.br = true,
"zstd" => res.zstd = true,
_ => {}
}
}
Expand All @@ -398,6 +418,7 @@ impl BitAnd for AcceptEncoding {
Self {
gzip: self.gzip && rhs.gzip,
br: self.br && rhs.br,
zstd: self.zstd && rhs.zstd,
}
}
}
22 changes: 22 additions & 0 deletions tests/static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,28 @@ async fn serves_br() {
assert_eq!(read_body(res).await, "fake brotli compression");
}

#[tokio::test]
async fn serves_zstd() {
let harness = Harness::new(vec![
("file1.html", "this is file1"),
("file1.html.br", "fake brotli compression"),
("file1.html.gz", "fake gzip compression"),
("file1.html.zst", "fake zstd compression"),
]);
let req = Request::builder()
.uri("/file1.html")
.header(header::ACCEPT_ENCODING, "zstd;q=1.0, br;q=0.8, gzip;q=0.5")
.body(())
.expect("unable to build request");

let res = harness.request(req).await.unwrap();
assert_eq!(
res.headers().get(header::CONTENT_ENCODING),
Some(&Encoding::Zstd.to_header_value())
);
assert_eq!(read_body(res).await, "fake zstd compression");
}

#[tokio::test]
async fn test_memory_fs() {
let dir = Harness::create_temp_dir(vec![
Expand Down

0 comments on commit 963eab5

Please sign in to comment.