1use std::{mem, rc::Rc};
2
3use actix_http::{body::MessageBody, Method};
4use actix_service::{
5 apply,
6 boxed::{self, BoxService},
7 fn_service, Service, ServiceFactory, ServiceFactoryExt, Transform,
8};
9use futures_core::future::LocalBoxFuture;
10
11use crate::{
12 guard::{self, Guard},
13 handler::{handler_service, Handler},
14 middleware::Compat,
15 service::{BoxedHttpServiceFactory, ServiceRequest, ServiceResponse},
16 Error, FromRequest, HttpResponse, Responder,
17};
18
19pub struct Route {
24 service: BoxedHttpServiceFactory,
25 guards: Rc<Vec<Box<dyn Guard>>>,
26}
27
28impl Route {
29 #[allow(clippy::new_without_default)]
31 pub fn new() -> Route {
32 Route {
33 service: boxed::factory(fn_service(|req: ServiceRequest| async {
34 Ok(req.into_response(HttpResponse::NotFound()))
35 })),
36 guards: Rc::new(Vec::new()),
37 }
38 }
39
40 #[doc(alias = "middleware")]
47 #[doc(alias = "use")] pub fn wrap<M, B>(self, mw: M) -> Route
49 where
50 M: Transform<
51 BoxService<ServiceRequest, ServiceResponse, Error>,
52 ServiceRequest,
53 Response = ServiceResponse<B>,
54 Error = Error,
55 InitError = (),
56 > + 'static,
57 B: MessageBody + 'static,
58 {
59 Route {
60 service: boxed::factory(apply(Compat::new(mw), self.service)),
61 guards: self.guards,
62 }
63 }
64
65 pub(crate) fn take_guards(&mut self) -> Vec<Box<dyn Guard>> {
66 mem::take(Rc::get_mut(&mut self.guards).unwrap())
67 }
68}
69
70impl ServiceFactory<ServiceRequest> for Route {
71 type Response = ServiceResponse;
72 type Error = Error;
73 type Config = ();
74 type Service = RouteService;
75 type InitError = ();
76 type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
77
78 fn new_service(&self, _: ()) -> Self::Future {
79 let fut = self.service.new_service(());
80 let guards = Rc::clone(&self.guards);
81
82 Box::pin(async move {
83 let service = fut.await?;
84 Ok(RouteService { service, guards })
85 })
86 }
87}
88
89pub struct RouteService {
90 service: BoxService<ServiceRequest, ServiceResponse, Error>,
91 guards: Rc<Vec<Box<dyn Guard>>>,
92}
93
94impl RouteService {
95 #[allow(clippy::needless_pass_by_ref_mut)]
97 pub fn check(&self, req: &mut ServiceRequest) -> bool {
98 let guard_ctx = req.guard_ctx();
99
100 for guard in self.guards.iter() {
101 if !guard.check(&guard_ctx) {
102 return false;
103 }
104 }
105 true
106 }
107}
108
109impl Service<ServiceRequest> for RouteService {
110 type Response = ServiceResponse;
111 type Error = Error;
112 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
113
114 actix_service::forward_ready!(service);
115
116 fn call(&self, req: ServiceRequest) -> Self::Future {
117 self.service.call(req)
118 }
119}
120
121impl Route {
122 pub fn method(mut self, method: Method) -> Self {
137 Rc::get_mut(&mut self.guards)
138 .unwrap()
139 .push(Box::new(guard::Method(method)));
140 self
141 }
142
143 pub fn guard<F: Guard + 'static>(mut self, f: F) -> Self {
158 Rc::get_mut(&mut self.guards).unwrap().push(Box::new(f));
159 self
160 }
161
162 pub fn to<F, Args>(mut self, handler: F) -> Self
211 where
212 F: Handler<Args>,
213 Args: FromRequest + 'static,
214 F::Output: Responder + 'static,
215 {
216 self.service = handler_service(handler);
217 self
218 }
219
220 pub fn service<S, E>(mut self, service_factory: S) -> Self
253 where
254 S: ServiceFactory<
255 ServiceRequest,
256 Response = ServiceResponse,
257 Error = E,
258 InitError = (),
259 Config = (),
260 > + 'static,
261 E: Into<Error> + 'static,
262 {
263 self.service = boxed::factory(service_factory.map_err(Into::into));
264 self
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use std::{convert::Infallible, time::Duration};
271
272 use actix_rt::time::sleep;
273 use bytes::Bytes;
274 use futures_core::future::LocalBoxFuture;
275 use serde::Serialize;
276
277 use crate::{
278 dev::{always_ready, fn_factory, fn_service, Service},
279 error,
280 http::{header, Method, StatusCode},
281 middleware::{DefaultHeaders, Logger},
282 service::{ServiceRequest, ServiceResponse},
283 test::{call_service, init_service, read_body, TestRequest},
284 web, App, HttpResponse,
285 };
286
287 #[derive(Serialize, PartialEq, Debug)]
288 struct MyObject {
289 name: String,
290 }
291
292 #[actix_rt::test]
293 async fn test_route() {
294 let srv =
295 init_service(
296 App::new()
297 .service(
298 web::resource("/test")
299 .route(web::get().to(HttpResponse::Ok))
300 .route(web::put().to(|| async {
301 Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
302 }))
303 .route(web::post().to(|| async {
304 sleep(Duration::from_millis(100)).await;
305 Ok::<_, Infallible>(HttpResponse::Created())
306 }))
307 .route(web::delete().to(|| async {
308 sleep(Duration::from_millis(100)).await;
309 Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
310 })),
311 )
312 .service(web::resource("/json").route(web::get().to(|| async {
313 sleep(Duration::from_millis(25)).await;
314 web::Json(MyObject {
315 name: "test".to_string(),
316 })
317 }))),
318 )
319 .await;
320
321 let req = TestRequest::with_uri("/test")
322 .method(Method::GET)
323 .to_request();
324 let resp = call_service(&srv, req).await;
325 assert_eq!(resp.status(), StatusCode::OK);
326
327 let req = TestRequest::with_uri("/test")
328 .method(Method::POST)
329 .to_request();
330 let resp = call_service(&srv, req).await;
331 assert_eq!(resp.status(), StatusCode::CREATED);
332
333 let req = TestRequest::with_uri("/test")
334 .method(Method::PUT)
335 .to_request();
336 let resp = call_service(&srv, req).await;
337 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
338
339 let req = TestRequest::with_uri("/test")
340 .method(Method::DELETE)
341 .to_request();
342 let resp = call_service(&srv, req).await;
343 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
344
345 let req = TestRequest::with_uri("/test")
346 .method(Method::HEAD)
347 .to_request();
348 let resp = call_service(&srv, req).await;
349 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
350
351 let req = TestRequest::with_uri("/json").to_request();
352 let resp = call_service(&srv, req).await;
353 assert_eq!(resp.status(), StatusCode::OK);
354
355 let body = read_body(resp).await;
356 assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}"));
357 }
358
359 #[actix_rt::test]
360 async fn route_middleware() {
361 let srv = init_service(
362 App::new()
363 .route("/", web::get().to(HttpResponse::Ok).wrap(Logger::default()))
364 .service(
365 web::resource("/test")
366 .route(web::get().to(HttpResponse::Ok))
367 .route(
368 web::post()
369 .to(HttpResponse::Created)
370 .wrap(DefaultHeaders::new().add(("x-test", "x-posted"))),
371 )
372 .route(
373 web::delete()
374 .to(HttpResponse::Accepted)
375 .wrap(Logger::default()),
377 ),
378 ),
379 )
380 .await;
381
382 let req = TestRequest::get().uri("/test").to_request();
383 let res = call_service(&srv, req).await;
384 assert_eq!(res.status(), StatusCode::OK);
385 assert!(!res.headers().contains_key("x-test"));
386
387 let req = TestRequest::post().uri("/test").to_request();
388 let res = call_service(&srv, req).await;
389 assert_eq!(res.status(), StatusCode::CREATED);
390 assert_eq!(res.headers().get("x-test").unwrap(), "x-posted");
391
392 let req = TestRequest::delete().uri("/test").to_request();
393 let res = call_service(&srv, req).await;
394 assert_eq!(res.status(), StatusCode::ACCEPTED);
395 }
396
397 #[actix_rt::test]
398 async fn test_service_handler() {
399 struct HelloWorld;
400
401 impl Service<ServiceRequest> for HelloWorld {
402 type Response = ServiceResponse;
403 type Error = crate::Error;
404 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
405
406 always_ready!();
407
408 fn call(&self, req: ServiceRequest) -> Self::Future {
409 let (req, _) = req.into_parts();
410
411 let res = HttpResponse::Ok()
412 .insert_header(header::ContentType::plaintext())
413 .body("Hello world!");
414
415 Box::pin(async move { Ok(ServiceResponse::new(req, res)) })
416 }
417 }
418
419 let srv = init_service(
420 App::new()
421 .route(
422 "/hello",
423 web::get().service(fn_factory(|| async { Ok(HelloWorld) })),
424 )
425 .route(
426 "/bye",
427 web::get().service(fn_factory(|| async {
428 Ok::<_, ()>(fn_service(|req: ServiceRequest| async {
429 let (req, _) = req.into_parts();
430
431 let res = HttpResponse::Ok()
432 .insert_header(header::ContentType::plaintext())
433 .body("Goodbye, and thanks for all the fish!");
434
435 Ok::<_, Infallible>(ServiceResponse::new(req, res))
436 }))
437 })),
438 ),
439 )
440 .await;
441
442 let req = TestRequest::get().uri("/hello").to_request();
443 let resp = call_service(&srv, req).await;
444 assert_eq!(resp.status(), StatusCode::OK);
445 let body = read_body(resp).await;
446 assert_eq!(body, Bytes::from_static(b"Hello world!"));
447
448 let req = TestRequest::get().uri("/bye").to_request();
449 let resp = call_service(&srv, req).await;
450 assert_eq!(resp.status(), StatusCode::OK);
451 let body = read_body(resp).await;
452 assert_eq!(
453 body,
454 Bytes::from_static(b"Goodbye, and thanks for all the fish!")
455 );
456 }
457}