From 95e28bfc6d27ba93967da1165d565ed9cde830b1 Mon Sep 17 00:00:00 2001 From: Benjamin Swart <32597377+SvizelPritula@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:45:26 +0200 Subject: [PATCH] fs: Fix serving precompressed files without an extension (#507) --- test-files/extensionless_precompressed | 1 + test-files/extensionless_precompressed.gz | Bin 0 -> 56 bytes .../extensionless_precompressed_missing | 1 + tower-http/CHANGELOG.md | 5 ++ tower-http/src/content_encoding.rs | 2 +- .../src/services/fs/serve_dir/open_file.rs | 24 +++++++-- tower-http/src/services/fs/serve_dir/tests.rs | 49 ++++++++++++++++++ 7 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 test-files/extensionless_precompressed create mode 100644 test-files/extensionless_precompressed.gz create mode 100644 test-files/extensionless_precompressed_missing diff --git a/test-files/extensionless_precompressed b/test-files/extensionless_precompressed new file mode 100644 index 00000000..fffc22b4 --- /dev/null +++ b/test-files/extensionless_precompressed @@ -0,0 +1 @@ +Content. \ No newline at end of file diff --git a/test-files/extensionless_precompressed.gz b/test-files/extensionless_precompressed.gz new file mode 100644 index 0000000000000000000000000000000000000000..0dcede2295ecafc885dd8715ee4441934a9ad71a GIT binary patch literal 56 zcmb2|=HR&G@+OUeIkloBHLo}`KQAY>xH!I`C^b1h7f2Qtr=~CzpF6MZc~<)(GebsP KSOf Self { + pub(crate) fn one() -> Self { Self(1000) } diff --git a/tower-http/src/services/fs/serve_dir/open_file.rs b/tower-http/src/services/fs/serve_dir/open_file.rs index 01c1e2f9..f182d422 100644 --- a/tower-http/src/services/fs/serve_dir/open_file.rs +++ b/tower-http/src/services/fs/serve_dir/open_file.rs @@ -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 @@ -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")); +} diff --git a/tower-http/src/services/fs/serve_dir/tests.rs b/tower-http/src/services/fs/serve_dir/tests.rs index d0d3952c..1fd768c2 100644 --- a/tower-http/src/services/fs/serve_dir/tests.rs +++ b/tower-http/src/services/fs/serve_dir/tests.rs @@ -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}; @@ -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("..");