1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-28 01:52:57 +01:00
actix-web/src/route.rs

384 lines
11 KiB
Rust
Raw Normal View History

2020-10-10 02:26:05 +02:00
#![allow(clippy::rc_buffer)] // inner value is mutated before being shared (`Rc::get_mut`)
2019-11-20 18:33:22 +01:00
use std::future::Future;
use std::pin::Pin;
2018-01-10 05:00:18 +01:00
use std::rc::Rc;
2019-11-20 18:33:22 +01:00
use std::task::{Context, Poll};
2017-12-05 01:09:22 +01:00
use actix_http::{http::Method, Error};
2019-11-20 18:33:22 +01:00
use actix_service::{Service, ServiceFactory};
2020-05-18 04:47:20 +02:00
use futures_util::future::{ready, FutureExt, LocalBoxFuture};
2019-03-02 07:51:32 +01:00
use crate::extract::FromRequest;
2019-03-03 21:09:38 +01:00
use crate::guard::{self, Guard};
2020-12-26 22:46:19 +01:00
use crate::handler::{Handler, HandlerService};
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 = Box<
2019-07-17 11:48:37 +02:00
dyn Service<
ServiceRequest,
Response = ServiceResponse,
Error = Error,
Future = LocalBoxFuture<'static, Result<ServiceResponse, Error>>,
2019-03-02 07:51:32 +01:00
>,
>;
type BoxedRouteNewService = Box<
2019-11-20 18:33:22 +01:00
dyn ServiceFactory<
ServiceRequest,
2019-05-12 17:34:51 +02:00
Config = (),
Response = ServiceResponse,
Error = Error,
2019-03-02 07:51:32 +01:00
InitError = (),
Service = BoxedRouteService,
Future = LocalBoxFuture<'static, Result<BoxedRouteService, ()>>,
2019-03-02 07:51:32 +01:00
>,
>;
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,
2019-07-17 11:48:37 +02:00
guards: Rc<Vec<Box<dyn Guard>>>,
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.
#[allow(clippy::new_without_default)]
pub fn new() -> Route {
Route {
2020-12-26 22:46:19 +01:00
service: Box::new(RouteNewService::new(HandlerService::new(|| {
2019-11-21 16:34:04 +01:00
ready(HttpResponse::NotFound())
2020-12-23 16:47:07 +01:00
}))),
2019-03-03 21:09:38 +01:00
guards: Rc::new(Vec::new()),
}
2019-03-02 07:51:32 +01:00
}
2019-07-17 11:48:37 +02:00
pub(crate) fn take_guards(&mut self) -> Vec<Box<dyn Guard>> {
2020-05-17 03:54:42 +02:00
std::mem::take(Rc::get_mut(&mut self.guards).unwrap())
}
2019-03-02 07:51:32 +01:00
}
impl ServiceFactory<ServiceRequest> for Route {
2019-05-12 17:34:51 +02:00
type Config = ();
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
2019-12-02 16:37:13 +01:00
fn new_service(&self, _: ()) -> Self::Future {
2019-03-02 07:51:32 +01:00
CreateRouteService {
2019-12-02 16:37:13 +01:00
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
}
}
}
pub struct CreateRouteService {
fut: LocalBoxFuture<'static, Result<BoxedRouteService, ()>>,
2019-07-17 11:48:37 +02:00
guards: Rc<Vec<Box<dyn Guard>>>,
2019-03-02 07:51:32 +01:00
}
impl Future for CreateRouteService {
2019-11-20 18:33:22 +01:00
type Output = Result<RouteService, ()>;
2019-03-02 07:51:32 +01:00
2019-12-07 19:46:51 +01:00
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
2019-11-20 18:33:22 +01:00
match this.fut.as_mut().poll(cx)? {
2019-11-20 18:33:22 +01:00
Poll::Ready(service) => Poll::Ready(Ok(RouteService {
2019-03-02 07:51:32 +01:00
service,
2019-11-20 18:33:22 +01:00
guards: this.guards.clone(),
2019-03-02 07:51:32 +01:00
})),
2019-11-20 18:33:22 +01:00
Poll::Pending => Poll::Pending,
2017-12-05 01:09:22 +01:00
}
}
}
pub struct RouteService {
service: BoxedRouteService,
2019-07-17 11:48:37 +02:00
guards: Rc<Vec<Box<dyn 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<ServiceRequest> for RouteService {
2019-03-02 07:51:32 +01:00
type Response = ServiceResponse;
type Error = Error;
2019-11-20 18:33:22 +01:00
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
2019-03-02 07:51:32 +01:00
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2019-11-20 18:33:22 +01:00
self.service.poll_ready(cx)
2017-12-05 01:09:22 +01:00
}
fn call(&self, req: ServiceRequest) -> Self::Future {
2020-12-06 12:42:15 +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
/// use actix_web::{web, http, App};
/// use serde_derive::Deserialize;
2018-03-27 08:10:31 +02:00
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// extract path info using serde
2019-11-21 16:34:04 +01:00
/// async 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-11-21 16:34:04 +01:00
/// async 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
/// );
/// }
/// ```
2020-12-26 22:46:19 +01:00
pub fn to<F, T, R>(mut self, handler: F) -> Self
2018-06-21 07:47:01 +02:00
where
2020-12-26 22:46:19 +01:00
F: Handler<T, R>,
T: FromRequest + 'static,
2020-12-26 22:46:19 +01:00
R: Future + 'static,
R::Output: Responder + 'static,
2018-01-10 05:00:18 +01:00
{
2020-12-26 22:46:19 +01:00
self.service = Box::new(RouteNewService::new(HandlerService::new(handler)));
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: ServiceFactory<ServiceRequest, Error = Error>,
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
2021-02-12 00:03:17 +01:00
T: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error>,
2019-03-02 07:51:32 +01:00
T::Future: 'static,
T::Service: 'static,
<T::Service as Service<ServiceRequest>>::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> ServiceFactory<ServiceRequest> for RouteNewService<T>
2019-03-02 07:51:32 +01:00
where
2021-02-12 00:03:17 +01:00
T: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error>,
2019-03-02 07:51:32 +01:00
T::Future: 'static,
T::Service: 'static,
<T::Service as Service<ServiceRequest>>::Future: 'static,
2019-03-02 07:51:32 +01:00
{
type Response = ServiceResponse;
type Error = Error;
type Config = ();
type Service = BoxedRouteService;
2019-03-02 07:51:32 +01:00
type InitError = ();
2019-11-20 18:33:22 +01:00
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
2019-03-02 07:51:32 +01:00
2019-12-02 16:37:13 +01:00
fn new_service(&self, _: ()) -> Self::Future {
2019-11-20 18:33:22 +01:00
self.service
2019-12-02 16:37:13 +01:00
.new_service(())
2019-11-20 18:33:22 +01:00
.map(|result| match result {
Ok(service) => {
let service = Box::new(RouteServiceWrapper { service }) as _;
2019-03-02 07:51:32 +01:00
Ok(service)
2019-11-20 18:33:22 +01:00
}
Err(_) => Err(()),
})
.boxed_local()
2018-01-10 05:00:18 +01:00
}
}
struct RouteServiceWrapper<T: Service<ServiceRequest>> {
2019-03-02 07:51:32 +01:00
service: T,
}
impl<T> Service<ServiceRequest> for RouteServiceWrapper<T>
2019-03-02 07:51:32 +01:00
where
T::Future: 'static,
T: Service<ServiceRequest, Response = ServiceResponse, Error = Error>,
2019-03-02 07:51:32 +01:00
{
type Response = ServiceResponse;
type Error = Error;
2019-11-20 18:33:22 +01:00
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
2019-03-02 07:51:32 +01:00
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&self, req: ServiceRequest) -> Self::Future {
Box::pin(self.service.call(req))
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 actix_rt::time::sleep;
use bytes::Bytes;
use serde_derive::Serialize;
2019-03-25 22:33:34 +01:00
2019-03-11 02:33:47 +01:00
use crate::http::{Method, StatusCode};
2019-11-26 06:25:50 +01:00
use crate::test::{call_service, init_service, read_body, TestRequest};
use crate::{error, web, App, HttpResponse};
2019-03-11 02:33:47 +01:00
#[derive(Serialize, PartialEq, Debug)]
struct MyObject {
name: String,
}
2019-11-26 06:25:50 +01:00
#[actix_rt::test]
async fn test_route() {
let srv = init_service(
2019-11-26 06:25:50 +01:00
App::new()
.service(
web::resource("/test")
.route(web::get().to(HttpResponse::Ok))
2020-05-21 10:56:53 +02:00
.route(web::put().to(|| async {
Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
2019-11-26 06:25:50 +01:00
}))
2020-05-21 10:56:53 +02:00
.route(web::post().to(|| async {
sleep(Duration::from_millis(100)).await;
Ok::<_, ()>(HttpResponse::Created())
2019-11-26 06:25:50 +01:00
}))
2020-05-21 10:56:53 +02:00
.route(web::delete().to(|| async {
sleep(Duration::from_millis(100)).await;
2020-05-21 10:56:53 +02:00
Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
2019-11-26 06:25:50 +01:00
})),
)
2020-05-21 10:56:53 +02:00
.service(web::resource("/json").route(web::get().to(|| async {
sleep(Duration::from_millis(25)).await;
2020-05-21 10:56:53 +02:00
web::Json(MyObject {
name: "test".to_string(),
})
2019-11-26 06:25:50 +01:00
}))),
)
.await;
let req = TestRequest::with_uri("/test")
.method(Method::GET)
.to_request();
let resp = call_service(&srv, req).await;
2019-11-26 06:25:50 +01:00
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test")
.method(Method::POST)
.to_request();
let resp = call_service(&srv, req).await;
2019-11-26 06:25:50 +01:00
assert_eq!(resp.status(), StatusCode::CREATED);
let req = TestRequest::with_uri("/test")
.method(Method::PUT)
.to_request();
let resp = call_service(&srv, req).await;
2019-11-26 06:25:50 +01:00
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/test")
.method(Method::DELETE)
.to_request();
let resp = call_service(&srv, req).await;
2019-11-26 06:25:50 +01:00
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/test")
.method(Method::HEAD)
.to_request();
let resp = call_service(&srv, req).await;
2019-11-26 06:25:50 +01:00
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let req = TestRequest::with_uri("/json").to_request();
let resp = call_service(&srv, req).await;
2019-11-26 06:25:50 +01:00
assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp).await;
assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}"));
2019-03-11 02:33:47 +01:00
}
}