mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into master
This commit is contained in:
commit
70defbe190
|
@ -3,6 +3,12 @@
|
||||||
## Unreleased - 2020-xx-xx
|
## 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
|
||||||
|
|
||||||
|
|
||||||
## 0.4.1 - 2020-11-24
|
## 0.4.1 - 2020-11-24
|
||||||
* Clarify order of parameters in `Files::new` and improve docs.
|
* Clarify order of parameters in `Files::new` and improve docs.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-files"
|
name = "actix-files"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Static file serving for Actix Web"
|
description = "Static file serving for Actix Web"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -23,13 +23,13 @@ bitflags = "1"
|
||||||
bytes = "0.5.3"
|
bytes = "0.5.3"
|
||||||
futures-core = { version = "0.3.7", default-features = false }
|
futures-core = { version = "0.3.7", default-features = false }
|
||||||
futures-util = { 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"
|
log = "0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
mime_guess = "2.0.1"
|
mime_guess = "2.0.1"
|
||||||
percent-encoding = "2.1"
|
percent-encoding = "2.1"
|
||||||
v_htmlescape = "0.11"
|
v_htmlescape = "0.12"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "1.0.0"
|
actix-rt = "1.0.0"
|
||||||
actix-web = { version = "3.0.0", features = ["openssl"] }
|
actix-web = "3.0.0"
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
> Static file serving for Actix Web
|
> Static file serving for Actix Web
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-files)
|
[](https://crates.io/crates/actix-files)
|
||||||
[](https://docs.rs/actix-files/0.4.1)
|
[](https://docs.rs/actix-files/0.5.0)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.42.html)
|
[](https://blog.rust-lang.org/2020/03/12/Rust-1.42.html)
|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-files/0.4.1)
|
[](https://deps.rs/crate/actix-files/0.5.0)
|
||||||
[](https://crates.io/crates/actix-files)
|
[](https://crates.io/crates/actix-files)
|
||||||
[](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ pub struct Files {
|
||||||
mime_override: Option<Rc<MimeOverride>>,
|
mime_override: Option<Rc<MimeOverride>>,
|
||||||
file_flags: named::Flags,
|
file_flags: named::Flags,
|
||||||
guards: Option<Rc<dyn Guard>>,
|
guards: Option<Rc<dyn Guard>>,
|
||||||
|
hidden_files: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Files {
|
impl fmt::Debug for Files {
|
||||||
|
@ -60,6 +61,7 @@ impl Clone for Files {
|
||||||
path: self.path.clone(),
|
path: self.path.clone(),
|
||||||
mime_override: self.mime_override.clone(),
|
mime_override: self.mime_override.clone(),
|
||||||
guards: self.guards.clone(),
|
guards: self.guards.clone(),
|
||||||
|
hidden_files: self.hidden_files,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +105,7 @@ impl Files {
|
||||||
mime_override: None,
|
mime_override: None,
|
||||||
file_flags: named::Flags::default(),
|
file_flags: named::Flags::default(),
|
||||||
guards: None,
|
guards: None,
|
||||||
|
hidden_files: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,6 +216,13 @@ impl Files {
|
||||||
|
|
||||||
self
|
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 {
|
impl HttpServiceFactory for Files {
|
||||||
|
@ -251,6 +261,7 @@ impl ServiceFactory for Files {
|
||||||
mime_override: self.mime_override.clone(),
|
mime_override: self.mime_override.clone(),
|
||||||
file_flags: self.file_flags,
|
file_flags: self.file_flags,
|
||||||
guards: self.guards.clone(),
|
guards: self.guards.clone(),
|
||||||
|
hidden_files: self.hidden_files,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(ref default) = *self.default.borrow() {
|
if let Some(ref default) = *self.default.borrow() {
|
||||||
|
|
|
@ -15,12 +15,19 @@ impl FromStr for PathBufWrap {
|
||||||
type Err = UriSegmentError;
|
type Err = UriSegmentError;
|
||||||
|
|
||||||
fn from_str(path: &str) -> Result<Self, Self::Err> {
|
fn from_str(path: &str) -> Result<Self, Self::Err> {
|
||||||
|
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<Self, UriSegmentError> {
|
||||||
let mut buf = PathBuf::new();
|
let mut buf = PathBuf::new();
|
||||||
|
|
||||||
for segment in path.split('/') {
|
for segment in path.split('/') {
|
||||||
if segment == ".." {
|
if segment == ".." {
|
||||||
buf.pop();
|
buf.pop();
|
||||||
} else if segment.starts_with('.') {
|
} else if !hidden_files && segment.starts_with('.') {
|
||||||
return Err(UriSegmentError::BadStart('.'));
|
return Err(UriSegmentError::BadStart('.'));
|
||||||
} else if segment.starts_with('*') {
|
} else if segment.starts_with('*') {
|
||||||
return Err(UriSegmentError::BadStart('*'));
|
return Err(UriSegmentError::BadStart('*'));
|
||||||
|
@ -96,4 +103,17 @@ mod tests {
|
||||||
PathBuf::from_iter(vec!["seg2"])
|
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"])
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ pub struct FilesService {
|
||||||
pub(crate) mime_override: Option<Rc<MimeOverride>>,
|
pub(crate) mime_override: Option<Rc<MimeOverride>>,
|
||||||
pub(crate) file_flags: named::Flags,
|
pub(crate) file_flags: named::Flags,
|
||||||
pub(crate) guards: Option<Rc<dyn Guard>>,
|
pub(crate) guards: Option<Rc<dyn Guard>>,
|
||||||
|
pub(crate) hidden_files: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilesServiceFuture = Either<
|
type FilesServiceFuture = Either<
|
||||||
|
@ -83,7 +84,8 @@ impl Service for FilesService {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let real_path: PathBufWrap = match req.match_info().path().parse() {
|
let real_path =
|
||||||
|
match PathBufWrap::parse_path(req.match_info().path(), self.hidden_files) {
|
||||||
Ok(item) => item,
|
Ok(item) => item,
|
||||||
Err(e) => return Either::Left(ok(req.error_response(e))),
|
Err(e) => return Either::Left(ok(req.error_response(e))),
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub use self::codec::Codec;
|
||||||
pub use self::dispatcher::Dispatcher;
|
pub use self::dispatcher::Dispatcher;
|
||||||
pub use self::expect::ExpectHandler;
|
pub use self::expect::ExpectHandler;
|
||||||
pub use self::payload::Payload;
|
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::upgrade::UpgradeHandler;
|
||||||
pub use self::utils::SendResponse;
|
pub use self::utils::SendResponse;
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,12 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
|
use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
|
||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use futures_util::future::{ok, Ready};
|
use futures_util::future::ready;
|
||||||
|
|
||||||
use crate::body::MessageBody;
|
use crate::body::MessageBody;
|
||||||
use crate::cloneable::CloneableService;
|
use crate::cloneable::CloneableService;
|
||||||
use crate::config::ServiceConfig;
|
use crate::config::ServiceConfig;
|
||||||
use crate::error::{DispatchError, Error, ParseError};
|
use crate::error::{DispatchError, Error};
|
||||||
use crate::helpers::DataFactory;
|
use crate::helpers::DataFactory;
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
|
@ -22,7 +22,7 @@ use crate::{ConnectCallback, Extensions};
|
||||||
|
|
||||||
use super::codec::Codec;
|
use super::codec::Codec;
|
||||||
use super::dispatcher::Dispatcher;
|
use super::dispatcher::Dispatcher;
|
||||||
use super::{ExpectHandler, Message, UpgradeHandler};
|
use super::{ExpectHandler, UpgradeHandler};
|
||||||
|
|
||||||
/// `ServiceFactory` implementation for HTTP1 transport
|
/// `ServiceFactory` implementation for HTTP1 transport
|
||||||
pub struct H1Service<T, S, B, X = ExpectHandler, U = UpgradeHandler<T>> {
|
pub struct H1Service<T, S, B, X = ExpectHandler, U = UpgradeHandler<T>> {
|
||||||
|
@ -90,7 +90,7 @@ where
|
||||||
> {
|
> {
|
||||||
pipeline_factory(|io: TcpStream| {
|
pipeline_factory(|io: TcpStream| {
|
||||||
let peer_addr = io.peer_addr().ok();
|
let peer_addr = io.peer_addr().ok();
|
||||||
ok((io, peer_addr))
|
ready(Ok((io, peer_addr)))
|
||||||
})
|
})
|
||||||
.and_then(self)
|
.and_then(self)
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ mod openssl {
|
||||||
)
|
)
|
||||||
.and_then(|io: SslStream<TcpStream>| {
|
.and_then(|io: SslStream<TcpStream>| {
|
||||||
let peer_addr = io.get_ref().peer_addr().ok();
|
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))
|
.and_then(self.map_err(TlsError::Service))
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ mod rustls {
|
||||||
)
|
)
|
||||||
.and_then(|io: TlsStream<TcpStream>| {
|
.and_then(|io: TlsStream<TcpStream>| {
|
||||||
let peer_addr = io.get_ref().0.peer_addr().ok();
|
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))
|
.and_then(self.map_err(TlsError::Service))
|
||||||
}
|
}
|
||||||
|
@ -500,103 +500,3 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `ServiceFactory` implementation for `OneRequestService` service
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct OneRequest<T> {
|
|
||||||
config: ServiceConfig,
|
|
||||||
_t: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> OneRequest<T>
|
|
||||||
where
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
/// Create new `H1SimpleService` instance.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
OneRequest {
|
|
||||||
config: ServiceConfig::default(),
|
|
||||||
_t: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ServiceFactory for OneRequest<T>
|
|
||||||
where
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
type Config = ();
|
|
||||||
type Request = T;
|
|
||||||
type Response = (Request, Framed<T, Codec>);
|
|
||||||
type Error = ParseError;
|
|
||||||
type InitError = ();
|
|
||||||
type Service = OneRequestService<T>;
|
|
||||||
type Future = Ready<Result<Self::Service, Self::InitError>>;
|
|
||||||
|
|
||||||
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> {
|
|
||||||
_t: PhantomData<T>,
|
|
||||||
config: ServiceConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Service for OneRequestService<T>
|
|
||||||
where
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
type Request = T;
|
|
||||||
type Response = (Request, Framed<T, Codec>);
|
|
||||||
type Error = ParseError;
|
|
||||||
type Future = OneRequestServiceResponse<T>;
|
|
||||||
|
|
||||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
||||||
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<T>
|
|
||||||
where
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
#[pin]
|
|
||||||
framed: Option<Framed<T, Codec>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Future for OneRequestServiceResponse<T>
|
|
||||||
where
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
type Output = Result<(Request, Framed<T, Codec>), ParseError>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
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)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue