actix_web/
request.rs

1use std::{
2    cell::{Ref, RefCell, RefMut},
3    fmt, net,
4    rc::Rc,
5    str,
6};
7
8use actix_http::{Message, RequestHead};
9use actix_router::{Path, Url};
10use actix_utils::future::{ok, Ready};
11#[cfg(feature = "cookies")]
12use cookie::{Cookie, ParseError as CookieParseError};
13use smallvec::SmallVec;
14
15use crate::{
16    app_service::AppInitServiceState,
17    config::AppConfig,
18    dev::{Extensions, Payload},
19    error::UrlGenerationError,
20    http::{header::HeaderMap, Method, Uri, Version},
21    info::ConnectionInfo,
22    rmap::ResourceMap,
23    Error, FromRequest, HttpMessage,
24};
25
26#[cfg(feature = "cookies")]
27struct Cookies(Vec<Cookie<'static>>);
28
29/// An incoming request.
30#[derive(Clone)]
31pub struct HttpRequest {
32    /// # Invariant
33    /// `Rc<HttpRequestInner>` is used exclusively and NO `Weak<HttpRequestInner>`
34    /// is allowed anywhere in the code. Weak pointer is purposely ignored when
35    /// doing `Rc`'s ref counter check. Expect panics if this invariant is violated.
36    pub(crate) inner: Rc<HttpRequestInner>,
37}
38
39pub(crate) struct HttpRequestInner {
40    pub(crate) head: Message<RequestHead>,
41    pub(crate) path: Path<Url>,
42    pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,
43    pub(crate) conn_data: Option<Rc<Extensions>>,
44    pub(crate) extensions: Rc<RefCell<Extensions>>,
45    app_state: Rc<AppInitServiceState>,
46}
47
48impl HttpRequest {
49    #[inline]
50    pub(crate) fn new(
51        path: Path<Url>,
52        head: Message<RequestHead>,
53        app_state: Rc<AppInitServiceState>,
54        app_data: Rc<Extensions>,
55        conn_data: Option<Rc<Extensions>>,
56        extensions: Rc<RefCell<Extensions>>,
57    ) -> HttpRequest {
58        let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();
59        data.push(app_data);
60
61        HttpRequest {
62            inner: Rc::new(HttpRequestInner {
63                head,
64                path,
65                app_state,
66                app_data: data,
67                conn_data,
68                extensions,
69            }),
70        }
71    }
72}
73
74impl HttpRequest {
75    /// This method returns reference to the request head
76    #[inline]
77    pub fn head(&self) -> &RequestHead {
78        &self.inner.head
79    }
80
81    /// This method returns mutable reference to the request head.
82    /// panics if multiple references of HTTP request exists.
83    #[inline]
84    pub(crate) fn head_mut(&mut self) -> &mut RequestHead {
85        &mut Rc::get_mut(&mut self.inner).unwrap().head
86    }
87
88    /// Request's uri.
89    #[inline]
90    pub fn uri(&self) -> &Uri {
91        &self.head().uri
92    }
93
94    /// Returns request's original full URL.
95    ///
96    /// Reconstructed URL is best-effort, using [`connection_info`](HttpRequest::connection_info())
97    /// to get forwarded scheme & host.
98    ///
99    /// ```
100    /// use actix_web::test::TestRequest;
101    /// let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
102    ///     .insert_header(("host", "example.com"))
103    ///     .to_http_request();
104    ///
105    /// assert_eq!(
106    ///     req.full_url().as_str(),
107    ///     "http://example.com/api?id=4&name=foo",
108    /// );
109    /// ```
110    pub fn full_url(&self) -> url::Url {
111        let info = self.connection_info();
112        let scheme = info.scheme();
113        let host = info.host();
114        let path_and_query = self
115            .uri()
116            .path_and_query()
117            .map(|paq| paq.as_str())
118            .unwrap_or("/");
119
120        url::Url::parse(&format!("{scheme}://{host}{path_and_query}")).unwrap()
121    }
122
123    /// Read the Request method.
124    #[inline]
125    pub fn method(&self) -> &Method {
126        &self.head().method
127    }
128
129    /// Read the Request Version.
130    #[inline]
131    pub fn version(&self) -> Version {
132        self.head().version
133    }
134
135    #[inline]
136    /// Returns request's headers.
137    pub fn headers(&self) -> &HeaderMap {
138        &self.head().headers
139    }
140
141    /// The target path of this request.
142    #[inline]
143    pub fn path(&self) -> &str {
144        self.head().uri.path()
145    }
146
147    /// The query string in the URL.
148    ///
149    /// Example: `id=10`
150    #[inline]
151    pub fn query_string(&self) -> &str {
152        self.uri().query().unwrap_or_default()
153    }
154
155    /// Returns a reference to the URL parameters container.
156    ///
157    /// A URL parameter is specified in the form `{identifier}`, where the identifier can be used
158    /// later in a request handler to access the matched value for that parameter.
159    ///
160    /// # Percent Encoding and URL Parameters
161    /// Because each URL parameter is able to capture multiple path segments, none of
162    /// `["%2F", "%25", "%2B"]` found in the request URI are decoded into `["/", "%", "+"]` in order
163    /// to preserve path integrity. If a URL parameter is expected to contain these characters, then
164    /// it is on the user to decode them or use the [`web::Path`](crate::web::Path) extractor which
165    /// _will_ decode these special sequences.
166    #[inline]
167    pub fn match_info(&self) -> &Path<Url> {
168        &self.inner.path
169    }
170
171    /// Returns a mutable reference to the URL parameters container.
172    ///
173    /// # Panics
174    /// Panics if this `HttpRequest` has been cloned.
175    #[inline]
176    pub(crate) fn match_info_mut(&mut self) -> &mut Path<Url> {
177        &mut Rc::get_mut(&mut self.inner).unwrap().path
178    }
179
180    /// The resource definition pattern that matched the path. Useful for logging and metrics.
181    ///
182    /// For example, when a resource with pattern `/user/{id}/profile` is defined and a call is made
183    /// to `/user/123/profile` this function would return `Some("/user/{id}/profile")`.
184    ///
185    /// Returns a None when no resource is fully matched, including default services.
186    #[inline]
187    pub fn match_pattern(&self) -> Option<String> {
188        self.resource_map().match_pattern(self.path())
189    }
190
191    /// The resource name that matched the path. Useful for logging and metrics.
192    ///
193    /// Returns a None when no resource is fully matched, including default services.
194    #[inline]
195    pub fn match_name(&self) -> Option<&str> {
196        self.resource_map().match_name(self.path())
197    }
198
199    /// Returns a reference a piece of connection data set in an [on-connect] callback.
200    ///
201    /// ```ignore
202    /// let opt_t = req.conn_data::<PeerCertificate>();
203    /// ```
204    ///
205    /// [on-connect]: crate::HttpServer::on_connect
206    pub fn conn_data<T: 'static>(&self) -> Option<&T> {
207        self.inner
208            .conn_data
209            .as_deref()
210            .and_then(|container| container.get::<T>())
211    }
212
213    /// Generates URL for a named resource.
214    ///
215    /// This substitutes in sequence all URL parameters that appear in the resource itself and in
216    /// parent [scopes](crate::web::scope), if any.
217    ///
218    /// It is worth noting that the characters `['/', '%']` are not escaped and therefore a single
219    /// URL parameter may expand into multiple path segments and `elements` can be percent-encoded
220    /// beforehand without worrying about double encoding. Any other character that is not valid in
221    /// a URL path context is escaped using percent-encoding.
222    ///
223    /// # Examples
224    /// ```
225    /// # use actix_web::{web, App, HttpRequest, HttpResponse};
226    /// fn index(req: HttpRequest) -> HttpResponse {
227    ///     let url = req.url_for("foo", &["1", "2", "3"]); // <- generate URL for "foo" resource
228    ///     HttpResponse::Ok().into()
229    /// }
230    ///
231    /// let app = App::new()
232    ///     .service(web::resource("/test/{one}/{two}/{three}")
233    ///          .name("foo")  // <- set resource name so it can be used in `url_for`
234    ///          .route(web::get().to(|| HttpResponse::Ok()))
235    ///     );
236    /// ```
237    pub fn url_for<U, I>(&self, name: &str, elements: U) -> Result<url::Url, UrlGenerationError>
238    where
239        U: IntoIterator<Item = I>,
240        I: AsRef<str>,
241    {
242        self.resource_map().url_for(self, name, elements)
243    }
244
245    /// Generate URL for named resource
246    ///
247    /// This method is similar to `HttpRequest::url_for()` but it can be used
248    /// for urls that do not contain variable parts.
249    pub fn url_for_static(&self, name: &str) -> Result<url::Url, UrlGenerationError> {
250        const NO_PARAMS: [&str; 0] = [];
251        self.url_for(name, NO_PARAMS)
252    }
253
254    /// Get a reference to a `ResourceMap` of current application.
255    #[inline]
256    pub fn resource_map(&self) -> &ResourceMap {
257        self.app_state().rmap()
258    }
259
260    /// Returns peer socket address.
261    ///
262    /// Peer address is the directly connected peer's socket address. If a proxy is used in front of
263    /// the Actix Web server, then it would be address of this proxy.
264    ///
265    /// For expanded client connection information, use [`connection_info`] instead.
266    ///
267    /// Will only return `None` when server is listening on [UDS socket] or when called in unit
268    /// tests unless [`TestRequest::peer_addr`] is used.
269    ///
270    /// [UDS socket]: crate::HttpServer::bind_uds
271    /// [`TestRequest::peer_addr`]: crate::test::TestRequest::peer_addr
272    /// [`connection_info`]: Self::connection_info
273    #[inline]
274    pub fn peer_addr(&self) -> Option<net::SocketAddr> {
275        self.head().peer_addr
276    }
277
278    /// Returns connection info for the current request.
279    ///
280    /// The return type, [`ConnectionInfo`], can also be used as an extractor.
281    ///
282    /// # Panics
283    /// Panics if request's extensions container is already borrowed.
284    #[inline]
285    pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
286        if !self.extensions().contains::<ConnectionInfo>() {
287            let info = ConnectionInfo::new(self.head(), self.app_config());
288            self.extensions_mut().insert(info);
289        }
290
291        Ref::map(self.extensions(), |data| data.get().unwrap())
292    }
293
294    /// Returns a reference to the application's connection configuration.
295    #[inline]
296    pub fn app_config(&self) -> &AppConfig {
297        self.app_state().config()
298    }
299
300    /// Retrieves a piece of application state.
301    ///
302    /// Extracts any object stored with [`App::app_data()`](crate::App::app_data) (or the
303    /// counterpart methods on [`Scope`](crate::Scope::app_data) and
304    /// [`Resource`](crate::Resource::app_data)) during application configuration.
305    ///
306    /// Since the Actix Web router layers application data, the returned object will reference the
307    /// "closest" instance of the type. For example, if an `App` stores a `u32`, a nested `Scope`
308    /// also stores a `u32`, and the delegated request handler falls within that `Scope`, then
309    /// calling `.app_data::<u32>()` on an `HttpRequest` within that handler will return the
310    /// `Scope`'s instance. However, using the same router set up and a request that does not get
311    /// captured by the `Scope`, `.app_data::<u32>()` would return the `App`'s instance.
312    ///
313    /// If the state was stored using the [`Data`] wrapper, then it must also be retrieved using
314    /// this same type.
315    ///
316    /// See also the [`Data`] extractor.
317    ///
318    /// # Examples
319    /// ```no_run
320    /// # use actix_web::{test::TestRequest, web::Data};
321    /// # let req = TestRequest::default().to_http_request();
322    /// # type T = u32;
323    /// let opt_t: Option<&Data<T>> = req.app_data::<Data<T>>();
324    /// ```
325    ///
326    /// [`Data`]: crate::web::Data
327    #[doc(alias = "state")]
328    pub fn app_data<T: 'static>(&self) -> Option<&T> {
329        for container in self.inner.app_data.iter().rev() {
330            if let Some(data) = container.get::<T>() {
331                return Some(data);
332            }
333        }
334
335        None
336    }
337
338    #[inline]
339    fn app_state(&self) -> &AppInitServiceState {
340        &self.inner.app_state
341    }
342
343    /// Load request cookies.
344    #[cfg(feature = "cookies")]
345    pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
346        use actix_http::header::COOKIE;
347
348        if self.extensions().get::<Cookies>().is_none() {
349            let mut cookies = Vec::new();
350            for hdr in self.headers().get_all(COOKIE) {
351                let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
352                for cookie_str in s.split(';').map(|s| s.trim()) {
353                    if !cookie_str.is_empty() {
354                        cookies.push(Cookie::parse_encoded(cookie_str)?.into_owned());
355                    }
356                }
357            }
358            self.extensions_mut().insert(Cookies(cookies));
359        }
360
361        Ok(Ref::map(self.extensions(), |ext| {
362            &ext.get::<Cookies>().unwrap().0
363        }))
364    }
365
366    /// Return request cookie.
367    #[cfg(feature = "cookies")]
368    pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
369        if let Ok(cookies) = self.cookies() {
370            for cookie in cookies.iter() {
371                if cookie.name() == name {
372                    return Some(cookie.to_owned());
373                }
374            }
375        }
376        None
377    }
378}
379
380impl HttpMessage for HttpRequest {
381    type Stream = ();
382
383    #[inline]
384    fn headers(&self) -> &HeaderMap {
385        &self.head().headers
386    }
387
388    #[inline]
389    fn extensions(&self) -> Ref<'_, Extensions> {
390        self.inner.extensions.borrow()
391    }
392
393    #[inline]
394    fn extensions_mut(&self) -> RefMut<'_, Extensions> {
395        self.inner.extensions.borrow_mut()
396    }
397
398    #[inline]
399    fn take_payload(&mut self) -> Payload<Self::Stream> {
400        Payload::None
401    }
402}
403
404impl Drop for HttpRequest {
405    fn drop(&mut self) {
406        // if possible, contribute to current worker's HttpRequest allocation pool
407
408        // This relies on no weak references to inner existing anywhere within the codebase.
409        if let Some(inner) = Rc::get_mut(&mut self.inner) {
410            if inner.app_state.pool().is_available() {
411                // clear additional app_data and keep the root one for reuse.
412                inner.app_data.truncate(1);
413
414                // Inner is borrowed mut here and; get req data mutably to reduce borrow check. Also
415                // we know the req_data Rc will not have any clones at this point to unwrap is okay.
416                Rc::get_mut(&mut inner.extensions)
417                    .unwrap()
418                    .get_mut()
419                    .clear();
420
421                // We can't use the same trick as req data because the conn_data is held by the
422                // dispatcher, too.
423                inner.conn_data = None;
424
425                // a re-borrow of pool is necessary here.
426                let req = Rc::clone(&self.inner);
427                self.app_state().pool().push(req);
428            }
429        }
430    }
431}
432
433/// It is possible to get `HttpRequest` as an extractor handler parameter
434///
435/// # Examples
436/// ```
437/// use actix_web::{web, App, HttpRequest};
438/// use serde::Deserialize;
439///
440/// /// extract `Thing` from request
441/// async fn index(req: HttpRequest) -> String {
442///    format!("Got thing: {:?}", req)
443/// }
444///
445/// let app = App::new().service(
446///     web::resource("/users/{first}").route(
447///         web::get().to(index))
448/// );
449/// ```
450impl FromRequest for HttpRequest {
451    type Error = Error;
452    type Future = Ready<Result<Self, Error>>;
453
454    #[inline]
455    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
456        ok(req.clone())
457    }
458}
459
460impl fmt::Debug for HttpRequest {
461    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
462        writeln!(
463            f,
464            "\nHttpRequest {:?} {}:{}",
465            self.inner.head.version,
466            self.inner.head.method,
467            self.path()
468        )?;
469
470        if !self.query_string().is_empty() {
471            writeln!(f, "  query: ?{:?}", self.query_string())?;
472        }
473
474        if !self.match_info().is_empty() {
475            writeln!(f, "  params: {:?}", self.match_info())?;
476        }
477
478        writeln!(f, "  headers:")?;
479
480        for (key, val) in self.headers().iter() {
481            match key {
482                // redact sensitive header values from debug output
483                &crate::http::header::AUTHORIZATION
484                | &crate::http::header::PROXY_AUTHORIZATION
485                | &crate::http::header::COOKIE => writeln!(f, "    {:?}: {:?}", key, "*redacted*")?,
486
487                _ => writeln!(f, "    {:?}: {:?}", key, val)?,
488            }
489        }
490
491        Ok(())
492    }
493}
494
495/// Slab-allocated `HttpRequest` Pool
496///
497/// Since request processing may yield for asynchronous events to complete, a worker may have many
498/// requests in-flight at any time. Pooling requests like this amortizes the performance and memory
499/// costs of allocating and de-allocating HttpRequest objects as frequently as they otherwise would.
500///
501/// Request objects are added when they are dropped (see `<HttpRequest as Drop>::drop`) and re-used
502/// in `<AppInitService as Service>::call` when there are available objects in the list.
503///
504/// The pool's default capacity is 128 items.
505pub(crate) struct HttpRequestPool {
506    inner: RefCell<Vec<Rc<HttpRequestInner>>>,
507    cap: usize,
508}
509
510impl Default for HttpRequestPool {
511    fn default() -> Self {
512        Self::with_capacity(128)
513    }
514}
515
516impl HttpRequestPool {
517    pub(crate) fn with_capacity(cap: usize) -> Self {
518        HttpRequestPool {
519            inner: RefCell::new(Vec::with_capacity(cap)),
520            cap,
521        }
522    }
523
524    /// Re-use a previously allocated (but now completed/discarded) HttpRequest object.
525    #[inline]
526    pub(crate) fn pop(&self) -> Option<HttpRequest> {
527        self.inner
528            .borrow_mut()
529            .pop()
530            .map(|inner| HttpRequest { inner })
531    }
532
533    /// Check if the pool still has capacity for request storage.
534    #[inline]
535    pub(crate) fn is_available(&self) -> bool {
536        self.inner.borrow_mut().len() < self.cap
537    }
538
539    /// Push a request to pool.
540    #[inline]
541    pub(crate) fn push(&self, req: Rc<HttpRequestInner>) {
542        self.inner.borrow_mut().push(req);
543    }
544
545    /// Clears all allocated HttpRequest objects.
546    pub(crate) fn clear(&self) {
547        self.inner.borrow_mut().clear()
548    }
549}
550
551#[cfg(test)]
552mod tests {
553    use bytes::Bytes;
554
555    use super::*;
556    use crate::{
557        dev::{ResourceDef, Service},
558        http::{header, StatusCode},
559        test::{self, call_service, init_service, read_body, TestRequest},
560        web, App, HttpResponse,
561    };
562
563    #[test]
564    fn test_debug() {
565        let req = TestRequest::default()
566            .insert_header(("content-type", "text/plain"))
567            .to_http_request();
568        let dbg = format!("{:?}", req);
569        assert!(dbg.contains("HttpRequest"));
570    }
571
572    #[test]
573    #[cfg(feature = "cookies")]
574    fn test_no_request_cookies() {
575        let req = TestRequest::default().to_http_request();
576        assert!(req.cookies().unwrap().is_empty());
577    }
578
579    #[test]
580    #[cfg(feature = "cookies")]
581    fn test_request_cookies() {
582        let req = TestRequest::default()
583            .append_header((header::COOKIE, "cookie1=value1"))
584            .append_header((header::COOKIE, "cookie2=value2"))
585            .to_http_request();
586        {
587            let cookies = req.cookies().unwrap();
588            assert_eq!(cookies.len(), 2);
589            assert_eq!(cookies[0].name(), "cookie1");
590            assert_eq!(cookies[0].value(), "value1");
591            assert_eq!(cookies[1].name(), "cookie2");
592            assert_eq!(cookies[1].value(), "value2");
593        }
594
595        let cookie = req.cookie("cookie1");
596        assert!(cookie.is_some());
597        let cookie = cookie.unwrap();
598        assert_eq!(cookie.name(), "cookie1");
599        assert_eq!(cookie.value(), "value1");
600
601        let cookie = req.cookie("cookie-unknown");
602        assert!(cookie.is_none());
603    }
604
605    #[test]
606    fn test_request_query() {
607        let req = TestRequest::with_uri("/?id=test").to_http_request();
608        assert_eq!(req.query_string(), "id=test");
609    }
610
611    #[test]
612    fn test_url_for() {
613        let mut res = ResourceDef::new("/user/{name}.{ext}");
614        res.set_name("index");
615
616        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
617        rmap.add(&mut res, None);
618        assert!(rmap.has_resource("/user/test.html"));
619        assert!(!rmap.has_resource("/test/unknown"));
620
621        let req = TestRequest::default()
622            .insert_header((header::HOST, "www.rust-lang.org"))
623            .rmap(rmap)
624            .to_http_request();
625
626        assert_eq!(
627            req.url_for("unknown", ["test"]),
628            Err(UrlGenerationError::ResourceNotFound)
629        );
630        assert_eq!(
631            req.url_for("index", ["test"]),
632            Err(UrlGenerationError::NotEnoughElements)
633        );
634        let url = req.url_for("index", ["test", "html"]);
635        assert_eq!(
636            url.ok().unwrap().as_str(),
637            "http://www.rust-lang.org/user/test.html"
638        );
639    }
640
641    #[test]
642    fn test_url_for_static() {
643        let mut rdef = ResourceDef::new("/index.html");
644        rdef.set_name("index");
645
646        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
647        rmap.add(&mut rdef, None);
648
649        assert!(rmap.has_resource("/index.html"));
650
651        let req = TestRequest::with_uri("/test")
652            .insert_header((header::HOST, "www.rust-lang.org"))
653            .rmap(rmap)
654            .to_http_request();
655        let url = req.url_for_static("index");
656        assert_eq!(
657            url.ok().unwrap().as_str(),
658            "http://www.rust-lang.org/index.html"
659        );
660    }
661
662    #[test]
663    fn test_match_name() {
664        let mut rdef = ResourceDef::new("/index.html");
665        rdef.set_name("index");
666
667        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
668        rmap.add(&mut rdef, None);
669
670        assert!(rmap.has_resource("/index.html"));
671
672        let req = TestRequest::default()
673            .uri("/index.html")
674            .rmap(rmap)
675            .to_http_request();
676
677        assert_eq!(req.match_name(), Some("index"));
678    }
679
680    #[test]
681    fn test_url_for_external() {
682        let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");
683
684        rdef.set_name("youtube");
685
686        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
687        rmap.add(&mut rdef, None);
688
689        let req = TestRequest::default().rmap(rmap).to_http_request();
690        let url = req.url_for("youtube", ["oHg5SJYRHA0"]);
691        assert_eq!(
692            url.ok().unwrap().as_str(),
693            "https://youtube.com/watch/oHg5SJYRHA0"
694        );
695    }
696
697    #[actix_rt::test]
698    async fn test_drop_http_request_pool() {
699        let srv = init_service(
700            App::new().service(web::resource("/").to(|req: HttpRequest| {
701                HttpResponse::Ok()
702                    .insert_header(("pool_cap", req.app_state().pool().cap))
703                    .finish()
704            })),
705        )
706        .await;
707
708        let req = TestRequest::default().to_request();
709        let resp = call_service(&srv, req).await;
710
711        drop(srv);
712
713        assert_eq!(resp.headers().get("pool_cap").unwrap(), "128");
714    }
715
716    #[actix_rt::test]
717    async fn test_data() {
718        let srv = init_service(App::new().app_data(10usize).service(web::resource("/").to(
719            |req: HttpRequest| {
720                if req.app_data::<usize>().is_some() {
721                    HttpResponse::Ok()
722                } else {
723                    HttpResponse::BadRequest()
724                }
725            },
726        )))
727        .await;
728
729        let req = TestRequest::default().to_request();
730        let resp = call_service(&srv, req).await;
731        assert_eq!(resp.status(), StatusCode::OK);
732
733        let srv = init_service(App::new().app_data(10u32).service(web::resource("/").to(
734            |req: HttpRequest| {
735                if req.app_data::<usize>().is_some() {
736                    HttpResponse::Ok()
737                } else {
738                    HttpResponse::BadRequest()
739                }
740            },
741        )))
742        .await;
743
744        let req = TestRequest::default().to_request();
745        let resp = call_service(&srv, req).await;
746        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
747    }
748
749    #[actix_rt::test]
750    async fn test_cascading_data() {
751        #[allow(dead_code)]
752        fn echo_usize(req: HttpRequest) -> HttpResponse {
753            let num = req.app_data::<usize>().unwrap();
754            HttpResponse::Ok().body(num.to_string())
755        }
756
757        let srv = init_service(
758            App::new()
759                .app_data(88usize)
760                .service(web::resource("/").route(web::get().to(echo_usize)))
761                .service(
762                    web::resource("/one")
763                        .app_data(1u32)
764                        .route(web::get().to(echo_usize)),
765                ),
766        )
767        .await;
768
769        let req = TestRequest::get().uri("/").to_request();
770        let resp = srv.call(req).await.unwrap();
771        let body = read_body(resp).await;
772        assert_eq!(body, Bytes::from_static(b"88"));
773
774        let req = TestRequest::get().uri("/one").to_request();
775        let resp = srv.call(req).await.unwrap();
776        let body = read_body(resp).await;
777        assert_eq!(body, Bytes::from_static(b"88"));
778    }
779
780    #[actix_rt::test]
781    async fn test_overwrite_data() {
782        #[allow(dead_code)]
783        fn echo_usize(req: HttpRequest) -> HttpResponse {
784            let num = req.app_data::<usize>().unwrap();
785            HttpResponse::Ok().body(num.to_string())
786        }
787
788        let srv = init_service(
789            App::new()
790                .app_data(88usize)
791                .service(web::resource("/").route(web::get().to(echo_usize)))
792                .service(
793                    web::resource("/one")
794                        .app_data(1usize)
795                        .route(web::get().to(echo_usize)),
796                ),
797        )
798        .await;
799
800        let req = TestRequest::get().uri("/").to_request();
801        let resp = srv.call(req).await.unwrap();
802        let body = read_body(resp).await;
803        assert_eq!(body, Bytes::from_static(b"88"));
804
805        let req = TestRequest::get().uri("/one").to_request();
806        let resp = srv.call(req).await.unwrap();
807        let body = read_body(resp).await;
808        assert_eq!(body, Bytes::from_static(b"1"));
809    }
810
811    #[actix_rt::test]
812    async fn test_app_data_dropped() {
813        struct Tracker {
814            pub dropped: bool,
815        }
816        struct Foo {
817            tracker: Rc<RefCell<Tracker>>,
818        }
819        impl Drop for Foo {
820            fn drop(&mut self) {
821                self.tracker.borrow_mut().dropped = true;
822            }
823        }
824
825        let tracker = Rc::new(RefCell::new(Tracker { dropped: false }));
826        {
827            let tracker2 = Rc::clone(&tracker);
828            let srv = init_service(App::new().service(web::resource("/").to(
829                move |req: HttpRequest| {
830                    req.extensions_mut().insert(Foo {
831                        tracker: Rc::clone(&tracker2),
832                    });
833                    HttpResponse::Ok()
834                },
835            )))
836            .await;
837
838            let req = TestRequest::default().to_request();
839            let resp = call_service(&srv, req).await;
840            assert_eq!(resp.status(), StatusCode::OK);
841        }
842
843        assert!(tracker.borrow().dropped);
844    }
845
846    #[actix_rt::test]
847    async fn extract_path_pattern() {
848        let srv = init_service(
849            App::new().service(
850                web::scope("/user/{id}")
851                    .service(web::resource("/profile").route(web::get().to(
852                        move |req: HttpRequest| {
853                            assert_eq!(req.match_pattern(), Some("/user/{id}/profile".to_owned()));
854
855                            HttpResponse::Ok().finish()
856                        },
857                    )))
858                    .default_service(web::to(move |req: HttpRequest| {
859                        assert!(req.match_pattern().is_none());
860                        HttpResponse::Ok().finish()
861                    })),
862            ),
863        )
864        .await;
865
866        let req = TestRequest::get().uri("/user/22/profile").to_request();
867        let res = call_service(&srv, req).await;
868        assert_eq!(res.status(), StatusCode::OK);
869
870        let req = TestRequest::get().uri("/user/22/not-exist").to_request();
871        let res = call_service(&srv, req).await;
872        assert_eq!(res.status(), StatusCode::OK);
873    }
874
875    #[actix_rt::test]
876    async fn extract_path_pattern_complex() {
877        let srv = init_service(
878            App::new()
879                .service(web::scope("/user").service(web::scope("/{id}").service(
880                    web::resource("").to(move |req: HttpRequest| {
881                        assert_eq!(req.match_pattern(), Some("/user/{id}".to_owned()));
882
883                        HttpResponse::Ok().finish()
884                    }),
885                )))
886                .service(web::resource("/").to(move |req: HttpRequest| {
887                    assert_eq!(req.match_pattern(), Some("/".to_owned()));
888
889                    HttpResponse::Ok().finish()
890                }))
891                .default_service(web::to(move |req: HttpRequest| {
892                    assert!(req.match_pattern().is_none());
893                    HttpResponse::Ok().finish()
894                })),
895        )
896        .await;
897
898        let req = TestRequest::get().uri("/user/test").to_request();
899        let res = call_service(&srv, req).await;
900        assert_eq!(res.status(), StatusCode::OK);
901
902        let req = TestRequest::get().uri("/").to_request();
903        let res = call_service(&srv, req).await;
904        assert_eq!(res.status(), StatusCode::OK);
905
906        let req = TestRequest::get().uri("/not-exist").to_request();
907        let res = call_service(&srv, req).await;
908        assert_eq!(res.status(), StatusCode::OK);
909    }
910
911    #[actix_rt::test]
912    async fn url_for_closest_named_resource() {
913        // we mount the route named 'nested' on 2 different scopes, 'a' and 'b'
914        let srv = test::init_service(
915            App::new()
916                .service(
917                    web::scope("/foo")
918                        .service(web::resource("/nested").name("nested").route(web::get().to(
919                            |req: HttpRequest| {
920                                HttpResponse::Ok()
921                                    .body(format!("{}", req.url_for_static("nested").unwrap()))
922                            },
923                        )))
924                        .service(web::scope("/baz").service(web::resource("deep")))
925                        .service(web::resource("{foo_param}")),
926                )
927                .service(web::scope("/bar").service(
928                    web::resource("/nested").name("nested").route(web::get().to(
929                        |req: HttpRequest| {
930                            HttpResponse::Ok()
931                                .body(format!("{}", req.url_for_static("nested").unwrap()))
932                        },
933                    )),
934                )),
935        )
936        .await;
937
938        let foo_resp =
939            test::call_service(&srv, TestRequest::with_uri("/foo/nested").to_request()).await;
940        assert_eq!(foo_resp.status(), StatusCode::OK);
941        let body = read_body(foo_resp).await;
942        // `body` equals http://localhost:8080/bar/nested
943        // because nested from /bar overrides /foo's
944        // to do this any other way would require something like a custom tree search
945        // see https://github.com/actix/actix-web/issues/1763
946        assert_eq!(body, "http://localhost:8080/bar/nested");
947
948        let bar_resp =
949            test::call_service(&srv, TestRequest::with_uri("/bar/nested").to_request()).await;
950        assert_eq!(bar_resp.status(), StatusCode::OK);
951        let body = read_body(bar_resp).await;
952        assert_eq!(body, "http://localhost:8080/bar/nested");
953    }
954
955    #[test]
956    fn authorization_header_hidden_in_debug() {
957        let authorization_header = "Basic bXkgdXNlcm5hbWU6bXkgcGFzc3dvcmQK";
958        let req = TestRequest::get()
959            .insert_header((crate::http::header::AUTHORIZATION, authorization_header))
960            .to_http_request();
961
962        assert!(!format!("{:?}", req).contains(authorization_header));
963    }
964
965    #[test]
966    fn proxy_authorization_header_hidden_in_debug() {
967        let proxy_authorization_header = "secret value";
968        let req = TestRequest::get()
969            .insert_header((
970                crate::http::header::PROXY_AUTHORIZATION,
971                proxy_authorization_header,
972            ))
973            .to_http_request();
974
975        assert!(!format!("{:?}", req).contains(proxy_authorization_header));
976    }
977
978    #[test]
979    fn cookie_header_hidden_in_debug() {
980        let cookie_header = "secret";
981        let req = TestRequest::get()
982            .insert_header((crate::http::header::COOKIE, cookie_header))
983            .to_http_request();
984
985        assert!(!format!("{:?}", req).contains(cookie_header));
986    }
987
988    #[test]
989    fn other_header_visible_in_debug() {
990        let location_header = "192.0.0.1";
991        let req = TestRequest::get()
992            .insert_header((crate::http::header::LOCATION, location_header))
993            .to_http_request();
994
995        assert!(format!("{:?}", req).contains(location_header));
996    }
997
998    #[test]
999    fn check_full_url() {
1000        let req = TestRequest::with_uri("/api?id=4&name=foo").to_http_request();
1001        assert_eq!(
1002            req.full_url().as_str(),
1003            "http://localhost:8080/api?id=4&name=foo",
1004        );
1005
1006        let req = TestRequest::with_uri("https://example.com/api?id=4&name=foo").to_http_request();
1007        assert_eq!(
1008            req.full_url().as_str(),
1009            "https://example.com/api?id=4&name=foo",
1010        );
1011
1012        let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
1013            .insert_header(("host", "example.com"))
1014            .to_http_request();
1015        assert_eq!(
1016            req.full_url().as_str(),
1017            "http://example.com/api?id=4&name=foo",
1018        );
1019    }
1020}