diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 38e2b16e9..3cc5afb53 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -2,6 +2,10 @@ ## Unreleased +- Add `Files::try_compressed()` to support serving pre-compressed static files [#2615] + +[#2615]: https://github.com/actix/actix-web/pull/2615 + ## 0.6.10 ### Security Notice @@ -51,13 +55,10 @@ We encourage updating your `actix-files` version as soon as possible. ## 0.6.3 - XHTML files now use `Content-Disposition: inline` instead of `attachment`. [#2903] -- Add `Files::try_compressed()` to support serving pre-compressed static files [#2615] - Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency. - Update `tokio-uring` dependency to `0.4`. [#2903]: https://github.com/actix/actix-web/pull/2903 -[#2615]: https://github.com/actix/actix-web/pull/2615 - ## 0.6.2 diff --git a/actix-files/src/service.rs b/actix-files/src/service.rs index 5e91eaaa7..ae6725385 100644 --- a/actix-files/src/service.rs +++ b/actix-files/src/service.rs @@ -100,6 +100,11 @@ impl FilesService { header::CONTENT_ENCODING, header::HeaderValue::from_static(header_value), ); + // Response representation varies by Accept-Encoding when serving pre-compressed assets. + res.headers_mut().append( + header::VARY, + header::HeaderValue::from_static("accept-encoding"), + ); } ServiceResponse::new(req, res) } @@ -168,6 +173,15 @@ impl Service for FilesService { // full file path let path = this.directory.join(&path_on_disk); + + // Try serving pre-compressed file even if the uncompressed file doesn't exist yet. + // Still handle directories (index/listing) through the normal branch below. + if this.try_compressed && !path.is_dir() { + if let Some((named_file, encoding)) = find_compressed(&req, &path).await { + return Ok(this.serve_named_file_with_encoding(req, named_file, encoding)); + } + } + if let Err(err) = path.canonicalize() { return this.handle_err(err, req).await; } @@ -216,11 +230,6 @@ impl Service for FilesService { )), } } else { - if this.try_compressed { - if let Some((named_file, encoding)) = find_compressed(&req, &path).await { - return Ok(this.serve_named_file_with_encoding(req, named_file, encoding)); - } - } match NamedFile::open_async(&path).await { Ok(named_file) => Ok(this.serve_named_file(req, named_file)), Err(err) => this.handle_err(err, req).await, @@ -293,11 +302,9 @@ async fn find_compressed( }; let mut compressed_path = original_path.to_owned(); - let filename = match compressed_path.file_name().and_then(|name| name.to_str()) { - Some(filename) => filename.to_owned(), - None => return None, - }; - compressed_path.set_file_name(filename + extension); + let mut filename = compressed_path.file_name()?.to_owned(); + filename.push(extension); + compressed_path.set_file_name(filename); match NamedFile::open_async(&compressed_path).await { Ok(mut named_file) => { diff --git a/actix-files/tests/encoding.rs b/actix-files/tests/encoding.rs index 80ad1c49e..12752a5a4 100644 --- a/actix-files/tests/encoding.rs +++ b/actix-files/tests/encoding.rs @@ -60,13 +60,17 @@ async fn test_compression_encodings() { res.headers().get(header::CONTENT_ENCODING), Some(&HeaderValue::from_static("gzip")), ); + assert_eq!( + res.headers().get(header::VARY), + Some(&HeaderValue::from_static("accept-encoding")), + ); assert_eq!(res.into_body().size(), actix_web::body::BodySize::Sized(76),); // Select the highest priority encoding let mut req = TestRequest::with_uri("/utf8.txt").to_request(); req.headers_mut().insert( header::ACCEPT_ENCODING, - header::HeaderValue::from_static("gz;q=0.6,br;q=0.8,*"), + header::HeaderValue::from_static("gzip;q=0.6,br;q=0.8,*"), ); let res = test::call_service(&srv, req).await; @@ -79,6 +83,10 @@ async fn test_compression_encodings() { res.headers().get(header::CONTENT_ENCODING), Some(&HeaderValue::from_static("br")), ); + assert_eq!( + res.headers().get(header::VARY), + Some(&HeaderValue::from_static("accept-encoding")), + ); assert_eq!(res.into_body().size(), actix_web::body::BodySize::Sized(49),); // Request encoding that doesn't exist on disk and fallback to no encoding @@ -142,6 +150,7 @@ async fn test_compression_encodings() { assert_eq!(res.headers().get(header::CONTENT_ENCODING), None); } +#[actix_web::test] async fn partial_range_response_encoding() { let srv = test::init_service(App::new().default_service(web::to(|| async { NamedFile::open_async("./tests/test.binary").await.unwrap()