From 4697a772d24af0212bfd2f784dda70e83cb5d1a2 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Thu, 1 Apr 2021 13:50:29 -0400 Subject: [PATCH 1/5] actix-files: named file service --- actix-files/src/named.rs | 45 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index 2846646a224..962dd06d7ce 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -1,3 +1,5 @@ +use actix_service::{Service, ServiceFactory}; +use actix_utils::future::{ok, ready, Ready}; use std::fs::{File, Metadata}; use std::io; use std::ops::{Deref, DerefMut}; @@ -8,14 +10,14 @@ use std::time::{SystemTime, UNIX_EPOCH}; use std::os::unix::fs::MetadataExt; use actix_web::{ - dev::{BodyEncoding, SizedStream}, + dev::{BodyEncoding, ServiceRequest, ServiceResponse, SizedStream}, http::{ header::{ self, Charset, ContentDisposition, DispositionParam, DispositionType, ExtendedValue, }, ContentEncoding, StatusCode, }, - HttpMessage, HttpRequest, HttpResponse, Responder, + Error, HttpMessage, HttpRequest, HttpResponse, Responder, }; use bitflags::bitflags; use mime_guess::from_path; @@ -480,3 +482,42 @@ impl Responder for NamedFile { self.into_response(req) } } + +impl ServiceFactory for NamedFile { + type Response = ServiceResponse; + type Error = Error; + type Config = (); + type InitError = io::Error; + type Service = NamedFileService; + type Future = Ready>; + + fn new_service(&self, _: ()) -> Self::Future { + ok(NamedFileService { + path: self.path.clone(), + }) + } +} + +#[doc(hidden)] +#[derive(Debug)] +pub struct NamedFileService { + path: PathBuf, +} + +impl Service for NamedFileService { + type Response = ServiceResponse; + type Error = Error; + type Future = Ready>; + + actix_service::always_ready!(); + + fn call(&self, req: ServiceRequest) -> Self::Future { + let (req, _) = req.into_parts(); + ready( + NamedFile::open(&self.path) + .map_err(|e| e.into()) + .map(|f| f.into_response(&req)) + .map(|res| ServiceResponse::new(req, res)), + ) + } +} From 36ff76141fbf6e307e19921fbedcda656c8bb6c0 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Thu, 1 Apr 2021 14:46:40 -0400 Subject: [PATCH 2/5] add docs for default file service --- actix-files/src/files.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/actix-files/src/files.rs b/actix-files/src/files.rs index ff4241340a9..9b24c4b2165 100644 --- a/actix-files/src/files.rs +++ b/actix-files/src/files.rs @@ -199,6 +199,18 @@ impl Files { } /// Sets default handler which is used when no matched file could be found. + /// + /// For example, you could set a fall back static file handler: + /// ```rust + /// use actix_files::{Files, NamedFile}; + /// + /// # fn run() -> Result<(), actix_web::Error> { + /// let files = Files::new("/", "./static") + /// .index_file("index.html") + /// .default_handler(NamedFile::open("./static/404.html")?); + /// # Ok(()) + /// # } + /// ``` pub fn default_handler(mut self, f: F) -> Self where F: IntoServiceFactory, From cd600a5118bc40a42da344f47f09095705915c98 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Thu, 1 Apr 2021 15:36:56 -0400 Subject: [PATCH 3/5] implement httpservicefactory for named file --- actix-files/src/named.rs | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index 962dd06d7ce..3db8757c8b5 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -1,5 +1,6 @@ use actix_service::{Service, ServiceFactory}; use actix_utils::future::{ok, ready, Ready}; +use actix_web::dev::{AppService, HttpServiceFactory, ResourceDef}; use std::fs::{File, Metadata}; use std::io; use std::ops::{Deref, DerefMut}; @@ -41,6 +42,29 @@ impl Default for Flags { } /// A file with an associated name. +/// +/// `NamedFile` can be registered as services: +/// ``` +/// use actix_web::App; +/// use actix_files::NamedFile; +/// +/// # fn run() -> Result<(), Box> { +/// let app = App::new() +/// .service(NamedFile::open("./static/index.html")?); +/// # Ok(()) +/// # } +/// ``` +/// +/// They can also be returned from handlers: +/// ``` +/// use actix_web::{Responder, get}; +/// use actix_files::NamedFile; +/// +/// #[get("/")] +/// async fn index() -> impl Responder { +/// NamedFile::open("./static/index.html") +/// } +/// ``` #[derive(Debug)] pub struct NamedFile { path: PathBuf, @@ -487,9 +511,9 @@ impl ServiceFactory for NamedFile { type Response = ServiceResponse; type Error = Error; type Config = (); - type InitError = io::Error; + type InitError = (); type Service = NamedFileService; - type Future = Ready>; + type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { ok(NamedFileService { @@ -521,3 +545,15 @@ impl Service for NamedFileService { ) } } + +impl HttpServiceFactory for NamedFile { + fn register(self, config: &mut AppService) { + let rdef = if config.is_root() { + ResourceDef::root_prefix(self.path.to_string_lossy().as_ref()) + } else { + ResourceDef::prefix(self.path.to_string_lossy().as_ref()) + }; + + config.register_service(rdef, None, self, None) + } +} From 498b9926b5d839b0c4fa82f0749199192eac589a Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Fri, 9 Apr 2021 09:41:25 -0400 Subject: [PATCH 4/5] named file service tests --- actix-files/src/lib.rs | 58 ++++++++++++++++++++++++++++++++++++++++ actix-files/src/named.rs | 13 +++++---- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 24b903c04bc..d0686a9bf03 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -754,4 +754,62 @@ mod tests { let res = test::call_service(&srv, req).await; assert_eq!(res.status(), StatusCode::OK); } + + #[actix_rt::test] + async fn test_serve_named_file() { + let srv = + test::init_service(App::new().service(NamedFile::open("Cargo.toml").unwrap())) + .await; + + let req = TestRequest::get().uri("/Cargo.toml").to_request(); + let res = test::call_service(&srv, req).await; + assert_eq!(res.status(), StatusCode::OK); + + let bytes = test::read_body(res).await; + let data = Bytes::from(fs::read("Cargo.toml").unwrap()); + assert_eq!(bytes, data); + + let req = TestRequest::get().uri("/test/unknown").to_request(); + let res = test::call_service(&srv, req).await; + assert_eq!(res.status(), StatusCode::NOT_FOUND); + } + + #[actix_rt::test] + async fn test_serve_named_file_prefix() { + let srv = test::init_service( + App::new() + .service(web::scope("/test").service(NamedFile::open("Cargo.toml").unwrap())), + ) + .await; + + let req = TestRequest::get().uri("/test/Cargo.toml").to_request(); + let res = test::call_service(&srv, req).await; + assert_eq!(res.status(), StatusCode::OK); + + let bytes = test::read_body(res).await; + let data = Bytes::from(fs::read("Cargo.toml").unwrap()); + assert_eq!(bytes, data); + + let req = TestRequest::get().uri("/Cargo.toml").to_request(); + let res = test::call_service(&srv, req).await; + assert_eq!(res.status(), StatusCode::NOT_FOUND); + } + + #[actix_rt::test] + async fn test_named_file_default_service() { + let srv = test::init_service( + App::new().default_service(NamedFile::open("Cargo.toml").unwrap()), + ) + .await; + + for route in ["/foobar", "/baz", "/"].iter() { + let req = TestRequest::get().uri(route).to_request(); + let res = test::call_service(&srv, req).await; + assert_eq!(res.status(), StatusCode::OK); + + let bytes = test::read_body(res).await; + let data = Bytes::from(fs::read("Cargo.toml").unwrap()); + assert_eq!(bytes, data); + } + } } diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index 3db8757c8b5..519234f0dde 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -548,12 +548,11 @@ impl Service for NamedFileService { impl HttpServiceFactory for NamedFile { fn register(self, config: &mut AppService) { - let rdef = if config.is_root() { - ResourceDef::root_prefix(self.path.to_string_lossy().as_ref()) - } else { - ResourceDef::prefix(self.path.to_string_lossy().as_ref()) - }; - - config.register_service(rdef, None, self, None) + config.register_service( + ResourceDef::root_prefix(self.path.to_string_lossy().as_ref()), + None, + self, + None, + ) } } From 9cb70a6f15a0eb0869a127158155595f3f4b5ce4 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Fri, 9 Apr 2021 09:51:46 -0400 Subject: [PATCH 5/5] named file default handler test --- actix-files/src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index d0686a9bf03..d3360d98452 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -812,4 +812,20 @@ mod tests { assert_eq!(bytes, data); } } + + #[actix_rt::test] + async fn test_default_handler_named_file() { + let st = Files::new("/", ".") + .default_handler(NamedFile::open("Cargo.toml").unwrap()) + .new_service(()) + .await + .unwrap(); + let req = TestRequest::with_uri("/missing").to_srv_request(); + let resp = test::call_service(&st, req).await; + + assert_eq!(resp.status(), StatusCode::OK); + let bytes = test::read_body(resp).await; + let data = Bytes::from(fs::read("Cargo.toml").unwrap()); + assert_eq!(bytes, data); + } }