1 use std::{
2     cell::{Ref, RefCell, RefMut},
3     fmt, net,
4     rc::Rc,
5     str,
6 };
7 
8 use actix_http::{
9     http::{HeaderMap, Method, Uri, Version},
10     Extensions, HttpMessage, Message, Payload, RequestHead,
11 };
12 use actix_router::{Path, Url};
13 use actix_utils::future::{ok, Ready};
14 #[cfg(feature = "cookies")]
15 use cookie::{Cookie, ParseError as CookieParseError};
16 use smallvec::SmallVec;
17 
18 use crate::{
19     app_service::AppInitServiceState, config::AppConfig, error::UrlGenerationError,
20     info::ConnectionInfo, rmap::ResourceMap, Error, FromRequest,
21 };
22 
23 #[cfg(feature = "cookies")]
24 struct Cookies(Vec<Cookie<'static>>);
25 
26 #[derive(Clone)]
27 /// An HTTP Request
28 pub struct HttpRequest {
29     /// # Panics
30     /// `Rc<HttpRequestInner>` is used exclusively and NO `Weak<HttpRequestInner>`
31     /// is allowed anywhere in the code. Weak pointer is purposely ignored when
32     /// doing `Rc`'s ref counter check. Expect panics if this invariant is violated.
33     pub(crate) inner: Rc<HttpRequestInner>,
34 }
35 
36 pub(crate) struct HttpRequestInner {
37     pub(crate) head: Message<RequestHead>,
38     pub(crate) path: Path<Url>,
39     pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,
40     app_state: Rc<AppInitServiceState>,
41 }
42 
43 impl HttpRequest {
44     #[inline]
new( path: Path<Url>, head: Message<RequestHead>, app_state: Rc<AppInitServiceState>, app_data: Rc<Extensions>, ) -> HttpRequest45     pub(crate) fn new(
46         path: Path<Url>,
47         head: Message<RequestHead>,
48         app_state: Rc<AppInitServiceState>,
49         app_data: Rc<Extensions>,
50     ) -> HttpRequest {
51         let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();
52         data.push(app_data);
53 
54         HttpRequest {
55             inner: Rc::new(HttpRequestInner {
56                 head,
57                 path,
58                 app_state,
59                 app_data: data,
60             }),
61         }
62     }
63 }
64 
65 impl HttpRequest {
66     /// This method returns reference to the request head
67     #[inline]
head(&self) -> &RequestHead68     pub fn head(&self) -> &RequestHead {
69         &self.inner.head
70     }
71 
72     /// This method returns mutable reference to the request head.
73     /// panics if multiple references of HTTP request exists.
74     #[inline]
head_mut(&mut self) -> &mut RequestHead75     pub(crate) fn head_mut(&mut self) -> &mut RequestHead {
76         &mut Rc::get_mut(&mut self.inner).unwrap().head
77     }
78 
79     /// Request's uri.
80     #[inline]
uri(&self) -> &Uri81     pub fn uri(&self) -> &Uri {
82         &self.head().uri
83     }
84 
85     /// Read the Request method.
86     #[inline]
method(&self) -> &Method87     pub fn method(&self) -> &Method {
88         &self.head().method
89     }
90 
91     /// Read the Request Version.
92     #[inline]
version(&self) -> Version93     pub fn version(&self) -> Version {
94         self.head().version
95     }
96 
97     #[inline]
98     /// Returns request's headers.
headers(&self) -> &HeaderMap99     pub fn headers(&self) -> &HeaderMap {
100         &self.head().headers
101     }
102 
103     /// The target path of this Request.
104     #[inline]
path(&self) -> &str105     pub fn path(&self) -> &str {
106         self.head().uri.path()
107     }
108 
109     /// The query string in the URL.
110     ///
111     /// E.g., id=10
112     #[inline]
query_string(&self) -> &str113     pub fn query_string(&self) -> &str {
114         self.uri().query().unwrap_or_default()
115     }
116 
117     /// Get a reference to the Path parameters.
118     ///
119     /// Params is a container for url parameters.
120     /// A variable segment is specified in the form `{identifier}`,
121     /// where the identifier can be used later in a request handler to
122     /// access the matched value for that segment.
123     #[inline]
match_info(&self) -> &Path<Url>124     pub fn match_info(&self) -> &Path<Url> {
125         &self.inner.path
126     }
127 
128     #[inline]
match_info_mut(&mut self) -> &mut Path<Url>129     pub(crate) fn match_info_mut(&mut self) -> &mut Path<Url> {
130         &mut Rc::get_mut(&mut self.inner).unwrap().path
131     }
132 
133     /// The resource definition pattern that matched the path. Useful for logging and metrics.
134     ///
135     /// For example, when a resource with pattern `/user/{id}/profile` is defined and a call is made
136     /// to `/user/123/profile` this function would return `Some("/user/{id}/profile")`.
137     ///
138     /// Returns a None when no resource is fully matched, including default services.
139     #[inline]
match_pattern(&self) -> Option<String>140     pub fn match_pattern(&self) -> Option<String> {
141         self.resource_map().match_pattern(self.path())
142     }
143 
144     /// The resource name that matched the path. Useful for logging and metrics.
145     ///
146     /// Returns a None when no resource is fully matched, including default services.
147     #[inline]
match_name(&self) -> Option<&str>148     pub fn match_name(&self) -> Option<&str> {
149         self.resource_map().match_name(self.path())
150     }
151 
152     /// Request extensions
153     #[inline]
extensions(&self) -> Ref<'_, Extensions>154     pub fn extensions(&self) -> Ref<'_, Extensions> {
155         self.head().extensions()
156     }
157 
158     /// Mutable reference to a the request's extensions
159     #[inline]
extensions_mut(&self) -> RefMut<'_, Extensions>160     pub fn extensions_mut(&self) -> RefMut<'_, Extensions> {
161         self.head().extensions_mut()
162     }
163 
164     /// Generate url for named resource
165     ///
166     /// ```
167     /// # use actix_web::{web, App, HttpRequest, HttpResponse};
168     /// #
169     /// fn index(req: HttpRequest) -> HttpResponse {
170     ///     let url = req.url_for("foo", &["1", "2", "3"]); // <- generate url for "foo" resource
171     ///     HttpResponse::Ok().into()
172     /// }
173     ///
174     /// fn main() {
175     ///     let app = App::new()
176     ///         .service(web::resource("/test/{one}/{two}/{three}")
177     ///              .name("foo")  // <- set resource name, then it could be used in `url_for`
178     ///              .route(web::get().to(|| HttpResponse::Ok()))
179     ///         );
180     /// }
181     /// ```
url_for<U, I>(&self, name: &str, elements: U) -> Result<url::Url, UrlGenerationError> where U: IntoIterator<Item = I>, I: AsRef<str>,182     pub fn url_for<U, I>(&self, name: &str, elements: U) -> Result<url::Url, UrlGenerationError>
183     where
184         U: IntoIterator<Item = I>,
185         I: AsRef<str>,
186     {
187         self.resource_map().url_for(&self, name, elements)
188     }
189 
190     /// Generate url for named resource
191     ///
192     /// This method is similar to `HttpRequest::url_for()` but it can be used
193     /// for urls that do not contain variable parts.
url_for_static(&self, name: &str) -> Result<url::Url, UrlGenerationError>194     pub fn url_for_static(&self, name: &str) -> Result<url::Url, UrlGenerationError> {
195         const NO_PARAMS: [&str; 0] = [];
196         self.url_for(name, &NO_PARAMS)
197     }
198 
199     #[inline]
200     /// Get a reference to a `ResourceMap` of current application.
resource_map(&self) -> &ResourceMap201     pub fn resource_map(&self) -> &ResourceMap {
202         &self.app_state().rmap()
203     }
204 
205     /// Peer socket address.
206     ///
207     /// Peer address is the directly connected peer's socket address. If a proxy is used in front of
208     /// the Actix Web server, then it would be address of this proxy.
209     ///
210     /// To get client connection information `.connection_info()` should be used.
211     ///
212     /// Will only return None when called in unit tests.
213     #[inline]
peer_addr(&self) -> Option<net::SocketAddr>214     pub fn peer_addr(&self) -> Option<net::SocketAddr> {
215         self.head().peer_addr
216     }
217 
218     /// Get *ConnectionInfo* for the current request.
219     ///
220     /// This method panics if request's extensions container is already
221     /// borrowed.
222     #[inline]
connection_info(&self) -> Ref<'_, ConnectionInfo>223     pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
224         ConnectionInfo::get(self.head(), self.app_config())
225     }
226 
227     /// App config
228     #[inline]
app_config(&self) -> &AppConfig229     pub fn app_config(&self) -> &AppConfig {
230         self.app_state().config()
231     }
232 
233     /// Get an application data object stored with `App::data` or `App::app_data`
234     /// methods during application configuration.
235     ///
236     /// If `App::data` was used to store object, use `Data<T>`:
237     ///
238     /// ```ignore
239     /// let opt_t = req.app_data::<Data<T>>();
240     /// ```
app_data<T: 'static>(&self) -> Option<&T>241     pub fn app_data<T: 'static>(&self) -> Option<&T> {
242         for container in self.inner.app_data.iter().rev() {
243             if let Some(data) = container.get::<T>() {
244                 return Some(data);
245             }
246         }
247 
248         None
249     }
250 
251     #[inline]
app_state(&self) -> &AppInitServiceState252     fn app_state(&self) -> &AppInitServiceState {
253         &*self.inner.app_state
254     }
255 
256     /// Load request cookies.
257     #[cfg(feature = "cookies")]
cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError>258     pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
259         use actix_http::http::header::COOKIE;
260 
261         if self.extensions().get::<Cookies>().is_none() {
262             let mut cookies = Vec::new();
263             for hdr in self.headers().get_all(COOKIE) {
264                 let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
265                 for cookie_str in s.split(';').map(|s| s.trim()) {
266                     if !cookie_str.is_empty() {
267                         cookies.push(Cookie::parse_encoded(cookie_str)?.into_owned());
268                     }
269                 }
270             }
271             self.extensions_mut().insert(Cookies(cookies));
272         }
273 
274         Ok(Ref::map(self.extensions(), |ext| {
275             &ext.get::<Cookies>().unwrap().0
276         }))
277     }
278 
279     /// Return request cookie.
280     #[cfg(feature = "cookies")]
cookie(&self, name: &str) -> Option<Cookie<'static>>281     pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
282         if let Ok(cookies) = self.cookies() {
283             for cookie in cookies.iter() {
284                 if cookie.name() == name {
285                     return Some(cookie.to_owned());
286                 }
287             }
288         }
289         None
290     }
291 }
292 
293 impl HttpMessage for HttpRequest {
294     type Stream = ();
295 
296     #[inline]
297     /// Returns Request's headers.
headers(&self) -> &HeaderMap298     fn headers(&self) -> &HeaderMap {
299         &self.head().headers
300     }
301 
302     /// Request extensions
303     #[inline]
extensions(&self) -> Ref<'_, Extensions>304     fn extensions(&self) -> Ref<'_, Extensions> {
305         self.inner.head.extensions()
306     }
307 
308     /// Mutable reference to a the request's extensions
309     #[inline]
extensions_mut(&self) -> RefMut<'_, Extensions>310     fn extensions_mut(&self) -> RefMut<'_, Extensions> {
311         self.inner.head.extensions_mut()
312     }
313 
314     #[inline]
take_payload(&mut self) -> Payload<Self::Stream>315     fn take_payload(&mut self) -> Payload<Self::Stream> {
316         Payload::None
317     }
318 }
319 
320 impl Drop for HttpRequest {
drop(&mut self)321     fn drop(&mut self) {
322         // if possible, contribute to current worker's HttpRequest allocation pool
323 
324         // This relies on no Weak<HttpRequestInner> exists anywhere.(There is none)
325         if let Some(inner) = Rc::get_mut(&mut self.inner) {
326             if inner.app_state.pool().is_available() {
327                 // clear additional app_data and keep the root one for reuse.
328                 inner.app_data.truncate(1);
329                 // inner is borrowed mut here. get head's Extension mutably
330                 // to reduce borrow check
331                 inner.head.extensions.get_mut().clear();
332 
333                 // a re-borrow of pool is necessary here.
334                 let req = self.inner.clone();
335                 self.app_state().pool().push(req);
336             }
337         }
338     }
339 }
340 
341 /// It is possible to get `HttpRequest` as an extractor handler parameter
342 ///
343 /// # Examples
344 /// ```
345 /// use actix_web::{web, App, HttpRequest};
346 /// use serde::Deserialize;
347 ///
348 /// /// extract `Thing` from request
349 /// async fn index(req: HttpRequest) -> String {
350 ///    format!("Got thing: {:?}", req)
351 /// }
352 ///
353 /// fn main() {
354 ///     let app = App::new().service(
355 ///         web::resource("/users/{first}").route(
356 ///             web::get().to(index))
357 ///     );
358 /// }
359 /// ```
360 impl FromRequest for HttpRequest {
361     type Config = ();
362     type Error = Error;
363     type Future = Ready<Result<Self, Error>>;
364 
365     #[inline]
from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future366     fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
367         ok(req.clone())
368     }
369 }
370 
371 impl fmt::Debug for HttpRequest {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result372     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373         writeln!(
374             f,
375             "\nHttpRequest {:?} {}:{}",
376             self.inner.head.version,
377             self.inner.head.method,
378             self.path()
379         )?;
380         if !self.query_string().is_empty() {
381             writeln!(f, "  query: ?{:?}", self.query_string())?;
382         }
383         if !self.match_info().is_empty() {
384             writeln!(f, "  params: {:?}", self.match_info())?;
385         }
386         writeln!(f, "  headers:")?;
387         for (key, val) in self.headers().iter() {
388             writeln!(f, "    {:?}: {:?}", key, val)?;
389         }
390         Ok(())
391     }
392 }
393 
394 /// Slab-allocated `HttpRequest` Pool
395 ///
396 /// Since request processing may yield for asynchronous events to complete, a worker may have many
397 /// requests in-flight at any time. Pooling requests like this amortizes the performance and memory
398 /// costs of allocating and de-allocating HttpRequest objects as frequently as they otherwise would.
399 ///
400 /// Request objects are added when they are dropped (see `<HttpRequest as Drop>::drop`) and re-used
401 /// in `<AppInitService as Service>::call` when there are available objects in the list.
402 ///
403 /// The pool's default capacity is 128 items.
404 pub(crate) struct HttpRequestPool {
405     inner: RefCell<Vec<Rc<HttpRequestInner>>>,
406     cap: usize,
407 }
408 
409 impl Default for HttpRequestPool {
default() -> Self410     fn default() -> Self {
411         Self::with_capacity(128)
412     }
413 }
414 
415 impl HttpRequestPool {
with_capacity(cap: usize) -> Self416     pub(crate) fn with_capacity(cap: usize) -> Self {
417         HttpRequestPool {
418             inner: RefCell::new(Vec::with_capacity(cap)),
419             cap,
420         }
421     }
422 
423     /// Re-use a previously allocated (but now completed/discarded) HttpRequest object.
424     #[inline]
pop(&self) -> Option<HttpRequest>425     pub(crate) fn pop(&self) -> Option<HttpRequest> {
426         self.inner
427             .borrow_mut()
428             .pop()
429             .map(|inner| HttpRequest { inner })
430     }
431 
432     /// Check if the pool still has capacity for request storage.
433     #[inline]
is_available(&self) -> bool434     pub(crate) fn is_available(&self) -> bool {
435         self.inner.borrow_mut().len() < self.cap
436     }
437 
438     /// Push a request to pool.
439     #[inline]
push(&self, req: Rc<HttpRequestInner>)440     pub(crate) fn push(&self, req: Rc<HttpRequestInner>) {
441         self.inner.borrow_mut().push(req);
442     }
443 
444     /// Clears all allocated HttpRequest objects.
clear(&self)445     pub(crate) fn clear(&self) {
446         self.inner.borrow_mut().clear()
447     }
448 }
449 
450 #[cfg(test)]
451 mod tests {
452     use actix_service::Service;
453     use bytes::Bytes;
454 
455     use super::*;
456     use crate::dev::{ResourceDef, ResourceMap};
457     use crate::http::{header, StatusCode};
458     use crate::test::{call_service, init_service, read_body, TestRequest};
459     use crate::{web, App, HttpResponse};
460 
461     #[test]
test_debug()462     fn test_debug() {
463         let req = TestRequest::default()
464             .insert_header(("content-type", "text/plain"))
465             .to_http_request();
466         let dbg = format!("{:?}", req);
467         assert!(dbg.contains("HttpRequest"));
468     }
469 
470     #[test]
471     #[cfg(feature = "cookies")]
test_no_request_cookies()472     fn test_no_request_cookies() {
473         let req = TestRequest::default().to_http_request();
474         assert!(req.cookies().unwrap().is_empty());
475     }
476 
477     #[test]
478     #[cfg(feature = "cookies")]
test_request_cookies()479     fn test_request_cookies() {
480         let req = TestRequest::default()
481             .append_header((header::COOKIE, "cookie1=value1"))
482             .append_header((header::COOKIE, "cookie2=value2"))
483             .to_http_request();
484         {
485             let cookies = req.cookies().unwrap();
486             assert_eq!(cookies.len(), 2);
487             assert_eq!(cookies[0].name(), "cookie1");
488             assert_eq!(cookies[0].value(), "value1");
489             assert_eq!(cookies[1].name(), "cookie2");
490             assert_eq!(cookies[1].value(), "value2");
491         }
492 
493         let cookie = req.cookie("cookie1");
494         assert!(cookie.is_some());
495         let cookie = cookie.unwrap();
496         assert_eq!(cookie.name(), "cookie1");
497         assert_eq!(cookie.value(), "value1");
498 
499         let cookie = req.cookie("cookie-unknown");
500         assert!(cookie.is_none());
501     }
502 
503     #[test]
test_request_query()504     fn test_request_query() {
505         let req = TestRequest::with_uri("/?id=test").to_http_request();
506         assert_eq!(req.query_string(), "id=test");
507     }
508 
509     #[test]
test_url_for()510     fn test_url_for() {
511         let mut res = ResourceDef::new("/user/{name}.{ext}");
512         *res.name_mut() = "index".to_string();
513 
514         let mut rmap = ResourceMap::new(ResourceDef::new(""));
515         rmap.add(&mut res, None);
516         assert!(rmap.has_resource("/user/test.html"));
517         assert!(!rmap.has_resource("/test/unknown"));
518 
519         let req = TestRequest::default()
520             .insert_header((header::HOST, "www.rust-lang.org"))
521             .rmap(rmap)
522             .to_http_request();
523 
524         assert_eq!(
525             req.url_for("unknown", &["test"]),
526             Err(UrlGenerationError::ResourceNotFound)
527         );
528         assert_eq!(
529             req.url_for("index", &["test"]),
530             Err(UrlGenerationError::NotEnoughElements)
531         );
532         let url = req.url_for("index", &["test", "html"]);
533         assert_eq!(
534             url.ok().unwrap().as_str(),
535             "http://www.rust-lang.org/user/test.html"
536         );
537     }
538 
539     #[test]
test_url_for_static()540     fn test_url_for_static() {
541         let mut rdef = ResourceDef::new("/index.html");
542         *rdef.name_mut() = "index".to_string();
543 
544         let mut rmap = ResourceMap::new(ResourceDef::new(""));
545         rmap.add(&mut rdef, None);
546 
547         assert!(rmap.has_resource("/index.html"));
548 
549         let req = TestRequest::with_uri("/test")
550             .insert_header((header::HOST, "www.rust-lang.org"))
551             .rmap(rmap)
552             .to_http_request();
553         let url = req.url_for_static("index");
554         assert_eq!(
555             url.ok().unwrap().as_str(),
556             "http://www.rust-lang.org/index.html"
557         );
558     }
559 
560     #[test]
test_match_name()561     fn test_match_name() {
562         let mut rdef = ResourceDef::new("/index.html");
563         *rdef.name_mut() = "index".to_string();
564 
565         let mut rmap = ResourceMap::new(ResourceDef::new(""));
566         rmap.add(&mut rdef, None);
567 
568         assert!(rmap.has_resource("/index.html"));
569 
570         let req = TestRequest::default()
571             .uri("/index.html")
572             .rmap(rmap)
573             .to_http_request();
574 
575         assert_eq!(req.match_name(), Some("index"));
576     }
577 
578     #[test]
test_url_for_external()579     fn test_url_for_external() {
580         let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");
581 
582         *rdef.name_mut() = "youtube".to_string();
583 
584         let mut rmap = ResourceMap::new(ResourceDef::new(""));
585         rmap.add(&mut rdef, None);
586         assert!(rmap.has_resource("https://youtube.com/watch/unknown"));
587 
588         let req = TestRequest::default().rmap(rmap).to_http_request();
589         let url = req.url_for("youtube", &["oHg5SJYRHA0"]);
590         assert_eq!(
591             url.ok().unwrap().as_str(),
592             "https://youtube.com/watch/oHg5SJYRHA0"
593         );
594     }
595 
596     #[actix_rt::test]
test_drop_http_request_pool()597     async fn test_drop_http_request_pool() {
598         let srv = init_service(App::new().service(web::resource("/").to(
599             |req: HttpRequest| {
600                 HttpResponse::Ok()
601                     .insert_header(("pool_cap", req.app_state().pool().cap))
602                     .finish()
603             },
604         )))
605         .await;
606 
607         let req = TestRequest::default().to_request();
608         let resp = call_service(&srv, req).await;
609 
610         drop(srv);
611 
612         assert_eq!(resp.headers().get("pool_cap").unwrap(), "128");
613     }
614 
615     #[actix_rt::test]
test_data()616     async fn test_data() {
617         let srv = init_service(App::new().app_data(10usize).service(web::resource("/").to(
618             |req: HttpRequest| {
619                 if req.app_data::<usize>().is_some() {
620                     HttpResponse::Ok()
621                 } else {
622                     HttpResponse::BadRequest()
623                 }
624             },
625         )))
626         .await;
627 
628         let req = TestRequest::default().to_request();
629         let resp = call_service(&srv, req).await;
630         assert_eq!(resp.status(), StatusCode::OK);
631 
632         let srv = init_service(App::new().app_data(10u32).service(web::resource("/").to(
633             |req: HttpRequest| {
634                 if req.app_data::<usize>().is_some() {
635                     HttpResponse::Ok()
636                 } else {
637                     HttpResponse::BadRequest()
638                 }
639             },
640         )))
641         .await;
642 
643         let req = TestRequest::default().to_request();
644         let resp = call_service(&srv, req).await;
645         assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
646     }
647 
648     #[actix_rt::test]
test_cascading_data()649     async fn test_cascading_data() {
650         #[allow(dead_code)]
651         fn echo_usize(req: HttpRequest) -> HttpResponse {
652             let num = req.app_data::<usize>().unwrap();
653             HttpResponse::Ok().body(num.to_string())
654         }
655 
656         let srv = init_service(
657             App::new()
658                 .app_data(88usize)
659                 .service(web::resource("/").route(web::get().to(echo_usize)))
660                 .service(
661                     web::resource("/one")
662                         .app_data(1u32)
663                         .route(web::get().to(echo_usize)),
664                 ),
665         )
666         .await;
667 
668         let req = TestRequest::get().uri("/").to_request();
669         let resp = srv.call(req).await.unwrap();
670         let body = read_body(resp).await;
671         assert_eq!(body, Bytes::from_static(b"88"));
672 
673         let req = TestRequest::get().uri("/one").to_request();
674         let resp = srv.call(req).await.unwrap();
675         let body = read_body(resp).await;
676         assert_eq!(body, Bytes::from_static(b"88"));
677     }
678 
679     #[actix_rt::test]
test_overwrite_data()680     async fn test_overwrite_data() {
681         #[allow(dead_code)]
682         fn echo_usize(req: HttpRequest) -> HttpResponse {
683             let num = req.app_data::<usize>().unwrap();
684             HttpResponse::Ok().body(num.to_string())
685         }
686 
687         let srv = init_service(
688             App::new()
689                 .app_data(88usize)
690                 .service(web::resource("/").route(web::get().to(echo_usize)))
691                 .service(
692                     web::resource("/one")
693                         .app_data(1usize)
694                         .route(web::get().to(echo_usize)),
695                 ),
696         )
697         .await;
698 
699         let req = TestRequest::get().uri("/").to_request();
700         let resp = srv.call(req).await.unwrap();
701         let body = read_body(resp).await;
702         assert_eq!(body, Bytes::from_static(b"88"));
703 
704         let req = TestRequest::get().uri("/one").to_request();
705         let resp = srv.call(req).await.unwrap();
706         let body = read_body(resp).await;
707         assert_eq!(body, Bytes::from_static(b"1"));
708     }
709 
710     // allow deprecated App::data
711     #[allow(deprecated)]
712     #[actix_rt::test]
test_extensions_dropped()713     async fn test_extensions_dropped() {
714         struct Tracker {
715             pub dropped: bool,
716         }
717         struct Foo {
718             tracker: Rc<RefCell<Tracker>>,
719         }
720         impl Drop for Foo {
721             fn drop(&mut self) {
722                 self.tracker.borrow_mut().dropped = true;
723             }
724         }
725 
726         let tracker = Rc::new(RefCell::new(Tracker { dropped: false }));
727         {
728             let tracker2 = Rc::clone(&tracker);
729             let srv = init_service(App::new().data(10u32).service(web::resource("/").to(
730                 move |req: HttpRequest| {
731                     req.extensions_mut().insert(Foo {
732                         tracker: Rc::clone(&tracker2),
733                     });
734                     HttpResponse::Ok()
735                 },
736             )))
737             .await;
738 
739             let req = TestRequest::default().to_request();
740             let resp = call_service(&srv, req).await;
741             assert_eq!(resp.status(), StatusCode::OK);
742         }
743 
744         assert!(tracker.borrow().dropped);
745     }
746 
747     #[actix_rt::test]
extract_path_pattern()748     async fn extract_path_pattern() {
749         let srv = init_service(
750             App::new().service(
751                 web::scope("/user/{id}")
752                     .service(web::resource("/profile").route(web::get().to(
753                         move |req: HttpRequest| {
754                             assert_eq!(
755                                 req.match_pattern(),
756                                 Some("/user/{id}/profile".to_owned())
757                             );
758 
759                             HttpResponse::Ok().finish()
760                         },
761                     )))
762                     .default_service(web::to(move |req: HttpRequest| {
763                         assert!(req.match_pattern().is_none());
764                         HttpResponse::Ok().finish()
765                     })),
766             ),
767         )
768         .await;
769 
770         let req = TestRequest::get().uri("/user/22/profile").to_request();
771         let res = call_service(&srv, req).await;
772         assert_eq!(res.status(), StatusCode::OK);
773 
774         let req = TestRequest::get().uri("/user/22/not-exist").to_request();
775         let res = call_service(&srv, req).await;
776         assert_eq!(res.status(), StatusCode::OK);
777     }
778 
779     #[actix_rt::test]
extract_path_pattern_complex()780     async fn extract_path_pattern_complex() {
781         let srv = init_service(
782             App::new()
783                 .service(web::scope("/user").service(web::scope("/{id}").service(
784                     web::resource("").to(move |req: HttpRequest| {
785                         assert_eq!(req.match_pattern(), Some("/user/{id}".to_owned()));
786 
787                         HttpResponse::Ok().finish()
788                     }),
789                 )))
790                 .service(web::resource("/").to(move |req: HttpRequest| {
791                     assert_eq!(req.match_pattern(), Some("/".to_owned()));
792 
793                     HttpResponse::Ok().finish()
794                 }))
795                 .default_service(web::to(move |req: HttpRequest| {
796                     assert!(req.match_pattern().is_none());
797                     HttpResponse::Ok().finish()
798                 })),
799         )
800         .await;
801 
802         let req = TestRequest::get().uri("/user/test").to_request();
803         let res = call_service(&srv, req).await;
804         assert_eq!(res.status(), StatusCode::OK);
805 
806         let req = TestRequest::get().uri("/").to_request();
807         let res = call_service(&srv, req).await;
808         assert_eq!(res.status(), StatusCode::OK);
809 
810         let req = TestRequest::get().uri("/not-exist").to_request();
811         let res = call_service(&srv, req).await;
812         assert_eq!(res.status(), StatusCode::OK);
813     }
814 }
815