Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix incorrect Content-Encoding for pre-compressed Zstd file requests #452

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

Merged
merged 1 commit into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/compression_static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ use crate::headers_ext::ContentCoding;
use crate::Error;

/// It defines the pre-compressed file variant metadata of a particular file path.
pub struct CompressedFileVariant<'a> {
pub struct CompressedFileVariant {
/// Current file path.
pub file_path: PathBuf,
/// The metadata of the current file.
pub metadata: Metadata,
/// The file extension.
pub extension: &'a str,
/// The content encoding based on the file extension.
pub encoding: ContentCoding,
}

/// Initializes static compression.
Expand Down Expand Up @@ -57,7 +57,7 @@ pub(crate) fn post_process<T>(
pub async fn precompressed_variant<'a>(
file_path: &Path,
headers: &'a HeaderMap<HeaderValue>,
) -> Option<CompressedFileVariant<'a>> {
) -> Option<CompressedFileVariant> {
tracing::trace!(
"preparing pre-compressed file variant path of {}",
file_path.display()
Expand Down Expand Up @@ -119,7 +119,7 @@ pub async fn precompressed_variant<'a>(
return Some(CompressedFileVariant {
file_path,
metadata,
extension: if comp_ext == "gz" { "gzip" } else { comp_ext },
encoding,
});
}

Expand Down
3 changes: 2 additions & 1 deletion src/fs/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use http::StatusCode;
use std::fs::Metadata;
use std::path::{Path, PathBuf};

use crate::headers_ext::ContentCoding;
use crate::Result;

/// It defines a composed file metadata structure containing the current file
Expand All @@ -24,7 +25,7 @@ pub(crate) struct FileMetadata<'a> {
// If either `file_path` or `precompressed_variant` is a directory.
pub is_dir: bool,
// The precompressed file variant for the current `file_path`.
pub precompressed_variant: Option<(PathBuf, &'a str)>,
pub precompressed_variant: Option<(PathBuf, ContentCoding)>,
}

/// Try to find the file system metadata for the given file path or return a `Not Found` error.
Expand Down
23 changes: 16 additions & 7 deletions src/static_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,22 @@ pub async fn handle<'a>(opts: &HandleOpts<'a>) -> Result<StaticFileResponse, Sta

// Check for a pre-compressed file variant if present under the `opts.compression_static` context
if let Some(precompressed_meta) = precompressed_variant {
let (precomp_path, precomp_ext) = precompressed_meta;
let (precomp_path, precomp_encoding) = precompressed_meta;
let mut resp = file_reply(headers_opt, file_path, &metadata, Some(precomp_path)).await?;

// Prepare corresponding headers to let know how to decode the payload
resp.headers_mut().remove(CONTENT_LENGTH);
resp.headers_mut()
.insert(CONTENT_ENCODING, precomp_ext.parse().unwrap());
let encoding = match HeaderValue::from_str(precomp_encoding.as_str()) {
Ok(val) => val,
Err(err) => {
tracing::error!(
"unable to parse header value from content encoding: {:?}",
err
);
return Err(StatusCode::INTERNAL_SERVER_ERROR);
}
};
resp.headers_mut().insert(CONTENT_ENCODING, encoding);

return Ok(StaticFileResponse {
resp,
Expand Down Expand Up @@ -245,7 +254,7 @@ async fn get_composed_file_metadata<'a>(
file_path,
metadata: p.metadata,
is_dir: false,
precompressed_variant: Some((p.file_path, p.extension)),
precompressed_variant: Some((p.file_path, p.encoding)),
});
}
}
Expand Down Expand Up @@ -293,7 +302,7 @@ async fn get_composed_file_metadata<'a>(
file_path,
metadata: p.metadata,
is_dir: false,
precompressed_variant: Some((p.file_path, p.extension)),
precompressed_variant: Some((p.file_path, p.encoding)),
});
}
}
Expand Down Expand Up @@ -324,7 +333,7 @@ async fn get_composed_file_metadata<'a>(
file_path,
metadata: p.metadata,
is_dir: false,
precompressed_variant: Some((p.file_path, p.extension)),
precompressed_variant: Some((p.file_path, p.encoding)),
});
}
}
Expand Down Expand Up @@ -362,7 +371,7 @@ async fn get_composed_file_metadata<'a>(
file_path,
metadata: p.metadata,
is_dir: false,
precompressed_variant: Some((p.file_path, p.extension)),
precompressed_variant: Some((p.file_path, p.encoding)),
});
}
}
Expand Down
70 changes: 70 additions & 0 deletions tests/compression_static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,74 @@ mod tests {
.await
.expect("unexpected error response on `handle` function");
}

#[tokio::test]
async fn compression_static_zstd_file_exists() {
let mut headers = HeaderMap::new();
headers.insert(
http::header::ACCEPT_ENCODING,
"gzip, deflate, br, zstd".parse().unwrap(),
);

let mainjs_zst_path = PathBuf::from("tests/fixtures/public/main.js.zst");
let mainjs_zst_path_public = public_dir().join("main.js.zst");
std::fs::copy(&mainjs_zst_path, &mainjs_zst_path_public)
.expect("unexpected error copying fixture file");

let result = static_files::handle(&HandleOpts {
method: &Method::GET,
headers: &headers,
base_path: &public_dir(),
uri_path: "main.js",
uri_query: None,
#[cfg(feature = "directory-listing")]
dir_listing: false,
#[cfg(feature = "directory-listing")]
dir_listing_order: 6,
#[cfg(feature = "directory-listing")]
dir_listing_format: &DirListFmt::Html,
redirect_trailing_slash: true,
#[cfg(any(
feature = "compression",
feature = "compression-deflate",
feature = "compression-gzip",
feature = "compression-deflate",
feature = "compression-brotli",
feature = "compression-zstd"
))]
compression_static: true,
ignore_hidden_files: false,
index_files: &[],
})
.await
.expect("unexpected error response on `handle` function");
let mut res = result.resp;

let mainjs_zst_buf =
std::fs::read(&mainjs_zst_path).expect("unexpected error when reading index.html.gz");
let mainjs_zst_buf = Bytes::from(mainjs_zst_buf);

std::fs::remove_file(mainjs_zst_path_public).unwrap();

let headers = res.headers();

assert_eq!(res.status(), 200);
assert!(!headers.contains_key("content-length"));
assert_eq!(headers["content-encoding"], "zstd");
assert_eq!(headers["accept-ranges"], "bytes");
assert!(!headers["last-modified"].is_empty());
assert_eq!(
&headers["content-type"], "application/javascript",
"content-type is not html"
);

let body = hyper::body::to_bytes(res.body_mut())
.await
.expect("unexpected bytes error during `body` conversion");

assert_eq!(
body, mainjs_zst_buf,
"body and mainjs_zst_buf are not equal in length"
);
}
}
2 changes: 1 addition & 1 deletion tests/dir_listing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ mod tests {

if method == Method::GET {
let entries: Vec<FileEntry> = serde_json::from_str(body_str).unwrap();
assert_eq!(entries.len(), 4);
assert_eq!(entries.len(), 5);

let first_entry = entries.first().unwrap();
assert_eq!(first_entry.name, "spécial-directöry.net");
Expand Down
Binary file added tests/fixtures/public/main.js.zst
Binary file not shown.
Loading