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

502 lines
15 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
use actix_http::{http::Method, Error, Extensions};
2019-03-02 07:51:32 +01:00
use actix_service::{NewService, Service};
use futures::future::{ok, Either, FutureResult};
2019-03-02 07:51:32 +01:00
use futures::{Async, Future, IntoFuture, Poll};
2019-03-17 05:09:11 +01:00
use crate::data::RouteData;
use crate::extract::FromRequest;
2019-03-03 21:09:38 +01:00
use crate::guard::{self, Guard};
use crate::handler::{AsyncFactory, AsyncHandler, Extract, Factory, Handler};
2019-03-02 07:51:32 +01:00
use crate::responder::Responder;
2019-04-07 23:43:07 +02:00
use crate::service::{ServiceRequest, ServiceResponse};
use crate::HttpResponse;
2019-03-02 07:51:32 +01:00
type BoxedRouteService<Req, Res> = Box<
Service<
Request = Req,
2019-03-02 07:51:32 +01:00
Response = Res,
Error = Error,
Future = Either<
FutureResult<Res, Error>,
Box<Future<Item = Res, Error = Error>>,
>,
2019-03-02 07:51:32 +01:00
>,
>;
type BoxedRouteNewService<Req, Res> = Box<
NewService<
Request = Req,
2019-03-02 07:51:32 +01:00
Response = Res,
Error = Error,
2019-03-02 07:51:32 +01:00
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.
pub struct Route {
service: BoxedRouteNewService<ServiceRequest, ServiceResponse>,
2019-03-03 21:09:38 +01:00
guards: Rc<Vec<Box<Guard>>>,
data: Option<Extensions>,
data_ref: Rc<RefCell<Option<Rc<Extensions>>>>,
2017-12-05 01:09:22 +01:00
}
impl Route {
2019-03-03 09:57:48 +01:00
/// Create new route which matches any request.
pub fn new() -> Route {
let data_ref = Rc::new(RefCell::new(None));
Route {
service: Box::new(RouteNewService::new(Extract::new(
data_ref.clone(),
Handler::new(|| HttpResponse::NotFound()),
))),
2019-03-03 21:09:38 +01:00
guards: Rc::new(Vec::new()),
data: None,
data_ref,
}
2019-03-02 07:51:32 +01:00
}
pub(crate) fn finish(mut self) -> Self {
*self.data_ref.borrow_mut() = self.data.take().map(Rc::new);
2019-03-03 09:57:48 +01:00
self
}
pub(crate) fn take_guards(&mut self) -> Vec<Box<Guard>> {
std::mem::replace(Rc::get_mut(&mut self.guards).unwrap(), Vec::new())
}
2019-03-02 07:51:32 +01:00
}
impl NewService for Route {
type Request = ServiceRequest;
2019-03-02 07:51:32 +01:00
type Response = ServiceResponse;
type Error = Error;
2019-03-02 07:51:32 +01:00
type InitError = ();
type Service = RouteService;
type Future = CreateRouteService;
2019-03-02 07:51:32 +01:00
fn new_service(&self, _: &()) -> Self::Future {
CreateRouteService {
fut: self.service.new_service(&()),
2019-03-03 21:09:38 +01:00
guards: self.guards.clone(),
2019-03-02 07:51:32 +01:00
}
}
}
type RouteFuture =
Box<Future<Item = BoxedRouteService<ServiceRequest, ServiceResponse>, Error = ()>>;
2019-03-02 07:51:32 +01:00
pub struct CreateRouteService {
fut: RouteFuture,
2019-03-03 21:09:38 +01:00
guards: Rc<Vec<Box<Guard>>>,
2019-03-02 07:51:32 +01:00
}
impl Future for CreateRouteService {
type Item = RouteService;
2019-03-02 07:51:32 +01:00
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.fut.poll()? {
Async::Ready(service) => Ok(Async::Ready(RouteService {
service,
2019-03-03 21:09:38 +01:00
guards: self.guards.clone(),
2019-03-02 07:51:32 +01:00
})),
Async::NotReady => Ok(Async::NotReady),
2017-12-05 01:09:22 +01:00
}
}
}
pub struct RouteService {
service: BoxedRouteService<ServiceRequest, ServiceResponse>,
2019-03-03 21:09:38 +01:00
guards: Rc<Vec<Box<Guard>>>,
2019-03-02 07:51:32 +01:00
}
impl RouteService {
pub fn check(&self, req: &mut ServiceRequest) -> bool {
2019-03-03 21:09:38 +01:00
for f in self.guards.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
impl Service for RouteService {
type Request = ServiceRequest;
2019-03-02 07:51:32 +01:00
type Response = ServiceResponse;
type Error = Error;
type Future = Either<
FutureResult<Self::Response, Self::Error>,
Box<Future<Item = Self::Response, Error = Self::Error>>,
>;
2019-03-02 07:51:32 +01:00
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready()
2017-12-05 01:09:22 +01:00
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
2019-03-02 07:51:32 +01:00
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 Route {
2019-03-03 21:09:38 +01:00
/// Add method guard to the route.
2017-12-20 07:36:06 +01:00
///
2019-03-03 21:09:38 +01:00
/// ```rust
2017-12-20 07:36:06 +01:00
/// # use actix_web::*;
/// # fn main() {
/// App::new().service(web::resource("/path").route(
/// web::get()
/// .method(http::Method::CONNECT)
2019-03-03 21:09:38 +01:00
/// .guard(guard::Header("content-type", "text/plain"))
/// .to(|req: HttpRequest| HttpResponse::Ok()))
/// );
2017-12-20 07:36:06 +01:00
/// # }
/// ```
2019-03-02 07:51:32 +01:00
pub fn method(mut self, method: Method) -> Self {
2019-03-03 21:09:38 +01:00
Rc::get_mut(&mut self.guards)
.unwrap()
2019-03-03 21:09:38 +01:00
.push(Box::new(guard::Method(method)));
2017-12-05 01:09:22 +01:00
self
}
2019-03-03 21:09:38 +01:00
/// Add guard to the route.
2019-03-02 07:51:32 +01:00
///
2019-03-03 21:09:38 +01:00
/// ```rust
2019-03-02 07:51:32 +01:00
/// # use actix_web::*;
/// # fn main() {
/// App::new().service(web::resource("/path").route(
/// web::route()
2019-03-03 21:09:38 +01:00
/// .guard(guard::Get())
/// .guard(guard::Header("content-type", "text/plain"))
/// .to(|req: HttpRequest| HttpResponse::Ok()))
/// );
2019-03-02 07:51:32 +01:00
/// # }
/// ```
2019-03-03 21:09:38 +01:00
pub fn guard<F: Guard + 'static>(mut self, f: F) -> Self {
Rc::get_mut(&mut self.guards).unwrap().push(Box::new(f));
2019-03-02 07:51:32 +01:00
self
2017-12-05 01:09:22 +01:00
}
2019-03-03 21:09:38 +01:00
/// Set handler function, use request extractors for parameters.
2018-03-27 08:10:31 +02:00
///
2019-03-03 21:09:38 +01:00
/// ```rust
2018-03-27 08:10:31 +02:00
/// #[macro_use] extern crate serde_derive;
/// use actix_web::{web, http, App};
2018-03-27 08:10:31 +02:00
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// extract path info using serde
/// fn index(info: web::Path<Info>) -> String {
2019-03-03 21:09:38 +01:00
/// format!("Welcome {}!", info.username)
2018-03-27 08:10:31 +02:00
/// }
///
/// fn main() {
/// let app = App::new().service(
/// web::resource("/{username}/index.html") // <- define path parameters
/// .route(web::get().to(index)) // <- register handler
2019-03-03 21:09:38 +01:00
/// );
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-03 21:09:38 +01:00
/// ```rust
/// # use std::collections::HashMap;
2019-03-03 21:09:38 +01:00
/// # use serde_derive::Deserialize;
2019-03-07 20:43:46 +01:00
/// use actix_web::{web, App};
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// extract path info using serde
2019-03-07 20:43:46 +01:00
/// fn index(path: web::Path<Info>, query: web::Query<HashMap<String, String>>, body: web::Json<Info>) -> String {
2019-03-03 21:09:38 +01:00
/// format!("Welcome {}!", path.username)
/// }
///
/// fn main() {
/// let app = App::new().service(
/// web::resource("/{username}/index.html") // <- define path parameters
/// .route(web::get().to(index))
2019-03-03 21:09:38 +01:00
/// );
/// }
/// ```
pub fn to<F, T, R>(mut self, handler: F) -> Route
2018-06-21 07:47:01 +02:00
where
2019-03-02 07:51:32 +01:00
F: Factory<T, R> + 'static,
T: FromRequest + 'static,
2018-06-21 07:47:01 +02:00
R: Responder + 'static,
{
self.service = Box::new(RouteNewService::new(Extract::new(
self.data_ref.clone(),
Handler::new(handler),
)));
self
2018-03-27 08:10:31 +02:00
}
2018-03-28 23:24:32 +02:00
2019-03-03 21:09:38 +01:00
/// Set async handler function, use request extractors for parameters.
/// This method has to be used if your handler function returns `impl Future<>`
///
2019-03-03 21:09:38 +01:00
/// ```rust
/// # use futures::future::ok;
/// #[macro_use] extern crate serde_derive;
/// use actix_web::{web, App, Error};
/// use futures::Future;
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// extract path info using serde
/// fn index(info: web::Path<Info>) -> impl Future<Item = &'static str, Error = Error> {
2019-03-03 21:09:38 +01:00
/// 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
2019-03-03 21:09:38 +01:00
/// );
/// }
/// ```
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 + 'static,
2019-03-02 07:51:32 +01:00
R: IntoFuture + 'static,
R::Item: Responder,
2019-03-02 07:51:32 +01:00
R::Error: Into<Error>,
2018-01-10 05:00:18 +01:00
{
self.service = Box::new(RouteNewService::new(Extract::new(
self.data_ref.clone(),
AsyncHandler::new(handler),
)));
self
2018-01-10 05:00:18 +01:00
}
2019-03-03 09:57:48 +01:00
/// Provide route specific data. This method allows to add extractor
/// configuration or specific state available via `RouteData<T>` extractor.
2019-03-03 09:57:48 +01:00
///
/// ```rust
2019-04-14 01:35:25 +02:00
/// use actix_web::{web, App, FromRequest};
2019-03-03 09:57:48 +01:00
///
/// /// 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(
2019-03-03 09:57:48 +01:00
/// web::get()
/// // limit size of the payload
2019-04-14 01:35:25 +02:00
/// .data(String::configure(|cfg| {
/// cfg.limit(4096)
/// }))
2019-03-03 09:57:48 +01:00
/// // register handler
/// .to(index)
/// ));
2019-03-03 09:57:48 +01:00
/// }
/// ```
2019-04-14 01:35:25 +02:00
pub fn data<T: 'static>(mut self, data: T) -> Self {
if self.data.is_none() {
self.data = Some(Extensions::new());
}
2019-03-17 05:09:11 +01:00
self.data.as_mut().unwrap().insert(RouteData::new(data));
2019-03-03 09:57:48 +01:00
self
}
2018-01-10 05:00:18 +01:00
}
struct RouteNewService<T>
2019-03-02 07:51:32 +01:00
where
T: NewService<Request = ServiceRequest, Error = (Error, ServiceRequest)>,
2019-03-02 07:51:32 +01:00
{
service: T,
2018-01-10 05:00:18 +01:00
}
impl<T> RouteNewService<T>
2019-03-02 07:51:32 +01:00
where
T: NewService<
Request = ServiceRequest,
2019-03-02 07:51:32 +01:00
Response = ServiceResponse,
Error = (Error, ServiceRequest),
2019-03-02 07:51:32 +01:00
>,
T::Future: 'static,
T::Service: 'static,
<T::Service as Service>::Future: 'static,
2019-03-02 07:51:32 +01:00
{
pub fn new(service: T) -> Self {
RouteNewService { service }
2018-01-10 05:00:18 +01:00
}
}
impl<T> NewService for RouteNewService<T>
2019-03-02 07:51:32 +01:00
where
T: NewService<
Request = ServiceRequest,
2019-03-02 07:51:32 +01:00
Response = ServiceResponse,
Error = (Error, ServiceRequest),
2019-03-02 07:51:32 +01:00
>,
T::Future: 'static,
T::Service: 'static,
<T::Service as Service>::Future: 'static,
2019-03-02 07:51:32 +01:00
{
type Request = ServiceRequest;
2019-03-02 07:51:32 +01:00
type Response = ServiceResponse;
type Error = Error;
2019-03-02 07:51:32 +01:00
type InitError = ();
type Service = BoxedRouteService<ServiceRequest, Self::Response>;
2019-03-02 07:51:32 +01:00
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 });
2019-03-02 07:51:32 +01:00
Ok(service)
}),
)
2018-01-10 05:00:18 +01:00
}
}
struct RouteServiceWrapper<T: Service> {
2019-03-02 07:51:32 +01:00
service: T,
}
impl<T> Service for RouteServiceWrapper<T>
2019-03-02 07:51:32 +01:00
where
T::Future: 'static,
T: Service<
Request = ServiceRequest,
2019-03-02 07:51:32 +01:00
Response = ServiceResponse,
Error = (Error, ServiceRequest),
2019-03-02 07:51:32 +01:00
>,
{
type Request = ServiceRequest;
2019-03-02 07:51:32 +01:00
type Response = ServiceResponse;
type Error = Error;
type Future = Either<
FutureResult<Self::Response, Self::Error>,
Box<Future<Item = Self::Response, Error = Self::Error>>,
>;
2019-03-02 07:51:32 +01:00
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready().map_err(|(e, _)| e)
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
let mut fut = self.service.call(req);
match fut.poll() {
Ok(Async::Ready(res)) => Either::A(ok(res)),
Err((e, req)) => Either::A(ok(req.error_response(e))),
Ok(Async::NotReady) => Either::B(Box::new(fut.then(|res| match res {
Ok(res) => Ok(res),
Err((err, req)) => Ok(req.error_response(err)),
}))),
}
2017-12-05 01:09:22 +01:00
}
}
2019-03-11 02:33:47 +01:00
#[cfg(test)]
mod tests {
2019-03-25 22:33:34 +01:00
use std::time::Duration;
use bytes::Bytes;
2019-03-25 22:33:34 +01:00
use futures::Future;
use serde_derive::Serialize;
2019-03-25 22:33:34 +01:00
use tokio_timer::sleep;
2019-03-11 02:33:47 +01:00
use crate::http::{Method, StatusCode};
use crate::test::{call_service, init_service, read_body, TestRequest};
2019-03-25 22:33:34 +01:00
use crate::{error, web, App, HttpResponse};
2019-03-11 02:33:47 +01:00
#[derive(Serialize, PartialEq, Debug)]
struct MyObject {
name: String,
}
2019-03-11 02:33:47 +01:00
#[test]
fn test_route() {
let mut srv = init_service(
App::new()
.service(
2019-03-25 22:33:34 +01:00
web::resource("/test")
.route(web::get().to(|| HttpResponse::Ok()))
.route(web::put().to(|| {
Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
}))
.route(web::post().to_async(|| {
sleep(Duration::from_millis(100))
.then(|_| HttpResponse::Created())
}))
.route(web::delete().to_async(|| {
sleep(Duration::from_millis(100)).then(|_| {
Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
})
})),
)
.service(web::resource("/json").route(web::get().to_async(|| {
sleep(Duration::from_millis(25)).then(|_| {
Ok::<_, crate::Error>(web::Json(MyObject {
name: "test".to_string(),
}))
})
}))),
);
2019-03-11 02:33:47 +01:00
let req = TestRequest::with_uri("/test")
.method(Method::GET)
.to_request();
let resp = call_service(&mut srv, req);
2019-03-11 02:33:47 +01:00
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test")
.method(Method::POST)
.to_request();
let resp = call_service(&mut srv, req);
2019-03-11 02:33:47 +01:00
assert_eq!(resp.status(), StatusCode::CREATED);
2019-03-25 22:33:34 +01:00
let req = TestRequest::with_uri("/test")
.method(Method::PUT)
.to_request();
let resp = call_service(&mut srv, req);
2019-03-25 22:33:34 +01:00
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/test")
.method(Method::DELETE)
.to_request();
let resp = call_service(&mut srv, req);
2019-03-25 22:33:34 +01:00
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
2019-03-11 02:33:47 +01:00
let req = TestRequest::with_uri("/test")
.method(Method::HEAD)
.to_request();
let resp = call_service(&mut srv, req);
2019-03-25 00:28:16 +01:00
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let req = TestRequest::with_uri("/json").to_request();
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp);
assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}"));
2019-03-11 02:33:47 +01:00
}
}