use std::cell::RefCell; use std::marker::PhantomData; 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::extract::{ConfigStorage, ExtractorConfig, FromRequest}; use crate::guard::{self, Guard}; use crate::handler::{AsyncFactory, AsyncHandle, Extract, Factory, Handle}; use crate::responder::Responder; use crate::service::{ServiceFromRequest, ServiceRequest, ServiceResponse}; use crate::HttpResponse; type BoxedRouteService = Box< Service< Req, Response = Res, Error = (), Future = Box>, >, >; type BoxedRouteNewService = Box< NewService< 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>, guards: 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!()), ), )), guards: 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 } pub(crate) fn take_guards(&mut self) -> Vec> { std::mem::replace(Rc::get_mut(&mut self.guards).unwrap(), Vec::new()) } } impl

NewService> for Route

{ 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(&()), guards: self.guards.clone(), } } } type RouteFuture

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

{ fut: RouteFuture

, guards: 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, guards: self.guards.clone(), })), Async::NotReady => Ok(Async::NotReady), } } } pub struct RouteService

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

RouteService

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

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

Service> for RouteService

{ 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: ServiceRequest

) -> Self::Future { self.service.call(req) } } impl Route

{ /// Add method guard to the route. /// /// ```rust /// # use actix_web::*; /// # fn main() { /// App::new().service(web::resource("/path").route( /// web::get() /// .method(http::Method::CONNECT) /// .guard(guard::Header("content-type", "text/plain")) /// .to(|req: HttpRequest| HttpResponse::Ok())) /// ); /// # } /// ``` pub fn method(mut self, method: Method) -> Self { Rc::get_mut(&mut self.guards) .unwrap() .push(Box::new(guard::Method(method))); self } /// Add guard to the route. /// /// ```rust /// # use actix_web::*; /// # fn main() { /// App::new().service(web::resource("/path").route( /// web::route() /// .guard(guard::Get()) /// .guard(guard::Header("content-type", "text/plain")) /// .to(|req: HttpRequest| HttpResponse::Ok())) /// ); /// # } /// ``` pub fn guard(mut self, f: F) -> Self { Rc::get_mut(&mut self.guards).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(), // guards: self.guards, // _t: PhantomData, // } // } /// Set handler function, use request extractors for parameters. /// /// ```rust /// #[macro_use] extern crate serde_derive; /// use actix_web::{web, http, App, extract::Path}; /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// extract path info using serde /// fn index(info: Path) -> String { /// format!("Welcome {}!", info.username) /// } /// /// fn main() { /// let app = App::new().service( /// web::resource("/{username}/index.html") // <- define path parameters /// .route(web::get().to(index)) // <- register handler /// ); /// } /// ``` /// /// It is possible to use multiple extractors for one handler function. /// /// ```rust /// # use std::collections::HashMap; /// # use serde_derive::Deserialize; /// use actix_web::{web, App, Json, extract::Path, extract::Query}; /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// extract path info using serde /// fn index(path: Path, query: Query>, body: Json) -> String { /// format!("Welcome {}!", path.username) /// } /// /// fn main() { /// let app = App::new().service( /// web::resource("/{username}/index.html") // <- define path parameters /// .route(web::get().to(index)) /// ); /// } /// ``` 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 extractors for parameters. /// This method has to be used if your handler function returns `impl Future<>` /// /// ```rust /// # use futures::future::ok; /// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App, Error, extract::Path}; /// use futures::Future; /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// extract path info using serde /// fn index(info: Path) -> impl Future { /// ok("Hello World!") /// } /// /// fn main() { /// let app = App::new().service( /// web::resource("/{username}/index.html") // <- define path parameters /// .route(web::get().to_async(index)) // <- register async handler /// ); /// } /// ``` #[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, extract, App}; /// /// /// extract text data from request /// fn index(body: String) -> String { /// format!("Body {}!", body) /// } /// /// fn main() { /// let app = App::new().service( /// web::resource("/index.html").route( /// web::get() /// // limit size of the payload /// .config(extract::PayloadConfig::new(4096)) /// // register handler /// .to(index) /// )); /// } /// ``` pub fn config(mut self, config: C) -> Self { self.config.store(config); self } } struct RouteNewService where T: NewService, Error = (Error, ServiceFromRequest

)>, { service: T, _t: PhantomData

, } impl RouteNewService where T: NewService< ServiceRequest

, Response = ServiceResponse, Error = (Error, ServiceFromRequest

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

, Response = ServiceResponse, Error = (Error, ServiceFromRequest

), >, T::Future: 'static, T::Service: 'static, >>::Future: 'static, { type Response = ServiceResponse; type Error = (); type InitError = (); type Service = BoxedRouteService, Self::Response>; 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, _t: PhantomData, }); Ok(service) }), ) } } struct RouteServiceWrapper>> { service: T, _t: PhantomData

, } impl Service> for RouteServiceWrapper where T::Future: 'static, T: Service< ServiceRequest

, Response = ServiceResponse, Error = (Error, ServiceFromRequest

), >, { 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)), })) } }