1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-12-01 02:54:36 +01:00
actix-web/src/scope.rs

1225 lines
39 KiB
Rust
Raw Normal View History

2018-06-21 19:06:23 +02:00
use std::cell::UnsafeCell;
2018-04-30 04:39:28 +02:00
use std::marker::PhantomData;
2018-05-07 23:40:04 +02:00
use std::mem;
2018-04-30 04:39:28 +02:00
use std::rc::Rc;
use futures::{Async, Future, Poll};
use error::Error;
2018-05-04 01:22:08 +02:00
use handler::{AsyncResult, AsyncResultItem, FromRequest, Responder, RouteHandler};
2018-06-19 19:46:58 +02:00
use http::{Method, StatusCode};
2018-04-30 04:39:28 +02:00
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middleware::{
Finished as MiddlewareFinished, Middleware, Response as MiddlewareResponse,
Started as MiddlewareStarted,
};
2018-05-07 23:40:04 +02:00
use pred::Predicate;
2018-04-30 04:39:28 +02:00
use resource::ResourceHandler;
use router::Resource;
type ScopeResource<S> = Rc<ResourceHandler<S>>;
2018-05-01 07:04:24 +02:00
type Route<S> = UnsafeCell<Box<RouteHandler<S>>>;
2018-06-19 19:46:58 +02:00
type ScopeResources<S> = Rc<Vec<(Resource, ScopeResource<S>)>>;
2018-05-07 23:40:04 +02:00
type NestedInfo<S> = (Resource, Route<S>, Vec<Box<Predicate<S>>>);
2018-04-30 04:39:28 +02:00
/// Resources scope
///
/// Scope is a set of resources with common root path.
/// Scopes collect multiple paths under a common path prefix.
/// Scope path can contain variable path segments as resources.
/// Scope prefix is always complete path segment, i.e `/app` would
/// be converted to a `/app/` and it would not match `/app` path.
///
2018-05-08 00:19:03 +02:00
/// You can get variable path segments from `HttpRequest::match_info()`.
2018-05-07 23:40:04 +02:00
/// `Path` extractor also is able to extract scope level variable segments.
2018-04-30 04:39:28 +02:00
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{http, App, HttpRequest, HttpResponse};
///
/// fn main() {
2018-06-01 18:37:14 +02:00
/// let app = App::new().scope("/{project_id}/", |scope| {
/// scope
/// .resource("/path1", |r| r.f(|_| HttpResponse::Ok()))
/// .resource("/path2", |r| r.f(|_| HttpResponse::Ok()))
/// .resource("/path3", |r| r.f(|_| HttpResponse::MethodNotAllowed()))
/// });
2018-04-30 04:39:28 +02:00
/// }
/// ```
///
/// In the above example three routes get registered:
/// * /{project_id}/path1 - reponds to all http method
/// * /{project_id}/path2 - `GET` requests
/// * /{project_id}/path3 - `HEAD` requests
2018-04-30 04:39:28 +02:00
///
2018-05-08 00:19:03 +02:00
#[derive(Default)]
2018-04-30 04:39:28 +02:00
pub struct Scope<S: 'static> {
2018-05-07 23:40:04 +02:00
filters: Vec<Box<Predicate<S>>>,
nested: Vec<NestedInfo<S>>,
2018-06-21 19:06:23 +02:00
middlewares: Rc<Vec<Box<Middleware<S>>>>,
2018-06-19 19:46:58 +02:00
default: Option<ScopeResource<S>>,
2018-04-30 04:39:28 +02:00
resources: ScopeResources<S>,
}
2018-05-08 00:19:03 +02:00
#[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
2018-04-30 04:39:28 +02:00
impl<S: 'static> Scope<S> {
/// Create a new scope
// TODO: Why is this not exactly the default impl?
2018-04-30 04:39:28 +02:00
pub fn new() -> Scope<S> {
Scope {
2018-05-07 23:40:04 +02:00
filters: Vec::new(),
2018-05-01 07:04:24 +02:00
nested: Vec::new(),
2018-04-30 04:39:28 +02:00
resources: Rc::new(Vec::new()),
2018-06-21 19:06:23 +02:00
middlewares: Rc::new(Vec::new()),
2018-06-19 19:46:58 +02:00
default: None,
2018-04-30 04:39:28 +02:00
}
}
2018-05-07 23:40:04 +02:00
#[inline]
pub(crate) fn take_filters(&mut self) -> Vec<Box<Predicate<S>>> {
mem::replace(&mut self.filters, Vec::new())
}
2018-05-09 02:40:18 +02:00
/// Add match predicate to scope.
2018-05-07 23:40:04 +02:00
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{http, pred, App, HttpRequest, HttpResponse, Path};
///
/// fn index(data: Path<(String, String)>) -> &'static str {
/// "Welcome!"
/// }
///
/// fn main() {
2018-06-01 18:37:14 +02:00
/// let app = App::new().scope("/app", |scope| {
/// scope
/// .filter(pred::Header("content-type", "text/plain"))
/// .route("/test1", http::Method::GET, index)
/// .route("/test2", http::Method::POST, |_: HttpRequest| {
/// HttpResponse::MethodNotAllowed()
/// })
/// });
2018-05-07 23:40:04 +02:00
/// }
/// ```
pub fn filter<T: Predicate<S> + 'static>(mut self, p: T) -> Self {
self.filters.push(Box::new(p));
self
}
2018-05-01 07:04:24 +02:00
/// Create nested scope with new state.
2018-04-30 07:28:16 +02:00
///
2018-04-30 07:19:52 +02:00
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{App, HttpRequest};
2018-04-30 07:19:52 +02:00
///
/// struct AppState;
///
/// fn index(req: HttpRequest<AppState>) -> &'static str {
/// "Welcome!"
/// }
///
/// fn main() {
2018-06-01 18:37:14 +02:00
/// let app = App::new().scope("/app", |scope| {
/// scope.with_state("/state2", AppState, |scope| {
/// scope.resource("/test1", |r| r.f(index))
/// })
/// });
2018-04-30 07:19:52 +02:00
/// }
/// ```
2018-05-01 07:04:24 +02:00
pub fn with_state<F, T: 'static>(mut self, path: &str, state: T, f: F) -> Scope<S>
2018-04-30 07:19:52 +02:00
where
F: FnOnce(Scope<T>) -> Scope<T>,
{
let scope = Scope {
2018-05-07 23:40:04 +02:00
filters: Vec::new(),
2018-05-01 07:04:24 +02:00
nested: Vec::new(),
2018-04-30 07:19:52 +02:00
resources: Rc::new(Vec::new()),
2018-06-21 19:06:23 +02:00
middlewares: Rc::new(Vec::new()),
2018-06-19 19:46:58 +02:00
default: None,
2018-04-30 07:19:52 +02:00
};
2018-05-07 23:40:04 +02:00
let mut scope = f(scope);
2018-04-30 07:19:52 +02:00
2018-05-07 23:40:04 +02:00
let state = Rc::new(state);
let filters: Vec<Box<Predicate<S>>> = vec![Box::new(FiltersWrapper {
state: Rc::clone(&state),
filters: scope.take_filters(),
})];
let handler = UnsafeCell::new(Box::new(Wrapper { scope, state }));
self.nested
2018-05-07 23:40:04 +02:00
.push((Resource::prefix("", &path), handler, filters));
2018-05-01 07:04:24 +02:00
self
}
/// Create nested scope.
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{App, HttpRequest};
///
/// struct AppState;
///
/// fn index(req: HttpRequest<AppState>) -> &'static str {
/// "Welcome!"
/// }
///
/// fn main() {
2018-06-01 18:37:14 +02:00
/// let app = App::with_state(AppState).scope("/app", |scope| {
/// scope.nested("/v1", |scope| scope.resource("/test1", |r| r.f(index)))
/// });
2018-05-01 07:04:24 +02:00
/// }
/// ```
pub fn nested<F>(mut self, path: &str, f: F) -> Scope<S>
where
F: FnOnce(Scope<S>) -> Scope<S>,
{
let scope = Scope {
2018-05-07 23:40:04 +02:00
filters: Vec::new(),
2018-05-01 07:04:24 +02:00
nested: Vec::new(),
resources: Rc::new(Vec::new()),
2018-06-21 19:06:23 +02:00
middlewares: Rc::new(Vec::new()),
2018-06-19 19:46:58 +02:00
default: None,
2018-05-01 07:04:24 +02:00
};
2018-05-07 23:40:04 +02:00
let mut scope = f(scope);
2018-05-01 07:04:24 +02:00
2018-05-07 23:40:04 +02:00
let filters = scope.take_filters();
self.nested.push((
Resource::prefix("", &path),
UnsafeCell::new(Box::new(scope)),
2018-05-07 23:40:04 +02:00
filters,
));
2018-04-30 07:19:52 +02:00
self
}
2018-04-30 04:39:28 +02:00
/// Configure route for a specific path.
///
/// This is a simplified version of the `Scope::resource()` method.
/// Handler functions need to accept one request extractor
/// argument.
///
/// This method could be called multiple times, in that case
/// multiple routes would be registered for same resource path.
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{http, App, HttpRequest, HttpResponse, Path};
///
/// fn index(data: Path<(String, String)>) -> &'static str {
/// "Welcome!"
/// }
///
/// fn main() {
2018-06-01 18:37:14 +02:00
/// let app = App::new().scope("/app", |scope| {
/// scope.route("/test1", http::Method::GET, index).route(
/// "/test2",
/// http::Method::POST,
/// |_: HttpRequest| HttpResponse::MethodNotAllowed(),
/// )
/// });
2018-04-30 04:39:28 +02:00
/// }
/// ```
pub fn route<T, F, R>(mut self, path: &str, method: Method, f: F) -> Scope<S>
where
F: Fn(T) -> R + 'static,
R: Responder + 'static,
T: FromRequest<S> + 'static,
{
2018-06-19 21:27:41 +02:00
// check if we have resource handler
let mut found = false;
for &(ref pattern, _) in self.resources.iter() {
2018-04-30 04:39:28 +02:00
if pattern.pattern() == path {
2018-06-19 21:27:41 +02:00
found = true;
break;
2018-04-30 04:39:28 +02:00
}
}
2018-06-19 21:27:41 +02:00
if found {
let resources = Rc::get_mut(&mut self.resources)
.expect("Multiple scope references are not allowed");
for &mut (ref pattern, ref mut resource) in resources.iter_mut() {
2018-06-19 21:27:41 +02:00
if pattern.pattern() == path {
let res = Rc::get_mut(resource)
.expect("Multiple scope references are not allowed");
res.method(method).with(f);
2018-06-19 21:27:41 +02:00
break;
}
}
} else {
let mut handler = ResourceHandler::default();
handler.method(method).with(f);
let pattern = Resource::with_prefix(
handler.get_name(),
path,
if path.is_empty() { "" } else { "/" },
false,
);
Rc::get_mut(&mut self.resources)
.expect("Can not use after configuration")
.push((pattern, Rc::new(handler)));
2018-06-19 21:27:41 +02:00
}
2018-04-30 04:39:28 +02:00
self
}
/// Configure resource for a specific path.
///
/// This method is similar to an `App::resource()` method.
/// Resources may have variable path segments. Resource path uses scope
/// path as a path prefix.
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::*;
///
/// fn main() {
2018-06-01 18:37:14 +02:00
/// let app = App::new().scope("/api", |scope| {
/// scope.resource("/users/{userid}/{friend}", |r| {
/// r.get().f(|_| HttpResponse::Ok());
/// r.head().f(|_| HttpResponse::MethodNotAllowed());
/// r.route()
/// .filter(pred::Any(pred::Get()).or(pred::Put()))
/// .filter(pred::Header("Content-Type", "text/plain"))
/// .f(|_| HttpResponse::Ok())
/// })
/// });
2018-04-30 04:39:28 +02:00
/// }
/// ```
pub fn resource<F, R>(mut self, path: &str, f: F) -> Scope<S>
where
F: FnOnce(&mut ResourceHandler<S>) -> R + 'static,
{
// add resource handler
let mut handler = ResourceHandler::default();
f(&mut handler);
let pattern = Resource::with_prefix(
handler.get_name(),
path,
if path.is_empty() { "" } else { "/" },
false,
);
2018-04-30 04:39:28 +02:00
Rc::get_mut(&mut self.resources)
.expect("Can not use after configuration")
.push((pattern, Rc::new(handler)));
2018-04-30 04:39:28 +02:00
self
}
/// Default resource to be used if no matching route could be found.
2018-06-19 19:46:58 +02:00
pub fn default_resource<F, R>(mut self, f: F) -> Scope<S>
2018-04-30 04:39:28 +02:00
where
F: FnOnce(&mut ResourceHandler<S>) -> R + 'static,
{
2018-06-19 19:46:58 +02:00
if self.default.is_none() {
self.default = Some(Rc::new(ResourceHandler::default_not_found()));
}
{
let default = Rc::get_mut(self.default.as_mut().unwrap())
.expect("Multiple copies of default handler");
f(default);
2018-06-19 19:46:58 +02:00
}
2018-04-30 04:39:28 +02:00
self
}
/// Register a scope middleware
///
/// This is similar to `App's` middlewares, but
/// middlewares get invoked on scope level.
///
/// *Note* `Middleware::finish()` fires right after response get
2018-05-08 00:19:03 +02:00
/// prepared. It does not wait until body get sent to the peer.
2018-04-30 04:39:28 +02:00
pub fn middleware<M: Middleware<S>>(mut self, mw: M) -> Scope<S> {
Rc::get_mut(&mut self.middlewares)
.expect("Can not use after configuration")
.push(Box::new(mw));
self
}
}
impl<S: 'static> RouteHandler<S> for Scope<S> {
fn handle(&self, mut req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
2018-06-19 12:45:26 +02:00
let tail = req.match_info().tail as usize;
2018-04-30 04:39:28 +02:00
2018-05-08 00:19:03 +02:00
// recognize resources
2018-04-30 04:49:26 +02:00
for &(ref pattern, ref resource) in self.resources.iter() {
2018-06-19 12:45:26 +02:00
if pattern.match_with_params(&mut req, tail, false) {
2018-06-21 19:06:23 +02:00
if self.middlewares.is_empty() {
return match resource.handle(req) {
2018-06-19 19:46:58 +02:00
Ok(result) => result,
Err(req) => {
if let Some(ref default) = self.default {
match default.handle(req) {
2018-06-19 19:46:58 +02:00
Ok(result) => result,
Err(_) => AsyncResult::ok(HttpResponse::new(
StatusCode::NOT_FOUND,
)),
}
} else {
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
}
}
};
2018-04-30 04:39:28 +02:00
} else {
2018-05-04 01:22:08 +02:00
return AsyncResult::async(Box::new(Compose::new(
2018-04-30 04:39:28 +02:00
req,
Rc::clone(&self.middlewares),
Rc::clone(&resource),
2018-05-04 01:22:08 +02:00
)));
2018-04-30 04:39:28 +02:00
}
}
}
2018-05-01 07:04:24 +02:00
// nested scopes
let len = req.prefix_len() as usize;
2018-05-07 23:40:04 +02:00
'outer: for &(ref prefix, ref handler, ref filters) in &self.nested {
2018-06-19 12:45:26 +02:00
if let Some(prefix_len) = prefix.match_prefix_with_params(&mut req, len) {
2018-05-07 23:40:04 +02:00
for filter in filters {
if !filter.check(&mut req) {
continue 'outer;
}
}
2018-06-19 12:45:26 +02:00
let url = req.url().clone();
let prefix_len = (len + prefix_len) as u16;
req.set_prefix_len(prefix_len);
req.match_info_mut().set_tail(prefix_len);
req.match_info_mut().set_url(url);
2018-05-01 07:04:24 +02:00
let hnd: &mut RouteHandler<_> =
unsafe { (&mut *(handler.get())).as_mut() };
return hnd.handle(req);
2018-04-30 07:19:52 +02:00
}
2018-04-30 04:39:28 +02:00
}
2018-05-01 07:04:24 +02:00
// default handler
2018-06-21 19:06:23 +02:00
if self.middlewares.is_empty() {
2018-06-19 19:46:58 +02:00
if let Some(ref default) = self.default {
match default.handle(req) {
2018-06-19 19:46:58 +02:00
Ok(result) => result,
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
}
} else {
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
}
} else if let Some(ref default) = self.default {
2018-05-04 01:22:08 +02:00
AsyncResult::async(Box::new(Compose::new(
2018-05-01 07:04:24 +02:00
req,
Rc::clone(&self.middlewares),
2018-06-19 19:46:58 +02:00
Rc::clone(default),
2018-05-04 01:22:08 +02:00
)))
2018-06-19 19:46:58 +02:00
} else {
unimplemented!()
2018-05-01 07:04:24 +02:00
}
2018-04-30 04:39:28 +02:00
}
fn has_default_resource(&self) -> bool {
self.default.is_some()
}
fn default_resource(&mut self, default: ScopeResource<S>) {
self.default = Some(default);
}
2018-04-30 04:39:28 +02:00
}
2018-04-30 07:19:52 +02:00
struct Wrapper<S: 'static> {
state: Rc<S>,
scope: Scope<S>,
}
impl<S: 'static, S2: 'static> RouteHandler<S2> for Wrapper<S> {
fn handle(&self, req: HttpRequest<S2>) -> AsyncResult<HttpResponse> {
self.scope.handle(req.change_state(Rc::clone(&self.state)))
2018-04-30 07:19:52 +02:00
}
}
2018-05-07 23:40:04 +02:00
struct FiltersWrapper<S: 'static> {
state: Rc<S>,
filters: Vec<Box<Predicate<S>>>,
}
impl<S: 'static, S2: 'static> Predicate<S2> for FiltersWrapper<S> {
fn check(&self, req: &mut HttpRequest<S2>) -> bool {
let mut req = req.change_state(Rc::clone(&self.state));
for filter in &self.filters {
if !filter.check(&mut req) {
return false;
}
}
true
}
}
2018-04-30 04:39:28 +02:00
/// Compose resource level middlewares with route handler.
struct Compose<S: 'static> {
info: ComposeInfo<S>,
state: ComposeState<S>,
}
struct ComposeInfo<S: 'static> {
count: usize,
req: HttpRequest<S>,
2018-06-21 19:06:23 +02:00
mws: Rc<Vec<Box<Middleware<S>>>>,
resource: Rc<ResourceHandler<S>>,
2018-04-30 04:39:28 +02:00
}
enum ComposeState<S: 'static> {
Starting(StartMiddlewares<S>),
Handler(WaitingResponse<S>),
RunMiddlewares(RunMiddlewares<S>),
Finishing(FinishingMiddlewares<S>),
Completed(Response<S>),
}
impl<S: 'static> ComposeState<S> {
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
match *self {
ComposeState::Starting(ref mut state) => state.poll(info),
ComposeState::Handler(ref mut state) => state.poll(info),
ComposeState::RunMiddlewares(ref mut state) => state.poll(info),
ComposeState::Finishing(ref mut state) => state.poll(info),
ComposeState::Completed(_) => None,
}
}
}
impl<S: 'static> Compose<S> {
fn new(
2018-06-21 19:06:23 +02:00
req: HttpRequest<S>, mws: Rc<Vec<Box<Middleware<S>>>>,
resource: Rc<ResourceHandler<S>>,
2018-04-30 04:39:28 +02:00
) -> Self {
let mut info = ComposeInfo {
count: 0,
req,
mws,
resource,
};
let state = StartMiddlewares::init(&mut info);
Compose { state, info }
}
}
impl<S> Future for Compose<S> {
type Item = HttpResponse;
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
if let ComposeState::Completed(ref mut resp) = self.state {
let resp = resp.resp.take().unwrap();
return Ok(Async::Ready(resp));
}
if let Some(state) = self.state.poll(&mut self.info) {
self.state = state;
} else {
return Ok(Async::NotReady);
}
}
}
}
/// Middlewares start executor
struct StartMiddlewares<S> {
fut: Option<Fut>,
_s: PhantomData<S>,
}
type Fut = Box<Future<Item = Option<HttpResponse>, Error = Error>>;
impl<S: 'static> StartMiddlewares<S> {
fn init(info: &mut ComposeInfo<S>) -> ComposeState<S> {
2018-06-21 19:06:23 +02:00
let len = info.mws.len();
2018-04-30 04:39:28 +02:00
loop {
if info.count == len {
2018-06-19 19:46:58 +02:00
let reply = {
let req = info.req.clone();
info.resource.handle(req).unwrap()
2018-04-30 04:39:28 +02:00
};
return WaitingResponse::init(info, reply);
} else {
2018-06-21 19:06:23 +02:00
let state = info.mws[info.count].start(&mut info.req);
match state {
2018-04-30 04:39:28 +02:00
Ok(MiddlewareStarted::Done) => info.count += 1,
Ok(MiddlewareStarted::Response(resp)) => {
return RunMiddlewares::init(info, resp)
}
2018-05-08 01:09:41 +02:00
Ok(MiddlewareStarted::Future(fut)) => {
return ComposeState::Starting(StartMiddlewares {
fut: Some(fut),
_s: PhantomData,
})
}
Err(err) => return RunMiddlewares::init(info, err.into()),
2018-04-30 04:39:28 +02:00
}
}
}
}
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
2018-06-21 19:06:23 +02:00
let len = info.mws.len();
2018-04-30 04:39:28 +02:00
'outer: loop {
match self.fut.as_mut().unwrap().poll() {
Ok(Async::NotReady) => return None,
Ok(Async::Ready(resp)) => {
info.count += 1;
if let Some(resp) = resp {
return Some(RunMiddlewares::init(info, resp));
}
loop {
if info.count == len {
2018-06-19 19:46:58 +02:00
let reply = {
let req = info.req.clone();
info.resource.handle(req).unwrap()
};
return Some(WaitingResponse::init(info, reply));
2018-04-30 04:39:28 +02:00
} else {
2018-06-21 19:06:23 +02:00
let state = info.mws[info.count].start(&mut info.req);
match state {
2018-04-30 04:39:28 +02:00
Ok(MiddlewareStarted::Done) => info.count += 1,
Ok(MiddlewareStarted::Response(resp)) => {
return Some(RunMiddlewares::init(info, resp));
}
Ok(MiddlewareStarted::Future(fut)) => {
self.fut = Some(fut);
continue 'outer;
}
Err(err) => {
return Some(RunMiddlewares::init(info, err.into()))
}
2018-04-30 04:39:28 +02:00
}
}
}
}
Err(err) => return Some(RunMiddlewares::init(info, err.into())),
2018-04-30 04:39:28 +02:00
}
}
}
}
// waiting for response
struct WaitingResponse<S> {
fut: Box<Future<Item = HttpResponse, Error = Error>>,
_s: PhantomData<S>,
}
impl<S: 'static> WaitingResponse<S> {
#[inline]
2018-05-04 01:22:08 +02:00
fn init(
2018-05-17 21:20:20 +02:00
info: &mut ComposeInfo<S>, reply: AsyncResult<HttpResponse>,
2018-05-04 01:22:08 +02:00
) -> ComposeState<S> {
2018-04-30 04:39:28 +02:00
match reply.into() {
2018-05-04 01:22:08 +02:00
AsyncResultItem::Ok(resp) => RunMiddlewares::init(info, resp),
AsyncResultItem::Err(err) => RunMiddlewares::init(info, err.into()),
AsyncResultItem::Future(fut) => ComposeState::Handler(WaitingResponse {
2018-04-30 04:39:28 +02:00
fut,
_s: PhantomData,
}),
}
}
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
match self.fut.poll() {
Ok(Async::NotReady) => None,
Ok(Async::Ready(response)) => Some(RunMiddlewares::init(info, response)),
Err(err) => Some(RunMiddlewares::init(info, err.into())),
}
}
}
/// Middlewares response executor
struct RunMiddlewares<S> {
curr: usize,
fut: Option<Box<Future<Item = HttpResponse, Error = Error>>>,
_s: PhantomData<S>,
}
impl<S: 'static> RunMiddlewares<S> {
fn init(info: &mut ComposeInfo<S>, mut resp: HttpResponse) -> ComposeState<S> {
let mut curr = 0;
2018-06-21 19:06:23 +02:00
let len = info.mws.len();
2018-04-30 04:39:28 +02:00
loop {
2018-06-21 19:06:23 +02:00
let state = info.mws[curr].response(&mut info.req, resp);
resp = match state {
2018-04-30 04:39:28 +02:00
Err(err) => {
info.count = curr + 1;
return FinishingMiddlewares::init(info, err.into());
}
Ok(MiddlewareResponse::Done(r)) => {
curr += 1;
if curr == len {
return FinishingMiddlewares::init(info, r);
} else {
r
}
}
Ok(MiddlewareResponse::Future(fut)) => {
return ComposeState::RunMiddlewares(RunMiddlewares {
curr,
fut: Some(fut),
_s: PhantomData,
})
}
};
}
}
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
2018-06-21 19:06:23 +02:00
let len = info.mws.len();
2018-04-30 04:39:28 +02:00
loop {
// poll latest fut
let mut resp = match self.fut.as_mut().unwrap().poll() {
Ok(Async::NotReady) => return None,
Ok(Async::Ready(resp)) => {
self.curr += 1;
resp
}
Err(err) => return Some(FinishingMiddlewares::init(info, err.into())),
};
loop {
if self.curr == len {
return Some(FinishingMiddlewares::init(info, resp));
} else {
2018-06-21 19:06:23 +02:00
let state = info.mws[self.curr].response(&mut info.req, resp);
match state {
2018-04-30 04:39:28 +02:00
Err(err) => {
return Some(FinishingMiddlewares::init(info, err.into()))
}
Ok(MiddlewareResponse::Done(r)) => {
self.curr += 1;
resp = r
}
Ok(MiddlewareResponse::Future(fut)) => {
self.fut = Some(fut);
break;
}
}
}
}
}
}
}
/// Middlewares start executor
struct FinishingMiddlewares<S> {
resp: Option<HttpResponse>,
fut: Option<Box<Future<Item = (), Error = Error>>>,
_s: PhantomData<S>,
}
impl<S: 'static> FinishingMiddlewares<S> {
fn init(info: &mut ComposeInfo<S>, resp: HttpResponse) -> ComposeState<S> {
if info.count == 0 {
Response::init(resp)
} else {
let mut state = FinishingMiddlewares {
resp: Some(resp),
fut: None,
_s: PhantomData,
};
if let Some(st) = state.poll(info) {
st
} else {
ComposeState::Finishing(state)
}
}
}
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
loop {
// poll latest fut
let not_ready = if let Some(ref mut fut) = self.fut {
match fut.poll() {
Ok(Async::NotReady) => true,
Ok(Async::Ready(())) => false,
Err(err) => {
error!("Middleware finish error: {}", err);
false
}
}
} else {
false
};
if not_ready {
return None;
}
self.fut = None;
if info.count == 0 {
return Some(Response::init(self.resp.take().unwrap()));
}
2018-04-30 04:39:28 +02:00
info.count -= 1;
2018-06-21 19:06:23 +02:00
let state = info.mws[info.count as usize]
.finish(&mut info.req, self.resp.as_ref().unwrap());
match state {
2018-04-30 04:39:28 +02:00
MiddlewareFinished::Done => {
if info.count == 0 {
return Some(Response::init(self.resp.take().unwrap()));
}
}
MiddlewareFinished::Future(fut) => {
self.fut = Some(fut);
}
}
}
}
}
struct Response<S> {
resp: Option<HttpResponse>,
_s: PhantomData<S>,
}
impl<S: 'static> Response<S> {
fn init(resp: HttpResponse) -> ComposeState<S> {
ComposeState::Completed(Response {
resp: Some(resp),
_s: PhantomData,
})
}
}
#[cfg(test)]
mod tests {
use bytes::Bytes;
2018-04-30 04:39:28 +02:00
use application::App;
use body::Body;
2018-05-07 23:40:04 +02:00
use http::{Method, StatusCode};
2018-05-08 00:19:03 +02:00
use httprequest::HttpRequest;
2018-04-30 04:39:28 +02:00
use httpresponse::HttpResponse;
2018-05-07 23:40:04 +02:00
use pred;
2018-04-30 04:39:28 +02:00
use test::TestRequest;
#[test]
fn test_scope() {
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-04-30 04:39:28 +02:00
.scope("/app", |scope| {
scope.resource("/path1", |r| r.f(|_| HttpResponse::Ok()))
})
.finish();
let req = TestRequest::with_uri("/app/path1").finish();
let resp = app.run(req);
2018-05-02 02:19:15 +02:00
assert_eq!(resp.as_msg().status(), StatusCode::OK);
2018-04-30 04:39:28 +02:00
}
#[test]
fn test_scope_root() {
2018-06-21 19:06:23 +02:00
let app = App::new()
.scope("/app", |scope| {
scope
.resource("", |r| r.f(|_| HttpResponse::Ok()))
.resource("/", |r| r.f(|_| HttpResponse::Created()))
})
.finish();
let req = TestRequest::with_uri("/app").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
}
2018-05-23 23:11:01 +02:00
#[test]
fn test_scope_root2() {
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-05-23 23:11:01 +02:00
.scope("/app/", |scope| {
scope.resource("", |r| r.f(|_| HttpResponse::Ok()))
})
.finish();
let req = TestRequest::with_uri("/app").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
}
#[test]
fn test_scope_root3() {
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-05-23 23:11:01 +02:00
.scope("/app/", |scope| {
scope.resource("/", |r| r.f(|_| HttpResponse::Ok()))
})
.finish();
let req = TestRequest::with_uri("/app").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
}
2018-05-08 00:19:03 +02:00
#[test]
fn test_scope_route() {
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-05-08 00:19:03 +02:00
.scope("app", |scope| {
2018-05-08 01:09:41 +02:00
scope
2018-05-09 00:44:50 +02:00
.route("/path1", Method::GET, |_: HttpRequest<_>| {
2018-05-08 01:09:41 +02:00
HttpResponse::Ok()
})
2018-05-09 00:44:50 +02:00
.route("/path1", Method::DELETE, |_: HttpRequest<_>| {
2018-05-08 01:09:41 +02:00
HttpResponse::Ok()
})
2018-05-08 00:19:03 +02:00
})
.finish();
let req = TestRequest::with_uri("/app/path1").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
2018-05-08 01:09:41 +02:00
let req = TestRequest::with_uri("/app/path1")
.method(Method::DELETE)
.finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
2018-05-08 00:19:03 +02:00
let req = TestRequest::with_uri("/app/path1")
.method(Method::POST)
.finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
}
2018-05-07 23:40:04 +02:00
#[test]
fn test_scope_filter() {
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-05-07 23:40:04 +02:00
.scope("/app", |scope| {
scope
.filter(pred::Get())
.resource("/path1", |r| r.f(|_| HttpResponse::Ok()))
})
.finish();
let req = TestRequest::with_uri("/app/path1")
.method(Method::POST)
.finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/path1")
.method(Method::GET)
.finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
}
#[test]
fn test_scope_variable_segment() {
2018-06-21 19:06:23 +02:00
let app = App::new()
.scope("/ab-{project}", |scope| {
scope.resource("/path1", |r| {
r.f(|r| {
HttpResponse::Ok()
.body(format!("project: {}", &r.match_info()["project"]))
})
})
})
.finish();
let req = TestRequest::with_uri("/ab-project1/path1").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
match resp.as_msg().body() {
2018-05-07 23:40:04 +02:00
&Body::Binary(ref b) => {
let bytes: Bytes = b.clone().into();
assert_eq!(bytes, Bytes::from_static(b"project: project1"));
}
_ => panic!(),
}
let req = TestRequest::with_uri("/aa-project1/path1").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
}
2018-04-30 07:19:52 +02:00
#[test]
fn test_scope_with_state() {
struct State;
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-04-30 07:19:52 +02:00
.scope("/app", |scope| {
2018-05-01 07:04:24 +02:00
scope.with_state("/t1", State, |scope| {
2018-04-30 07:19:52 +02:00
scope.resource("/path1", |r| r.f(|_| HttpResponse::Created()))
})
})
.finish();
2018-05-01 07:04:24 +02:00
let req = TestRequest::with_uri("/app/t1/path1").finish();
let resp = app.run(req);
2018-05-02 02:19:15 +02:00
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
2018-05-01 07:04:24 +02:00
}
#[test]
fn test_scope_with_state_root() {
struct State;
2018-06-21 19:06:23 +02:00
let app = App::new()
.scope("/app", |scope| {
scope.with_state("/t1", State, |scope| {
scope
.resource("", |r| r.f(|_| HttpResponse::Ok()))
.resource("/", |r| r.f(|_| HttpResponse::Created()))
})
})
.finish();
let req = TestRequest::with_uri("/app/t1").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/t1/").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
}
2018-05-23 23:11:01 +02:00
#[test]
fn test_scope_with_state_root2() {
struct State;
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-05-23 23:11:01 +02:00
.scope("/app", |scope| {
scope.with_state("/t1/", State, |scope| {
scope.resource("", |r| r.f(|_| HttpResponse::Ok()))
})
})
.finish();
let req = TestRequest::with_uri("/app/t1").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/t1/").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
}
#[test]
fn test_scope_with_state_root3() {
struct State;
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-05-23 23:11:01 +02:00
.scope("/app", |scope| {
scope.with_state("/t1/", State, |scope| {
scope.resource("/", |r| r.f(|_| HttpResponse::Ok()))
})
})
.finish();
let req = TestRequest::with_uri("/app/t1").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/t1/").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
}
2018-05-07 23:40:04 +02:00
#[test]
fn test_scope_with_state_filter() {
struct State;
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-05-07 23:40:04 +02:00
.scope("/app", |scope| {
scope.with_state("/t1", State, |scope| {
scope
.filter(pred::Get())
.resource("/path1", |r| r.f(|_| HttpResponse::Ok()))
})
})
.finish();
let req = TestRequest::with_uri("/app/t1/path1")
.method(Method::POST)
.finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/t1/path1")
.method(Method::GET)
.finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
}
2018-05-01 07:04:24 +02:00
#[test]
fn test_nested_scope() {
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-05-01 07:04:24 +02:00
.scope("/app", |scope| {
scope.nested("/t1", |scope| {
scope.resource("/path1", |r| r.f(|_| HttpResponse::Created()))
})
})
.finish();
let req = TestRequest::with_uri("/app/t1/path1").finish();
2018-04-30 07:19:52 +02:00
let resp = app.run(req);
2018-05-02 02:19:15 +02:00
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
2018-04-30 07:19:52 +02:00
}
#[test]
fn test_nested_scope_root() {
2018-06-21 19:06:23 +02:00
let app = App::new()
.scope("/app", |scope| {
scope.nested("/t1", |scope| {
scope
.resource("", |r| r.f(|_| HttpResponse::Ok()))
.resource("/", |r| r.f(|_| HttpResponse::Created()))
})
})
.finish();
let req = TestRequest::with_uri("/app/t1").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/t1/").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
}
2018-05-07 23:40:04 +02:00
#[test]
fn test_nested_scope_filter() {
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-05-07 23:40:04 +02:00
.scope("/app", |scope| {
scope.nested("/t1", |scope| {
scope
.filter(pred::Get())
.resource("/path1", |r| r.f(|_| HttpResponse::Ok()))
})
})
.finish();
let req = TestRequest::with_uri("/app/t1/path1")
.method(Method::POST)
.finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/t1/path1")
.method(Method::GET)
.finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
}
#[test]
fn test_nested_scope_with_variable_segment() {
2018-06-21 19:06:23 +02:00
let app = App::new()
.scope("/app", |scope| {
scope.nested("/{project_id}", |scope| {
scope.resource("/path1", |r| {
r.f(|r| {
HttpResponse::Created().body(format!(
"project: {}",
&r.match_info()["project_id"]
))
})
})
})
})
.finish();
let req = TestRequest::with_uri("/app/project_1/path1").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
match resp.as_msg().body() {
2018-05-07 23:40:04 +02:00
&Body::Binary(ref b) => {
let bytes: Bytes = b.clone().into();
assert_eq!(bytes, Bytes::from_static(b"project: project_1"));
}
_ => panic!(),
}
}
#[test]
fn test_nested2_scope_with_variable_segment() {
2018-06-21 19:06:23 +02:00
let app = App::new()
.scope("/app", |scope| {
scope.nested("/{project}", |scope| {
scope.nested("/{id}", |scope| {
scope.resource("/path1", |r| {
r.f(|r| {
HttpResponse::Created().body(format!(
"project: {} - {}",
&r.match_info()["project"],
&r.match_info()["id"],
))
})
})
})
})
})
.finish();
let req = TestRequest::with_uri("/app/test/1/path1").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
match resp.as_msg().body() {
2018-05-07 23:40:04 +02:00
&Body::Binary(ref b) => {
let bytes: Bytes = b.clone().into();
assert_eq!(bytes, Bytes::from_static(b"project: test - 1"));
}
_ => panic!(),
}
let req = TestRequest::with_uri("/app/test/1/path2").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
}
2018-04-30 04:39:28 +02:00
#[test]
fn test_default_resource() {
2018-06-21 19:06:23 +02:00
let app = App::new()
2018-04-30 04:39:28 +02:00
.scope("/app", |scope| {
scope
.resource("/path1", |r| r.f(|_| HttpResponse::Ok()))
.default_resource(|r| r.f(|_| HttpResponse::BadRequest()))
})
.finish();
let req = TestRequest::with_uri("/app/path2").finish();
let resp = app.run(req);
2018-05-02 02:19:15 +02:00
assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST);
2018-04-30 04:39:28 +02:00
let req = TestRequest::with_uri("/path2").finish();
let resp = app.run(req);
2018-05-02 02:19:15 +02:00
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
2018-04-30 04:39:28 +02:00
}
#[test]
fn test_default_resource_propagation() {
2018-06-21 19:06:23 +02:00
let app = App::new()
.scope("/app1", |scope| {
scope.default_resource(|r| r.f(|_| HttpResponse::BadRequest()))
})
.scope("/app2", |scope| scope)
.default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed()))
.finish();
let req = TestRequest::with_uri("/non-exist").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
let req = TestRequest::with_uri("/app1/non-exist").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/app2/non-exist").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
}
2018-04-30 04:39:28 +02:00
}