From 404b5a7709b7aaac8703a7b9751262508e4d7ca9 Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Sat, 26 Dec 2020 04:36:15 +0100 Subject: [PATCH 1/4] Add optional support for hidden files/directories (#1811) --- actix-files/CHANGES.md | 3 +++ actix-files/src/files.rs | 11 +++++++++++ actix-files/src/path_buf.rs | 22 +++++++++++++++++++++- actix-files/src/service.rs | 10 ++++++---- 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index c4d56010f..afd4696e8 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2020-xx-xx +* Optionally support hidden files/directories. [#1811] + +[#1811]: https://github.com/actix/actix-web/pull/1811 ## 0.4.1 - 2020-11-24 diff --git a/actix-files/src/files.rs b/actix-files/src/files.rs index a99b4699e..d0cac6aa4 100644 --- a/actix-files/src/files.rs +++ b/actix-files/src/files.rs @@ -39,6 +39,7 @@ pub struct Files { mime_override: Option>, file_flags: named::Flags, guards: Option>, + hidden_files: bool, } impl fmt::Debug for Files { @@ -60,6 +61,7 @@ impl Clone for Files { path: self.path.clone(), mime_override: self.mime_override.clone(), guards: self.guards.clone(), + hidden_files: self.hidden_files, } } } @@ -103,6 +105,7 @@ impl Files { mime_override: None, file_flags: named::Flags::default(), guards: None, + hidden_files: false, } } @@ -213,6 +216,13 @@ impl Files { self } + + /// Enables serving hidden files and directories, allowing a leading dots in url fragments. + #[inline] + pub fn use_hidden_files(mut self) -> Self { + self.hidden_files = true; + self + } } impl HttpServiceFactory for Files { @@ -251,6 +261,7 @@ impl ServiceFactory for Files { mime_override: self.mime_override.clone(), file_flags: self.file_flags, guards: self.guards.clone(), + hidden_files: self.hidden_files, }; if let Some(ref default) = *self.default.borrow() { diff --git a/actix-files/src/path_buf.rs b/actix-files/src/path_buf.rs index 2f3ae84d4..dd8e5b503 100644 --- a/actix-files/src/path_buf.rs +++ b/actix-files/src/path_buf.rs @@ -15,12 +15,19 @@ impl FromStr for PathBufWrap { type Err = UriSegmentError; fn from_str(path: &str) -> Result { + Self::parse_path(path, false) + } +} + +impl PathBufWrap { + /// Parse a path, giving the choice of allowing hidden files to be considered valid segments. + pub fn parse_path(path: &str, hidden_files: bool) -> Result { let mut buf = PathBuf::new(); for segment in path.split('/') { if segment == ".." { buf.pop(); - } else if segment.starts_with('.') { + } else if !hidden_files && segment.starts_with('.') { return Err(UriSegmentError::BadStart('.')); } else if segment.starts_with('*') { return Err(UriSegmentError::BadStart('*')); @@ -96,4 +103,17 @@ mod tests { PathBuf::from_iter(vec!["seg2"]) ); } + + #[test] + fn test_parse_path() { + assert_eq!( + PathBufWrap::parse_path("/test/.tt", false).map(|t| t.0), + Err(UriSegmentError::BadStart('.')) + ); + + assert_eq!( + PathBufWrap::parse_path("/test/.tt", true).unwrap().0, + PathBuf::from_iter(vec!["test", ".tt"]) + ); + } } diff --git a/actix-files/src/service.rs b/actix-files/src/service.rs index cbf4c2d3b..dc4f2bd2c 100644 --- a/actix-files/src/service.rs +++ b/actix-files/src/service.rs @@ -31,6 +31,7 @@ pub struct FilesService { pub(crate) mime_override: Option>, pub(crate) file_flags: named::Flags, pub(crate) guards: Option>, + pub(crate) hidden_files: bool, } type FilesServiceFuture = Either< @@ -83,10 +84,11 @@ impl Service for FilesService { ))); } - let real_path: PathBufWrap = match req.match_info().path().parse() { - Ok(item) => item, - Err(e) => return Either::Left(ok(req.error_response(e))), - }; + let real_path = + match PathBufWrap::parse_path(req.match_info().path(), self.hidden_files) { + Ok(item) => item, + Err(e) => return Either::Left(ok(req.error_response(e))), + }; // full file path let path = match self.directory.join(&real_path).canonicalize() { From b373e1370d21b34ea5fdd79a97a1c15a44c03202 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 26 Dec 2020 04:05:45 +0000 Subject: [PATCH 2/4] prepare files 0.5.0 release --- actix-files/CHANGES.md | 3 +++ actix-files/Cargo.toml | 8 ++++---- actix-files/README.md | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index afd4696e8..86e5e6563 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2020-xx-xx + + +## 0.5.0 - 2020-12-26 * Optionally support hidden files/directories. [#1811] [#1811]: https://github.com/actix/actix-web/pull/1811 diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index f7d32f8ec..b67abb1c1 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.4.1" +version = "0.5.0" authors = ["Nikolay Kim "] description = "Static file serving for Actix Web" readme = "README.md" @@ -23,13 +23,13 @@ bitflags = "1" bytes = "0.5.3" futures-core = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false } -derive_more = "0.99.2" +derive_more = "0.99.5" log = "0.4" mime = "0.3" mime_guess = "2.0.1" percent-encoding = "2.1" -v_htmlescape = "0.11" +v_htmlescape = "0.12" [dev-dependencies] actix-rt = "1.0.0" -actix-web = { version = "3.0.0", features = ["openssl"] } +actix-web = "3.0.0" diff --git a/actix-files/README.md b/actix-files/README.md index 685e5dbe5..2953b4458 100644 --- a/actix-files/README.md +++ b/actix-files/README.md @@ -3,11 +3,11 @@ > Static file serving for Actix Web [![crates.io](https://img.shields.io/crates/v/actix-files?label=latest)](https://crates.io/crates/actix-files) -[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.4.1)](https://docs.rs/actix-files/0.4.1) +[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.5.0)](https://docs.rs/actix-files/0.5.0) [![Version](https://img.shields.io/badge/rustc-1.42+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.42.html) ![License](https://img.shields.io/crates/l/actix-files.svg)
-[![dependency status](https://deps.rs/crate/actix-files/0.4.1/status.svg)](https://deps.rs/crate/actix-files/0.4.1) +[![dependency status](https://deps.rs/crate/actix-files/0.5.0/status.svg)](https://deps.rs/crate/actix-files/0.5.0) [![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) From 1032f04deddecd8bdc3e95798382d51efb74a30b Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sat, 26 Dec 2020 20:46:36 +0800 Subject: [PATCH 3/4] remove unused `actix_http::h1::OneRequest` (#1853) --- actix-http/src/h1/mod.rs | 2 +- actix-http/src/h1/service.rs | 112 ++--------------------------------- 2 files changed, 7 insertions(+), 107 deletions(-) diff --git a/actix-http/src/h1/mod.rs b/actix-http/src/h1/mod.rs index 0c85f076a..3d5dea5d6 100644 --- a/actix-http/src/h1/mod.rs +++ b/actix-http/src/h1/mod.rs @@ -17,7 +17,7 @@ pub use self::codec::Codec; pub use self::dispatcher::Dispatcher; pub use self::expect::ExpectHandler; pub use self::payload::Payload; -pub use self::service::{H1Service, H1ServiceHandler, OneRequest}; +pub use self::service::{H1Service, H1ServiceHandler}; pub use self::upgrade::UpgradeHandler; pub use self::utils::SendResponse; diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 5008791c0..cbba0609c 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -9,12 +9,12 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_rt::net::TcpStream; use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; use futures_core::ready; -use futures_util::future::{ok, Ready}; +use futures_util::future::ready; use crate::body::MessageBody; use crate::cloneable::CloneableService; use crate::config::ServiceConfig; -use crate::error::{DispatchError, Error, ParseError}; +use crate::error::{DispatchError, Error}; use crate::helpers::DataFactory; use crate::request::Request; use crate::response::Response; @@ -22,7 +22,7 @@ use crate::{ConnectCallback, Extensions}; use super::codec::Codec; use super::dispatcher::Dispatcher; -use super::{ExpectHandler, Message, UpgradeHandler}; +use super::{ExpectHandler, UpgradeHandler}; /// `ServiceFactory` implementation for HTTP1 transport pub struct H1Service> { @@ -90,7 +90,7 @@ where > { pipeline_factory(|io: TcpStream| { let peer_addr = io.peer_addr().ok(); - ok((io, peer_addr)) + ready(Ok((io, peer_addr))) }) .and_then(self) } @@ -139,7 +139,7 @@ mod openssl { ) .and_then(|io: SslStream| { let peer_addr = io.get_ref().peer_addr().ok(); - ok((io, peer_addr)) + ready(Ok((io, peer_addr))) }) .and_then(self.map_err(TlsError::Service)) } @@ -189,7 +189,7 @@ mod rustls { ) .and_then(|io: TlsStream| { let peer_addr = io.get_ref().0.peer_addr().ok(); - ok((io, peer_addr)) + ready(Ok((io, peer_addr))) }) .and_then(self.map_err(TlsError::Service)) } @@ -500,103 +500,3 @@ where ) } } - -/// `ServiceFactory` implementation for `OneRequestService` service -#[derive(Default)] -pub struct OneRequest { - config: ServiceConfig, - _t: PhantomData, -} - -impl OneRequest -where - T: AsyncRead + AsyncWrite + Unpin, -{ - /// Create new `H1SimpleService` instance. - pub fn new() -> Self { - OneRequest { - config: ServiceConfig::default(), - _t: PhantomData, - } - } -} - -impl ServiceFactory for OneRequest -where - T: AsyncRead + AsyncWrite + Unpin, -{ - type Config = (); - type Request = T; - type Response = (Request, Framed); - type Error = ParseError; - type InitError = (); - type Service = OneRequestService; - type Future = Ready>; - - fn new_service(&self, _: ()) -> Self::Future { - ok(OneRequestService { - _t: PhantomData, - config: self.config.clone(), - }) - } -} - -/// `Service` implementation for HTTP1 transport. Reads one request and returns -/// request and framed object. -pub struct OneRequestService { - _t: PhantomData, - config: ServiceConfig, -} - -impl Service for OneRequestService -where - T: AsyncRead + AsyncWrite + Unpin, -{ - type Request = T; - type Response = (Request, Framed); - type Error = ParseError; - type Future = OneRequestServiceResponse; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Self::Request) -> Self::Future { - OneRequestServiceResponse { - framed: Some(Framed::new(req, Codec::new(self.config.clone()))), - } - } -} - -#[doc(hidden)] -#[pin_project::pin_project] -pub struct OneRequestServiceResponse -where - T: AsyncRead + AsyncWrite + Unpin, -{ - #[pin] - framed: Option>, -} - -impl Future for OneRequestServiceResponse -where - T: AsyncRead + AsyncWrite + Unpin, -{ - type Output = Result<(Request, Framed), ParseError>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.as_mut().project(); - - match ready!(this.framed.as_pin_mut().unwrap().next_item(cx)) { - Some(Ok(req)) => match req { - Message::Item(req) => { - let mut this = self.as_mut().project(); - Poll::Ready(Ok((req, this.framed.take().unwrap()))) - } - Message::Chunk(_) => unreachable!("Something is wrong"), - }, - Some(Err(err)) => Poll::Ready(Err(err)), - None => Poll::Ready(Err(ParseError::Incomplete)), - } - } -} From cbda928a33ce69e5d83059ff459464ba2a60b19f Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Sat, 26 Dec 2020 16:46:19 -0500 Subject: [PATCH 4/4] Rename factory to handler (#1852) --- CHANGES.md | 4 +-- src/handler.rs | 93 ++++++++++++++++++++++++++----------------------- src/lib.rs | 2 +- src/resource.rs | 10 +++--- src/route.rs | 14 ++++---- src/web.rs | 10 +++--- 6 files changed, 69 insertions(+), 64 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ee9b9308d..da04c5aa3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,13 +3,13 @@ ## Unreleased - 2020-xx-xx ### Changed * Bumped `rand` to `0.8` +* Rename `Handler` to `HandlerService` and rename `Factory` to `Handler`. [#1852] ### Fixed * added the actual parsing error to `test::read_body_json` [#1812] [#1812]: https://github.com/actix/actix-web/pull/1812 - - +[#1852]: https://github.com/actix/actix-web/pull/1852 ## 3.3.2 - 2020-12-01 ### Fixed diff --git a/src/handler.rs b/src/handler.rs index 3d0a2382e..b928939a5 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -14,20 +14,25 @@ use crate::request::HttpRequest; use crate::responder::Responder; use crate::service::{ServiceRequest, ServiceResponse}; -/// Async handler converter factory -pub trait Factory: Clone + 'static +/// A request handler is an async function that accepts zero or more parameters that can be +/// extracted from a request (ie, [`impl FromRequest`](crate::FromRequest)) and returns a type that can be converted into +/// an [`HttpResponse`](crate::HttpResponse) (ie, [`impl Responder`](crate::Responder)). +/// +/// If you got the error `the trait Handler<_, _, _> is not implemented`, then your function is not +/// a valid handler. See [Request Handlers](https://actix.rs/docs/handlers/) for more information. +pub trait Handler: Clone + 'static where - R: Future, - O: Responder, + R: Future, + R::Output: Responder, { fn call(&self, param: T) -> R; } -impl Factory<(), R, O> for F +impl Handler<(), R> for F where F: Fn() -> R + Clone + 'static, - R: Future, - O: Responder, + R: Future, + R::Output: Responder, { fn call(&self, _: ()) -> R { (self)() @@ -36,53 +41,53 @@ where #[doc(hidden)] /// Extract arguments from request, run factory function and make response. -pub struct Handler +pub struct HandlerService where - F: Factory, + F: Handler, T: FromRequest, - R: Future, - O: Responder, + R: Future, + R::Output: Responder, { hnd: F, - _t: PhantomData<(T, R, O)>, + _t: PhantomData<(T, R)>, } -impl Handler +impl HandlerService where - F: Factory, + F: Handler, T: FromRequest, - R: Future, - O: Responder, + R: Future, + R::Output: Responder, { pub fn new(hnd: F) -> Self { - Handler { + Self { hnd, _t: PhantomData, } } } -impl Clone for Handler +impl Clone for HandlerService where - F: Factory, + F: Handler, T: FromRequest, - R: Future, - O: Responder, + R: Future, + R::Output: Responder, { fn clone(&self) -> Self { - Handler { + Self { hnd: self.hnd.clone(), _t: PhantomData, } } } -impl ServiceFactory for Handler +impl ServiceFactory for HandlerService where - F: Factory, + F: Handler, T: FromRequest, - R: Future, - O: Responder, + R: Future, + R::Output: Responder, { type Request = ServiceRequest; type Response = ServiceResponse; @@ -97,18 +102,18 @@ where } } -// Handler is both it's ServiceFactory and Service Type. -impl Service for Handler +// Handler is both it's ServiceHandler and Service Type. +impl Service for HandlerService where - F: Factory, + F: Handler, T: FromRequest, - R: Future, - O: Responder, + R: Future, + R::Output: Responder, { type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = HandlerServiceFuture; + type Future = HandlerServiceFuture; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) @@ -123,24 +128,24 @@ where #[doc(hidden)] #[pin_project(project = HandlerProj)] -pub enum HandlerServiceFuture +pub enum HandlerServiceFuture where - F: Factory, + F: Handler, T: FromRequest, - R: Future, - O: Responder, + R: Future, + R::Output: Responder, { Extract(#[pin] T::Future, Option, F), Handle(#[pin] R, Option), - Respond(#[pin] O::Future, Option), + Respond(#[pin] ::Future, Option), } -impl Future for HandlerServiceFuture +impl Future for HandlerServiceFuture where - F: Factory, + F: Handler, T: FromRequest, - R: Future, - O: Responder, + R: Future, + R::Output: Responder, { // Error type in this future is a placeholder type. // all instances of error must be converted to ServiceResponse and return in Ok. @@ -181,10 +186,10 @@ where /// FromRequest trait impl for tuples macro_rules! factory_tuple ({ $(($n:tt, $T:ident)),+} => { - impl Factory<($($T,)+), Res, O> for Func + impl Handler<($($T,)+), Res> for Func where Func: Fn($($T,)+) -> Res + Clone + 'static, - Res: Future, - O: Responder, + Res: Future, + Res::Output: Responder, { fn call(&self, param: ($($T,)+)) -> Res { (self)($(param.$n,)+) diff --git a/src/lib.rs b/src/lib.rs index b8346d966..8246c8286 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,7 +121,7 @@ pub mod dev { pub use crate::config::{AppConfig, AppService}; #[doc(hidden)] - pub use crate::handler::Factory; + pub use crate::handler::Handler; pub use crate::info::ConnectionInfo; pub use crate::rmap::ResourceMap; pub use crate::service::{ diff --git a/src/resource.rs b/src/resource.rs index dd9b23012..f6046d652 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -17,7 +17,7 @@ use crate::data::Data; use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef}; use crate::extract::FromRequest; use crate::guard::Guard; -use crate::handler::Factory; +use crate::handler::Handler; use crate::responder::Responder; use crate::route::{CreateRouteService, Route, RouteService}; use crate::service::{ServiceRequest, ServiceResponse}; @@ -227,12 +227,12 @@ where /// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } /// App::new().service(web::resource("/").route(web::route().to(index))); /// ``` - pub fn to(mut self, handler: F) -> Self + pub fn to(mut self, handler: F) -> Self where - F: Factory, + F: Handler, I: FromRequest + 'static, - R: Future + 'static, - U: Responder + 'static, + R: Future + 'static, + R::Output: Responder + 'static, { self.routes.push(Route::new().to(handler)); self diff --git a/src/route.rs b/src/route.rs index 439ae6c4a..00d93fce9 100644 --- a/src/route.rs +++ b/src/route.rs @@ -11,7 +11,7 @@ use futures_util::future::{ready, FutureExt, LocalBoxFuture}; use crate::extract::FromRequest; use crate::guard::{self, Guard}; -use crate::handler::{Factory, Handler}; +use crate::handler::{Handler, HandlerService}; use crate::responder::Responder; use crate::service::{ServiceRequest, ServiceResponse}; use crate::HttpResponse; @@ -51,7 +51,7 @@ impl Route { #[allow(clippy::new_without_default)] pub fn new() -> Route { Route { - service: Box::new(RouteNewService::new(Handler::new(|| { + service: Box::new(RouteNewService::new(HandlerService::new(|| { ready(HttpResponse::NotFound()) }))), guards: Rc::new(Vec::new()), @@ -219,14 +219,14 @@ impl Route { /// ); /// } /// ``` - pub fn to(mut self, handler: F) -> Self + pub fn to(mut self, handler: F) -> Self where - F: Factory, + F: Handler, T: FromRequest + 'static, - R: Future + 'static, - U: Responder + 'static, + R: Future + 'static, + R::Output: Responder + 'static, { - self.service = Box::new(RouteNewService::new(Handler::new(handler))); + self.service = Box::new(RouteNewService::new(HandlerService::new(handler))); self } } diff --git a/src/web.rs b/src/web.rs index bf2158917..85e5f2e7b 100644 --- a/src/web.rs +++ b/src/web.rs @@ -9,7 +9,7 @@ pub use futures_channel::oneshot::Canceled; use crate::error::BlockingError; use crate::extract::FromRequest; -use crate::handler::Factory; +use crate::handler::Handler; use crate::resource::Resource; use crate::responder::Responder; use crate::route::Route; @@ -244,12 +244,12 @@ pub fn method(method: Method) -> Route { /// web::to(index)) /// ); /// ``` -pub fn to(handler: F) -> Route +pub fn to(handler: F) -> Route where - F: Factory, + F: Handler, I: FromRequest + 'static, - R: Future + 'static, - U: Responder + 'static, + R: Future + 'static, + R::Output: Responder + 'static, { Route::new().to(handler) }