use std::marker::PhantomData; use std::mem; use std::ops::Deref; use futures::future::{err, ok, Future}; use futures::{Async, Poll}; use error::Error; use httprequest::HttpRequest; use httpresponse::HttpResponse; /// Trait defines object that could be registered as route handler #[allow(unused_variables)] pub trait Handler: 'static { /// The type of value that handler will return. type Result: Responder; /// Handle request fn handle(&mut self, req: HttpRequest) -> Self::Result; } /// Trait implemented by types that generate responses for clients. /// /// Types that implement this trait can be used as the return type of a handler. pub trait Responder { /// The associated item which can be returned. type Item: Into>; /// The associated error which can be returned. type Error: Into; /// Convert itself to `Reply` or `Error`. fn respond_to(self, req: HttpRequest) -> Result; } /// Trait implemented by types that can be extracted from request. /// /// Types that implement this trait can be used with `Route::with()` method. pub trait FromRequest: Sized where S: 'static, { /// Configuration for conversion process type Config: Default; /// Future that resolves to a Self type Result: Into>; /// Convert request to a Self fn from_request(req: &mut HttpRequest, cfg: &Self::Config) -> Self::Result; } /// Combines two different responder types into a single type /// /// ```rust /// # extern crate actix_web; /// # extern crate futures; /// # use futures::future::Future; /// use futures::future::result; /// use actix_web::{Either, Error, HttpRequest, HttpResponse, AsyncResponder}; /// /// type RegisterResult = Either>>; /// /// /// fn index(req: HttpRequest) -> RegisterResult { /// if is_a_variant() { // <- choose variant A /// Either::A( /// HttpResponse::BadRequest().body("Bad data")) /// } else { /// Either::B( // <- variant B /// result(Ok(HttpResponse::Ok() /// .content_type("text/html") /// .body("Hello!"))) /// .responder()) /// } /// } /// # fn is_a_variant() -> bool { true } /// # fn main() {} /// ``` #[derive(Debug)] pub enum Either { /// First branch of the type A(A), /// Second branch of the type B(B), } impl Responder for Either where A: Responder, B: Responder, { type Item = Reply; type Error = Error; fn respond_to(self, req: HttpRequest) -> Result, Error> { match self { Either::A(a) => match a.respond_to(req) { Ok(val) => Ok(val.into()), Err(err) => Err(err.into()), }, Either::B(b) => match b.respond_to(req) { Ok(val) => Ok(val.into()), Err(err) => Err(err.into()), }, } } } impl Future for Either where A: Future, B: Future, { type Item = I; type Error = E; fn poll(&mut self) -> Poll { match *self { Either::A(ref mut fut) => fut.poll(), Either::B(ref mut fut) => fut.poll(), } } } /// Convenience trait that converts `Future` object to a `Boxed` future /// /// For example loading json from request's body is async operation. /// /// ```rust /// # extern crate actix_web; /// # extern crate futures; /// # #[macro_use] extern crate serde_derive; /// use futures::future::Future; /// use actix_web::{ /// App, HttpRequest, HttpResponse, HttpMessage, Error, AsyncResponder}; /// /// #[derive(Deserialize, Debug)] /// struct MyObj { /// name: String, /// } /// /// fn index(mut req: HttpRequest) -> Box> { /// req.json() // <- get JsonBody future /// .from_err() /// .and_then(|val: MyObj| { // <- deserialized value /// Ok(HttpResponse::Ok().into()) /// }) /// // Construct boxed future by using `AsyncResponder::responder()` method /// .responder() /// } /// # fn main() {} /// ``` pub trait AsyncResponder: Sized { fn responder(self) -> Box>; } impl AsyncResponder for F where F: Future + 'static, I: Responder + 'static, E: Into + 'static, { fn responder(self) -> Box> { Box::new(self) } } /// Handler for Fn() impl Handler for F where F: Fn(HttpRequest) -> R + 'static, R: Responder + 'static, { type Result = R; fn handle(&mut self, req: HttpRequest) -> R { (self)(req) } } /// Represents reply process. /// /// Reply could be in tree different forms. /// * Message(T) - ready item /// * Error(Error) - error happen during reply process /// * Future - reply process completes in the future pub struct Reply(ReplyItem); impl Future for Reply { type Item = T; type Error = Error; fn poll(&mut self) -> Poll { let item = mem::replace(&mut self.0, ReplyItem::None); match item { ReplyItem::Error(err) => Err(err), ReplyItem::Message(msg) => Ok(Async::Ready(msg)), ReplyItem::Future(mut fut) => match fut.poll() { Ok(Async::NotReady) => { self.0 = ReplyItem::Future(fut); Ok(Async::NotReady) } Ok(Async::Ready(msg)) => Ok(Async::Ready(msg)), Err(err) => Err(err), }, ReplyItem::None => panic!("use after resolve"), } } } pub(crate) enum ReplyItem { None, Error(Error), Message(T), Future(Box>), } impl Reply { /// Create async response #[inline] pub fn async(fut: F) -> Reply where F: Future + 'static, { Reply(ReplyItem::Future(Box::new(fut))) } /// Send response #[inline] pub fn response>(response: R) -> Reply { Reply(ReplyItem::Message(response.into())) } /// Send error #[inline] pub fn error>(err: R) -> Reply { Reply(ReplyItem::Error(err.into())) } #[inline] pub(crate) fn into(self) -> ReplyItem { self.0 } #[cfg(test)] pub(crate) fn as_msg(&self) -> &T { match self.0 { ReplyItem::Message(ref resp) => resp, _ => panic!(), } } #[cfg(test)] pub(crate) fn as_err(&self) -> Option<&Error> { match self.0 { ReplyItem::Error(ref err) => Some(err), _ => None, } } } impl Responder for Reply { type Item = Reply; type Error = Error; fn respond_to(self, _: HttpRequest) -> Result, Error> { Ok(self) } } impl Responder for HttpResponse { type Item = Reply; type Error = Error; #[inline] fn respond_to(self, _: HttpRequest) -> Result, Error> { Ok(Reply(ReplyItem::Message(self))) } } impl From for Reply { #[inline] fn from(resp: T) -> Reply { Reply(ReplyItem::Message(resp)) } } impl> Responder for Result { type Item = ::Item; type Error = Error; fn respond_to(self, req: HttpRequest) -> Result { match self { Ok(val) => match val.respond_to(req) { Ok(val) => Ok(val), Err(err) => Err(err.into()), }, Err(err) => Err(err.into()), } } } impl> From, E>> for Reply { #[inline] fn from(res: Result, E>) -> Self { match res { Ok(val) => val, Err(err) => Reply(ReplyItem::Error(err.into())), } } } impl> From> for Reply { #[inline] fn from(res: Result) -> Self { match res { Ok(val) => Reply(ReplyItem::Message(val)), Err(err) => Reply(ReplyItem::Error(err.into())), } } } impl> From>, E>> for Reply { #[inline] fn from(res: Result>, E>) -> Self { match res { Ok(fut) => Reply(ReplyItem::Future(fut)), Err(err) => Reply(ReplyItem::Error(err.into())), } } } impl From>> for Reply { #[inline] fn from(fut: Box>) -> Reply { Reply(ReplyItem::Future(fut)) } } /// Convenience type alias pub type FutureResponse = Box>; impl Responder for Box> where I: Responder + 'static, E: Into + 'static, { type Item = Reply; type Error = Error; #[inline] fn respond_to(self, req: HttpRequest) -> Result, Error> { let fut = self.map_err(|e| e.into()) .then(move |r| match r.respond_to(req) { Ok(reply) => match reply.into().0 { ReplyItem::Message(resp) => ok(resp), _ => panic!("Nested async replies are not supported"), }, Err(e) => err(e), }); Ok(Reply::async(fut)) } } /// Trait defines object that could be registered as resource route pub(crate) trait RouteHandler: 'static { fn handle(&mut self, req: HttpRequest) -> Reply; } /// Route handler wrapper for Handler pub(crate) struct WrapHandler where H: Handler, R: Responder, S: 'static, { h: H, s: PhantomData, } impl WrapHandler where H: Handler, R: Responder, S: 'static, { pub fn new(h: H) -> Self { WrapHandler { h, s: PhantomData, } } } impl RouteHandler for WrapHandler where H: Handler, R: Responder + 'static, S: 'static, { fn handle(&mut self, req: HttpRequest) -> Reply { let req2 = req.drop_state(); match self.h.handle(req).respond_to(req2) { Ok(reply) => reply.into(), Err(err) => Reply::response(err.into()), } } } /// Async route handler pub(crate) struct AsyncHandler where H: Fn(HttpRequest) -> F + 'static, F: Future + 'static, R: Responder + 'static, E: Into + 'static, S: 'static, { h: Box, s: PhantomData, } impl AsyncHandler where H: Fn(HttpRequest) -> F + 'static, F: Future + 'static, R: Responder + 'static, E: Into + 'static, S: 'static, { pub fn new(h: H) -> Self { AsyncHandler { h: Box::new(h), s: PhantomData, } } } impl RouteHandler for AsyncHandler where H: Fn(HttpRequest) -> F + 'static, F: Future + 'static, R: Responder + 'static, E: Into + 'static, S: 'static, { fn handle(&mut self, req: HttpRequest) -> Reply { let req2 = req.drop_state(); let fut = (self.h)(req).map_err(|e| e.into()).then(move |r| { match r.respond_to(req2) { Ok(reply) => match reply.into().0 { ReplyItem::Message(resp) => ok(resp), _ => panic!("Nested async replies are not supported"), }, Err(e) => err(e), } }); Reply::async(fut) } } /// Access an application state /// /// `S` - application state type /// /// ## Example /// /// ```rust /// # extern crate bytes; /// # extern crate actix_web; /// # extern crate futures; /// #[macro_use] extern crate serde_derive; /// use actix_web::{App, Path, State, http}; /// /// /// Application state /// struct MyApp {msg: &'static str} /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// extract path info using serde /// fn index(state: State, info: Path) -> String { /// format!("{} {}!", state.msg, info.username) /// } /// /// fn main() { /// let app = App::with_state(MyApp{msg: "Welcome"}).resource( /// "/{username}/index.html", // <- define path parameters /// |r| r.method(http::Method::GET).with2(index)); // <- use `with` extractor /// } /// ``` pub struct State(HttpRequest); impl Deref for State { type Target = S; fn deref(&self) -> &S { self.0.state() } } impl FromRequest for State { type Config = (); type Result = State; #[inline] fn from_request(req: &mut HttpRequest, _: &Self::Config) -> Self::Result { State(req.clone()).into() } }