use std::cell::RefCell; use std::rc::Rc; use actix_http::{http::Method, Error, Extensions, Response}; use actix_service::{NewService, Service}; use futures::{Async, Future, IntoFuture, Poll}; use crate::filter::{self, Filter}; use crate::handler::{ AsyncFactory, AsyncHandle, ConfigStorage, Extract, ExtractorConfig, Factory, FromRequest, Handle, }; use crate::responder::Responder; use crate::service::{ServiceFromRequest, ServiceRequest, ServiceResponse}; use crate::HttpResponse; type BoxedRouteService = Box< Service< Request = Req, Response = Res, Error = (), Future = Box>, >, >; type BoxedRouteNewService = Box< NewService< Request = Req, Response = Res, Error = (), InitError = (), Service = BoxedRouteService, Future = Box, Error = ()>>, >, >; /// 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

{ service: BoxedRouteNewService, ServiceResponse>, filters: Rc>>, config: ConfigStorage, config_ref: Rc>>>, } impl Route

{ /// Create new route which matches any request. pub fn new() -> Route

{ let config_ref = Rc::new(RefCell::new(None)); Route { service: Box::new(RouteNewService::new( Extract::new(config_ref.clone()).and_then( Handle::new(|| HttpResponse::NotFound()).map_err(|_| panic!()), ), )), filters: Rc::new(Vec::new()), config: ConfigStorage::default(), config_ref, } } /// Create new `GET` route. pub fn get() -> Route

{ Route::new().method(Method::GET) } /// Create new `POST` route. pub fn post() -> Route

{ Route::new().method(Method::POST) } /// Create new `PUT` route. pub fn put() -> Route

{ Route::new().method(Method::PUT) } /// Create new `DELETE` route. pub fn delete() -> Route

{ Route::new().method(Method::DELETE) } pub(crate) fn finish(self) -> Self { *self.config_ref.borrow_mut() = self.config.storage.clone(); self } } impl

NewService for Route

{ type Request = ServiceRequest

; type Response = ServiceResponse; type Error = (); type InitError = (); type Service = RouteService

; type Future = CreateRouteService

; fn new_service(&self, _: &()) -> Self::Future { CreateRouteService { fut: self.service.new_service(&()), filters: self.filters.clone(), } } } type RouteFuture

= Box< Future, ServiceResponse>, Error = ()>, >; pub struct CreateRouteService

{ fut: RouteFuture

, filters: Rc>>, } impl

Future for CreateRouteService

{ type Item = RouteService

; type Error = (); fn poll(&mut self) -> Poll { match self.fut.poll()? { Async::Ready(service) => Ok(Async::Ready(RouteService { service, filters: self.filters.clone(), })), Async::NotReady => Ok(Async::NotReady), } } } pub struct RouteService

{ service: BoxedRouteService, ServiceResponse>, filters: Rc>>, } impl

RouteService

{ pub fn check(&self, req: &mut ServiceRequest

) -> bool { for f in self.filters.iter() { if !f.check(req.head()) { return false; } } true } } impl

Service for RouteService

{ type Request = ServiceRequest

; type Response = ServiceResponse; type Error = (); type Future = Box>; fn poll_ready(&mut self) -> Poll<(), Self::Error> { self.service.poll_ready() } fn call(&mut self, req: Self::Request) -> Self::Future { self.service.call(req) } } impl Route

{ /// Add method match filter to the route. /// /// ```rust,ignore /// # 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 method(mut self, method: Method) -> Self { Rc::get_mut(&mut self.filters) .unwrap() .push(Box::new(filter::Method(method))); self } /// Add filter to the route. /// /// ```rust,ignore /// # 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(mut self, f: F) -> Self { Rc::get_mut(&mut self.filters).unwrap().push(Box::new(f)); self } // pub fn map>( // self, // md: F, // ) -> RouteServiceBuilder // where // T: NewService< // Request = HandlerRequest, // Response = HandlerRequest, // InitError = (), // >, // { // RouteServiceBuilder { // service: md.into_new_service(), // filters: self.filters, // _t: PhantomData, // } // } /// Set handler function, use request extractor for parameters. /// /// ```rust,ignore /// # 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 multiple extractors for one handler function. /// /// ```rust,ignore /// # 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( /// path: Path, query: Query>, body: Json, /// ) -> Result { /// Ok(format!("Welcome {}!", path.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 to(mut self, handler: F) -> Route

where F: Factory + 'static, T: FromRequest

+ 'static, R: Responder + 'static, { T::Config::store_default(&mut self.config); self.service = Box::new(RouteNewService::new( Extract::new(self.config_ref.clone()) .and_then(Handle::new(handler).map_err(|_| panic!())), )); self } /// Set async handler function, use request extractor for parameters. /// Also this method needs to be used if your handler function returns /// `impl Future<>` /// /// ```rust,ignore /// # 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 /// } /// ``` #[allow(clippy::wrong_self_convention)] pub fn to_async(mut self, handler: F) -> Self where F: AsyncFactory, T: FromRequest

+ 'static, R: IntoFuture + 'static, R::Item: Into, R::Error: Into, { self.service = Box::new(RouteNewService::new( Extract::new(self.config_ref.clone()) .and_then(AsyncHandle::new(handler).map_err(|_| panic!())), )); self } /// This method allows to add extractor configuration /// for specific route. /// /// ```rust /// use actix_web::{web, extractor, App}; /// /// /// extract text data from request /// fn index(body: String) -> String { /// format!("Body {}!", body) /// } /// /// fn main() { /// let app = App::new().resource("/index.html", |r| { /// r.route( /// web::get() /// // limit size of the payload /// .config(extractor::PayloadConfig::new(4096)) /// // register handler /// .to(index) /// ) /// }); /// } /// ``` pub fn config(mut self, config: C) -> Self { self.config.store(config); self } } // pub struct RouteServiceBuilder { // service: T, // filters: Vec>, // _t: PhantomData<(P, U1, U2)>, // } // impl RouteServiceBuilder // where // T: NewService< // Request = HandlerRequest, // Response = HandlerRequest, // Error = Error, // InitError = (), // >, // { // pub fn new>(factory: F) -> Self { // RouteServiceBuilder { // service: factory.into_new_service(), // filters: Vec::new(), // _t: PhantomData, // } // } // /// Add method match filter to the 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 method(mut self, method: Method) -> Self { // self.filters.push(Box::new(filter::Method(method))); // self // } // /// Add filter to the 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, f: F) -> &mut Self { // self.filters.push(Box::new(f)); // self // } // pub fn map>( // self, // md: F, // ) -> RouteServiceBuilder< // impl NewService< // Request = HandlerRequest, // Response = HandlerRequest, // Error = Error, // InitError = (), // >, // S, // U1, // U2, // > // where // T1: NewService< // Request = HandlerRequest, // Response = HandlerRequest, // InitError = (), // >, // T1::Error: Into, // { // RouteServiceBuilder { // service: self // .service // .and_then(md.into_new_service().map_err(|e| e.into())), // filters: self.filters, // _t: PhantomData, // } // } // pub fn to_async(self, handler: F) -> Route // where // F: AsyncFactory, // P: FromRequest + 'static, // R: IntoFuture, // R::Item: Into, // R::Error: Into, // { // Route { // service: self // .service // .and_then(Extract::new(P::Config::default())) // .then(AsyncHandle::new(handler)), // filters: Rc::new(self.filters), // } // } // pub fn to(self, handler: F) -> Route // where // F: Factory + 'static, // P: FromRequest + 'static, // R: Responder + 'static, // { // Route { // service: Box::new(RouteNewService::new( // self.service // .and_then(Extract::new(P::Config::default())) // .and_then(Handle::new(handler)), // )), // filters: Rc::new(self.filters), // } // } // } struct RouteNewService where T: NewService, Error = (Error, ServiceFromRequest

)>, { service: T, } impl RouteNewService where T: NewService< Request = ServiceRequest

, Response = ServiceResponse, Error = (Error, ServiceFromRequest

), >, T::Future: 'static, T::Service: 'static, ::Future: 'static, { pub fn new(service: T) -> Self { RouteNewService { service } } } impl NewService for RouteNewService where T: NewService< Request = ServiceRequest

, Response = ServiceResponse, Error = (Error, ServiceFromRequest

), >, T::Future: 'static, T::Service: 'static, ::Future: 'static, { type Request = ServiceRequest

; type Response = ServiceResponse; type Error = (); type InitError = (); type Service = BoxedRouteService; type Future = Box>; fn new_service(&self, _: &()) -> Self::Future { Box::new( self.service .new_service(&()) .map_err(|_| ()) .and_then(|service| { let service: BoxedRouteService<_, _> = Box::new(RouteServiceWrapper { service }); Ok(service) }), ) } } struct RouteServiceWrapper>> { service: T, } impl Service for RouteServiceWrapper where T::Future: 'static, T: Service< Request = ServiceRequest

, Response = ServiceResponse, Error = (Error, ServiceFromRequest

), >, { type Request = ServiceRequest

; type Response = ServiceResponse; type Error = (); type Future = Box>; fn poll_ready(&mut self) -> Poll<(), Self::Error> { self.service.poll_ready().map_err(|_| ()) } fn call(&mut self, req: ServiceRequest

) -> Self::Future { Box::new(self.service.call(req).then(|res| match res { Ok(res) => Ok(res), Err((err, req)) => Ok(req.error_response(err)), })) } }