use std::cell::{RefCell, 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::{With, WithAsync}; /// 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 parameters. /// /// ```rust /// # extern crate bytes; /// # extern crate actix_web; /// # extern crate futures; /// #[macro_use] extern crate serde_derive; /// use actix_web::{http, App, Path, Result}; /// /// #[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 /// } /// ``` /// /// It is possible to use tuples for specifing multiple extractors for one /// handler function. /// /// ```rust /// # extern crate bytes; /// # extern crate actix_web; /// # extern crate futures; /// #[macro_use] extern crate serde_derive; /// # use std::collections::HashMap; /// use actix_web::{http, App, Json, Path, Query, Result}; /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// extract path info using serde /// fn index( /// info: (Path, Query>, Json), /// ) -> Result { /// Ok(format!("Welcome {}!", info.0.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) where F: Fn(T) -> R + 'static, R: Responder + 'static, T: FromRequest + 'static, { self.h(With::new(handler, ::default())); } /// Set handler function. Same as `.with()` but it allows to configure /// extractor. /// /// ```rust /// # extern crate bytes; /// # extern crate actix_web; /// # extern crate futures; /// #[macro_use] extern crate serde_derive; /// use actix_web::{http, App, Path, Result}; /// /// /// extract text data from request /// fn index(body: String) -> Result { /// Ok(format!("Body {}!", body)) /// } /// /// fn main() { /// let app = App::new().resource("/index.html", |r| { /// r.method(http::Method::GET) /// .with_config(index, |cfg| { // <- register handler /// cfg.limit(4096); // <- limit size of the payload /// }) /// }); /// } /// ``` pub fn with_config(&mut self, handler: F, cfg_f: C) where F: Fn(T) -> R + 'static, R: Responder + 'static, T: FromRequest + 'static, C: FnOnce(&mut T::Config), { let mut cfg = ::default(); cfg_f(&mut cfg); self.h(With::new(handler, cfg)); } /// Set async handler function, use request extractor for parameters. /// Also this method needs to be used if your handler function returns /// `impl Future<>` /// /// ```rust /// # extern crate bytes; /// # extern crate actix_web; /// # extern crate futures; /// #[macro_use] extern crate serde_derive; /// use actix_web::{http, App, Error, Path}; /// use futures::Future; /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// extract path info using serde /// fn index(info: Path) -> Box> { /// unimplemented!() /// } /// /// fn main() { /// let app = App::new().resource( /// "/{username}/index.html", // <- define path parameters /// |r| r.method(http::Method::GET).with_async(index), /// ); // <- use `with` extractor /// } /// ``` pub fn with_async(&mut self, handler: F) where F: Fn(T) -> R + 'static, R: Future + 'static, I: Responder + 'static, E: Into + 'static, T: FromRequest + 'static, { self.h(WithAsync::new(handler, ::default())); } /// Set async handler function, use request extractor for parameters. /// This method allows to configure extractor. /// /// ```rust /// # extern crate bytes; /// # extern crate actix_web; /// # extern crate futures; /// #[macro_use] extern crate serde_derive; /// use actix_web::{http, App, Error, Form}; /// use futures::Future; /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// extract path info using serde /// fn index(info: Form) -> Box> { /// unimplemented!() /// } /// /// fn main() { /// let app = App::new().resource( /// "/{username}/index.html", // <- define path parameters /// |r| r.method(http::Method::GET) /// .with_async_config(index, |cfg| { /// cfg.limit(4096); /// }), /// ); // <- use `with` extractor /// } /// ``` pub fn with_async_config(&mut self, handler: F, cfg: C) where F: Fn(T) -> R + 'static, R: Future + 'static, I: Responder + 'static, E: Into + 'static, T: FromRequest + 'static, C: FnOnce(&mut T::Config), { let mut extractor_cfg = ::default(); cfg(&mut extractor_cfg); self.h(WithAsync::new(handler, extractor_cfg)); } } /// `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 sync 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.borrow().len(); loop { if info.count == len { let reply = info.handler.handle(info.req.clone()); return WaitingResponse::init(info, reply); } else { let state = info.mws.borrow_mut()[info.count].start(&mut info.req); match state { Ok(MiddlewareStarted::Done) => info.count += 1, Ok(MiddlewareStarted::Response(resp)) => { return RunMiddlewares::init(info, resp) } Ok(MiddlewareStarted::Future(fut)) => { return ComposeState::Starting(StartMiddlewares { fut: Some(fut), _s: PhantomData, }) } Err(err) => return RunMiddlewares::init(info, err.into()), } } } } fn poll(&mut self, info: &mut ComposeInfo) -> Option> { let len = info.mws.borrow().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)); } loop { if info.count == len { let reply = info.handler.handle(info.req.clone()); return Some(WaitingResponse::init(info, reply)); } else { let state = info.mws.borrow_mut()[info.count].start(&mut info.req); match state { 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())) } } } } } Err(err) => return Some(RunMiddlewares::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.borrow().len(); loop { let state = info.mws.borrow_mut()[curr].response(&mut info.req, resp); resp = match state { 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.borrow().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 { let state = info.mws.borrow_mut()[self.curr].response(&mut info.req, resp); match state { 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; let state = info.mws.borrow_mut()[info.count as usize] .finish(&mut info.req, self.resp.as_ref().unwrap()); match state { 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, }) } }