Skip to content

Commit 95e28bf

Browse files
fs: Fix serving precompressed files without an extension (#507)
1 parent c999623 commit 95e28bf

7 files changed

+76
-6
lines changed
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Content.
56 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Content.

tower-http/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- `body` module is disabled except for `catch-panic`, `decompression-*`, `fs`, or `limit` features (BREAKING) ([#477])
1313
- Update to `tower` 0.5 ([#503])
1414

15+
## Fixed
16+
17+
- **fs:** Precompression of static files now supports files without a file extension ([#507])
18+
1519
[#477]: https://github.com/tower-rs/tower-http/pull/477
1620
[#503]: https://github.com/tower-rs/tower-http/pull/503
21+
[#507]: https://github.com/tower-rs/tower-http/pull/507
1722

1823
# 0.5.2
1924

tower-http/src/content_encoding.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ pub(crate) struct QValue(u16);
142142
))]
143143
impl QValue {
144144
#[inline]
145-
fn one() -> Self {
145+
pub(crate) fn one() -> Self {
146146
Self(1000)
147147
}
148148

tower-http/src/services/fs/serve_dir/open_file.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -181,16 +181,16 @@ fn preferred_encoding(
181181
if let Some(file_extension) =
182182
preferred_encoding.and_then(|encoding| encoding.to_file_extension())
183183
{
184-
let new_extension = path
185-
.extension()
186-
.map(|extension| {
187-
let mut os_string = extension.to_os_string();
184+
let new_file_name = path
185+
.file_name()
186+
.map(|file_name| {
187+
let mut os_string = file_name.to_os_string();
188188
os_string.push(file_extension);
189189
os_string
190190
})
191191
.unwrap_or_else(|| file_extension.to_os_string());
192192

193-
path.set_extension(new_extension);
193+
path.set_file_name(new_file_name);
194194
}
195195

196196
preferred_encoding
@@ -319,3 +319,17 @@ fn append_slash_on_path(uri: Uri) -> Uri {
319319

320320
uri_builder.build().unwrap()
321321
}
322+
323+
#[test]
324+
fn preferred_encoding_with_extension() {
325+
let mut path = PathBuf::from("hello.txt");
326+
preferred_encoding(&mut path, &[(Encoding::Gzip, QValue::one())]);
327+
assert_eq!(path, PathBuf::from("hello.txt.gz"));
328+
}
329+
330+
#[test]
331+
fn preferred_encoding_without_extension() {
332+
let mut path = PathBuf::from("hello");
333+
preferred_encoding(&mut path, &[(Encoding::Gzip, QValue::one())]);
334+
assert_eq!(path, PathBuf::from("hello.gz"));
335+
}

tower-http/src/services/fs/serve_dir/tests.rs

+49
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use http::{Request, StatusCode};
99
use http_body::Body as HttpBody;
1010
use http_body_util::BodyExt;
1111
use std::convert::Infallible;
12+
use std::fs;
1213
use std::io::Read;
1314
use tower::{service_fn, ServiceExt};
1415

@@ -252,6 +253,54 @@ async fn missing_precompressed_variant_fallbacks_to_uncompressed_for_head_reques
252253
assert!(res.into_body().frame().await.is_none());
253254
}
254255

256+
#[tokio::test]
257+
async fn precompressed_without_extension() {
258+
let svc = ServeDir::new("../test-files").precompressed_gzip();
259+
260+
let request = Request::builder()
261+
.uri("/extensionless_precompressed")
262+
.header("Accept-Encoding", "gzip")
263+
.body(Body::empty())
264+
.unwrap();
265+
let res = svc.oneshot(request).await.unwrap();
266+
267+
assert_eq!(res.status(), StatusCode::OK);
268+
269+
assert_eq!(res.headers()["content-type"], "application/octet-stream");
270+
assert_eq!(res.headers()["content-encoding"], "gzip");
271+
272+
let body = res.into_body().collect().await.unwrap().to_bytes();
273+
let mut decoder = GzDecoder::new(&body[..]);
274+
let mut decompressed = String::new();
275+
decoder.read_to_string(&mut decompressed).unwrap();
276+
277+
let correct = fs::read_to_string("../test-files/extensionless_precompressed").unwrap();
278+
assert_eq!(decompressed, correct);
279+
}
280+
281+
#[tokio::test]
282+
async fn missing_precompressed_without_extension_fallbacks_to_uncompressed() {
283+
let svc = ServeDir::new("../test-files").precompressed_gzip();
284+
285+
let request = Request::builder()
286+
.uri("/extensionless_precompressed_missing")
287+
.header("Accept-Encoding", "gzip")
288+
.body(Body::empty())
289+
.unwrap();
290+
let res = svc.oneshot(request).await.unwrap();
291+
292+
assert_eq!(res.status(), StatusCode::OK);
293+
294+
assert_eq!(res.headers()["content-type"], "application/octet-stream");
295+
assert!(res.headers().get("content-encoding").is_none());
296+
297+
let body = res.into_body().collect().await.unwrap().to_bytes();
298+
let body = String::from_utf8(body.to_vec()).unwrap();
299+
300+
let correct = fs::read_to_string("../test-files/extensionless_precompressed_missing").unwrap();
301+
assert_eq!(body, correct);
302+
}
303+
255304
#[tokio::test]
256305
async fn access_to_sub_dirs() {
257306
let svc = ServeDir::new("..");

0 commit comments

Comments
 (0)