Skip to main content

actix_web/
request.rs

1use std::{
2    cell::{Cell, Ref, RefCell, RefMut},
3    collections::HashMap,
4    fmt,
5    hash::{BuildHasher, Hash},
6    net,
7    rc::Rc,
8    str,
9};
10
11use actix_http::{Message, RequestHead};
12use actix_router::{Path, Url};
13#[expect(deprecated, reason = "preserve the public ready future type")]
14use actix_utils::future::{ready, Ready};
15#[cfg(feature = "cookies")]
16use cookie::{Cookie, ParseError as CookieParseError};
17use smallvec::SmallVec;
18
19use crate::{
20    app_service::AppInitServiceState,
21    config::AppConfig,
22    dev::{Extensions, Payload},
23    error::UrlGenerationError,
24    http::{header::HeaderMap, Method, Uri, Version},
25    info::ConnectionInfo,
26    rmap::ResourceMap,
27    Error, FromRequest, HttpMessage,
28};
29
30#[cfg(feature = "cookies")]
31struct Cookies(Vec<Cookie<'static>>);
32
33#[cfg(feature = "cookies")]
34struct RawCookies(Vec<Cookie<'static>>);
35
36/// An incoming request.
37#[derive(Clone)]
38pub struct HttpRequest {
39    /// # Invariant
40    /// `Rc<HttpRequestInner>` is used exclusively and NO `Weak<HttpRequestInner>`
41    /// is allowed anywhere in the code. Weak pointer is purposely ignored when
42    /// doing `Rc`'s ref counter check. Expect panics if this invariant is violated.
43    pub(crate) inner: Rc<HttpRequestInner>,
44}
45
46pub(crate) struct HttpRequestInner {
47    pub(crate) head: Message<RequestHead>,
48    pub(crate) path: Path<Url>,
49    pub(crate) resource_path: SmallVec<[u16; 4]>,
50    pub(crate) resource_path_matched: bool,
51    pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,
52    pub(crate) conn_data: Option<Rc<Extensions>>,
53    pub(crate) extensions: Rc<RefCell<Extensions>>,
54    app_state: Rc<AppInitServiceState>,
55}
56
57impl HttpRequest {
58    #[inline]
59    pub(crate) fn new(
60        path: Path<Url>,
61        head: Message<RequestHead>,
62        app_state: Rc<AppInitServiceState>,
63        app_data: Rc<Extensions>,
64        conn_data: Option<Rc<Extensions>>,
65        extensions: Rc<RefCell<Extensions>>,
66    ) -> HttpRequest {
67        let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();
68        data.push(app_data);
69
70        HttpRequest {
71            inner: Rc::new(HttpRequestInner {
72                head,
73                path,
74                resource_path: SmallVec::new(),
75                resource_path_matched: false,
76                app_state,
77                app_data: data,
78                conn_data,
79                extensions,
80            }),
81        }
82    }
83}
84
85impl HttpRequest {
86    /// This method returns reference to the request head
87    #[inline]
88    pub fn head(&self) -> &RequestHead {
89        &self.inner.head
90    }
91
92    /// This method returns mutable reference to the request head.
93    ///
94    /// # Panics
95    ///
96    /// Panics if multiple references of HTTP request exists.
97    #[inline]
98    pub(crate) fn head_mut(&mut self) -> &mut RequestHead {
99        &mut Rc::get_mut(&mut self.inner).unwrap().head
100    }
101
102    /// Request's uri.
103    #[inline]
104    pub fn uri(&self) -> &Uri {
105        &self.head().uri
106    }
107
108    /// Returns request's original full URL.
109    ///
110    /// Reconstructed URL is best-effort, using [`connection_info`](HttpRequest::connection_info())
111    /// to get forwarded scheme & host.
112    ///
113    /// # Panics
114    ///
115    /// Panics when the reconstructed URL cannot be parsed, such as when the host is malformed.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use actix_web::test::TestRequest;
121    /// let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
122    ///     .insert_header(("host", "example.com"))
123    ///     .to_http_request();
124    ///
125    /// assert_eq!(
126    ///     req.full_url().as_str(),
127    ///     "http://example.com/api?id=4&name=foo",
128    /// );
129    /// ```
130    pub fn full_url(&self) -> url::Url {
131        let info = self.connection_info();
132        let scheme = info.scheme();
133        let host = info.host();
134        let path_and_query = self
135            .uri()
136            .path_and_query()
137            .map(|paq| paq.as_str())
138            .unwrap_or("/");
139
140        url::Url::parse(&format!("{scheme}://{host}{path_and_query}")).unwrap()
141    }
142
143    /// Read the Request method.
144    #[inline]
145    pub fn method(&self) -> &Method {
146        &self.head().method
147    }
148
149    /// Read the Request Version.
150    #[inline]
151    pub fn version(&self) -> Version {
152        self.head().version
153    }
154
155    #[inline]
156    /// Returns request's headers.
157    pub fn headers(&self) -> &HeaderMap {
158        &self.head().headers
159    }
160
161    /// The target path of this request.
162    #[inline]
163    pub fn path(&self) -> &str {
164        self.head().uri.path()
165    }
166
167    /// The query string in the URL.
168    ///
169    /// Example: `id=10`
170    #[inline]
171    pub fn query_string(&self) -> &str {
172        self.uri().query().unwrap_or_default()
173    }
174
175    /// Returns a reference to the URL parameters container.
176    ///
177    /// A URL parameter is specified in the form `{identifier}`, where the identifier can be used
178    /// later in a request handler to access the matched value for that parameter.
179    ///
180    /// # Percent Encoding and URL Parameters
181    /// Because each URL parameter is able to capture multiple path segments, none of
182    /// `["%2F", "%25", "%2B"]` found in the request URI are decoded into `["/", "%", "+"]` in order
183    /// to preserve path integrity. If a URL parameter is expected to contain these characters, then
184    /// it is on the user to decode them or use the [`web::Path`](crate::web::Path) extractor which
185    /// _will_ decode these special sequences.
186    #[inline]
187    pub fn match_info(&self) -> &Path<Url> {
188        &self.inner.path
189    }
190
191    /// Returns a mutable reference to the URL parameters container.
192    ///
193    /// # Panics
194    /// Panics if this `HttpRequest` has been cloned.
195    #[inline]
196    pub(crate) fn match_info_mut(&mut self) -> &mut Path<Url> {
197        &mut Rc::get_mut(&mut self.inner).unwrap().path
198    }
199
200    #[inline]
201    pub(crate) fn push_resource_id(&mut self, id: u16) {
202        Rc::get_mut(&mut self.inner).unwrap().resource_path.push(id);
203    }
204
205    #[inline]
206    pub(crate) fn mark_resource_path(&mut self, is_matched: bool) {
207        Rc::get_mut(&mut self.inner).unwrap().resource_path_matched = is_matched;
208    }
209
210    #[inline]
211    pub(crate) fn resource_path(&self) -> &[u16] {
212        &self.inner.resource_path
213    }
214
215    #[inline]
216    pub(crate) fn is_resource_path_matched(&self) -> bool {
217        self.inner.resource_path_matched
218    }
219
220    /// The resource definition pattern that matched the path. Useful for logging and metrics.
221    ///
222    /// For example, when a resource with pattern `/user/{id}/profile` is defined and a call is made
223    /// to `/user/123/profile` this function would return `Some("/user/{id}/profile")`.
224    ///
225    /// Returns a None when no resource is fully matched, including default services.
226    #[inline]
227    pub fn match_pattern(&self) -> Option<String> {
228        if self.is_resource_path_matched() {
229            if let Some(pattern) = self
230                .resource_map()
231                .match_pattern_by_resource_path(self.resource_path())
232            {
233                return Some(pattern);
234            }
235        }
236
237        self.resource_map().match_pattern(self.path())
238    }
239
240    /// The resource name that matched the path. Useful for logging and metrics.
241    ///
242    /// Returns a None when no resource is fully matched, including default services.
243    #[inline]
244    pub fn match_name(&self) -> Option<&str> {
245        if self.is_resource_path_matched() {
246            if let Some(name) = self
247                .resource_map()
248                .match_name_by_resource_path(self.resource_path())
249            {
250                return Some(name);
251            }
252        }
253
254        self.resource_map().match_name(self.path())
255    }
256
257    /// Returns a reference a piece of connection data set in an [on-connect] callback.
258    ///
259    /// ```ignore
260    /// let opt_t = req.conn_data::<PeerCertificate>();
261    /// ```
262    ///
263    /// [on-connect]: crate::HttpServer::on_connect
264    pub fn conn_data<T: 'static>(&self) -> Option<&T> {
265        self.inner
266            .conn_data
267            .as_deref()
268            .and_then(|container| container.get::<T>())
269    }
270
271    /// Generates URL for a named resource.
272    ///
273    /// This substitutes in sequence all URL parameters that appear in the resource itself and in
274    /// parent [scopes](crate::web::scope), if any.
275    ///
276    /// It is worth noting that the characters `['/', '%']` are not escaped and therefore a single
277    /// URL parameter may expand into multiple path segments and `elements` can be percent-encoded
278    /// beforehand without worrying about double encoding. Any other character that is not valid in
279    /// a URL path context is escaped using percent-encoding.
280    ///
281    /// # Examples
282    /// ```
283    /// # use actix_web::{web, App, HttpRequest, HttpResponse};
284    /// fn index(req: HttpRequest) -> HttpResponse {
285    ///     let url = req.url_for("foo", &["1", "2", "3"]); // <- generate URL for "foo" resource
286    ///     HttpResponse::Ok().into()
287    /// }
288    ///
289    /// let app = App::new()
290    ///     .service(web::resource("/test/{one}/{two}/{three}")
291    ///          .name("foo")  // <- set resource name so it can be used in `url_for`
292    ///          .route(web::get().to(|| HttpResponse::Ok()))
293    ///     );
294    /// ```
295    pub fn url_for<U, I>(&self, name: &str, elements: U) -> Result<url::Url, UrlGenerationError>
296    where
297        U: IntoIterator<Item = I>,
298        I: AsRef<str>,
299    {
300        self.resource_map().url_for(self, name, elements)
301    }
302
303    /// Generates URL for a named resource using a map of dynamic segment values.
304    ///
305    /// This substitutes URL parameters by name from `elements`, including parameters from parent
306    /// scopes.
307    ///
308    /// # Examples
309    /// ```
310    /// # use std::collections::HashMap;
311    /// # use actix_web::{web, App, HttpRequest, HttpResponse};
312    /// fn index(req: HttpRequest) -> HttpResponse {
313    ///     let mut params = HashMap::new();
314    ///     params.insert("one", "1");
315    ///     params.insert("two", "2");
316    ///     let url = req.url_for_map("foo", &params); // <- generate URL for "foo" resource
317    ///     HttpResponse::Ok().into()
318    /// }
319    ///
320    /// let app = App::new()
321    ///     .service(web::resource("/test/{one}/{two}")
322    ///          .name("foo")  // <- set resource name so it can be used in `url_for_map`
323    ///          .route(web::get().to(|| HttpResponse::Ok()))
324    ///     );
325    /// ```
326    pub fn url_for_map<K, V, S>(
327        &self,
328        name: &str,
329        elements: &HashMap<K, V, S>,
330    ) -> Result<url::Url, UrlGenerationError>
331    where
332        K: std::borrow::Borrow<str> + Eq + Hash,
333        V: AsRef<str>,
334        S: BuildHasher,
335    {
336        self.resource_map().url_for_map(self, name, elements)
337    }
338
339    /// Generates URL for a named resource using an iterator of key-value pairs.
340    ///
341    /// This is a convenience wrapper around [`HttpRequest::url_for_map`].
342    ///
343    /// Note: passing a borrowed map (e.g. `&HashMap<String, String>`) directly does not satisfy the
344    /// trait bounds because the iterator yields `(&String, &String)`. Prefer `url_for_map` for
345    /// borrowed maps, or map entries to `&str`:
346    ///
347    /// ```
348    /// # use std::collections::HashMap;
349    /// # use actix_web::{web, App, HttpRequest, HttpResponse};
350    /// fn index(req: HttpRequest) -> HttpResponse {
351    ///     let mut params = HashMap::new();
352    ///     params.insert("one".to_string(), "1".to_string());
353    ///     params.insert("two".to_string(), "2".to_string());
354    ///
355    ///     let iter = params.iter().map(|(k, v)| (k.as_str(), v.as_str()));
356    ///     let url = req.url_for_iter("foo", iter);
357    ///     HttpResponse::Ok().into()
358    /// }
359    /// ```
360    pub fn url_for_iter<K, V, I>(
361        &self,
362        name: &str,
363        elements: I,
364    ) -> Result<url::Url, UrlGenerationError>
365    where
366        I: IntoIterator<Item = (K, V)>,
367        K: std::borrow::Borrow<str> + Eq + Hash,
368        V: AsRef<str>,
369    {
370        self.resource_map().url_for_iter(self, name, elements)
371    }
372
373    /// Generate URL for named resource
374    ///
375    /// This method is similar to `HttpRequest::url_for()` but it can be used
376    /// for urls that do not contain variable parts.
377    pub fn url_for_static(&self, name: &str) -> Result<url::Url, UrlGenerationError> {
378        const NO_PARAMS: [&str; 0] = [];
379        self.url_for(name, NO_PARAMS)
380    }
381
382    /// Get a reference to a `ResourceMap` of current application.
383    #[inline]
384    pub fn resource_map(&self) -> &ResourceMap {
385        self.app_state().rmap()
386    }
387
388    /// Returns peer socket address.
389    ///
390    /// Peer address is the directly connected peer's socket address. If a proxy is used in front of
391    /// the Actix Web server, then it would be address of this proxy.
392    ///
393    /// For expanded client connection information, use [`connection_info`] instead.
394    ///
395    /// Will only return `None` when server is listening on [UDS socket] or when called in unit
396    /// tests unless [`TestRequest::peer_addr`] is used.
397    ///
398    /// [UDS socket]: crate::HttpServer::bind_uds
399    /// [`TestRequest::peer_addr`]: crate::test::TestRequest::peer_addr
400    /// [`connection_info`]: Self::connection_info
401    #[inline]
402    pub fn peer_addr(&self) -> Option<net::SocketAddr> {
403        self.head().peer_addr
404    }
405
406    /// Returns connection info for the current request.
407    ///
408    /// The return type, [`ConnectionInfo`], can also be used as an extractor.
409    ///
410    /// # Panics
411    /// Panics if request's extensions container is already borrowed.
412    #[inline]
413    pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
414        if !self.extensions().contains::<ConnectionInfo>() {
415            let info = ConnectionInfo::new(self.head(), self.app_config());
416            self.extensions_mut().insert(info);
417        }
418
419        Ref::map(self.extensions(), |data| data.get().unwrap())
420    }
421
422    /// Returns a reference to the application's connection configuration.
423    #[inline]
424    pub fn app_config(&self) -> &AppConfig {
425        self.app_state().config()
426    }
427
428    /// Retrieves a piece of application state.
429    ///
430    /// Extracts any object stored with [`App::app_data()`](crate::App::app_data) (or the
431    /// counterpart methods on [`Scope`](crate::Scope::app_data) and
432    /// [`Resource`](crate::Resource::app_data)) during application configuration.
433    ///
434    /// Since the Actix Web router layers application data, the returned object will reference the
435    /// "closest" instance of the type. For example, if an `App` stores a `u32`, a nested `Scope`
436    /// also stores a `u32`, and the delegated request handler falls within that `Scope`, then
437    /// calling `.app_data::<u32>()` on an `HttpRequest` within that handler will return the
438    /// `Scope`'s instance. However, using the same router set up and a request that does not get
439    /// captured by the `Scope`, `.app_data::<u32>()` would return the `App`'s instance.
440    ///
441    /// If the state was stored using the [`Data`] wrapper, then it must also be retrieved using
442    /// this same type.
443    ///
444    /// See also the [`Data`] extractor.
445    ///
446    /// # Examples
447    /// ```no_run
448    /// # use actix_web::{test::TestRequest, web::Data};
449    /// # let req = TestRequest::default().to_http_request();
450    /// # type T = u32;
451    /// let opt_t: Option<&Data<T>> = req.app_data::<Data<T>>();
452    /// ```
453    ///
454    /// [`Data`]: crate::web::Data
455    #[doc(alias = "state")]
456    pub fn app_data<T: 'static>(&self) -> Option<&T> {
457        for container in self.inner.app_data.iter().rev() {
458            if let Some(data) = container.get::<T>() {
459                return Some(data);
460            }
461        }
462
463        None
464    }
465
466    #[inline]
467    fn app_state(&self) -> &AppInitServiceState {
468        &self.inner.app_state
469    }
470
471    /// Load request cookies.
472    ///
473    /// The names and values of cookies are percent-decoded.
474    ///
475    /// Any cookie that cannot be parsed is omitted from the result.
476    /// This includes cookies with an empty name (e.g. `document.cookie = "=value"`).
477    #[cfg(feature = "cookies")]
478    pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
479        use actix_http::header::COOKIE;
480
481        if self.extensions().get::<Cookies>().is_none() {
482            let mut cookies = Vec::new();
483            for hdr in self.headers().get_all(COOKIE) {
484                let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
485                for cookie_str in s.split(';').map(|s| s.trim()).filter(|s| !s.is_empty()) {
486                    if let Ok(cookie) = Cookie::parse_encoded(cookie_str) {
487                        cookies.push(cookie.into_owned());
488                    }
489                }
490            }
491            self.extensions_mut().insert(Cookies(cookies));
492        }
493
494        Ok(Ref::map(self.extensions(), |ext| {
495            &ext.get::<Cookies>().unwrap().0
496        }))
497    }
498
499    /// Load request cookies **without** percent-decoding their names and values.
500    ///
501    /// Any cookie that cannot be parsed is omitted from the result.
502    /// This includes cookies with an empty name (e.g. `document.cookie = "=value"`).
503    #[cfg(feature = "cookies")]
504    pub fn cookies_raw(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
505        use actix_http::header::COOKIE;
506
507        if self.extensions().get::<RawCookies>().is_none() {
508            let mut cookies = Vec::new();
509            for hdr in self.headers().get_all(COOKIE) {
510                let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
511                for cookie_str in s.split(';').map(|s| s.trim()).filter(|s| !s.is_empty()) {
512                    if let Ok(cookie) = Cookie::parse(cookie_str) {
513                        cookies.push(cookie.into_owned());
514                    }
515                }
516            }
517            self.extensions_mut().insert(RawCookies(cookies));
518        }
519
520        Ok(Ref::map(self.extensions(), |ext| {
521            &ext.get::<RawCookies>().unwrap().0
522        }))
523    }
524
525    /// Return request cookie.
526    #[cfg(feature = "cookies")]
527    pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
528        if let Ok(cookies) = self.cookies() {
529            return cookies.iter().find(|cookie| cookie.name() == name).cloned();
530        }
531
532        None
533    }
534
535    /// Return request cookie **without** percent-decoding its name and value.
536    #[cfg(feature = "cookies")]
537    pub fn cookie_raw(&self, name: &str) -> Option<Cookie<'static>> {
538        if let Ok(cookies) = self.cookies_raw() {
539            return cookies.iter().find(|cookie| cookie.name() == name).cloned();
540        }
541
542        None
543    }
544}
545
546impl HttpMessage for HttpRequest {
547    type Stream = ();
548
549    #[inline]
550    fn headers(&self) -> &HeaderMap {
551        &self.head().headers
552    }
553
554    #[inline]
555    fn extensions(&self) -> Ref<'_, Extensions> {
556        self.inner.extensions.borrow()
557    }
558
559    #[inline]
560    fn extensions_mut(&self) -> RefMut<'_, Extensions> {
561        self.inner.extensions.borrow_mut()
562    }
563
564    #[inline]
565    fn take_payload(&mut self) -> Payload<Self::Stream> {
566        Payload::None
567    }
568}
569
570impl Drop for HttpRequest {
571    fn drop(&mut self) {
572        // if possible, contribute to current worker's HttpRequest allocation pool
573
574        // This relies on no weak references to inner existing anywhere within the codebase.
575        if let Some(inner) = Rc::get_mut(&mut self.inner) {
576            if inner.app_state.pool().is_available() {
577                // clear additional app_data and keep the root one for reuse.
578                inner.app_data.truncate(1);
579
580                // Inner is borrowed mut here and; get req data mutably to reduce borrow check. Also
581                // we know the req_data Rc will not have any clones at this point to unwrap is okay.
582                Rc::get_mut(&mut inner.extensions)
583                    .unwrap()
584                    .get_mut()
585                    .clear();
586
587                // We can't use the same trick as req data because the conn_data is held by the
588                // dispatcher, too.
589                inner.conn_data = None;
590
591                // a re-borrow of pool is necessary here.
592                let req = Rc::clone(&self.inner);
593                self.app_state().pool().push(req);
594            }
595        }
596    }
597}
598
599/// It is possible to get `HttpRequest` as an extractor handler parameter
600///
601/// # Examples
602/// ```
603/// use actix_web::{web, App, HttpRequest};
604/// use serde::Deserialize;
605///
606/// /// extract `Thing` from request
607/// async fn index(req: HttpRequest) -> String {
608///    format!("Got thing: {:?}", req)
609/// }
610///
611/// let app = App::new().service(
612///     web::resource("/users/{first}").route(
613///         web::get().to(index))
614/// );
615/// ```
616#[expect(deprecated, reason = "preserve the public ready future type")]
617impl FromRequest for HttpRequest {
618    type Error = Error;
619    type Future = Ready<Result<Self, Error>>;
620
621    #[inline]
622    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
623        ready(Ok(req.clone()))
624    }
625}
626
627impl fmt::Debug for HttpRequest {
628    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
629        writeln!(
630            f,
631            "\nHttpRequest {:?} {}:{}",
632            self.inner.head.version,
633            self.inner.head.method,
634            self.path()
635        )?;
636
637        if !self.query_string().is_empty() {
638            writeln!(f, "  query: ?{:?}", self.query_string())?;
639        }
640
641        if !self.match_info().is_empty() {
642            writeln!(f, "  params: {:?}", self.match_info())?;
643        }
644
645        writeln!(f, "  headers:")?;
646
647        for (key, val) in self.headers().iter() {
648            match key {
649                // redact sensitive header values from debug output
650                &crate::http::header::AUTHORIZATION
651                | &crate::http::header::PROXY_AUTHORIZATION
652                | &crate::http::header::COOKIE => writeln!(f, "    {:?}: {:?}", key, "*redacted*")?,
653
654                _ => writeln!(f, "    {:?}: {:?}", key, val)?,
655            }
656        }
657
658        Ok(())
659    }
660}
661
662/// Slab-allocated `HttpRequest` Pool
663///
664/// Since request processing may yield for asynchronous events to complete, a worker may have many
665/// requests in-flight at any time. Pooling requests like this amortizes the performance and memory
666/// costs of allocating and de-allocating HttpRequest objects as frequently as they otherwise would.
667///
668/// Request objects are added when they are dropped (see `<HttpRequest as Drop>::drop`) and re-used
669/// in `<AppInitService as Service>::call` when there are available objects in the list.
670///
671/// The pool's default capacity is 128 items.
672pub(crate) struct HttpRequestPool {
673    inner: RefCell<Vec<Rc<HttpRequestInner>>>,
674    enabled: Cell<bool>,
675    cap: usize,
676}
677
678impl Default for HttpRequestPool {
679    fn default() -> Self {
680        Self::with_capacity(128)
681    }
682}
683
684impl HttpRequestPool {
685    pub(crate) fn with_capacity(cap: usize) -> Self {
686        HttpRequestPool {
687            inner: RefCell::new(Vec::with_capacity(cap)),
688            enabled: Cell::new(true),
689            cap,
690        }
691    }
692
693    /// Re-use a previously allocated (but now completed/discarded) HttpRequest object.
694    #[inline]
695    pub(crate) fn pop(&self) -> Option<HttpRequest> {
696        self.inner
697            .borrow_mut()
698            .pop()
699            .map(|inner| HttpRequest { inner })
700    }
701
702    /// Check if the pool still has capacity for request storage.
703    #[inline]
704    pub(crate) fn is_available(&self) -> bool {
705        self.enabled.get() && self.inner.borrow().len() < self.cap
706    }
707
708    /// Push a request to pool.
709    #[inline]
710    pub(crate) fn push(&self, req: Rc<HttpRequestInner>) {
711        self.inner.borrow_mut().push(req);
712    }
713
714    /// Prevents future requests from being returned to the pool and clears existing entries.
715    pub(crate) fn disable(&self) {
716        self.enabled.set(false);
717        self.inner.borrow_mut().clear();
718    }
719}
720
721#[cfg(test)]
722mod tests {
723    use std::{collections::HashMap, sync::Arc};
724
725    use bytes::Bytes;
726
727    use super::*;
728    use crate::{
729        dev::{ResourceDef, Service},
730        guard,
731        http::{header, StatusCode},
732        test::{self, call_service, init_service, read_body, TestRequest},
733        web, App, HttpResponse,
734    };
735
736    #[test]
737    fn test_debug() {
738        let req = TestRequest::default()
739            .insert_header(("content-type", "text/plain"))
740            .to_http_request();
741        let dbg = format!("{:?}", req);
742        assert!(dbg.contains("HttpRequest"));
743    }
744
745    #[test]
746    #[cfg(feature = "cookies")]
747    fn test_no_request_cookies() {
748        let req = TestRequest::default().to_http_request();
749        assert!(req.cookies().unwrap().is_empty());
750    }
751
752    #[test]
753    #[cfg(feature = "cookies")]
754    fn test_request_cookies() {
755        let req = TestRequest::default()
756            .append_header((header::COOKIE, "cookie1=value1"))
757            .append_header((header::COOKIE, "cookie2=value2"))
758            .to_http_request();
759        {
760            let cookies = req.cookies().unwrap();
761            assert_eq!(cookies.len(), 2);
762            assert_eq!(cookies[0].name(), "cookie1");
763            assert_eq!(cookies[0].value(), "value1");
764            assert_eq!(cookies[1].name(), "cookie2");
765            assert_eq!(cookies[1].value(), "value2");
766        }
767
768        let cookie = req.cookie("cookie1");
769        assert!(cookie.is_some());
770        let cookie = cookie.unwrap();
771        assert_eq!(cookie.name(), "cookie1");
772        assert_eq!(cookie.value(), "value1");
773
774        let cookie = req.cookie("cookie-unknown");
775        assert!(cookie.is_none());
776    }
777
778    #[test]
779    #[cfg(feature = "cookies")]
780    fn test_request_cookies_raw() {
781        let req = TestRequest::default()
782            .append_header((header::COOKIE, "cookie1=hello%20world"))
783            .append_header((header::COOKIE, "cookie2=%db"))
784            .to_http_request();
785        {
786            let cookies = req.cookies_raw().unwrap();
787            assert_eq!(cookies.len(), 2);
788            assert_eq!(cookies[0].name(), "cookie1");
789            assert_eq!(cookies[0].value(), "hello%20world");
790            assert_eq!(cookies[1].name(), "cookie2");
791            assert_eq!(cookies[1].value(), "%db");
792        }
793
794        let cookie = req.cookie_raw("cookie1");
795        assert!(cookie.is_some());
796        let cookie = cookie.unwrap();
797        assert_eq!(cookie.name(), "cookie1");
798        assert_eq!(cookie.value(), "hello%20world");
799
800        let cookie = req.cookie_raw("cookie2");
801        assert!(cookie.is_some());
802        let cookie = cookie.unwrap();
803        assert_eq!(cookie.name(), "cookie2");
804        assert_eq!(cookie.value(), "%db");
805    }
806
807    #[test]
808    #[cfg(feature = "cookies")]
809    fn test_request_cookies_raw_is_independent_from_encoded_cookies() {
810        let req = TestRequest::default()
811            .append_header((header::COOKIE, "cookie=%20"))
812            .to_http_request();
813
814        let cookie = req.cookie("cookie").unwrap();
815        assert_eq!(cookie.value(), " ");
816
817        let raw_cookie = req.cookie_raw("cookie").unwrap();
818        assert_eq!(raw_cookie.value(), "%20");
819    }
820
821    #[test]
822    #[cfg(feature = "cookies")]
823    fn test_empty_key() {
824        let req = TestRequest::default()
825            .append_header((header::COOKIE, "cookie1=value1; value2; cookie3=value3"))
826            .to_http_request();
827        {
828            let cookies = req.cookies().unwrap();
829            assert_eq!(cookies.len(), 2);
830            assert_eq!(cookies[0].name(), "cookie1");
831            assert_eq!(cookies[0].value(), "value1");
832            assert_eq!(cookies[1].name(), "cookie3");
833            assert_eq!(cookies[1].value(), "value3");
834        }
835    }
836
837    #[test]
838    fn test_request_query() {
839        let req = TestRequest::with_uri("/?id=test").to_http_request();
840        assert_eq!(req.query_string(), "id=test");
841    }
842
843    #[test]
844    fn test_url_for() {
845        let mut res = ResourceDef::new("/user/{name}.{ext}");
846        res.set_name("index");
847
848        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
849        rmap.add(&mut res, None);
850        assert!(rmap.has_resource("/user/test.html"));
851        assert!(!rmap.has_resource("/test/unknown"));
852
853        let req = TestRequest::default()
854            .insert_header((header::HOST, "www.rust-lang.org"))
855            .rmap(rmap)
856            .to_http_request();
857
858        assert_eq!(
859            req.url_for("unknown", ["test"]),
860            Err(UrlGenerationError::ResourceNotFound)
861        );
862        assert_eq!(
863            req.url_for("index", ["test"]),
864            Err(UrlGenerationError::NotEnoughElements)
865        );
866        let url = req.url_for("index", ["test", "html"]);
867        assert_eq!(
868            url.ok().unwrap().as_str(),
869            "http://www.rust-lang.org/user/test.html"
870        );
871    }
872
873    #[test]
874    fn test_url_for_map() {
875        let mut res = ResourceDef::new("/user/{name}.{ext}");
876        res.set_name("index");
877
878        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
879        rmap.add(&mut res, None);
880
881        let req = TestRequest::default()
882            .insert_header((header::HOST, "www.actix.rs"))
883            .rmap(rmap)
884            .to_http_request();
885
886        let mut params = HashMap::new();
887        params.insert("name", "test");
888        params.insert("ext", "html");
889
890        let url = req.url_for_map("index", &params);
891        assert_eq!(
892            url.ok().unwrap().as_str(),
893            "http://www.actix.rs/user/test.html"
894        );
895
896        params.remove("ext");
897        assert_eq!(
898            req.url_for_map("index", &params),
899            Err(UrlGenerationError::NotEnoughElements)
900        );
901    }
902
903    #[test]
904    fn test_url_for_iter() {
905        let mut res = ResourceDef::new("/user/{name}.{ext}");
906        res.set_name("index");
907
908        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
909        rmap.add(&mut res, None);
910
911        let req = TestRequest::default()
912            .insert_header((header::HOST, "www.actix.rs"))
913            .rmap(rmap)
914            .to_http_request();
915
916        let url = req.url_for_iter("index", [("ext", "html"), ("name", "test")]);
917        assert_eq!(
918            url.ok().unwrap().as_str(),
919            "http://www.actix.rs/user/test.html"
920        );
921
922        let url = req.url_for_iter("index", [("name", "test")]);
923        assert_eq!(url, Err(UrlGenerationError::NotEnoughElements));
924    }
925
926    #[test]
927    fn test_url_for_static() {
928        let mut rdef = ResourceDef::new("/index.html");
929        rdef.set_name("index");
930
931        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
932        rmap.add(&mut rdef, None);
933
934        assert!(rmap.has_resource("/index.html"));
935
936        let req = TestRequest::with_uri("/test")
937            .insert_header((header::HOST, "www.rust-lang.org"))
938            .rmap(rmap)
939            .to_http_request();
940        let url = req.url_for_static("index");
941        assert_eq!(
942            url.ok().unwrap().as_str(),
943            "http://www.rust-lang.org/index.html"
944        );
945    }
946
947    #[test]
948    fn test_match_name() {
949        let mut rdef = ResourceDef::new("/index.html");
950        rdef.set_name("index");
951
952        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
953        rmap.add(&mut rdef, None);
954
955        assert!(rmap.has_resource("/index.html"));
956
957        let req = TestRequest::default()
958            .uri("/index.html")
959            .rmap(rmap)
960            .to_http_request();
961
962        assert_eq!(req.match_name(), Some("index"));
963    }
964
965    #[test]
966    fn test_url_for_external() {
967        let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");
968
969        rdef.set_name("youtube");
970
971        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
972        rmap.add(&mut rdef, None);
973
974        let req = TestRequest::default().rmap(rmap).to_http_request();
975        let url = req.url_for("youtube", ["oHg5SJYRHA0"]);
976        assert_eq!(
977            url.ok().unwrap().as_str(),
978            "https://youtube.com/watch/oHg5SJYRHA0"
979        );
980    }
981
982    #[actix_rt::test]
983    async fn test_drop_http_request_pool() {
984        let srv = init_service(
985            App::new().service(web::resource("/").to(|req: HttpRequest| {
986                HttpResponse::Ok()
987                    .insert_header(("pool_cap", req.app_state().pool().cap))
988                    .finish()
989            })),
990        )
991        .await;
992
993        let req = TestRequest::default().to_request();
994        let resp = call_service(&srv, req).await;
995
996        drop(srv);
997
998        assert_eq!(resp.headers().get("pool_cap").unwrap(), "128");
999    }
1000
1001    #[actix_rt::test]
1002    async fn test_request_dropped_after_service_does_not_reenter_pool() {
1003        struct State {
1004            _data: Arc<String>,
1005        }
1006
1007        let (weak_data, app_data) = {
1008            let data = Arc::new("data".to_owned());
1009            (Arc::downgrade(&data), web::Data::new(State { _data: data }))
1010        };
1011
1012        let held_req = Rc::new(RefCell::new(None));
1013
1014        {
1015            let held_req = Rc::clone(&held_req);
1016            let srv = init_service(App::new().app_data(app_data).service(web::resource("/").to(
1017                move |req: HttpRequest| {
1018                    *held_req.borrow_mut() = Some(req.clone());
1019                    HttpResponse::Ok()
1020                },
1021            )))
1022            .await;
1023
1024            let resp = call_service(&srv, TestRequest::default().to_request()).await;
1025            assert_eq!(resp.status(), StatusCode::OK);
1026
1027            drop(resp);
1028            drop(srv);
1029        }
1030
1031        assert!(weak_data.upgrade().is_some());
1032        drop(held_req.borrow_mut().take());
1033        assert!(weak_data.upgrade().is_none());
1034    }
1035
1036    #[actix_rt::test]
1037    async fn test_data() {
1038        let srv = init_service(App::new().app_data(10usize).service(web::resource("/").to(
1039            |req: HttpRequest| {
1040                if req.app_data::<usize>().is_some() {
1041                    HttpResponse::Ok()
1042                } else {
1043                    HttpResponse::BadRequest()
1044                }
1045            },
1046        )))
1047        .await;
1048
1049        let req = TestRequest::default().to_request();
1050        let resp = call_service(&srv, req).await;
1051        assert_eq!(resp.status(), StatusCode::OK);
1052
1053        let srv = init_service(App::new().app_data(10u32).service(web::resource("/").to(
1054            |req: HttpRequest| {
1055                if req.app_data::<usize>().is_some() {
1056                    HttpResponse::Ok()
1057                } else {
1058                    HttpResponse::BadRequest()
1059                }
1060            },
1061        )))
1062        .await;
1063
1064        let req = TestRequest::default().to_request();
1065        let resp = call_service(&srv, req).await;
1066        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
1067    }
1068
1069    #[actix_rt::test]
1070    async fn test_cascading_data() {
1071        #[allow(dead_code)]
1072        fn echo_usize(req: HttpRequest) -> HttpResponse {
1073            let num = req.app_data::<usize>().unwrap();
1074            HttpResponse::Ok().body(num.to_string())
1075        }
1076
1077        let srv = init_service(
1078            App::new()
1079                .app_data(88usize)
1080                .service(web::resource("/").route(web::get().to(echo_usize)))
1081                .service(
1082                    web::resource("/one")
1083                        .app_data(1u32)
1084                        .route(web::get().to(echo_usize)),
1085                ),
1086        )
1087        .await;
1088
1089        let req = TestRequest::get().uri("/").to_request();
1090        let resp = srv.call(req).await.unwrap();
1091        let body = read_body(resp).await;
1092        assert_eq!(body, Bytes::from_static(b"88"));
1093
1094        let req = TestRequest::get().uri("/one").to_request();
1095        let resp = srv.call(req).await.unwrap();
1096        let body = read_body(resp).await;
1097        assert_eq!(body, Bytes::from_static(b"88"));
1098    }
1099
1100    #[actix_rt::test]
1101    async fn test_overwrite_data() {
1102        #[allow(dead_code)]
1103        fn echo_usize(req: HttpRequest) -> HttpResponse {
1104            let num = req.app_data::<usize>().unwrap();
1105            HttpResponse::Ok().body(num.to_string())
1106        }
1107
1108        let srv = init_service(
1109            App::new()
1110                .app_data(88usize)
1111                .service(web::resource("/").route(web::get().to(echo_usize)))
1112                .service(
1113                    web::resource("/one")
1114                        .app_data(1usize)
1115                        .route(web::get().to(echo_usize)),
1116                ),
1117        )
1118        .await;
1119
1120        let req = TestRequest::get().uri("/").to_request();
1121        let resp = srv.call(req).await.unwrap();
1122        let body = read_body(resp).await;
1123        assert_eq!(body, Bytes::from_static(b"88"));
1124
1125        let req = TestRequest::get().uri("/one").to_request();
1126        let resp = srv.call(req).await.unwrap();
1127        let body = read_body(resp).await;
1128        assert_eq!(body, Bytes::from_static(b"1"));
1129    }
1130
1131    #[actix_rt::test]
1132    async fn test_app_data_dropped() {
1133        struct Tracker {
1134            pub dropped: bool,
1135        }
1136        struct Foo {
1137            tracker: Rc<RefCell<Tracker>>,
1138        }
1139        impl Drop for Foo {
1140            fn drop(&mut self) {
1141                self.tracker.borrow_mut().dropped = true;
1142            }
1143        }
1144
1145        let tracker = Rc::new(RefCell::new(Tracker { dropped: false }));
1146        {
1147            let tracker2 = Rc::clone(&tracker);
1148            let srv = init_service(App::new().service(web::resource("/").to(
1149                move |req: HttpRequest| {
1150                    req.extensions_mut().insert(Foo {
1151                        tracker: Rc::clone(&tracker2),
1152                    });
1153                    HttpResponse::Ok()
1154                },
1155            )))
1156            .await;
1157
1158            let req = TestRequest::default().to_request();
1159            let resp = call_service(&srv, req).await;
1160            assert_eq!(resp.status(), StatusCode::OK);
1161        }
1162
1163        assert!(tracker.borrow().dropped);
1164    }
1165
1166    #[actix_rt::test]
1167    async fn extract_path_pattern() {
1168        let srv = init_service(
1169            App::new().service(
1170                web::scope("/user/{id}")
1171                    .service(web::resource("/profile").route(web::get().to(
1172                        move |req: HttpRequest| {
1173                            assert_eq!(req.match_pattern(), Some("/user/{id}/profile".to_owned()));
1174
1175                            HttpResponse::Ok().finish()
1176                        },
1177                    )))
1178                    .default_service(web::to(move |req: HttpRequest| {
1179                        assert!(req.match_pattern().is_none());
1180                        HttpResponse::Ok().finish()
1181                    })),
1182            ),
1183        )
1184        .await;
1185
1186        let req = TestRequest::get().uri("/user/22/profile").to_request();
1187        let res = call_service(&srv, req).await;
1188        assert_eq!(res.status(), StatusCode::OK);
1189
1190        let req = TestRequest::get().uri("/user/22/not-exist").to_request();
1191        let res = call_service(&srv, req).await;
1192        assert_eq!(res.status(), StatusCode::OK);
1193    }
1194
1195    #[actix_rt::test]
1196    async fn extract_path_pattern_with_guards() {
1197        let srv = init_service(
1198            App::new().service(
1199                web::scope("/widgets")
1200                    .service(
1201                        web::resource("/{id}")
1202                            .name("get_widget")
1203                            .guard(guard::Get())
1204                            .to(|req: HttpRequest| {
1205                                assert_eq!(req.match_pattern(), Some("/widgets/{id}".to_owned()));
1206                                assert_eq!(req.match_name(), Some("get_widget"));
1207                                HttpResponse::Ok().finish()
1208                            }),
1209                    )
1210                    .service(
1211                        web::resource("/action")
1212                            .name("widget_action")
1213                            .guard(guard::Post())
1214                            .to(|req: HttpRequest| {
1215                                assert_eq!(req.match_pattern(), Some("/widgets/action".to_owned()));
1216                                assert_eq!(req.match_name(), Some("widget_action"));
1217                                HttpResponse::Ok().finish()
1218                            }),
1219                    ),
1220            ),
1221        )
1222        .await;
1223
1224        let req = TestRequest::get().uri("/widgets/42").to_request();
1225        let res = call_service(&srv, req).await;
1226        assert_eq!(res.status(), StatusCode::OK);
1227
1228        let req = TestRequest::post().uri("/widgets/action").to_request();
1229        let res = call_service(&srv, req).await;
1230        assert_eq!(res.status(), StatusCode::OK);
1231    }
1232
1233    #[actix_rt::test]
1234    async fn extract_path_pattern_complex() {
1235        let srv = init_service(
1236            App::new()
1237                .service(web::scope("/user").service(web::scope("/{id}").service(
1238                    web::resource("").to(move |req: HttpRequest| {
1239                        assert_eq!(req.match_pattern(), Some("/user/{id}".to_owned()));
1240
1241                        HttpResponse::Ok().finish()
1242                    }),
1243                )))
1244                .service(web::resource("/").to(move |req: HttpRequest| {
1245                    assert_eq!(req.match_pattern(), Some("/".to_owned()));
1246
1247                    HttpResponse::Ok().finish()
1248                }))
1249                .default_service(web::to(move |req: HttpRequest| {
1250                    assert!(req.match_pattern().is_none());
1251                    HttpResponse::Ok().finish()
1252                })),
1253        )
1254        .await;
1255
1256        let req = TestRequest::get().uri("/user/test").to_request();
1257        let res = call_service(&srv, req).await;
1258        assert_eq!(res.status(), StatusCode::OK);
1259
1260        let req = TestRequest::get().uri("/").to_request();
1261        let res = call_service(&srv, req).await;
1262        assert_eq!(res.status(), StatusCode::OK);
1263
1264        let req = TestRequest::get().uri("/not-exist").to_request();
1265        let res = call_service(&srv, req).await;
1266        assert_eq!(res.status(), StatusCode::OK);
1267    }
1268
1269    #[actix_rt::test]
1270    async fn url_for_closest_named_resource() {
1271        // we mount the route named 'nested' on 2 different scopes, 'a' and 'b'
1272        let srv = test::init_service(
1273            App::new()
1274                .service(
1275                    web::scope("/foo")
1276                        .service(web::resource("/nested").name("nested").route(web::get().to(
1277                            |req: HttpRequest| {
1278                                HttpResponse::Ok()
1279                                    .body(format!("{}", req.url_for_static("nested").unwrap()))
1280                            },
1281                        )))
1282                        .service(web::scope("/baz").service(web::resource("deep")))
1283                        .service(web::resource("{foo_param}")),
1284                )
1285                .service(web::scope("/bar").service(
1286                    web::resource("/nested").name("nested").route(web::get().to(
1287                        |req: HttpRequest| {
1288                            HttpResponse::Ok()
1289                                .body(format!("{}", req.url_for_static("nested").unwrap()))
1290                        },
1291                    )),
1292                )),
1293        )
1294        .await;
1295
1296        let foo_resp =
1297            test::call_service(&srv, TestRequest::with_uri("/foo/nested").to_request()).await;
1298        assert_eq!(foo_resp.status(), StatusCode::OK);
1299        let body = read_body(foo_resp).await;
1300        // `body` equals http://localhost:8080/bar/nested
1301        // because nested from /bar overrides /foo's
1302        // to do this any other way would require something like a custom tree search
1303        // see https://github.com/actix/actix-web/issues/1763
1304        assert_eq!(body, "http://localhost:8080/bar/nested");
1305
1306        let bar_resp =
1307            test::call_service(&srv, TestRequest::with_uri("/bar/nested").to_request()).await;
1308        assert_eq!(bar_resp.status(), StatusCode::OK);
1309        let body = read_body(bar_resp).await;
1310        assert_eq!(body, "http://localhost:8080/bar/nested");
1311    }
1312
1313    #[test]
1314    fn authorization_header_hidden_in_debug() {
1315        let authorization_header = "Basic bXkgdXNlcm5hbWU6bXkgcGFzc3dvcmQK";
1316        let req = TestRequest::get()
1317            .insert_header((crate::http::header::AUTHORIZATION, authorization_header))
1318            .to_http_request();
1319
1320        assert!(!format!("{:?}", req).contains(authorization_header));
1321    }
1322
1323    #[test]
1324    fn proxy_authorization_header_hidden_in_debug() {
1325        let proxy_authorization_header = "secret value";
1326        let req = TestRequest::get()
1327            .insert_header((
1328                crate::http::header::PROXY_AUTHORIZATION,
1329                proxy_authorization_header,
1330            ))
1331            .to_http_request();
1332
1333        assert!(!format!("{:?}", req).contains(proxy_authorization_header));
1334    }
1335
1336    #[test]
1337    fn cookie_header_hidden_in_debug() {
1338        let cookie_header = "secret";
1339        let req = TestRequest::get()
1340            .insert_header((crate::http::header::COOKIE, cookie_header))
1341            .to_http_request();
1342
1343        assert!(!format!("{:?}", req).contains(cookie_header));
1344    }
1345
1346    #[test]
1347    fn other_header_visible_in_debug() {
1348        let location_header = "192.0.0.1";
1349        let req = TestRequest::get()
1350            .insert_header((crate::http::header::LOCATION, location_header))
1351            .to_http_request();
1352
1353        assert!(format!("{:?}", req).contains(location_header));
1354    }
1355
1356    #[test]
1357    fn check_full_url() {
1358        let req = TestRequest::with_uri("/api?id=4&name=foo").to_http_request();
1359        assert_eq!(
1360            req.full_url().as_str(),
1361            "http://localhost:8080/api?id=4&name=foo",
1362        );
1363
1364        let req = TestRequest::with_uri("https://example.com/api?id=4&name=foo").to_http_request();
1365        assert_eq!(
1366            req.full_url().as_str(),
1367            "https://example.com/api?id=4&name=foo",
1368        );
1369
1370        let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
1371            .insert_header(("host", "example.com"))
1372            .to_http_request();
1373        assert_eq!(
1374            req.full_url().as_str(),
1375            "http://example.com/api?id=4&name=foo",
1376        );
1377    }
1378}