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 called in unit tests unless [`TestRequest::peer_addr`] is used.
268    ///
269    /// [`TestRequest::peer_addr`]: crate::test::TestRequest::peer_addr
270    /// [`connection_info`]: Self::connection_info
271    #[inline]
272    pub fn peer_addr(&self) -> Option<net::SocketAddr> {
273        self.head().peer_addr
274    }
275
276    /// Returns connection info for the current request.
277    ///
278    /// The return type, [`ConnectionInfo`], can also be used as an extractor.
279    ///
280    /// # Panics
281    /// Panics if request's extensions container is already borrowed.
282    #[inline]
283    pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
284        if !self.extensions().contains::<ConnectionInfo>() {
285            let info = ConnectionInfo::new(self.head(), self.app_config());
286            self.extensions_mut().insert(info);
287        }
288
289        Ref::map(self.extensions(), |data| data.get().unwrap())
290    }
291
292    /// Returns a reference to the application's connection configuration.
293    #[inline]
294    pub fn app_config(&self) -> &AppConfig {
295        self.app_state().config()
296    }
297
298    /// Retrieves a piece of application state.
299    ///
300    /// Extracts any object stored with [`App::app_data()`](crate::App::app_data) (or the
301    /// counterpart methods on [`Scope`](crate::Scope::app_data) and
302    /// [`Resource`](crate::Resource::app_data)) during application configuration.
303    ///
304    /// Since the Actix Web router layers application data, the returned object will reference the
305    /// "closest" instance of the type. For example, if an `App` stores a `u32`, a nested `Scope`
306    /// also stores a `u32`, and the delegated request handler falls within that `Scope`, then
307    /// calling `.app_data::<u32>()` on an `HttpRequest` within that handler will return the
308    /// `Scope`'s instance. However, using the same router set up and a request that does not get
309    /// captured by the `Scope`, `.app_data::<u32>()` would return the `App`'s instance.
310    ///
311    /// If the state was stored using the [`Data`] wrapper, then it must also be retrieved using
312    /// this same type.
313    ///
314    /// See also the [`Data`] extractor.
315    ///
316    /// # Examples
317    /// ```no_run
318    /// # use actix_web::{test::TestRequest, web::Data};
319    /// # let req = TestRequest::default().to_http_request();
320    /// # type T = u32;
321    /// let opt_t: Option<&Data<T>> = req.app_data::<Data<T>>();
322    /// ```
323    ///
324    /// [`Data`]: crate::web::Data
325    #[doc(alias = "state")]
326    pub fn app_data<T: 'static>(&self) -> Option<&T> {
327        for container in self.inner.app_data.iter().rev() {
328            if let Some(data) = container.get::<T>() {
329                return Some(data);
330            }
331        }
332
333        None
334    }
335
336    #[inline]
337    fn app_state(&self) -> &AppInitServiceState {
338        &self.inner.app_state
339    }
340
341    /// Load request cookies.
342    #[cfg(feature = "cookies")]
343    pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
344        use actix_http::header::COOKIE;
345
346        if self.extensions().get::<Cookies>().is_none() {
347            let mut cookies = Vec::new();
348            for hdr in self.headers().get_all(COOKIE) {
349                let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
350                for cookie_str in s.split(';').map(|s| s.trim()) {
351                    if !cookie_str.is_empty() {
352                        cookies.push(Cookie::parse_encoded(cookie_str)?.into_owned());
353                    }
354                }
355            }
356            self.extensions_mut().insert(Cookies(cookies));
357        }
358
359        Ok(Ref::map(self.extensions(), |ext| {
360            &ext.get::<Cookies>().unwrap().0
361        }))
362    }
363
364    /// Return request cookie.
365    #[cfg(feature = "cookies")]
366    pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
367        if let Ok(cookies) = self.cookies() {
368            for cookie in cookies.iter() {
369                if cookie.name() == name {
370                    return Some(cookie.to_owned());
371                }
372            }
373        }
374        None
375    }
376}
377
378impl HttpMessage for HttpRequest {
379    type Stream = ();
380
381    #[inline]
382    fn headers(&self) -> &HeaderMap {
383        &self.head().headers
384    }
385
386    #[inline]
387    fn extensions(&self) -> Ref<'_, Extensions> {
388        self.inner.extensions.borrow()
389    }
390
391    #[inline]
392    fn extensions_mut(&self) -> RefMut<'_, Extensions> {
393        self.inner.extensions.borrow_mut()
394    }
395
396    #[inline]
397    fn take_payload(&mut self) -> Payload<Self::Stream> {
398        Payload::None
399    }
400}
401
402impl Drop for HttpRequest {
403    fn drop(&mut self) {
404        // if possible, contribute to current worker's HttpRequest allocation pool
405
406        // This relies on no weak references to inner existing anywhere within the codebase.
407        if let Some(inner) = Rc::get_mut(&mut self.inner) {
408            if inner.app_state.pool().is_available() {
409                // clear additional app_data and keep the root one for reuse.
410                inner.app_data.truncate(1);
411
412                // Inner is borrowed mut here and; get req data mutably to reduce borrow check. Also
413                // we know the req_data Rc will not have any clones at this point to unwrap is okay.
414                Rc::get_mut(&mut inner.extensions)
415                    .unwrap()
416                    .get_mut()
417                    .clear();
418
419                // We can't use the same trick as req data because the conn_data is held by the
420                // dispatcher, too.
421                inner.conn_data = None;
422
423                // a re-borrow of pool is necessary here.
424                let req = Rc::clone(&self.inner);
425                self.app_state().pool().push(req);
426            }
427        }
428    }
429}
430
431/// It is possible to get `HttpRequest` as an extractor handler parameter
432///
433/// # Examples
434/// ```
435/// use actix_web::{web, App, HttpRequest};
436/// use serde::Deserialize;
437///
438/// /// extract `Thing` from request
439/// async fn index(req: HttpRequest) -> String {
440///    format!("Got thing: {:?}", req)
441/// }
442///
443/// let app = App::new().service(
444///     web::resource("/users/{first}").route(
445///         web::get().to(index))
446/// );
447/// ```
448impl FromRequest for HttpRequest {
449    type Error = Error;
450    type Future = Ready<Result<Self, Error>>;
451
452    #[inline]
453    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
454        ok(req.clone())
455    }
456}
457
458impl fmt::Debug for HttpRequest {
459    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460        writeln!(
461            f,
462            "\nHttpRequest {:?} {}:{}",
463            self.inner.head.version,
464            self.inner.head.method,
465            self.path()
466        )?;
467
468        if !self.query_string().is_empty() {
469            writeln!(f, "  query: ?{:?}", self.query_string())?;
470        }
471
472        if !self.match_info().is_empty() {
473            writeln!(f, "  params: {:?}", self.match_info())?;
474        }
475
476        writeln!(f, "  headers:")?;
477
478        for (key, val) in self.headers().iter() {
479            match key {
480                // redact sensitive header values from debug output
481                &crate::http::header::AUTHORIZATION
482                | &crate::http::header::PROXY_AUTHORIZATION
483                | &crate::http::header::COOKIE => writeln!(f, "    {:?}: {:?}", key, "*redacted*")?,
484
485                _ => writeln!(f, "    {:?}: {:?}", key, val)?,
486            }
487        }
488
489        Ok(())
490    }
491}
492
493/// Slab-allocated `HttpRequest` Pool
494///
495/// Since request processing may yield for asynchronous events to complete, a worker may have many
496/// requests in-flight at any time. Pooling requests like this amortizes the performance and memory
497/// costs of allocating and de-allocating HttpRequest objects as frequently as they otherwise would.
498///
499/// Request objects are added when they are dropped (see `<HttpRequest as Drop>::drop`) and re-used
500/// in `<AppInitService as Service>::call` when there are available objects in the list.
501///
502/// The pool's default capacity is 128 items.
503pub(crate) struct HttpRequestPool {
504    inner: RefCell<Vec<Rc<HttpRequestInner>>>,
505    cap: usize,
506}
507
508impl Default for HttpRequestPool {
509    fn default() -> Self {
510        Self::with_capacity(128)
511    }
512}
513
514impl HttpRequestPool {
515    pub(crate) fn with_capacity(cap: usize) -> Self {
516        HttpRequestPool {
517            inner: RefCell::new(Vec::with_capacity(cap)),
518            cap,
519        }
520    }
521
522    /// Re-use a previously allocated (but now completed/discarded) HttpRequest object.
523    #[inline]
524    pub(crate) fn pop(&self) -> Option<HttpRequest> {
525        self.inner
526            .borrow_mut()
527            .pop()
528            .map(|inner| HttpRequest { inner })
529    }
530
531    /// Check if the pool still has capacity for request storage.
532    #[inline]
533    pub(crate) fn is_available(&self) -> bool {
534        self.inner.borrow_mut().len() < self.cap
535    }
536
537    /// Push a request to pool.
538    #[inline]
539    pub(crate) fn push(&self, req: Rc<HttpRequestInner>) {
540        self.inner.borrow_mut().push(req);
541    }
542
543    /// Clears all allocated HttpRequest objects.
544    pub(crate) fn clear(&self) {
545        self.inner.borrow_mut().clear()
546    }
547}
548
549#[cfg(test)]
550mod tests {
551    use bytes::Bytes;
552
553    use super::*;
554    use crate::{
555        dev::{ResourceDef, Service},
556        http::{header, StatusCode},
557        test::{self, call_service, init_service, read_body, TestRequest},
558        web, App, HttpResponse,
559    };
560
561    #[test]
562    fn test_debug() {
563        let req = TestRequest::default()
564            .insert_header(("content-type", "text/plain"))
565            .to_http_request();
566        let dbg = format!("{:?}", req);
567        assert!(dbg.contains("HttpRequest"));
568    }
569
570    #[test]
571    #[cfg(feature = "cookies")]
572    fn test_no_request_cookies() {
573        let req = TestRequest::default().to_http_request();
574        assert!(req.cookies().unwrap().is_empty());
575    }
576
577    #[test]
578    #[cfg(feature = "cookies")]
579    fn test_request_cookies() {
580        let req = TestRequest::default()
581            .append_header((header::COOKIE, "cookie1=value1"))
582            .append_header((header::COOKIE, "cookie2=value2"))
583            .to_http_request();
584        {
585            let cookies = req.cookies().unwrap();
586            assert_eq!(cookies.len(), 2);
587            assert_eq!(cookies[0].name(), "cookie1");
588            assert_eq!(cookies[0].value(), "value1");
589            assert_eq!(cookies[1].name(), "cookie2");
590            assert_eq!(cookies[1].value(), "value2");
591        }
592
593        let cookie = req.cookie("cookie1");
594        assert!(cookie.is_some());
595        let cookie = cookie.unwrap();
596        assert_eq!(cookie.name(), "cookie1");
597        assert_eq!(cookie.value(), "value1");
598
599        let cookie = req.cookie("cookie-unknown");
600        assert!(cookie.is_none());
601    }
602
603    #[test]
604    fn test_request_query() {
605        let req = TestRequest::with_uri("/?id=test").to_http_request();
606        assert_eq!(req.query_string(), "id=test");
607    }
608
609    #[test]
610    fn test_url_for() {
611        let mut res = ResourceDef::new("/user/{name}.{ext}");
612        res.set_name("index");
613
614        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
615        rmap.add(&mut res, None);
616        assert!(rmap.has_resource("/user/test.html"));
617        assert!(!rmap.has_resource("/test/unknown"));
618
619        let req = TestRequest::default()
620            .insert_header((header::HOST, "www.rust-lang.org"))
621            .rmap(rmap)
622            .to_http_request();
623
624        assert_eq!(
625            req.url_for("unknown", ["test"]),
626            Err(UrlGenerationError::ResourceNotFound)
627        );
628        assert_eq!(
629            req.url_for("index", ["test"]),
630            Err(UrlGenerationError::NotEnoughElements)
631        );
632        let url = req.url_for("index", ["test", "html"]);
633        assert_eq!(
634            url.ok().unwrap().as_str(),
635            "http://www.rust-lang.org/user/test.html"
636        );
637    }
638
639    #[test]
640    fn test_url_for_static() {
641        let mut rdef = ResourceDef::new("/index.html");
642        rdef.set_name("index");
643
644        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
645        rmap.add(&mut rdef, None);
646
647        assert!(rmap.has_resource("/index.html"));
648
649        let req = TestRequest::with_uri("/test")
650            .insert_header((header::HOST, "www.rust-lang.org"))
651            .rmap(rmap)
652            .to_http_request();
653        let url = req.url_for_static("index");
654        assert_eq!(
655            url.ok().unwrap().as_str(),
656            "http://www.rust-lang.org/index.html"
657        );
658    }
659
660    #[test]
661    fn test_match_name() {
662        let mut rdef = ResourceDef::new("/index.html");
663        rdef.set_name("index");
664
665        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
666        rmap.add(&mut rdef, None);
667
668        assert!(rmap.has_resource("/index.html"));
669
670        let req = TestRequest::default()
671            .uri("/index.html")
672            .rmap(rmap)
673            .to_http_request();
674
675        assert_eq!(req.match_name(), Some("index"));
676    }
677
678    #[test]
679    fn test_url_for_external() {
680        let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");
681
682        rdef.set_name("youtube");
683
684        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
685        rmap.add(&mut rdef, None);
686
687        let req = TestRequest::default().rmap(rmap).to_http_request();
688        let url = req.url_for("youtube", ["oHg5SJYRHA0"]);
689        assert_eq!(
690            url.ok().unwrap().as_str(),
691            "https://youtube.com/watch/oHg5SJYRHA0"
692        );
693    }
694
695    #[actix_rt::test]
696    async fn test_drop_http_request_pool() {
697        let srv = init_service(
698            App::new().service(web::resource("/").to(|req: HttpRequest| {
699                HttpResponse::Ok()
700                    .insert_header(("pool_cap", req.app_state().pool().cap))
701                    .finish()
702            })),
703        )
704        .await;
705
706        let req = TestRequest::default().to_request();
707        let resp = call_service(&srv, req).await;
708
709        drop(srv);
710
711        assert_eq!(resp.headers().get("pool_cap").unwrap(), "128");
712    }
713
714    #[actix_rt::test]
715    async fn test_data() {
716        let srv = init_service(App::new().app_data(10usize).service(web::resource("/").to(
717            |req: HttpRequest| {
718                if req.app_data::<usize>().is_some() {
719                    HttpResponse::Ok()
720                } else {
721                    HttpResponse::BadRequest()
722                }
723            },
724        )))
725        .await;
726
727        let req = TestRequest::default().to_request();
728        let resp = call_service(&srv, req).await;
729        assert_eq!(resp.status(), StatusCode::OK);
730
731        let srv = init_service(App::new().app_data(10u32).service(web::resource("/").to(
732            |req: HttpRequest| {
733                if req.app_data::<usize>().is_some() {
734                    HttpResponse::Ok()
735                } else {
736                    HttpResponse::BadRequest()
737                }
738            },
739        )))
740        .await;
741
742        let req = TestRequest::default().to_request();
743        let resp = call_service(&srv, req).await;
744        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
745    }
746
747    #[actix_rt::test]
748    async fn test_cascading_data() {
749        #[allow(dead_code)]
750        fn echo_usize(req: HttpRequest) -> HttpResponse {
751            let num = req.app_data::<usize>().unwrap();
752            HttpResponse::Ok().body(num.to_string())
753        }
754
755        let srv = init_service(
756            App::new()
757                .app_data(88usize)
758                .service(web::resource("/").route(web::get().to(echo_usize)))
759                .service(
760                    web::resource("/one")
761                        .app_data(1u32)
762                        .route(web::get().to(echo_usize)),
763                ),
764        )
765        .await;
766
767        let req = TestRequest::get().uri("/").to_request();
768        let resp = srv.call(req).await.unwrap();
769        let body = read_body(resp).await;
770        assert_eq!(body, Bytes::from_static(b"88"));
771
772        let req = TestRequest::get().uri("/one").to_request();
773        let resp = srv.call(req).await.unwrap();
774        let body = read_body(resp).await;
775        assert_eq!(body, Bytes::from_static(b"88"));
776    }
777
778    #[actix_rt::test]
779    async fn test_overwrite_data() {
780        #[allow(dead_code)]
781        fn echo_usize(req: HttpRequest) -> HttpResponse {
782            let num = req.app_data::<usize>().unwrap();
783            HttpResponse::Ok().body(num.to_string())
784        }
785
786        let srv = init_service(
787            App::new()
788                .app_data(88usize)
789                .service(web::resource("/").route(web::get().to(echo_usize)))
790                .service(
791                    web::resource("/one")
792                        .app_data(1usize)
793                        .route(web::get().to(echo_usize)),
794                ),
795        )
796        .await;
797
798        let req = TestRequest::get().uri("/").to_request();
799        let resp = srv.call(req).await.unwrap();
800        let body = read_body(resp).await;
801        assert_eq!(body, Bytes::from_static(b"88"));
802
803        let req = TestRequest::get().uri("/one").to_request();
804        let resp = srv.call(req).await.unwrap();
805        let body = read_body(resp).await;
806        assert_eq!(body, Bytes::from_static(b"1"));
807    }
808
809    #[actix_rt::test]
810    async fn test_app_data_dropped() {
811        struct Tracker {
812            pub dropped: bool,
813        }
814        struct Foo {
815            tracker: Rc<RefCell<Tracker>>,
816        }
817        impl Drop for Foo {
818            fn drop(&mut self) {
819                self.tracker.borrow_mut().dropped = true;
820            }
821        }
822
823        let tracker = Rc::new(RefCell::new(Tracker { dropped: false }));
824        {
825            let tracker2 = Rc::clone(&tracker);
826            let srv = init_service(App::new().service(web::resource("/").to(
827                move |req: HttpRequest| {
828                    req.extensions_mut().insert(Foo {
829                        tracker: Rc::clone(&tracker2),
830                    });
831                    HttpResponse::Ok()
832                },
833            )))
834            .await;
835
836            let req = TestRequest::default().to_request();
837            let resp = call_service(&srv, req).await;
838            assert_eq!(resp.status(), StatusCode::OK);
839        }
840
841        assert!(tracker.borrow().dropped);
842    }
843
844    #[actix_rt::test]
845    async fn extract_path_pattern() {
846        let srv = init_service(
847            App::new().service(
848                web::scope("/user/{id}")
849                    .service(web::resource("/profile").route(web::get().to(
850                        move |req: HttpRequest| {
851                            assert_eq!(req.match_pattern(), Some("/user/{id}/profile".to_owned()));
852
853                            HttpResponse::Ok().finish()
854                        },
855                    )))
856                    .default_service(web::to(move |req: HttpRequest| {
857                        assert!(req.match_pattern().is_none());
858                        HttpResponse::Ok().finish()
859                    })),
860            ),
861        )
862        .await;
863
864        let req = TestRequest::get().uri("/user/22/profile").to_request();
865        let res = call_service(&srv, req).await;
866        assert_eq!(res.status(), StatusCode::OK);
867
868        let req = TestRequest::get().uri("/user/22/not-exist").to_request();
869        let res = call_service(&srv, req).await;
870        assert_eq!(res.status(), StatusCode::OK);
871    }
872
873    #[actix_rt::test]
874    async fn extract_path_pattern_complex() {
875        let srv = init_service(
876            App::new()
877                .service(web::scope("/user").service(web::scope("/{id}").service(
878                    web::resource("").to(move |req: HttpRequest| {
879                        assert_eq!(req.match_pattern(), Some("/user/{id}".to_owned()));
880
881                        HttpResponse::Ok().finish()
882                    }),
883                )))
884                .service(web::resource("/").to(move |req: HttpRequest| {
885                    assert_eq!(req.match_pattern(), Some("/".to_owned()));
886
887                    HttpResponse::Ok().finish()
888                }))
889                .default_service(web::to(move |req: HttpRequest| {
890                    assert!(req.match_pattern().is_none());
891                    HttpResponse::Ok().finish()
892                })),
893        )
894        .await;
895
896        let req = TestRequest::get().uri("/user/test").to_request();
897        let res = call_service(&srv, req).await;
898        assert_eq!(res.status(), StatusCode::OK);
899
900        let req = TestRequest::get().uri("/").to_request();
901        let res = call_service(&srv, req).await;
902        assert_eq!(res.status(), StatusCode::OK);
903
904        let req = TestRequest::get().uri("/not-exist").to_request();
905        let res = call_service(&srv, req).await;
906        assert_eq!(res.status(), StatusCode::OK);
907    }
908
909    #[actix_rt::test]
910    async fn url_for_closest_named_resource() {
911        // we mount the route named 'nested' on 2 different scopes, 'a' and 'b'
912        let srv = test::init_service(
913            App::new()
914                .service(
915                    web::scope("/foo")
916                        .service(web::resource("/nested").name("nested").route(web::get().to(
917                            |req: HttpRequest| {
918                                HttpResponse::Ok()
919                                    .body(format!("{}", req.url_for_static("nested").unwrap()))
920                            },
921                        )))
922                        .service(web::scope("/baz").service(web::resource("deep")))
923                        .service(web::resource("{foo_param}")),
924                )
925                .service(web::scope("/bar").service(
926                    web::resource("/nested").name("nested").route(web::get().to(
927                        |req: HttpRequest| {
928                            HttpResponse::Ok()
929                                .body(format!("{}", req.url_for_static("nested").unwrap()))
930                        },
931                    )),
932                )),
933        )
934        .await;
935
936        let foo_resp =
937            test::call_service(&srv, TestRequest::with_uri("/foo/nested").to_request()).await;
938        assert_eq!(foo_resp.status(), StatusCode::OK);
939        let body = read_body(foo_resp).await;
940        // `body` equals http://localhost:8080/bar/nested
941        // because nested from /bar overrides /foo's
942        // to do this any other way would require something like a custom tree search
943        // see https://github.com/actix/actix-web/issues/1763
944        assert_eq!(body, "http://localhost:8080/bar/nested");
945
946        let bar_resp =
947            test::call_service(&srv, TestRequest::with_uri("/bar/nested").to_request()).await;
948        assert_eq!(bar_resp.status(), StatusCode::OK);
949        let body = read_body(bar_resp).await;
950        assert_eq!(body, "http://localhost:8080/bar/nested");
951    }
952
953    #[test]
954    fn authorization_header_hidden_in_debug() {
955        let authorization_header = "Basic bXkgdXNlcm5hbWU6bXkgcGFzc3dvcmQK";
956        let req = TestRequest::get()
957            .insert_header((crate::http::header::AUTHORIZATION, authorization_header))
958            .to_http_request();
959
960        assert!(!format!("{:?}", req).contains(authorization_header));
961    }
962
963    #[test]
964    fn proxy_authorization_header_hidden_in_debug() {
965        let proxy_authorization_header = "secret value";
966        let req = TestRequest::get()
967            .insert_header((
968                crate::http::header::PROXY_AUTHORIZATION,
969                proxy_authorization_header,
970            ))
971            .to_http_request();
972
973        assert!(!format!("{:?}", req).contains(proxy_authorization_header));
974    }
975
976    #[test]
977    fn cookie_header_hidden_in_debug() {
978        let cookie_header = "secret";
979        let req = TestRequest::get()
980            .insert_header((crate::http::header::COOKIE, cookie_header))
981            .to_http_request();
982
983        assert!(!format!("{:?}", req).contains(cookie_header));
984    }
985
986    #[test]
987    fn other_header_visible_in_debug() {
988        let location_header = "192.0.0.1";
989        let req = TestRequest::get()
990            .insert_header((crate::http::header::LOCATION, location_header))
991            .to_http_request();
992
993        assert!(format!("{:?}", req).contains(location_header));
994    }
995
996    #[test]
997    fn check_full_url() {
998        let req = TestRequest::with_uri("/api?id=4&name=foo").to_http_request();
999        assert_eq!(
1000            req.full_url().as_str(),
1001            "http://localhost:8080/api?id=4&name=foo",
1002        );
1003
1004        let req = TestRequest::with_uri("https://example.com/api?id=4&name=foo").to_http_request();
1005        assert_eq!(
1006            req.full_url().as_str(),
1007            "https://example.com/api?id=4&name=foo",
1008        );
1009
1010        let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
1011            .insert_header(("host", "example.com"))
1012            .to_http_request();
1013        assert_eq!(
1014            req.full_url().as_str(),
1015            "http://example.com/api?id=4&name=foo",
1016        );
1017    }
1018}