mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into either-extract-fut
This commit is contained in:
commit
a88d08de69
|
@ -11,6 +11,9 @@
|
||||||
## 0.6.0-beta.4 - 2021-04-02
|
## 0.6.0-beta.4 - 2021-04-02
|
||||||
* No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
* Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046]
|
||||||
|
|
||||||
|
[#2046]: https://github.com/actix/actix-web/pull/2046
|
||||||
|
|
||||||
## 0.6.0-beta.3 - 2021-03-09
|
## 0.6.0-beta.3 - 2021-03-09
|
||||||
* No notable changes.
|
* No notable changes.
|
||||||
|
|
|
@ -37,7 +37,8 @@ pub struct Files {
|
||||||
renderer: Rc<DirectoryRenderer>,
|
renderer: Rc<DirectoryRenderer>,
|
||||||
mime_override: Option<Rc<MimeOverride>>,
|
mime_override: Option<Rc<MimeOverride>>,
|
||||||
file_flags: named::Flags,
|
file_flags: named::Flags,
|
||||||
guards: Option<Rc<dyn Guard>>,
|
use_guards: Option<Rc<dyn Guard>>,
|
||||||
|
guards: Vec<Box<Rc<dyn Guard>>>,
|
||||||
hidden_files: bool,
|
hidden_files: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,12 +60,12 @@ impl Clone for Files {
|
||||||
file_flags: self.file_flags,
|
file_flags: self.file_flags,
|
||||||
path: self.path.clone(),
|
path: self.path.clone(),
|
||||||
mime_override: self.mime_override.clone(),
|
mime_override: self.mime_override.clone(),
|
||||||
|
use_guards: self.use_guards.clone(),
|
||||||
guards: self.guards.clone(),
|
guards: self.guards.clone(),
|
||||||
hidden_files: self.hidden_files,
|
hidden_files: self.hidden_files,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Files {
|
impl Files {
|
||||||
/// Create new `Files` instance for a specified base directory.
|
/// Create new `Files` instance for a specified base directory.
|
||||||
///
|
///
|
||||||
|
@ -80,10 +81,9 @@ impl Files {
|
||||||
/// If the mount path is set as the root path `/`, services registered after this one will
|
/// If the mount path is set as the root path `/`, services registered after this one will
|
||||||
/// be inaccessible. Register more specific handlers and services first.
|
/// be inaccessible. Register more specific handlers and services first.
|
||||||
///
|
///
|
||||||
/// `Files` uses a threadpool for blocking filesystem operations. By default, the pool uses a
|
/// `Files` utilizes the existing Tokio thread-pool for blocking filesystem operations.
|
||||||
/// max number of threads equal to `512 * HttpServer::worker`. Real time thread count are
|
/// The number of running threads is adjusted over time as needed, up to a maximum of 512 times
|
||||||
/// adjusted with work load. More threads would spawn when need and threads goes idle for a
|
/// the number of server [workers](HttpServer::workers), by default.
|
||||||
/// period of time would be de-spawned.
|
|
||||||
pub fn new<T: Into<PathBuf>>(mount_path: &str, serve_from: T) -> Files {
|
pub fn new<T: Into<PathBuf>>(mount_path: &str, serve_from: T) -> Files {
|
||||||
let orig_dir = serve_from.into();
|
let orig_dir = serve_from.into();
|
||||||
let dir = match orig_dir.canonicalize() {
|
let dir = match orig_dir.canonicalize() {
|
||||||
|
@ -104,7 +104,8 @@ impl Files {
|
||||||
renderer: Rc::new(directory_listing),
|
renderer: Rc::new(directory_listing),
|
||||||
mime_override: None,
|
mime_override: None,
|
||||||
file_flags: named::Flags::default(),
|
file_flags: named::Flags::default(),
|
||||||
guards: None,
|
use_guards: None,
|
||||||
|
guards: Vec::new(),
|
||||||
hidden_files: false,
|
hidden_files: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +157,6 @@ impl Files {
|
||||||
/// Specifies whether to use ETag or not.
|
/// Specifies whether to use ETag or not.
|
||||||
///
|
///
|
||||||
/// Default is true.
|
/// Default is true.
|
||||||
#[inline]
|
|
||||||
pub fn use_etag(mut self, value: bool) -> Self {
|
pub fn use_etag(mut self, value: bool) -> Self {
|
||||||
self.file_flags.set(named::Flags::ETAG, value);
|
self.file_flags.set(named::Flags::ETAG, value);
|
||||||
self
|
self
|
||||||
|
@ -165,7 +165,6 @@ impl Files {
|
||||||
/// Specifies whether to use Last-Modified or not.
|
/// Specifies whether to use Last-Modified or not.
|
||||||
///
|
///
|
||||||
/// Default is true.
|
/// Default is true.
|
||||||
#[inline]
|
|
||||||
pub fn use_last_modified(mut self, value: bool) -> Self {
|
pub fn use_last_modified(mut self, value: bool) -> Self {
|
||||||
self.file_flags.set(named::Flags::LAST_MD, value);
|
self.file_flags.set(named::Flags::LAST_MD, value);
|
||||||
self
|
self
|
||||||
|
@ -174,25 +173,55 @@ impl Files {
|
||||||
/// Specifies whether text responses should signal a UTF-8 encoding.
|
/// Specifies whether text responses should signal a UTF-8 encoding.
|
||||||
///
|
///
|
||||||
/// Default is false (but will default to true in a future version).
|
/// Default is false (but will default to true in a future version).
|
||||||
#[inline]
|
|
||||||
pub fn prefer_utf8(mut self, value: bool) -> Self {
|
pub fn prefer_utf8(mut self, value: bool) -> Self {
|
||||||
self.file_flags.set(named::Flags::PREFER_UTF8, value);
|
self.file_flags.set(named::Flags::PREFER_UTF8, value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies custom guards to use for directory listings and files.
|
/// Adds a routing guard.
|
||||||
///
|
///
|
||||||
/// Default behaviour allows GET and HEAD.
|
/// Use this to allow multiple chained file services that respond to strictly different
|
||||||
#[inline]
|
/// properties of a request. Due to the way routing works, if a guard check returns true and the
|
||||||
pub fn use_guards<G: Guard + 'static>(mut self, guards: G) -> Self {
|
/// request starts being handled by the file service, it will not be able to back-out and try
|
||||||
self.guards = Some(Rc::new(guards));
|
/// the next service, you will simply get a 404 (or 405) error response.
|
||||||
|
///
|
||||||
|
/// To allow `POST` requests to retrieve files, see [`Files::use_guards`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use actix_web::{guard::Header, App};
|
||||||
|
/// use actix_files::Files;
|
||||||
|
///
|
||||||
|
/// App::new().service(
|
||||||
|
/// Files::new("/","/my/site/files")
|
||||||
|
/// .guard(Header("Host", "example.com"))
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
|
||||||
|
self.guards.push(Box::new(Rc::new(guard)));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specifies guard to check before fetching directory listings or files.
|
||||||
|
///
|
||||||
|
/// Note that this guard has no effect on routing; it's main use is to guard on the request's
|
||||||
|
/// method just before serving the file, only allowing `GET` and `HEAD` requests by default.
|
||||||
|
/// See [`Files::guard`] for routing guards.
|
||||||
|
pub fn method_guard<G: Guard + 'static>(mut self, guard: G) -> Self {
|
||||||
|
self.use_guards = Some(Rc::new(guard));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[deprecated(since = "0.6.0", note = "Renamed to `method_guard`.")]
|
||||||
|
/// See [`Files::method_guard`].
|
||||||
|
pub fn use_guards<G: Guard + 'static>(self, guard: G) -> Self {
|
||||||
|
self.method_guard(guard)
|
||||||
|
}
|
||||||
|
|
||||||
/// Disable `Content-Disposition` header.
|
/// Disable `Content-Disposition` header.
|
||||||
///
|
///
|
||||||
/// By default Content-Disposition` header is enabled.
|
/// By default Content-Disposition` header is enabled.
|
||||||
#[inline]
|
|
||||||
pub fn disable_content_disposition(mut self) -> Self {
|
pub fn disable_content_disposition(mut self) -> Self {
|
||||||
self.file_flags.remove(named::Flags::CONTENT_DISPOSITION);
|
self.file_flags.remove(named::Flags::CONTENT_DISPOSITION);
|
||||||
self
|
self
|
||||||
|
@ -200,8 +229,9 @@ impl Files {
|
||||||
|
|
||||||
/// Sets default handler which is used when no matched file could be found.
|
/// Sets default handler which is used when no matched file could be found.
|
||||||
///
|
///
|
||||||
/// For example, you could set a fall back static file handler:
|
/// # Examples
|
||||||
/// ```rust
|
/// Setting a fallback static file handler:
|
||||||
|
/// ```
|
||||||
/// use actix_files::{Files, NamedFile};
|
/// use actix_files::{Files, NamedFile};
|
||||||
///
|
///
|
||||||
/// # fn run() -> Result<(), actix_web::Error> {
|
/// # fn run() -> Result<(), actix_web::Error> {
|
||||||
|
@ -230,7 +260,6 @@ impl Files {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables serving hidden files and directories, allowing a leading dots in url fragments.
|
/// Enables serving hidden files and directories, allowing a leading dots in url fragments.
|
||||||
#[inline]
|
|
||||||
pub fn use_hidden_files(mut self) -> Self {
|
pub fn use_hidden_files(mut self) -> Self {
|
||||||
self.hidden_files = true;
|
self.hidden_files = true;
|
||||||
self
|
self
|
||||||
|
@ -238,7 +267,19 @@ impl Files {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpServiceFactory for Files {
|
impl HttpServiceFactory for Files {
|
||||||
fn register(self, config: &mut AppService) {
|
fn register(mut self, config: &mut AppService) {
|
||||||
|
let guards = if self.guards.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let guards = std::mem::take(&mut self.guards);
|
||||||
|
Some(
|
||||||
|
guards
|
||||||
|
.into_iter()
|
||||||
|
.map(|guard| -> Box<dyn Guard> { guard })
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
if self.default.borrow().is_none() {
|
if self.default.borrow().is_none() {
|
||||||
*self.default.borrow_mut() = Some(config.default_service());
|
*self.default.borrow_mut() = Some(config.default_service());
|
||||||
}
|
}
|
||||||
|
@ -249,7 +290,7 @@ impl HttpServiceFactory for Files {
|
||||||
ResourceDef::prefix(&self.path)
|
ResourceDef::prefix(&self.path)
|
||||||
};
|
};
|
||||||
|
|
||||||
config.register_service(rdef, None, self, None)
|
config.register_service(rdef, guards, self, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +312,7 @@ impl ServiceFactory<ServiceRequest> for Files {
|
||||||
renderer: self.renderer.clone(),
|
renderer: self.renderer.clone(),
|
||||||
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.use_guards.clone(),
|
||||||
hidden_files: self.hidden_files,
|
hidden_files: self.hidden_files,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -532,7 +532,7 @@ mod tests {
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_files_guards() {
|
async fn test_files_guards() {
|
||||||
let srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(Files::new("/", ".").use_guards(guard::Post())),
|
App::new().service(Files::new("/", ".").method_guard(guard::Post())),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
first
|
|
@ -0,0 +1 @@
|
||||||
|
second
|
|
@ -0,0 +1,36 @@
|
||||||
|
use actix_files::Files;
|
||||||
|
use actix_web::{
|
||||||
|
guard::Host,
|
||||||
|
http::StatusCode,
|
||||||
|
test::{self, TestRequest},
|
||||||
|
App,
|
||||||
|
};
|
||||||
|
use bytes::Bytes;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_guard_filter() {
|
||||||
|
let srv = test::init_service(
|
||||||
|
App::new()
|
||||||
|
.service(Files::new("/", "./tests/fixtures/guards/first").guard(Host("first.com")))
|
||||||
|
.service(
|
||||||
|
Files::new("/", "./tests/fixtures/guards/second").guard(Host("second.com")),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/index.txt")
|
||||||
|
.append_header(("Host", "first.com"))
|
||||||
|
.to_request();
|
||||||
|
let res = test::call_service(&srv, req).await;
|
||||||
|
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
assert_eq!(test::read_body(res).await, Bytes::from("first"));
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/index.txt")
|
||||||
|
.append_header(("Host", "second.com"))
|
||||||
|
.to_request();
|
||||||
|
let res = test::call_service(&srv, req).await;
|
||||||
|
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
assert_eq!(test::read_body(res).await, Bytes::from("second"));
|
||||||
|
}
|
|
@ -26,6 +26,8 @@
|
||||||
//! ```
|
//! ```
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use actix_http::http::{self, header, uri::Uri};
|
use actix_http::http::{self, header, uri::Uri};
|
||||||
use actix_http::RequestHead;
|
use actix_http::RequestHead;
|
||||||
|
@ -40,6 +42,12 @@ pub trait Guard {
|
||||||
fn check(&self, request: &RequestHead) -> bool;
|
fn check(&self, request: &RequestHead) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Guard for Rc<dyn Guard> {
|
||||||
|
fn check(&self, request: &RequestHead) -> bool {
|
||||||
|
self.deref().check(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create guard object for supplied function.
|
/// Create guard object for supplied function.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::{
|
||||||
use actix_http::Payload;
|
use actix_http::Payload;
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use encoding_rs::{Encoding, UTF_8};
|
use encoding_rs::{Encoding, UTF_8};
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::{future::LocalBoxFuture, ready};
|
||||||
use futures_util::{FutureExt as _, StreamExt as _};
|
use futures_util::{FutureExt as _, StreamExt as _};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
||||||
|
@ -123,11 +123,10 @@ where
|
||||||
{
|
{
|
||||||
type Config = FormConfig;
|
type Config = FormConfig;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self, Error>>;
|
type Future = FormExtractFut<T>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
||||||
let req2 = req.clone();
|
|
||||||
let (limit, err_handler) = req
|
let (limit, err_handler) = req
|
||||||
.app_data::<Self::Config>()
|
.app_data::<Self::Config>()
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
|
@ -137,16 +136,42 @@ where
|
||||||
.map(|c| (c.limit, c.err_handler.clone()))
|
.map(|c| (c.limit, c.err_handler.clone()))
|
||||||
.unwrap_or((16384, None));
|
.unwrap_or((16384, None));
|
||||||
|
|
||||||
UrlEncoded::new(req, payload)
|
FormExtractFut {
|
||||||
.limit(limit)
|
fut: UrlEncoded::new(req, payload).limit(limit),
|
||||||
.map(move |res| match res {
|
req: req.clone(),
|
||||||
Err(err) => match err_handler {
|
err_handler,
|
||||||
Some(err_handler) => Err((err_handler)(err, &req2)),
|
}
|
||||||
None => Err(err.into()),
|
}
|
||||||
},
|
}
|
||||||
Ok(item) => Ok(Form(item)),
|
|
||||||
})
|
type FormErrHandler = Option<Rc<dyn Fn(UrlencodedError, &HttpRequest) -> Error>>;
|
||||||
.boxed_local()
|
|
||||||
|
pub struct FormExtractFut<T> {
|
||||||
|
fut: UrlEncoded<T>,
|
||||||
|
err_handler: FormErrHandler,
|
||||||
|
req: HttpRequest,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Future for FormExtractFut<T>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned + 'static,
|
||||||
|
{
|
||||||
|
type Output = Result<Form<T>, Error>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
|
||||||
|
let res = ready!(Pin::new(&mut this.fut).poll(cx));
|
||||||
|
|
||||||
|
let res = match res {
|
||||||
|
Err(err) => match &this.err_handler {
|
||||||
|
Some(err_handler) => Err((err_handler)(err, &this.req)),
|
||||||
|
None => Err(err.into()),
|
||||||
|
},
|
||||||
|
Ok(item) => Ok(Form(item)),
|
||||||
|
};
|
||||||
|
|
||||||
|
Poll::Ready(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +218,7 @@ impl<T: Serialize> Responder for Form<T> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FormConfig {
|
pub struct FormConfig {
|
||||||
limit: usize,
|
limit: usize,
|
||||||
err_handler: Option<Rc<dyn Fn(UrlencodedError, &HttpRequest) -> Error>>,
|
err_handler: FormErrHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormConfig {
|
impl FormConfig {
|
||||||
|
|
Loading…
Reference in New Issue