1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-30 10:42:55 +01:00

Merge branch 'master' into multipart-content-disposition

This commit is contained in:
Nikolay Kim 2018-06-07 21:20:11 -07:00 committed by GitHub
commit 5a7902ff9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 156 additions and 13 deletions

View File

@ -22,11 +22,23 @@
* Added header `User-Agent: Actix-web/<current_version>` to default headers when building a request
### Removed
* 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
* Support chunked encoding for UrlEncoded body #262

View File

@ -69,7 +69,7 @@ You may consider checking out
## 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).

View File

@ -22,6 +22,7 @@ pub struct HttpApplication<S = ()> {
prefix_len: usize,
router: Router,
inner: Rc<UnsafeCell<Inner<S>>>,
filters: Option<Vec<Box<Predicate<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('/'))
};
if m {
let mut req = req.with_state(Rc::clone(&self.state), self.router.clone());
let tp = self.get_handler(&mut req);
let mut req2 =
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);
Ok(Box::new(Pipeline::new(
req,
req2,
Rc::clone(&self.middlewares),
inner,
tp,
@ -168,6 +179,7 @@ struct ApplicationParts<S> {
external: HashMap<String, Resource>,
encoding: ContentEncoding,
middlewares: Vec<Box<Middleware<S>>>,
filters: Vec<Box<Predicate<S>>>,
}
/// Structure that follows the builder pattern for building application
@ -190,6 +202,7 @@ impl App<()> {
handlers: Vec::new(),
external: HashMap::new(),
encoding: ContentEncoding::Auto,
filters: Vec::new(),
middlewares: Vec::new(),
}),
}
@ -229,6 +242,7 @@ where
handlers: Vec::new(),
external: HashMap::new(),
middlewares: Vec::new(),
filters: Vec::new(),
encoding: ContentEncoding::Auto,
}),
}
@ -285,6 +299,26 @@ where
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.
///
/// This is a simplified version of the `App::resource()` method.
@ -608,6 +642,11 @@ where
handlers: parts.handlers,
resources,
}));
let filters = if parts.filters.is_empty() {
None
} else {
Some(parts.filters)
};
HttpApplication {
state: Rc::new(parts.state),
@ -616,6 +655,7 @@ where
prefix,
prefix_len,
inner,
filters,
}
}
@ -700,7 +740,8 @@ mod tests {
use http::StatusCode;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use test::TestRequest;
use pred;
use test::{TestRequest, TestServer};
#[test]
fn test_default_resource() {
@ -899,4 +940,21 @@ mod tests {
let resp = app.run(req);
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);
}
}

View File

@ -51,7 +51,9 @@ pub struct Error {
impl 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 {
self.cause.as_ref()
}
@ -97,14 +99,14 @@ impl Error {
//
// This currently requires a transmute. This could be avoided if failure
// 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 {
pub struct CompatWrappedError {
error: failure::Error,
}
let compat: &CompatWrappedError = unsafe {
::std::mem::transmute(compat)
};
let compat: &CompatWrappedError =
unsafe { &*(compat as *const _ as *const CompatWrappedError) };
compat.error.downcast_ref()
} else {
None
@ -126,8 +128,12 @@ pub trait InternalResponseErrorAsFail {
#[doc(hidden)]
impl<T: ResponseError> InternalResponseErrorAsFail for T {
fn as_fail(&self) -> &Fail { self }
fn as_mut_fail(&mut self) -> &mut Fail { self }
fn as_fail(&self) -> &Fail {
self
}
fn as_mut_fail(&mut self) -> &mut Fail {
self
}
}
/// Error that can be converted to `HttpResponse`

View File

@ -141,6 +141,12 @@ impl HttpRequest<()> {
pub fn with_state<S>(self, state: Rc<S>, router: Router) -> HttpRequest<S> {
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> {

View File

@ -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)]
mod tests {
use super::*;
@ -228,6 +267,28 @@ mod tests {
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]
fn test_methods() {
let mut req = HttpRequest::new(