1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-25 08:22:43 +01:00
actix-extras/src/route.rs

578 lines
16 KiB
Rust
Raw Normal View History

2019-03-03 09:57:48 +01:00
use std::cell::RefCell;
2018-01-10 05:00:18 +01:00
use std::rc::Rc;
2017-12-05 01:09:22 +01:00
2019-03-03 09:57:48 +01:00
use actix_http::{http::Method, Error, Extensions, Response};
2019-03-02 07:51:32 +01:00
use actix_service::{NewService, Service};
use futures::{Async, Future, IntoFuture, Poll};
use crate::filter::{self, Filter};
2019-03-03 09:57:48 +01:00
use crate::handler::{
AsyncFactory, AsyncHandle, ConfigStorage, Extract, ExtractorConfig, Factory,
FromRequest, Handle,
};
2019-03-02 07:51:32 +01:00
use crate::responder::Responder;
use crate::service::{ServiceFromRequest, ServiceRequest, ServiceResponse};
use crate::HttpResponse;
2019-03-02 07:51:32 +01:00
type BoxedRouteService<Req, Res> = Box<
Service<
Request = Req,
Response = Res,
Error = (),
Future = Box<Future<Item = Res, Error = ()>>,
>,
>;
type BoxedRouteNewService<Req, Res> = Box<
NewService<
Request = Req,
Response = Res,
Error = (),
InitError = (),
Service = BoxedRouteService<Req, Res>,
Future = Box<Future<Item = BoxedRouteService<Req, Res>, Error = ()>>,
>,
>;
2017-12-05 01:09:22 +01:00
/// Resource route definition
///
/// Route uses builder-like pattern for configuration.
/// If handler is not explicitly set, default *404 Not Found* handler is used.
2019-03-02 07:51:32 +01:00
pub struct Route<P> {
service: BoxedRouteNewService<ServiceRequest<P>, ServiceResponse>,
filters: Rc<Vec<Box<Filter>>>,
2019-03-03 09:57:48 +01:00
config: ConfigStorage,
config_ref: Rc<RefCell<Option<Rc<Extensions>>>>,
2017-12-05 01:09:22 +01:00
}
2019-03-02 07:51:32 +01:00
impl<P: 'static> Route<P> {
2019-03-03 09:57:48 +01:00
/// Create new route which matches any request.
pub fn new() -> Route<P> {
2019-03-03 09:57:48 +01:00
let config_ref = Rc::new(RefCell::new(None));
Route {
2019-03-03 09:57:48 +01:00
service: Box::new(RouteNewService::new(
Extract::new(config_ref.clone()).and_then(
Handle::new(|| HttpResponse::NotFound()).map_err(|_| panic!()),
),
)),
filters: Rc::new(Vec::new()),
2019-03-03 09:57:48 +01:00
config: ConfigStorage::default(),
config_ref,
}
2019-03-02 07:51:32 +01:00
}
2019-03-03 09:57:48 +01:00
/// Create new `GET` route.
pub fn get() -> Route<P> {
Route::new().method(Method::GET)
2019-03-02 07:51:32 +01:00
}
2019-03-03 09:57:48 +01:00
/// Create new `POST` route.
pub fn post() -> Route<P> {
Route::new().method(Method::POST)
2019-03-02 07:51:32 +01:00
}
2019-03-03 09:57:48 +01:00
/// Create new `PUT` route.
pub fn put() -> Route<P> {
Route::new().method(Method::PUT)
2019-03-02 07:51:32 +01:00
}
2019-03-03 09:57:48 +01:00
/// Create new `DELETE` route.
pub fn delete() -> Route<P> {
Route::new().method(Method::DELETE)
2019-03-02 07:51:32 +01:00
}
2019-03-03 09:57:48 +01:00
pub(crate) fn finish(self) -> Self {
*self.config_ref.borrow_mut() = self.config.storage.clone();
self
}
2019-03-02 07:51:32 +01:00
}
impl<P> NewService for Route<P> {
type Request = ServiceRequest<P>;
type Response = ServiceResponse;
type Error = ();
type InitError = ();
type Service = RouteService<P>;
type Future = CreateRouteService<P>;
fn new_service(&self, _: &()) -> Self::Future {
CreateRouteService {
fut: self.service.new_service(&()),
filters: self.filters.clone(),
}
}
}
type RouteFuture<P> = Box<
Future<Item = BoxedRouteService<ServiceRequest<P>, ServiceResponse>, Error = ()>,
>;
pub struct CreateRouteService<P> {
fut: RouteFuture<P>,
filters: Rc<Vec<Box<Filter>>>,
}
impl<P> Future for CreateRouteService<P> {
type Item = RouteService<P>;
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.fut.poll()? {
Async::Ready(service) => Ok(Async::Ready(RouteService {
service,
filters: self.filters.clone(),
})),
Async::NotReady => Ok(Async::NotReady),
2017-12-05 01:09:22 +01:00
}
}
}
2019-03-02 07:51:32 +01:00
pub struct RouteService<P> {
service: BoxedRouteService<ServiceRequest<P>, ServiceResponse>,
filters: Rc<Vec<Box<Filter>>>,
}
impl<P> RouteService<P> {
pub fn check(&self, req: &mut ServiceRequest<P>) -> bool {
for f in self.filters.iter() {
if !f.check(req.head()) {
2018-04-14 01:02:01 +02:00
return false;
2017-12-05 01:09:22 +01:00
}
}
true
}
2019-03-02 07:51:32 +01:00
}
2017-12-05 01:09:22 +01:00
2019-03-02 07:51:32 +01:00
impl<P> Service for RouteService<P> {
type Request = ServiceRequest<P>;
type Response = ServiceResponse;
type Error = ();
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready()
2017-12-05 01:09:22 +01:00
}
2019-03-02 07:51:32 +01:00
fn call(&mut self, req: Self::Request) -> Self::Future {
self.service.call(req)
2018-01-10 05:00:18 +01:00
}
2019-03-02 07:51:32 +01:00
}
2018-01-10 05:00:18 +01:00
impl<P: 'static> Route<P> {
2019-03-02 07:51:32 +01:00
/// Add method match filter to the route.
2017-12-20 07:36:06 +01:00
///
2019-03-02 07:51:32 +01:00
/// ```rust,ignore
2017-12-20 07:36:06 +01:00
/// # extern crate actix_web;
/// # use actix_web::*;
/// # fn main() {
2018-06-01 18:37:14 +02:00
/// App::new().resource("/path", |r| {
/// r.route()
/// .filter(pred::Get())
/// .filter(pred::Header("content-type", "text/plain"))
/// .f(|req| HttpResponse::Ok())
/// })
2017-12-20 07:36:06 +01:00
/// # .finish();
/// # }
/// ```
2019-03-02 07:51:32 +01:00
pub fn method(mut self, method: Method) -> Self {
Rc::get_mut(&mut self.filters)
.unwrap()
.push(Box::new(filter::Method(method)));
2017-12-05 01:09:22 +01:00
self
}
2019-03-02 07:51:32 +01:00
/// 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();
/// # }
/// ```
2019-03-03 01:24:14 +01:00
pub fn filter<F: Filter + 'static>(mut self, f: F) -> Self {
Rc::get_mut(&mut self.filters).unwrap().push(Box::new(f));
2019-03-02 07:51:32 +01:00
self
2017-12-05 01:09:22 +01:00
}
2019-03-02 07:51:32 +01:00
// pub fn map<T, U, F: IntoNewService<T>>(
// self,
// md: F,
// ) -> RouteServiceBuilder<T, S, (), U>
// where
// T: NewService<
// Request = HandlerRequest<S>,
// Response = HandlerRequest<S, U>,
// InitError = (),
// >,
// {
// RouteServiceBuilder {
// service: md.into_new_service(),
// filters: self.filters,
// _t: PhantomData,
// }
// }
2018-03-27 08:10:31 +02:00
2018-05-06 18:07:30 +02:00
/// Set handler function, use request extractor for parameters.
2018-03-27 08:10:31 +02:00
///
2019-03-02 07:51:32 +01:00
/// ```rust,ignore
2018-03-27 08:10:31 +02:00
/// # extern crate bytes;
/// # extern crate actix_web;
/// # extern crate futures;
/// #[macro_use] extern crate serde_derive;
2018-06-01 18:37:14 +02:00
/// use actix_web::{http, App, Path, Result};
2018-03-27 08:10:31 +02:00
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// extract path info using serde
2018-03-28 05:33:24 +02:00
/// fn index(info: Path<Info>) -> Result<String> {
2018-03-27 08:10:31 +02:00
/// Ok(format!("Welcome {}!", info.username))
/// }
///
/// fn main() {
2018-03-31 09:16:55 +02:00
/// let app = App::new().resource(
2018-06-01 18:37:14 +02:00
/// "/{username}/index.html", // <- define path parameters
/// |r| r.method(http::Method::GET).with(index),
/// ); // <- use `with` extractor
2018-03-27 08:10:31 +02:00
/// }
/// ```
///
2018-10-02 06:16:56 +02:00
/// It is possible to use multiple extractors for one handler function.
///
2019-03-02 07:51:32 +01:00
/// ```rust,ignore
/// # extern crate bytes;
/// # extern crate actix_web;
/// # extern crate futures;
/// #[macro_use] extern crate serde_derive;
/// # use std::collections::HashMap;
2018-06-01 18:37:14 +02:00
/// use actix_web::{http, App, Json, Path, Query, Result};
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// extract path info using serde
2018-06-01 18:37:14 +02:00
/// fn index(
2018-10-02 06:16:56 +02:00
/// path: Path<Info>, query: Query<HashMap<String, String>>, body: Json<Info>,
2018-06-01 18:37:14 +02:00
/// ) -> Result<String> {
2018-10-02 06:16:56 +02:00
/// Ok(format!("Welcome {}!", path.username))
/// }
///
/// fn main() {
/// let app = App::new().resource(
2018-06-01 18:37:14 +02:00
/// "/{username}/index.html", // <- define path parameters
/// |r| r.method(http::Method::GET).with(index),
/// ); // <- use `with` extractor
/// }
/// ```
pub fn to<F, T, R>(mut self, handler: F) -> Route<P>
2018-06-21 07:47:01 +02:00
where
2019-03-02 07:51:32 +01:00
F: Factory<T, R> + 'static,
T: FromRequest<P> + 'static,
2018-06-21 07:47:01 +02:00
R: Responder + 'static,
{
2019-03-03 09:57:48 +01:00
T::Config::store_default(&mut self.config);
self.service = Box::new(RouteNewService::new(
2019-03-03 09:57:48 +01:00
Extract::new(self.config_ref.clone())
.and_then(Handle::new(handler).map_err(|_| panic!())),
));
self
2018-03-27 08:10:31 +02:00
}
2018-03-28 23:24:32 +02:00
/// Set async handler function, use request extractor for parameters.
2018-05-10 22:04:56 +02:00
/// Also this method needs to be used if your handler function returns
/// `impl Future<>`
///
2019-03-02 07:51:32 +01:00
/// ```rust,ignore
/// # extern crate bytes;
/// # extern crate actix_web;
/// # extern crate futures;
/// #[macro_use] extern crate serde_derive;
2018-06-01 18:37:14 +02:00
/// use actix_web::{http, App, Error, Path};
/// use futures::Future;
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// extract path info using serde
2018-06-01 18:37:14 +02:00
/// fn index(info: Path<Info>) -> Box<Future<Item = &'static str, Error = Error>> {
/// unimplemented!()
/// }
///
/// fn main() {
/// let app = App::new().resource(
2018-06-01 18:37:14 +02:00
/// "/{username}/index.html", // <- define path parameters
/// |r| r.method(http::Method::GET).with_async(index),
/// ); // <- use `with` extractor
/// }
/// ```
2019-03-02 07:51:32 +01:00
#[allow(clippy::wrong_self_convention)]
pub fn to_async<F, T, R>(mut self, handler: F) -> Self
2018-04-14 01:02:01 +02:00
where
2019-03-02 07:51:32 +01:00
F: AsyncFactory<T, R>,
T: FromRequest<P> + 'static,
R: IntoFuture + 'static,
R::Item: Into<Response>,
R::Error: Into<Error>,
2018-01-10 05:00:18 +01:00
{
self.service = Box::new(RouteNewService::new(
2019-03-03 09:57:48 +01:00
Extract::new(self.config_ref.clone())
.and_then(AsyncHandle::new(handler).map_err(|_| panic!())),
));
self
2018-01-10 05:00:18 +01:00
}
2019-03-03 09:57:48 +01:00
/// 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<C: ExtractorConfig>(mut self, config: C) -> Self {
self.config.store(config);
self
}
2018-01-10 05:00:18 +01:00
}
2019-03-02 20:53:05 +01:00
// pub struct RouteServiceBuilder<P, T, U1, U2> {
// service: T,
// filters: Vec<Box<Filter>>,
// _t: PhantomData<(P, U1, U2)>,
// }
2018-01-10 05:00:18 +01:00
2019-03-02 07:51:32 +01:00
// impl<T, S: 'static, U1, U2> RouteServiceBuilder<T, S, U1, U2>
// where
// T: NewService<
// Request = HandlerRequest<S, U1>,
// Response = HandlerRequest<S, U2>,
// Error = Error,
// InitError = (),
// >,
// {
// pub fn new<F: IntoNewService<T>>(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<F: Filter<S> + 'static>(&mut self, f: F) -> &mut Self {
// self.filters.push(Box::new(f));
// self
// }
// pub fn map<T1, U3, F: IntoNewService<T1>>(
// self,
// md: F,
// ) -> RouteServiceBuilder<
// impl NewService<
// Request = HandlerRequest<S, U1>,
// Response = HandlerRequest<S, U3>,
// Error = Error,
// InitError = (),
// >,
// S,
// U1,
// U2,
// >
// where
// T1: NewService<
// Request = HandlerRequest<S, U2>,
// Response = HandlerRequest<S, U3>,
// InitError = (),
// >,
// T1::Error: Into<Error>,
// {
// RouteServiceBuilder {
// service: self
// .service
// .and_then(md.into_new_service().map_err(|e| e.into())),
// filters: self.filters,
// _t: PhantomData,
// }
// }
// pub fn to_async<F, P, R>(self, handler: F) -> Route<S>
// where
// F: AsyncFactory<S, U2, P, R>,
// P: FromRequest<S> + 'static,
// R: IntoFuture,
// R::Item: Into<Response>,
// R::Error: Into<Error>,
// {
// Route {
// service: self
// .service
// .and_then(Extract::new(P::Config::default()))
// .then(AsyncHandle::new(handler)),
// filters: Rc::new(self.filters),
// }
// }
// pub fn to<F, P, R>(self, handler: F) -> Route<S>
// where
// F: Factory<S, U2, P, R> + 'static,
// P: FromRequest<S> + 'static,
// R: Responder<S> + '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<P, T>
where
T: NewService<Request = ServiceRequest<P>, Error = (Error, ServiceFromRequest<P>)>,
2019-03-02 07:51:32 +01:00
{
service: T,
2018-01-10 05:00:18 +01:00
}
2019-03-02 07:51:32 +01:00
impl<P: 'static, T> RouteNewService<P, T>
where
T: NewService<
Request = ServiceRequest<P>,
Response = ServiceResponse,
Error = (Error, ServiceFromRequest<P>),
2019-03-02 07:51:32 +01:00
>,
T::Future: 'static,
T::Service: 'static,
<T::Service as Service>::Future: 'static,
{
pub fn new(service: T) -> Self {
RouteNewService { service }
2018-01-10 05:00:18 +01:00
}
}
2019-03-02 07:51:32 +01:00
impl<P: 'static, T> NewService for RouteNewService<P, T>
where
T: NewService<
Request = ServiceRequest<P>,
Response = ServiceResponse,
Error = (Error, ServiceFromRequest<P>),
2019-03-02 07:51:32 +01:00
>,
T::Future: 'static,
T::Service: 'static,
<T::Service as Service>::Future: 'static,
{
type Request = ServiceRequest<P>;
type Response = ServiceResponse;
type Error = ();
type InitError = ();
type Service = BoxedRouteService<Self::Request, Self::Response>;
type Future = Box<Future<Item = Self::Service, Error = Self::InitError>>;
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)
}),
)
2018-01-10 05:00:18 +01:00
}
}
2019-03-02 07:51:32 +01:00
struct RouteServiceWrapper<P, T: Service<Request = ServiceRequest<P>>> {
service: T,
}
2019-03-02 07:51:32 +01:00
impl<P, T> Service for RouteServiceWrapper<P, T>
where
T::Future: 'static,
T: Service<
Request = ServiceRequest<P>,
Response = ServiceResponse,
Error = (Error, ServiceFromRequest<P>),
2019-03-02 07:51:32 +01:00
>,
{
type Request = ServiceRequest<P>;
type Response = ServiceResponse;
type Error = ();
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready().map_err(|_| ())
}
2019-03-02 07:51:32 +01:00
fn call(&mut self, req: ServiceRequest<P>) -> Self::Future {
Box::new(self.service.call(req).then(|res| match res {
Ok(res) => Ok(res),
Err((err, req)) => Ok(req.error_response(err)),
}))
2017-12-05 01:09:22 +01:00
}
}