Skip to content

Commit

Permalink
fs: Fix serving precompressed files without an extension (#507)
Browse files Browse the repository at this point in the history
  • Loading branch information
SvizelPritula authored Sep 19, 2024
1 parent c999623 commit 95e28bf
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 6 deletions.
1 change: 1 addition & 0 deletions test-files/extensionless_precompressed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Content.
Binary file added test-files/extensionless_precompressed.gz
Binary file not shown.
1 change: 1 addition & 0 deletions test-files/extensionless_precompressed_missing
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Content.
5 changes: 5 additions & 0 deletions tower-http/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `body` module is disabled except for `catch-panic`, `decompression-*`, `fs`, or `limit` features (BREAKING) ([#477])
- Update to `tower` 0.5 ([#503])

## Fixed

- **fs:** Precompression of static files now supports files without a file extension ([#507])

[#477]: https://github.com/tower-rs/tower-http/pull/477
[#503]: https://github.com/tower-rs/tower-http/pull/503
[#507]: https://github.com/tower-rs/tower-http/pull/507

# 0.5.2

Expand Down
2 changes: 1 addition & 1 deletion tower-http/src/content_encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ pub(crate) struct QValue(u16);
))]
impl QValue {
#[inline]
fn one() -> Self {
pub(crate) fn one() -> Self {
Self(1000)
}

Expand Down
24 changes: 19 additions & 5 deletions tower-http/src/services/fs/serve_dir/open_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,16 @@ fn preferred_encoding(
if let Some(file_extension) =
preferred_encoding.and_then(|encoding| encoding.to_file_extension())
{
let new_extension = path
.extension()
.map(|extension| {
let mut os_string = extension.to_os_string();
let new_file_name = path
.file_name()
.map(|file_name| {
let mut os_string = file_name.to_os_string();
os_string.push(file_extension);
os_string
})
.unwrap_or_else(|| file_extension.to_os_string());

path.set_extension(new_extension);
path.set_file_name(new_file_name);
}

preferred_encoding
Expand Down Expand Up @@ -319,3 +319,17 @@ fn append_slash_on_path(uri: Uri) -> Uri {

uri_builder.build().unwrap()
}

#[test]
fn preferred_encoding_with_extension() {
let mut path = PathBuf::from("hello.txt");
preferred_encoding(&mut path, &[(Encoding::Gzip, QValue::one())]);
assert_eq!(path, PathBuf::from("hello.txt.gz"));
}

#[test]
fn preferred_encoding_without_extension() {
let mut path = PathBuf::from("hello");
preferred_encoding(&mut path, &[(Encoding::Gzip, QValue::one())]);
assert_eq!(path, PathBuf::from("hello.gz"));
}
49 changes: 49 additions & 0 deletions tower-http/src/services/fs/serve_dir/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use http::{Request, StatusCode};
use http_body::Body as HttpBody;
use http_body_util::BodyExt;
use std::convert::Infallible;
use std::fs;
use std::io::Read;
use tower::{service_fn, ServiceExt};

Expand Down Expand Up @@ -252,6 +253,54 @@ async fn missing_precompressed_variant_fallbacks_to_uncompressed_for_head_reques
assert!(res.into_body().frame().await.is_none());
}

#[tokio::test]
async fn precompressed_without_extension() {
let svc = ServeDir::new("../test-files").precompressed_gzip();

let request = Request::builder()
.uri("/extensionless_precompressed")
.header("Accept-Encoding", "gzip")
.body(Body::empty())
.unwrap();
let res = svc.oneshot(request).await.unwrap();

assert_eq!(res.status(), StatusCode::OK);

assert_eq!(res.headers()["content-type"], "application/octet-stream");
assert_eq!(res.headers()["content-encoding"], "gzip");

let body = res.into_body().collect().await.unwrap().to_bytes();
let mut decoder = GzDecoder::new(&body[..]);
let mut decompressed = String::new();
decoder.read_to_string(&mut decompressed).unwrap();

let correct = fs::read_to_string("../test-files/extensionless_precompressed").unwrap();
assert_eq!(decompressed, correct);
}

#[tokio::test]
async fn missing_precompressed_without_extension_fallbacks_to_uncompressed() {
let svc = ServeDir::new("../test-files").precompressed_gzip();

let request = Request::builder()
.uri("/extensionless_precompressed_missing")
.header("Accept-Encoding", "gzip")
.body(Body::empty())
.unwrap();
let res = svc.oneshot(request).await.unwrap();

assert_eq!(res.status(), StatusCode::OK);

assert_eq!(res.headers()["content-type"], "application/octet-stream");
assert!(res.headers().get("content-encoding").is_none());

let body = res.into_body().collect().await.unwrap().to_bytes();
let body = String::from_utf8(body.to_vec()).unwrap();

let correct = fs::read_to_string("../test-files/extensionless_precompressed_missing").unwrap();
assert_eq!(body, correct);
}

#[tokio::test]
async fn access_to_sub_dirs() {
let svc = ServeDir::new("..");
Expand Down

0 comments on commit 95e28bf

Please sign in to comment.