use std::mem; use std::rc::Rc; use std::marker::PhantomData; use futures::{Async, Future, Poll}; use error::Error; use pred::Predicate; use handler::{Reply, ReplyItem, Handler, Responder, RouteHandler, AsyncHandler, WrapHandler}; use middleware::{Middleware, Response as MiddlewareResponse, Started as MiddlewareStarted}; use httpcodes::HTTPNotFound; use httprequest::HttpRequest; use httpresponse::HttpResponse; /// Resource route definition /// /// Route uses builder-like pattern for configuration. /// If handler is not explicitly set, default *404 Not Found* handler is used. pub struct Route { preds: Vec>>, handler: InnerHandler, } impl Default for Route { fn default() -> Route { Route { preds: Vec::new(), handler: InnerHandler::new(|_| HTTPNotFound), } } } impl Route { #[inline] pub(crate) fn check(&self, req: &mut HttpRequest) -> bool { for pred in &self.preds { if !pred.check(req) { return false } } true } #[inline] pub(crate) fn handle(&mut self, req: HttpRequest) -> Reply { self.handler.handle(req) } #[inline] pub(crate) fn compose(&mut self, req: HttpRequest, mws: Rc>>>) -> Reply { Reply::async(Compose::new(req, mws, self.handler.clone())) } /// Add match predicate to route. /// /// ```rust /// # extern crate actix_web; /// # use actix_web::*; /// # use actix_web::httpcodes::*; /// # fn main() { /// Application::new() /// .resource("/path", |r| /// r.route() /// .p(pred::Get()) /// .p(pred::Header("content-type", "text/plain")) /// .f(|req| HTTPOk) /// ) /// # .finish(); /// # } /// ``` pub fn p + 'static>(&mut self, p: T) -> &mut Self { self.preds.push(Box::new(p)); self } /// Set handler object. Usually call to this method is last call /// during route configuration, because it does not return reference to self. pub fn h>(&mut self, handler: H) { self.handler = InnerHandler::new(handler); } /// Set handler function. Usually call to this method is last call /// during route configuration, because it does not return reference to self. pub fn f(&mut self, handler: F) where F: Fn(HttpRequest) -> R + 'static, R: Responder + 'static, { self.handler = InnerHandler::new(handler); } /// Set async handler function. pub fn a(&mut self, handler: H) where H: Fn(HttpRequest) -> F + 'static, F: Future + 'static, R: Responder + 'static, E: Into + 'static { self.handler = InnerHandler::async(handler); } } /// RouteHandler wrapper. This struct is required because it needs to be shared /// for resource level middlewares. struct InnerHandler(Rc>>); impl InnerHandler { #[inline] fn new>(h: H) -> Self { InnerHandler(Rc::new(Box::new(WrapHandler::new(h)))) } #[inline] fn async(h: H) -> Self where H: Fn(HttpRequest) -> F + 'static, F: Future + 'static, R: Responder + 'static, E: Into + 'static { InnerHandler(Rc::new(Box::new(AsyncHandler::new(h)))) } #[inline] pub fn handle(&self, req: HttpRequest) -> Reply { // reason: handler is unique per thread, // handler get called from async code, and handler doesnt have side effects #[allow(mutable_transmutes)] #[cfg_attr(feature = "cargo-clippy", allow(borrowed_box))] let h: &mut Box> = unsafe { mem::transmute(self.0.as_ref()) }; h.handle(req) } } impl Clone for InnerHandler { #[inline] fn clone(&self) -> Self { InnerHandler(Rc::clone(&self.0)) } } /// Compose resource level middlewares with route handler. struct Compose { info: ComposeInfo, state: ComposeState, } struct ComposeInfo { count: usize, req: HttpRequest, mws: Rc>>>, handler: InnerHandler, } enum ComposeState { Starting(StartMiddlewares), Handler(WaitingResponse), RunMiddlewares(RunMiddlewares), Response(Response), } impl ComposeState { fn poll(&mut self, info: &mut ComposeInfo) -> Option> { 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::Response(_) => None, } } } impl Compose { fn new(req: HttpRequest, mws: Rc>>>, handler: InnerHandler) -> Self { let mut info = ComposeInfo { count: 0, req: req, mws: mws, handler: handler }; let state = StartMiddlewares::init(&mut info); Compose {state: state, info: info} } } impl Future for Compose { type Item = HttpResponse; type Error = Error; fn poll(&mut self) -> Poll { loop { if let ComposeState::Response(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 { fut: Option, _s: PhantomData, } type Fut = Box, Error=Error>>; impl StartMiddlewares { fn init(info: &mut ComposeInfo) -> ComposeState { let len = info.mws.len(); loop { if info.count == len { let reply = info.handler.handle(info.req.clone()); return WaitingResponse::init(info, reply) } else { match info.mws[info.count].start(&mut info.req) { Ok(MiddlewareStarted::Done) => info.count += 1, Ok(MiddlewareStarted::Response(resp)) => return RunMiddlewares::init(info, resp), Ok(MiddlewareStarted::Future(mut fut)) => match fut.poll() { Ok(Async::NotReady) => return ComposeState::Starting(StartMiddlewares { fut: Some(fut), _s: PhantomData}), Ok(Async::Ready(resp)) => { if let Some(resp) = resp { return RunMiddlewares::init(info, resp); } info.count += 1; } Err(err) => return Response::init(err.into()), }, Err(err) => return Response::init(err.into()), } } } } fn poll(&mut self, info: &mut ComposeInfo) -> Option> { let len = info.mws.len(); '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)); } if info.count == len { let reply = info.handler.handle(info.req.clone()); return Some(WaitingResponse::init(info, reply)); } else { loop { match info.mws[info.count].start(&mut info.req) { 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(Response::init(err.into())) } } } } Err(err) => return Some(Response::init(err.into())) } } } } // waiting for response struct WaitingResponse { fut: Box>, _s: PhantomData, } impl WaitingResponse { #[inline] fn init(info: &mut ComposeInfo, reply: Reply) -> ComposeState { match reply.into() { ReplyItem::Message(resp) => RunMiddlewares::init(info, resp), ReplyItem::Future(fut) => ComposeState::Handler( WaitingResponse { fut: fut, _s: PhantomData }), } } fn poll(&mut self, info: &mut ComposeInfo) -> Option> { match self.fut.poll() { Ok(Async::NotReady) => None, Ok(Async::Ready(response)) => Some(RunMiddlewares::init(info, response)), Err(err) => Some(Response::init(err.into())), } } } /// Middlewares response executor struct RunMiddlewares { curr: usize, fut: Option>>, _s: PhantomData, } impl RunMiddlewares { fn init(info: &mut ComposeInfo, mut resp: HttpResponse) -> ComposeState { let mut curr = 0; let len = info.mws.len(); loop { resp = match info.mws[curr].response(&mut info.req, resp) { Err(err) => { info.count = curr + 1; return Response::init(err.into()) }, Ok(MiddlewareResponse::Done(r)) => { curr += 1; if curr == len { return Response::init(r) } else { r } }, Ok(MiddlewareResponse::Future(fut)) => { return ComposeState::RunMiddlewares( RunMiddlewares { curr: curr, fut: Some(fut), _s: PhantomData }) }, }; } } fn poll(&mut self, info: &mut ComposeInfo) -> Option> { let len = info.mws.len(); 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(Response::init(err.into())), }; loop { if self.curr == len { return Some(Response::init(resp)); } else { match info.mws[self.curr].response(&mut info.req, resp) { Err(err) => return Some(Response::init(err.into())), Ok(MiddlewareResponse::Done(r)) => { self.curr += 1; resp = r }, Ok(MiddlewareResponse::Future(fut)) => { self.fut = Some(fut); break }, } } } } } } struct Response { resp: Option, _s: PhantomData, } impl Response { fn init(resp: HttpResponse) -> ComposeState { ComposeState::Response( Response{resp: Some(resp), _s: PhantomData}) } }