2020-10-10 02:26:05 +02:00
|
|
|
#![allow(clippy::rc_buffer)] // inner value is mutated before being shared (`Rc::get_mut`)
|
|
|
|
|
2021-02-27 22:00:36 +01:00
|
|
|
use std::{future::Future, rc::Rc};
|
2017-12-05 01:09:22 +01:00
|
|
|
|
2021-06-17 18:57:58 +02:00
|
|
|
use actix_http::http::Method;
|
2021-02-27 22:00:36 +01:00
|
|
|
use actix_service::{
|
|
|
|
boxed::{self, BoxService, BoxServiceFactory},
|
|
|
|
Service, ServiceFactory,
|
|
|
|
};
|
|
|
|
use futures_core::future::LocalBoxFuture;
|
2019-03-02 07:51:32 +01:00
|
|
|
|
2021-06-17 18:57:58 +02:00
|
|
|
use crate::{
|
|
|
|
guard::{self, Guard},
|
|
|
|
handler::{Handler, HandlerService},
|
|
|
|
service::{ServiceRequest, ServiceResponse},
|
|
|
|
Error, FromRequest, HttpResponse, Responder,
|
|
|
|
};
|
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.
|
2019-04-13 23:50:54 +02:00
|
|
|
pub struct Route {
|
2021-02-27 22:00:36 +01:00
|
|
|
service: BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>,
|
2019-07-17 11:48:37 +02:00
|
|
|
guards: Rc<Vec<Box<dyn Guard>>>,
|
2017-12-05 01:09:22 +01:00
|
|
|
}
|
|
|
|
|
2019-04-13 23:50:54 +02:00
|
|
|
impl Route {
|
2019-03-03 09:57:48 +01:00
|
|
|
/// Create new route which matches any request.
|
2020-06-22 21:09:48 +02:00
|
|
|
#[allow(clippy::new_without_default)]
|
2019-04-13 23:50:54 +02:00
|
|
|
pub fn new() -> Route {
|
2019-03-03 04:19:56 +01:00
|
|
|
Route {
|
2021-02-27 22:00:36 +01:00
|
|
|
service: boxed::factory(HandlerService::new(HttpResponse::NotFound)),
|
2019-03-03 21:09:38 +01:00
|
|
|
guards: Rc::new(Vec::new()),
|
2019-03-03 04:19:56 +01:00
|
|
|
}
|
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-07 00:47:15 +01:00
|
|
|
}
|
2019-03-02 07:51:32 +01:00
|
|
|
}
|
|
|
|
|
2021-01-04 00:47:04 +01:00
|
|
|
impl ServiceFactory<ServiceRequest> for Route {
|
2019-03-02 07:51:32 +01:00
|
|
|
type Response = ServiceResponse;
|
2019-03-11 00:35:38 +01:00
|
|
|
type Error = Error;
|
2021-02-27 22:00:36 +01:00
|
|
|
type Config = ();
|
2019-04-13 23:50:54 +02:00
|
|
|
type Service = RouteService;
|
2021-02-27 22:00:36 +01:00
|
|
|
type InitError = ();
|
|
|
|
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 {
|
2021-02-27 22:00:36 +01:00
|
|
|
let fut = self.service.new_service(());
|
|
|
|
let guards = self.guards.clone();
|
2019-03-02 07:51:32 +01:00
|
|
|
|
2021-02-27 22:00:36 +01:00
|
|
|
Box::pin(async move {
|
|
|
|
let service = fut.await?;
|
|
|
|
Ok(RouteService { service, guards })
|
|
|
|
})
|
2017-12-05 01:09:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-13 23:50:54 +02:00
|
|
|
pub struct RouteService {
|
2021-02-27 22:00:36 +01:00
|
|
|
service: BoxService<ServiceRequest, ServiceResponse, Error>,
|
2019-07-17 11:48:37 +02:00
|
|
|
guards: Rc<Vec<Box<dyn Guard>>>,
|
2019-03-02 07:51:32 +01:00
|
|
|
}
|
|
|
|
|
2019-04-13 23:50:54 +02: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() {
|
2019-03-03 04:19:56 +01:00
|
|
|
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
|
|
|
|
2021-01-04 00:47:04 +01:00
|
|
|
impl Service<ServiceRequest> for RouteService {
|
2019-03-02 07:51:32 +01:00
|
|
|
type Response = ServiceResponse;
|
2019-03-11 00:35:38 +01:00
|
|
|
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
|
|
|
|
2021-02-27 22:00:36 +01:00
|
|
|
actix_service::forward_ready!(service);
|
2017-12-05 01:09:22 +01:00
|
|
|
|
2021-02-07 02:00:40 +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
|
|
|
|
2019-04-13 23:50:54 +02: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
|
|
|
///
|
2021-03-25 09:45:52 +01:00
|
|
|
/// ```
|
2017-12-20 07:36:06 +01:00
|
|
|
/// # use actix_web::*;
|
|
|
|
/// # fn main() {
|
2019-03-07 00:47:15 +01:00
|
|
|
/// 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()))
|
2019-03-07 00:47:15 +01:00
|
|
|
/// );
|
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)
|
2019-03-03 04:19:56 +01:00
|
|
|
.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
|
|
|
///
|
2021-03-25 09:45:52 +01:00
|
|
|
/// ```
|
2019-03-02 07:51:32 +01:00
|
|
|
/// # use actix_web::*;
|
|
|
|
/// # fn main() {
|
2019-03-07 00:47:15 +01:00
|
|
|
/// 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-07 00:47:15 +01:00
|
|
|
/// );
|
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
|
|
|
///
|
2021-03-25 09:45:52 +01:00
|
|
|
/// ```
|
2019-03-07 23:01:52 +01:00
|
|
|
/// use actix_web::{web, http, App};
|
2019-10-07 07:29:11 +02:00
|
|
|
/// 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() {
|
2019-03-07 00:47:15 +01:00
|
|
|
/// 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-05-10 01:27:31 +02:00
|
|
|
///
|
2018-10-02 06:16:56 +02:00
|
|
|
/// It is possible to use multiple extractors for one handler function.
|
2018-05-10 01:27:31 +02:00
|
|
|
///
|
2021-03-25 09:45:52 +01:00
|
|
|
/// ```
|
2018-05-10 01:27:31 +02:00
|
|
|
/// # 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};
|
2018-05-10 01:27: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(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)
|
2018-05-10 01:27:31 +02:00
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// fn main() {
|
2019-03-07 00:47:15 +01:00
|
|
|
/// 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
|
|
|
/// );
|
2018-05-10 01:27:31 +02: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>,
|
2019-04-13 23:50:54 +02:00
|
|
|
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
|
|
|
{
|
2021-02-27 22:00:36 +01:00
|
|
|
self.service = boxed::factory(HandlerService::new(handler));
|
2019-03-03 09:57:48 +01:00
|
|
|
self
|
|
|
|
}
|
2018-01-10 05:00:18 +01:00
|
|
|
}
|
|
|
|
|
2019-03-11 02:33:47 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-06-17 18:57:58 +02:00
|
|
|
use std::{convert::Infallible, time::Duration};
|
2019-03-25 22:33:34 +01:00
|
|
|
|
2021-01-04 00:47:04 +01:00
|
|
|
use actix_rt::time::sleep;
|
2019-04-22 23:22:08 +02:00
|
|
|
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
|
|
|
|
2019-04-22 23:22:08 +02:00
|
|
|
#[derive(Serialize, PartialEq, Debug)]
|
|
|
|
struct MyObject {
|
|
|
|
name: String,
|
|
|
|
}
|
|
|
|
|
2019-11-26 06:25:50 +01:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_route() {
|
2021-02-07 02:00:40 +01:00
|
|
|
let srv = init_service(
|
2019-11-26 06:25:50 +01:00
|
|
|
App::new()
|
|
|
|
.service(
|
|
|
|
web::resource("/test")
|
2020-07-22 01:28:33 +02:00
|
|
|
.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 {
|
2021-01-04 00:47:04 +01:00
|
|
|
sleep(Duration::from_millis(100)).await;
|
2021-06-17 18:57:58 +02:00
|
|
|
Ok::<_, Infallible>(HttpResponse::Created())
|
2019-11-26 06:25:50 +01:00
|
|
|
}))
|
2020-05-21 10:56:53 +02:00
|
|
|
.route(web::delete().to(|| async {
|
2021-01-04 00:47:04 +01:00
|
|
|
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 {
|
2021-01-04 00:47:04 +01:00
|
|
|
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();
|
2021-02-07 02:00:40 +01:00
|
|
|
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();
|
2021-02-07 02:00:40 +01:00
|
|
|
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();
|
2021-02-07 02:00:40 +01:00
|
|
|
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();
|
2021-02-07 02:00:40 +01:00
|
|
|
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();
|
2021-02-07 02:00:40 +01:00
|
|
|
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();
|
2021-02-07 02:00:40 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|