1 use std::{any::type_name, ops::Deref};
2 
3 use actix_utils::future::{err, ok, Ready};
4 
5 use crate::{dev::Payload, error::ErrorInternalServerError, Error, FromRequest, HttpRequest};
6 
7 /// Request-local data extractor.
8 ///
9 /// Request-local data is arbitrary data attached to an individual request, usually
10 /// by middleware. It can be set via `extensions_mut` on [`HttpRequest`][htr_ext_mut]
11 /// or [`ServiceRequest`][srv_ext_mut].
12 ///
13 /// Unlike app data, request data is dropped when the request has finished processing. This makes it
14 /// useful as a kind of messaging system between middleware and request handlers. It uses the same
15 /// types-as-keys storage system as app data.
16 ///
17 /// # Mutating Request Data
18 /// Note that since extractors must output owned data, only types that `impl Clone` can use this
19 /// extractor. A clone is taken of the required request data and can, therefore, not be directly
20 /// mutated in-place. To mutate request data, continue to use [`HttpRequest::extensions_mut`] or
21 /// re-insert the cloned data back into the extensions map. A `DerefMut` impl is intentionally not
22 /// provided to make this potential foot-gun more obvious.
23 ///
24 /// # Example
25 /// ```no_run
26 /// # use actix_web::{web, HttpResponse, HttpRequest, Responder};
27 ///
28 /// #[derive(Debug, Clone, PartialEq)]
29 /// struct FlagFromMiddleware(String);
30 ///
31 /// /// Use the `ReqData<T>` extractor to access request data in a handler.
32 /// async fn handler(
33 ///     req: HttpRequest,
34 ///     opt_flag: Option<web::ReqData<FlagFromMiddleware>>,
35 /// ) -> impl Responder {
36 ///     // use an optional extractor if the middleware is
37 ///     // not guaranteed to add this type of requests data
38 ///     if let Some(flag) = opt_flag {
39 ///         assert_eq!(&flag.into_inner(), req.extensions().get::<FlagFromMiddleware>().unwrap());
40 ///     }
41 ///
42 ///     HttpResponse::Ok()
43 /// }
44 /// ```
45 ///
46 /// [htr_ext_mut]: crate::HttpRequest::extensions_mut
47 /// [srv_ext_mut]: crate::dev::ServiceRequest::extensions_mut
48 #[derive(Debug, Clone)]
49 pub struct ReqData<T: Clone + 'static>(T);
50 
51 impl<T: Clone + 'static> ReqData<T> {
52     /// Consumes the `ReqData`, returning its wrapped data.
into_inner(self) -> T53     pub fn into_inner(self) -> T {
54         self.0
55     }
56 }
57 
58 impl<T: Clone + 'static> Deref for ReqData<T> {
59     type Target = T;
60 
deref(&self) -> &T61     fn deref(&self) -> &T {
62         &self.0
63     }
64 }
65 
66 impl<T: Clone + 'static> FromRequest for ReqData<T> {
67     type Config = ();
68     type Error = Error;
69     type Future = Ready<Result<Self, Error>>;
70 
from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future71     fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
72         if let Some(st) = req.extensions().get::<T>() {
73             ok(ReqData(st.clone()))
74         } else {
75             log::debug!(
76                 "Failed to construct App-level ReqData extractor. \
77                  Request path: {:?} (type: {})",
78                 req.path(),
79                 type_name::<T>(),
80             );
81             err(ErrorInternalServerError(
82                 "Missing expected request extension data",
83             ))
84         }
85     }
86 }
87 
88 #[cfg(test)]
89 mod tests {
90     use std::{cell::RefCell, rc::Rc};
91 
92     use futures_util::TryFutureExt as _;
93 
94     use super::*;
95     use crate::{
96         dev::Service,
97         http::{Method, StatusCode},
98         test::{init_service, TestRequest},
99         web, App, HttpMessage, HttpResponse,
100     };
101 
102     #[actix_rt::test]
req_data_extractor()103     async fn req_data_extractor() {
104         let srv = init_service(
105             App::new()
106                 .wrap_fn(|req, srv| {
107                     if req.method() == Method::POST {
108                         req.extensions_mut().insert(42u32);
109                     }
110 
111                     srv.call(req)
112                 })
113                 .service(web::resource("/test").to(
114                     |req: HttpRequest, data: Option<ReqData<u32>>| {
115                         if req.method() != Method::POST {
116                             assert!(data.is_none());
117                         }
118 
119                         if let Some(data) = data {
120                             assert_eq!(*data, 42);
121                             assert_eq!(
122                                 Some(data.into_inner()),
123                                 req.extensions().get::<u32>().copied()
124                             );
125                         }
126 
127                         HttpResponse::Ok()
128                     },
129                 )),
130         )
131         .await;
132 
133         let req = TestRequest::get().uri("/test").to_request();
134         let resp = srv.call(req).await.unwrap();
135         assert_eq!(resp.status(), StatusCode::OK);
136 
137         let req = TestRequest::post().uri("/test").to_request();
138         let resp = srv.call(req).await.unwrap();
139         assert_eq!(resp.status(), StatusCode::OK);
140     }
141 
142     #[actix_rt::test]
req_data_internal_mutability()143     async fn req_data_internal_mutability() {
144         let srv = init_service(
145             App::new()
146                 .wrap_fn(|req, srv| {
147                     let data_before = Rc::new(RefCell::new(42u32));
148                     req.extensions_mut().insert(data_before);
149 
150                     srv.call(req).map_ok(|res| {
151                         {
152                             let ext = res.request().extensions();
153                             let data_after = ext.get::<Rc<RefCell<u32>>>().unwrap();
154                             assert_eq!(*data_after.borrow(), 53u32);
155                         }
156 
157                         res
158                     })
159                 })
160                 .default_service(web::to(|data: ReqData<Rc<RefCell<u32>>>| {
161                     assert_eq!(*data.borrow(), 42);
162                     *data.borrow_mut() += 11;
163                     assert_eq!(*data.borrow(), 53);
164 
165                     HttpResponse::Ok()
166                 })),
167         )
168         .await;
169 
170         let req = TestRequest::get().uri("/test").to_request();
171         let resp = srv.call(req).await.unwrap();
172         assert_eq!(resp.status(), StatusCode::OK);
173     }
174 }
175