mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-30 18:34:36 +01:00
Merge branch 'master' into multipart-content-disposition
This commit is contained in:
commit
5a7902ff9a
12
CHANGES.md
12
CHANGES.md
@ -22,11 +22,23 @@
|
|||||||
|
|
||||||
* Added header `User-Agent: Actix-web/<current_version>` to default headers when building a request
|
* Added header `User-Agent: Actix-web/<current_version>` to default headers when building a request
|
||||||
|
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
* Remove `Route::with2()` and `Route::with3()` use tuple of extractors instead.
|
* Remove `Route::with2()` and `Route::with3()` use tuple of extractors instead.
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.12] - 2018-06-07
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add `Host` filter #287
|
||||||
|
|
||||||
|
* Allow to filter applications
|
||||||
|
|
||||||
|
* Improved failure interoperability with downcasting #285
|
||||||
|
|
||||||
|
|
||||||
## [0.6.11] - 2018-06-05
|
## [0.6.11] - 2018-06-05
|
||||||
|
|
||||||
* Support chunked encoding for UrlEncoded body #262
|
* Support chunked encoding for UrlEncoded body #262
|
||||||
|
@ -69,7 +69,7 @@ You may consider checking out
|
|||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
* [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r15&hw=ph&test=plaintext)
|
* [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext)
|
||||||
|
|
||||||
* Some basic benchmarks could be found in this [repository](https://github.com/fafhrd91/benchmarks).
|
* Some basic benchmarks could be found in this [repository](https://github.com/fafhrd91/benchmarks).
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ pub struct HttpApplication<S = ()> {
|
|||||||
prefix_len: usize,
|
prefix_len: usize,
|
||||||
router: Router,
|
router: Router,
|
||||||
inner: Rc<UnsafeCell<Inner<S>>>,
|
inner: Rc<UnsafeCell<Inner<S>>>,
|
||||||
|
filters: Option<Vec<Box<Predicate<S>>>>,
|
||||||
middlewares: Rc<RefCell<Vec<Box<Middleware<S>>>>>,
|
middlewares: Rc<RefCell<Vec<Box<Middleware<S>>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,11 +144,21 @@ impl<S: 'static> HttpHandler for HttpApplication<S> {
|
|||||||
|| path.split_at(self.prefix_len).1.starts_with('/'))
|
|| path.split_at(self.prefix_len).1.starts_with('/'))
|
||||||
};
|
};
|
||||||
if m {
|
if m {
|
||||||
let mut req = req.with_state(Rc::clone(&self.state), self.router.clone());
|
let mut req2 =
|
||||||
let tp = self.get_handler(&mut req);
|
req.clone_with_state(Rc::clone(&self.state), self.router.clone());
|
||||||
|
|
||||||
|
if let Some(ref filters) = self.filters {
|
||||||
|
for filter in filters {
|
||||||
|
if !filter.check(&mut req2) {
|
||||||
|
return Err(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tp = self.get_handler(&mut req2);
|
||||||
let inner = Rc::clone(&self.inner);
|
let inner = Rc::clone(&self.inner);
|
||||||
Ok(Box::new(Pipeline::new(
|
Ok(Box::new(Pipeline::new(
|
||||||
req,
|
req2,
|
||||||
Rc::clone(&self.middlewares),
|
Rc::clone(&self.middlewares),
|
||||||
inner,
|
inner,
|
||||||
tp,
|
tp,
|
||||||
@ -168,6 +179,7 @@ struct ApplicationParts<S> {
|
|||||||
external: HashMap<String, Resource>,
|
external: HashMap<String, Resource>,
|
||||||
encoding: ContentEncoding,
|
encoding: ContentEncoding,
|
||||||
middlewares: Vec<Box<Middleware<S>>>,
|
middlewares: Vec<Box<Middleware<S>>>,
|
||||||
|
filters: Vec<Box<Predicate<S>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Structure that follows the builder pattern for building application
|
/// Structure that follows the builder pattern for building application
|
||||||
@ -190,6 +202,7 @@ impl App<()> {
|
|||||||
handlers: Vec::new(),
|
handlers: Vec::new(),
|
||||||
external: HashMap::new(),
|
external: HashMap::new(),
|
||||||
encoding: ContentEncoding::Auto,
|
encoding: ContentEncoding::Auto,
|
||||||
|
filters: Vec::new(),
|
||||||
middlewares: Vec::new(),
|
middlewares: Vec::new(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -229,6 +242,7 @@ where
|
|||||||
handlers: Vec::new(),
|
handlers: Vec::new(),
|
||||||
external: HashMap::new(),
|
external: HashMap::new(),
|
||||||
middlewares: Vec::new(),
|
middlewares: Vec::new(),
|
||||||
|
filters: Vec::new(),
|
||||||
encoding: ContentEncoding::Auto,
|
encoding: ContentEncoding::Auto,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -285,6 +299,26 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add match predicate to application.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// # use actix_web::*;
|
||||||
|
/// # fn main() {
|
||||||
|
/// App::new()
|
||||||
|
/// .filter(pred::Host("www.rust-lang.org"))
|
||||||
|
/// .resource("/path", |r| r.f(|_| HttpResponse::Ok()))
|
||||||
|
/// # .finish();
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn filter<T: Predicate<S> + 'static>(mut self, p: T) -> App<S> {
|
||||||
|
{
|
||||||
|
let parts = self.parts.as_mut().expect("Use after finish");
|
||||||
|
parts.filters.push(Box::new(p));
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Configure route for a specific path.
|
/// Configure route for a specific path.
|
||||||
///
|
///
|
||||||
/// This is a simplified version of the `App::resource()` method.
|
/// This is a simplified version of the `App::resource()` method.
|
||||||
@ -608,6 +642,11 @@ where
|
|||||||
handlers: parts.handlers,
|
handlers: parts.handlers,
|
||||||
resources,
|
resources,
|
||||||
}));
|
}));
|
||||||
|
let filters = if parts.filters.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(parts.filters)
|
||||||
|
};
|
||||||
|
|
||||||
HttpApplication {
|
HttpApplication {
|
||||||
state: Rc::new(parts.state),
|
state: Rc::new(parts.state),
|
||||||
@ -616,6 +655,7 @@ where
|
|||||||
prefix,
|
prefix,
|
||||||
prefix_len,
|
prefix_len,
|
||||||
inner,
|
inner,
|
||||||
|
filters,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,7 +740,8 @@ mod tests {
|
|||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::HttpResponse;
|
use httpresponse::HttpResponse;
|
||||||
use test::TestRequest;
|
use pred;
|
||||||
|
use test::{TestRequest, TestServer};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_resource() {
|
fn test_default_resource() {
|
||||||
@ -899,4 +940,21 @@ mod tests {
|
|||||||
let resp = app.run(req);
|
let resp = app.run(req);
|
||||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filter() {
|
||||||
|
let mut srv = TestServer::with_factory(|| {
|
||||||
|
App::new()
|
||||||
|
.filter(pred::Get())
|
||||||
|
.handler("/test", |_| HttpResponse::Ok())
|
||||||
|
});
|
||||||
|
|
||||||
|
let request = srv.get().uri(srv.url("/test")).finish().unwrap();
|
||||||
|
let response = srv.execute(request.send()).unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
let request = srv.post().uri(srv.url("/test")).finish().unwrap();
|
||||||
|
let response = srv.execute(request.send()).unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
20
src/error.rs
20
src/error.rs
@ -51,7 +51,9 @@ pub struct Error {
|
|||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
/// Deprecated way to reference the underlying response error.
|
/// Deprecated way to reference the underlying response error.
|
||||||
#[deprecated(since = "0.6.0", note = "please use `Error::as_response_error()` instead")]
|
#[deprecated(
|
||||||
|
since = "0.6.0", note = "please use `Error::as_response_error()` instead"
|
||||||
|
)]
|
||||||
pub fn cause(&self) -> &ResponseError {
|
pub fn cause(&self) -> &ResponseError {
|
||||||
self.cause.as_ref()
|
self.cause.as_ref()
|
||||||
}
|
}
|
||||||
@ -97,14 +99,14 @@ impl Error {
|
|||||||
//
|
//
|
||||||
// This currently requires a transmute. This could be avoided if failure
|
// This currently requires a transmute. This could be avoided if failure
|
||||||
// provides a deref: https://github.com/rust-lang-nursery/failure/pull/213
|
// provides a deref: https://github.com/rust-lang-nursery/failure/pull/213
|
||||||
let compat: Option<&failure::Compat<failure::Error>> = Fail::downcast_ref(self.cause.as_fail());
|
let compat: Option<&failure::Compat<failure::Error>> =
|
||||||
|
Fail::downcast_ref(self.cause.as_fail());
|
||||||
if let Some(compat) = compat {
|
if let Some(compat) = compat {
|
||||||
pub struct CompatWrappedError {
|
pub struct CompatWrappedError {
|
||||||
error: failure::Error,
|
error: failure::Error,
|
||||||
}
|
}
|
||||||
let compat: &CompatWrappedError = unsafe {
|
let compat: &CompatWrappedError =
|
||||||
::std::mem::transmute(compat)
|
unsafe { &*(compat as *const _ as *const CompatWrappedError) };
|
||||||
};
|
|
||||||
compat.error.downcast_ref()
|
compat.error.downcast_ref()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -126,8 +128,12 @@ pub trait InternalResponseErrorAsFail {
|
|||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
impl<T: ResponseError> InternalResponseErrorAsFail for T {
|
impl<T: ResponseError> InternalResponseErrorAsFail for T {
|
||||||
fn as_fail(&self) -> &Fail { self }
|
fn as_fail(&self) -> &Fail {
|
||||||
fn as_mut_fail(&mut self) -> &mut Fail { self }
|
self
|
||||||
|
}
|
||||||
|
fn as_mut_fail(&mut self) -> &mut Fail {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error that can be converted to `HttpResponse`
|
/// Error that can be converted to `HttpResponse`
|
||||||
|
@ -141,6 +141,12 @@ impl HttpRequest<()> {
|
|||||||
pub fn with_state<S>(self, state: Rc<S>, router: Router) -> HttpRequest<S> {
|
pub fn with_state<S>(self, state: Rc<S>, router: Router) -> HttpRequest<S> {
|
||||||
HttpRequest(self.0, Some(state), Some(router))
|
HttpRequest(self.0, Some(state), Some(router))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clone_with_state<S>(
|
||||||
|
&self, state: Rc<S>, router: Router,
|
||||||
|
) -> HttpRequest<S> {
|
||||||
|
HttpRequest(self.0.clone(), Some(state), Some(router))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> HttpMessage for HttpRequest<S> {
|
impl<S> HttpMessage for HttpRequest<S> {
|
||||||
|
61
src/pred.rs
61
src/pred.rs
@ -196,6 +196,45 @@ impl<S: 'static> Predicate<S> for HeaderPredicate<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return predicate that matches if request contains specified Host name.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// use actix_web::{pred, App, HttpResponse};
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// App::new().resource("/index.html", |r| {
|
||||||
|
/// r.route()
|
||||||
|
/// .filter(pred::Host("www.rust-lang.org"))
|
||||||
|
/// .f(|_| HttpResponse::MethodNotAllowed())
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn Host<S: 'static, H: AsRef<str>>(host: H) -> HostPredicate<S> {
|
||||||
|
HostPredicate(host.as_ref().to_string(), None, PhantomData)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct HostPredicate<S>(String, Option<String>, PhantomData<S>);
|
||||||
|
|
||||||
|
impl<S> HostPredicate<S> {
|
||||||
|
/// Set reuest scheme to match
|
||||||
|
pub fn scheme<H: AsRef<str>>(&mut self, scheme: H) {
|
||||||
|
self.1 = Some(scheme.as_ref().to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static> Predicate<S> for HostPredicate<S> {
|
||||||
|
fn check(&self, req: &mut HttpRequest<S>) -> bool {
|
||||||
|
let info = req.connection_info();
|
||||||
|
if let Some(ref scheme) = self.1 {
|
||||||
|
self.0 == info.host() && scheme == info.scheme()
|
||||||
|
} else {
|
||||||
|
self.0 == info.host()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -228,6 +267,28 @@ mod tests {
|
|||||||
assert!(!pred.check(&mut req));
|
assert!(!pred.check(&mut req));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_host() {
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers.insert(
|
||||||
|
header::HOST,
|
||||||
|
header::HeaderValue::from_static("www.rust-lang.org"),
|
||||||
|
);
|
||||||
|
let mut req = HttpRequest::new(
|
||||||
|
Method::GET,
|
||||||
|
Uri::from_str("/").unwrap(),
|
||||||
|
Version::HTTP_11,
|
||||||
|
headers,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let pred = Host("www.rust-lang.org");
|
||||||
|
assert!(pred.check(&mut req));
|
||||||
|
|
||||||
|
let pred = Host("localhost");
|
||||||
|
assert!(!pred.check(&mut req));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_methods() {
|
fn test_methods() {
|
||||||
let mut req = HttpRequest::new(
|
let mut req = HttpRequest::new(
|
||||||
|
Loading…
Reference in New Issue
Block a user