use std::cell::UnsafeCell; use std::marker::PhantomData; use std::rc::Rc; use futures::{Async, Future, Poll}; use error::Error; use handler::{AsyncHandler, AsyncResult, AsyncResultItem, FromRequest, Handler, Responder, RouteHandler, WrapHandler}; use http::StatusCode; use httprequest::HttpRequest; use httpresponse::HttpResponse; use middleware::{Finished as MiddlewareFinished, Middleware, Response as MiddlewareResponse, Started as MiddlewareStarted}; use pred::Predicate; use with::{ExtractorConfig, With, With2, With3}; /// 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(|_| HttpResponse::new(StatusCode::NOT_FOUND)), } } } 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) -> AsyncResult { self.handler.handle(req) } #[inline] pub(crate) fn compose( &mut self, req: HttpRequest, mws: Rc>>>, ) -> AsyncResult { AsyncResult::async(Box::new(Compose::new(req, mws, self.handler.clone()))) } /// Add match predicate to route. /// /// ```rust /// # extern crate actix_web; /// # use actix_web::*; /// # fn main() { /// App::new() /// .resource("/path", |r| /// r.route() /// .filter(pred::Get()) /// .filter(pred::Header("content-type", "text/plain")) /// .f(|req| HttpResponse::Ok()) /// ) /// # .finish(); /// # } /// ``` pub fn filter + '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, so 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, so 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); } /// Set handler function, use request extractor for paramters. /// /// ```rust /// # extern crate bytes; /// # extern crate actix_web; /// # extern crate futures; /// #[macro_use] extern crate serde_derive; /// use actix_web::{App, Path, Result, http}; /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// extract path info using serde /// fn index(info: Path) -> Result { /// Ok(format!("Welcome {}!", info.username)) /// } /// /// fn main() { /// let app = App::new().resource( /// "/{username}/index.html", // <- define path parameters /// |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor /// } /// ``` pub fn with(&mut self, handler: F) -> ExtractorConfig where F: Fn(T) -> R + 'static, R: Responder + 'static, T: FromRequest + 'static, { let cfg = ExtractorConfig::default(); self.h(With::new(handler, Clone::clone(&cfg))); cfg } /// Set handler function, use request extractor for both paramters. /// /// ```rust /// # extern crate bytes; /// # extern crate actix_web; /// # extern crate futures; /// #[macro_use] extern crate serde_derive; /// use actix_web::{App, Query, Path, Result, http}; /// /// #[derive(Deserialize)] /// struct PParam { /// username: String, /// } /// /// #[derive(Deserialize)] /// struct QParam { /// count: u32, /// } /// /// /// extract path and query information using serde /// fn index(p: Path, q: Query) -> Result { /// Ok(format!("Welcome {}!", p.username)) /// } /// /// fn main() { /// let app = App::new().resource( /// "/{username}/index.html", // <- define path parameters /// |r| r.method(http::Method::GET).with2(index)); // <- use `with` extractor /// } /// ``` pub fn with2( &mut self, handler: F, ) -> (ExtractorConfig, ExtractorConfig) where F: Fn(T1, T2) -> R + 'static, R: Responder + 'static, T1: FromRequest + 'static, T2: FromRequest + 'static, { let cfg1 = ExtractorConfig::default(); let cfg2 = ExtractorConfig::default(); self.h(With2::new( handler, Clone::clone(&cfg1), Clone::clone(&cfg2), )); (cfg1, cfg2) } /// Set handler function, use request extractor for all paramters. pub fn with3( &mut self, handler: F, ) -> ( ExtractorConfig, ExtractorConfig, ExtractorConfig, ) where F: Fn(T1, T2, T3) -> R + 'static, R: Responder + 'static, T1: FromRequest + 'static, T2: FromRequest + 'static, T3: FromRequest + 'static, { let cfg1 = ExtractorConfig::default(); let cfg2 = ExtractorConfig::default(); let cfg3 = ExtractorConfig::default(); self.h(With3::new( handler, Clone::clone(&cfg1), Clone::clone(&cfg2), Clone::clone(&cfg3), )); (cfg1, cfg2, cfg3) } } /// `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(UnsafeCell::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(UnsafeCell::new(Box::new(AsyncHandler::new( h, ))))) } #[inline] pub fn handle(&self, req: HttpRequest) -> AsyncResult { // reason: handler is unique per thread, handler get called from async code only let h = unsafe { &mut *self.0.as_ref().get() }; 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), Finishing(FinishingMiddlewares), Completed(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::Finishing(ref mut state) => state.poll(info), ComposeState::Completed(_) => None, } } } impl Compose { fn new( req: HttpRequest, mws: Rc>>>, handler: InnerHandler, ) -> Self { let mut info = ComposeInfo { count: 0, req, mws, handler, }; let state = StartMiddlewares::init(&mut info); Compose { state, info } } } impl Future for Compose { type Item = HttpResponse; type Error = Error; fn poll(&mut self) -> Poll { 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 { 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 FinishingMiddlewares::init(info, err.into()), }, Err(err) => return FinishingMiddlewares::init(info, 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(FinishingMiddlewares::init( info, err.into(), )) } } } } } Err(err) => return Some(FinishingMiddlewares::init(info, err.into())), } } } } // waiting for response struct WaitingResponse { fut: Box>, _s: PhantomData, } impl WaitingResponse { #[inline] fn init( info: &mut ComposeInfo, reply: AsyncResult, ) -> ComposeState { match reply.into() { AsyncResultItem::Err(err) => RunMiddlewares::init(info, err.into()), AsyncResultItem::Ok(resp) => RunMiddlewares::init(info, resp), AsyncResultItem::Future(fut) => ComposeState::Handler(WaitingResponse { 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(RunMiddlewares::init(info, 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 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) -> 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(FinishingMiddlewares::init(info, err.into())), }; loop { if self.curr == len { return Some(FinishingMiddlewares::init(info, resp)); } else { match info.mws[self.curr].response(&mut info.req, resp) { 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 { resp: Option, fut: Option>>, _s: PhantomData, } impl FinishingMiddlewares { fn init(info: &mut ComposeInfo, resp: HttpResponse) -> ComposeState { 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) -> Option> { 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())); } info.count -= 1; match info.mws[info.count as usize] .finish(&mut info.req, self.resp.as_ref().unwrap()) { MiddlewareFinished::Done => { if info.count == 0 { return Some(Response::init(self.resp.take().unwrap())); } } MiddlewareFinished::Future(fut) => { self.fut = Some(fut); } } } } } struct Response { resp: Option, _s: PhantomData, } impl Response { fn init(resp: HttpResponse) -> ComposeState { ComposeState::Completed(Response { resp: Some(resp), _s: PhantomData, }) } }