diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index c4918b56d..c6b6e722b 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,5 +1,13 @@ # Changes +## [0.2.2] - 2020-05-26 + +* Minimize `futures` dependency +* Support sending Content-Length when Content-Range is specified [#1496] +* Update `actix-web` to 2.0.0 + +[#1496]: https://github.com/actix/actix-web/issues/1496 + ## [0.2.1] - 2019-12-22 * Use the same format for file URLs regardless of platforms diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 104eb3dfa..d7ccaa989 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.2.1" +version = "0.2.2" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -18,12 +18,13 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-rc", default-features = false } +actix-web = { version = "2.0.0", default-features = false } actix-http = "1.0.1" actix-service = "1.0.1" bitflags = "1" bytes = "0.5.3" -futures = "0.3.1" +futures-core = { version = "0.3.5", default-features = false } +futures-util = { version = "0.3.5", default-features = false } derive_more = "0.99.2" log = "0.4" mime = "0.3" @@ -33,4 +34,4 @@ v_htmlescape = "0.4" [dev-dependencies] actix-rt = "1.0.0" -actix-web = { version = "2.0.0-rc", features=["openssl"] } +actix-web = { version = "2.0.0", features = ["openssl"] } diff --git a/actix-files/src/error.rs b/actix-files/src/error.rs index 49a46e58d..9b30cbaa2 100644 --- a/actix-files/src/error.rs +++ b/actix-files/src/error.rs @@ -5,6 +5,7 @@ use derive_more::Display; #[derive(Display, Debug, PartialEq)] pub enum FilesError { /// Path is not a directory + #[allow(dead_code)] #[display(fmt = "Path is not a directory. Unable to serve static files")] IsNotDirectory, diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index d910b7d5f..76c68ce25 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -24,8 +24,8 @@ use actix_web::http::header::{self, DispositionType}; use actix_web::http::Method; use actix_web::{web, FromRequest, HttpRequest, HttpResponse}; use bytes::Bytes; -use futures::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready}; -use futures::Stream; +use futures_core::Stream; +use futures_util::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready}; use mime; use mime_guess::from_ext; use percent_encoding::{utf8_percent_encode, CONTROLS}; @@ -521,7 +521,7 @@ impl Service for FilesService { Err(e) => return Either::Left(ok(req.error_response(e))), }; - // full filepath + // full file path let path = match self.directory.join(&real_path.0).canonicalize() { Ok(path) => path, Err(e) => return self.handle_err(e, req), @@ -952,135 +952,92 @@ mod tests { #[actix_rt::test] async fn test_named_file_content_range_headers() { - let mut srv = test::init_service( - App::new().service(Files::new("/test", ".").index_file("tests/test.binary")), - ) - .await; + let srv = test::start(|| { + App::new().service(Files::new("/", ".")) + }); // Valid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") + let response = srv + .get("/tests/test.binary") .header(header::RANGE, "bytes=10-20") - .to_request(); - - let response = test::call_service(&mut srv, request).await; - let contentrange = response - .headers() - .get(header::CONTENT_RANGE) - .unwrap() - .to_str() + .send() + .await .unwrap(); - - assert_eq!(contentrange, "bytes 10-20/100"); + let content_range = response.headers().get(header::CONTENT_RANGE).unwrap(); + assert_eq!(content_range.to_str().unwrap(), "bytes 10-20/100"); // Invalid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") + let response = srv + .get("/tests/test.binary") .header(header::RANGE, "bytes=10-5") - .to_request(); - let response = test::call_service(&mut srv, request).await; - - let contentrange = response - .headers() - .get(header::CONTENT_RANGE) - .unwrap() - .to_str() + .send() + .await .unwrap(); - - assert_eq!(contentrange, "bytes */100"); + let content_range = response.headers().get(header::CONTENT_RANGE).unwrap(); + assert_eq!(content_range.to_str().unwrap(), "bytes */100"); } #[actix_rt::test] async fn test_named_file_content_length_headers() { - // use actix_web::body::{MessageBody, ResponseBody}; - - let mut srv = test::init_service( - App::new().service(Files::new("test", ".").index_file("tests/test.binary")), - ) - .await; + let srv = test::start(|| { + App::new().service(Files::new("/", ".")) + }); // Valid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") + let response = srv + .get("/tests/test.binary") .header(header::RANGE, "bytes=10-20") - .to_request(); - let _response = test::call_service(&mut srv, request).await; + .send() + .await + .unwrap(); + let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(content_length.to_str().unwrap(), "11"); - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "11"); - - // Invalid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .header(header::RANGE, "bytes=10-8") - .to_request(); - let response = test::call_service(&mut srv, request).await; - assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); + // Valid range header, starting from 0 + let response = srv + .get("/tests/test.binary") + .header(header::RANGE, "bytes=0-20") + .send() + .await + .unwrap(); + let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(content_length.to_str().unwrap(), "21"); // Without range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - // .no_default_headers() - .to_request(); - let _response = test::call_service(&mut srv, request).await; + let mut response = srv.get("/tests/test.binary").send().await.unwrap(); + let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(content_length.to_str().unwrap(), "100"); - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "100"); + // Should be no transfer-encoding + let transfer_encoding = response.headers().get(header::TRANSFER_ENCODING); + assert!(transfer_encoding.is_none()); - // chunked - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .to_request(); - let response = test::call_service(&mut srv, request).await; - - // with enabled compression - // { - // let te = response - // .headers() - // .get(header::TRANSFER_ENCODING) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(te, "chunked"); - // } - - let bytes = test::read_body(response).await; + // Check file contents + let bytes = response.body().await.unwrap(); let data = Bytes::from(fs::read("tests/test.binary").unwrap()); assert_eq!(bytes, data); } #[actix_rt::test] async fn test_head_content_length_headers() { - let mut srv = test::init_service( - App::new().service(Files::new("test", ".").index_file("tests/test.binary")), - ) - .await; + let srv = test::start(|| { + App::new().service(Files::new("/", ".")) + }); - // Valid range header - let request = TestRequest::default() - .method(Method::HEAD) - .uri("/t%65st/tests/test.binary") - .to_request(); - let _response = test::call_service(&mut srv, request).await; + let response = srv + .head("/tests/test.binary") + .send() + .await + .unwrap(); - // TODO: fix check - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "100"); + let content_length = response + .headers() + .get(header::CONTENT_LENGTH) + .unwrap() + .to_str() + .unwrap(); + + assert_eq!(content_length, "100"); } #[actix_rt::test] diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index fdb055998..6ee561a4b 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -18,7 +18,7 @@ use actix_web::http::header::{ }; use actix_web::http::{ContentEncoding, StatusCode}; use actix_web::{Error, HttpMessage, HttpRequest, HttpResponse, Responder}; -use futures::future::{ready, Ready}; +use futures_util::future::{ready, Ready}; use crate::range::HttpRange; use crate::ChunkedReadFile; @@ -388,11 +388,12 @@ impl NamedFile { fut: None, counter: 0, }; + if offset != 0 || length != self.md.len() { - Ok(resp.status(StatusCode::PARTIAL_CONTENT).streaming(reader)) - } else { - Ok(resp.body(SizedStream::new(length, reader))) + resp.status(StatusCode::PARTIAL_CONTENT); } + + Ok(resp.body(SizedStream::new(length, reader))) } }