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};
13use actix_utils::future::{ok, Ready};
14#[cfg(feature = "cookies")]
15use cookie::{Cookie, ParseError as CookieParseError};
16use smallvec::SmallVec;
17
18use crate::{
19 app_service::AppInitServiceState,
20 config::AppConfig,
21 dev::{Extensions, Payload},
22 error::UrlGenerationError,
23 http::{header::HeaderMap, Method, Uri, Version},
24 info::ConnectionInfo,
25 rmap::ResourceMap,
26 Error, FromRequest, HttpMessage,
27};
28
29#[cfg(feature = "cookies")]
30struct Cookies(Vec<Cookie<'static>>);
31
32#[cfg(feature = "cookies")]
33struct RawCookies(Vec<Cookie<'static>>);
34
35#[derive(Clone)]
37pub struct HttpRequest {
38 pub(crate) inner: Rc<HttpRequestInner>,
43}
44
45pub(crate) struct HttpRequestInner {
46 pub(crate) head: Message<RequestHead>,
47 pub(crate) path: Path<Url>,
48 pub(crate) resource_path: SmallVec<[u16; 4]>,
49 pub(crate) resource_path_matched: bool,
50 pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,
51 pub(crate) conn_data: Option<Rc<Extensions>>,
52 pub(crate) extensions: Rc<RefCell<Extensions>>,
53 app_state: Rc<AppInitServiceState>,
54}
55
56impl HttpRequest {
57 #[inline]
58 pub(crate) fn new(
59 path: Path<Url>,
60 head: Message<RequestHead>,
61 app_state: Rc<AppInitServiceState>,
62 app_data: Rc<Extensions>,
63 conn_data: Option<Rc<Extensions>>,
64 extensions: Rc<RefCell<Extensions>>,
65 ) -> HttpRequest {
66 let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();
67 data.push(app_data);
68
69 HttpRequest {
70 inner: Rc::new(HttpRequestInner {
71 head,
72 path,
73 resource_path: SmallVec::new(),
74 resource_path_matched: false,
75 app_state,
76 app_data: data,
77 conn_data,
78 extensions,
79 }),
80 }
81 }
82}
83
84impl HttpRequest {
85 #[inline]
87 pub fn head(&self) -> &RequestHead {
88 &self.inner.head
89 }
90
91 #[inline]
97 pub(crate) fn head_mut(&mut self) -> &mut RequestHead {
98 &mut Rc::get_mut(&mut self.inner).unwrap().head
99 }
100
101 #[inline]
103 pub fn uri(&self) -> &Uri {
104 &self.head().uri
105 }
106
107 pub fn full_url(&self) -> url::Url {
130 let info = self.connection_info();
131 let scheme = info.scheme();
132 let host = info.host();
133 let path_and_query = self
134 .uri()
135 .path_and_query()
136 .map(|paq| paq.as_str())
137 .unwrap_or("/");
138
139 url::Url::parse(&format!("{scheme}://{host}{path_and_query}")).unwrap()
140 }
141
142 #[inline]
144 pub fn method(&self) -> &Method {
145 &self.head().method
146 }
147
148 #[inline]
150 pub fn version(&self) -> Version {
151 self.head().version
152 }
153
154 #[inline]
155 pub fn headers(&self) -> &HeaderMap {
157 &self.head().headers
158 }
159
160 #[inline]
162 pub fn path(&self) -> &str {
163 self.head().uri.path()
164 }
165
166 #[inline]
170 pub fn query_string(&self) -> &str {
171 self.uri().query().unwrap_or_default()
172 }
173
174 #[inline]
186 pub fn match_info(&self) -> &Path<Url> {
187 &self.inner.path
188 }
189
190 #[inline]
195 pub(crate) fn match_info_mut(&mut self) -> &mut Path<Url> {
196 &mut Rc::get_mut(&mut self.inner).unwrap().path
197 }
198
199 #[inline]
200 pub(crate) fn push_resource_id(&mut self, id: u16) {
201 Rc::get_mut(&mut self.inner).unwrap().resource_path.push(id);
202 }
203
204 #[inline]
205 pub(crate) fn mark_resource_path(&mut self, is_matched: bool) {
206 Rc::get_mut(&mut self.inner).unwrap().resource_path_matched = is_matched;
207 }
208
209 #[inline]
210 pub(crate) fn resource_path(&self) -> &[u16] {
211 &self.inner.resource_path
212 }
213
214 #[inline]
215 pub(crate) fn is_resource_path_matched(&self) -> bool {
216 self.inner.resource_path_matched
217 }
218
219 #[inline]
226 pub fn match_pattern(&self) -> Option<String> {
227 if self.is_resource_path_matched() {
228 if let Some(pattern) = self
229 .resource_map()
230 .match_pattern_by_resource_path(self.resource_path())
231 {
232 return Some(pattern);
233 }
234 }
235
236 self.resource_map().match_pattern(self.path())
237 }
238
239 #[inline]
243 pub fn match_name(&self) -> Option<&str> {
244 if self.is_resource_path_matched() {
245 if let Some(name) = self
246 .resource_map()
247 .match_name_by_resource_path(self.resource_path())
248 {
249 return Some(name);
250 }
251 }
252
253 self.resource_map().match_name(self.path())
254 }
255
256 pub fn conn_data<T: 'static>(&self) -> Option<&T> {
264 self.inner
265 .conn_data
266 .as_deref()
267 .and_then(|container| container.get::<T>())
268 }
269
270 pub fn url_for<U, I>(&self, name: &str, elements: U) -> Result<url::Url, UrlGenerationError>
295 where
296 U: IntoIterator<Item = I>,
297 I: AsRef<str>,
298 {
299 self.resource_map().url_for(self, name, elements)
300 }
301
302 pub fn url_for_map<K, V, S>(
326 &self,
327 name: &str,
328 elements: &HashMap<K, V, S>,
329 ) -> Result<url::Url, UrlGenerationError>
330 where
331 K: std::borrow::Borrow<str> + Eq + Hash,
332 V: AsRef<str>,
333 S: BuildHasher,
334 {
335 self.resource_map().url_for_map(self, name, elements)
336 }
337
338 pub fn url_for_iter<K, V, I>(
360 &self,
361 name: &str,
362 elements: I,
363 ) -> Result<url::Url, UrlGenerationError>
364 where
365 I: IntoIterator<Item = (K, V)>,
366 K: std::borrow::Borrow<str> + Eq + Hash,
367 V: AsRef<str>,
368 {
369 self.resource_map().url_for_iter(self, name, elements)
370 }
371
372 pub fn url_for_static(&self, name: &str) -> Result<url::Url, UrlGenerationError> {
377 const NO_PARAMS: [&str; 0] = [];
378 self.url_for(name, NO_PARAMS)
379 }
380
381 #[inline]
383 pub fn resource_map(&self) -> &ResourceMap {
384 self.app_state().rmap()
385 }
386
387 #[inline]
401 pub fn peer_addr(&self) -> Option<net::SocketAddr> {
402 self.head().peer_addr
403 }
404
405 #[inline]
412 pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
413 if !self.extensions().contains::<ConnectionInfo>() {
414 let info = ConnectionInfo::new(self.head(), self.app_config());
415 self.extensions_mut().insert(info);
416 }
417
418 Ref::map(self.extensions(), |data| data.get().unwrap())
419 }
420
421 #[inline]
423 pub fn app_config(&self) -> &AppConfig {
424 self.app_state().config()
425 }
426
427 #[doc(alias = "state")]
455 pub fn app_data<T: 'static>(&self) -> Option<&T> {
456 for container in self.inner.app_data.iter().rev() {
457 if let Some(data) = container.get::<T>() {
458 return Some(data);
459 }
460 }
461
462 None
463 }
464
465 #[inline]
466 fn app_state(&self) -> &AppInitServiceState {
467 &self.inner.app_state
468 }
469
470 #[cfg(feature = "cookies")]
477 pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
478 use actix_http::header::COOKIE;
479
480 if self.extensions().get::<Cookies>().is_none() {
481 let mut cookies = Vec::new();
482 for hdr in self.headers().get_all(COOKIE) {
483 let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
484 for cookie_str in s.split(';').map(|s| s.trim()).filter(|s| !s.is_empty()) {
485 if let Ok(cookie) = Cookie::parse_encoded(cookie_str) {
486 cookies.push(cookie.into_owned());
487 }
488 }
489 }
490 self.extensions_mut().insert(Cookies(cookies));
491 }
492
493 Ok(Ref::map(self.extensions(), |ext| {
494 &ext.get::<Cookies>().unwrap().0
495 }))
496 }
497
498 #[cfg(feature = "cookies")]
503 pub fn cookies_raw(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
504 use actix_http::header::COOKIE;
505
506 if self.extensions().get::<RawCookies>().is_none() {
507 let mut cookies = Vec::new();
508 for hdr in self.headers().get_all(COOKIE) {
509 let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
510 for cookie_str in s.split(';').map(|s| s.trim()).filter(|s| !s.is_empty()) {
511 if let Ok(cookie) = Cookie::parse(cookie_str) {
512 cookies.push(cookie.into_owned());
513 }
514 }
515 }
516 self.extensions_mut().insert(RawCookies(cookies));
517 }
518
519 Ok(Ref::map(self.extensions(), |ext| {
520 &ext.get::<RawCookies>().unwrap().0
521 }))
522 }
523
524 #[cfg(feature = "cookies")]
526 pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
527 if let Ok(cookies) = self.cookies() {
528 return cookies.iter().find(|cookie| cookie.name() == name).cloned();
529 }
530
531 None
532 }
533
534 #[cfg(feature = "cookies")]
536 pub fn cookie_raw(&self, name: &str) -> Option<Cookie<'static>> {
537 if let Ok(cookies) = self.cookies_raw() {
538 return cookies.iter().find(|cookie| cookie.name() == name).cloned();
539 }
540
541 None
542 }
543}
544
545impl HttpMessage for HttpRequest {
546 type Stream = ();
547
548 #[inline]
549 fn headers(&self) -> &HeaderMap {
550 &self.head().headers
551 }
552
553 #[inline]
554 fn extensions(&self) -> Ref<'_, Extensions> {
555 self.inner.extensions.borrow()
556 }
557
558 #[inline]
559 fn extensions_mut(&self) -> RefMut<'_, Extensions> {
560 self.inner.extensions.borrow_mut()
561 }
562
563 #[inline]
564 fn take_payload(&mut self) -> Payload<Self::Stream> {
565 Payload::None
566 }
567}
568
569impl Drop for HttpRequest {
570 fn drop(&mut self) {
571 if let Some(inner) = Rc::get_mut(&mut self.inner) {
575 if inner.app_state.pool().is_available() {
576 inner.app_data.truncate(1);
578
579 Rc::get_mut(&mut inner.extensions)
582 .unwrap()
583 .get_mut()
584 .clear();
585
586 inner.conn_data = None;
589
590 let req = Rc::clone(&self.inner);
592 self.app_state().pool().push(req);
593 }
594 }
595 }
596}
597
598impl FromRequest for HttpRequest {
616 type Error = Error;
617 type Future = Ready<Result<Self, Error>>;
618
619 #[inline]
620 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
621 ok(req.clone())
622 }
623}
624
625impl fmt::Debug for HttpRequest {
626 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
627 writeln!(
628 f,
629 "\nHttpRequest {:?} {}:{}",
630 self.inner.head.version,
631 self.inner.head.method,
632 self.path()
633 )?;
634
635 if !self.query_string().is_empty() {
636 writeln!(f, " query: ?{:?}", self.query_string())?;
637 }
638
639 if !self.match_info().is_empty() {
640 writeln!(f, " params: {:?}", self.match_info())?;
641 }
642
643 writeln!(f, " headers:")?;
644
645 for (key, val) in self.headers().iter() {
646 match key {
647 &crate::http::header::AUTHORIZATION
649 | &crate::http::header::PROXY_AUTHORIZATION
650 | &crate::http::header::COOKIE => writeln!(f, " {:?}: {:?}", key, "*redacted*")?,
651
652 _ => writeln!(f, " {:?}: {:?}", key, val)?,
653 }
654 }
655
656 Ok(())
657 }
658}
659
660pub(crate) struct HttpRequestPool {
671 inner: RefCell<Vec<Rc<HttpRequestInner>>>,
672 enabled: Cell<bool>,
673 cap: usize,
674}
675
676impl Default for HttpRequestPool {
677 fn default() -> Self {
678 Self::with_capacity(128)
679 }
680}
681
682impl HttpRequestPool {
683 pub(crate) fn with_capacity(cap: usize) -> Self {
684 HttpRequestPool {
685 inner: RefCell::new(Vec::with_capacity(cap)),
686 enabled: Cell::new(true),
687 cap,
688 }
689 }
690
691 #[inline]
693 pub(crate) fn pop(&self) -> Option<HttpRequest> {
694 self.inner
695 .borrow_mut()
696 .pop()
697 .map(|inner| HttpRequest { inner })
698 }
699
700 #[inline]
702 pub(crate) fn is_available(&self) -> bool {
703 self.enabled.get() && self.inner.borrow().len() < self.cap
704 }
705
706 #[inline]
708 pub(crate) fn push(&self, req: Rc<HttpRequestInner>) {
709 self.inner.borrow_mut().push(req);
710 }
711
712 pub(crate) fn disable(&self) {
714 self.enabled.set(false);
715 self.inner.borrow_mut().clear();
716 }
717}
718
719#[cfg(test)]
720mod tests {
721 use std::{collections::HashMap, sync::Arc};
722
723 use bytes::Bytes;
724
725 use super::*;
726 use crate::{
727 dev::{ResourceDef, Service},
728 guard,
729 http::{header, StatusCode},
730 test::{self, call_service, init_service, read_body, TestRequest},
731 web, App, HttpResponse,
732 };
733
734 #[test]
735 fn test_debug() {
736 let req = TestRequest::default()
737 .insert_header(("content-type", "text/plain"))
738 .to_http_request();
739 let dbg = format!("{:?}", req);
740 assert!(dbg.contains("HttpRequest"));
741 }
742
743 #[test]
744 #[cfg(feature = "cookies")]
745 fn test_no_request_cookies() {
746 let req = TestRequest::default().to_http_request();
747 assert!(req.cookies().unwrap().is_empty());
748 }
749
750 #[test]
751 #[cfg(feature = "cookies")]
752 fn test_request_cookies() {
753 let req = TestRequest::default()
754 .append_header((header::COOKIE, "cookie1=value1"))
755 .append_header((header::COOKIE, "cookie2=value2"))
756 .to_http_request();
757 {
758 let cookies = req.cookies().unwrap();
759 assert_eq!(cookies.len(), 2);
760 assert_eq!(cookies[0].name(), "cookie1");
761 assert_eq!(cookies[0].value(), "value1");
762 assert_eq!(cookies[1].name(), "cookie2");
763 assert_eq!(cookies[1].value(), "value2");
764 }
765
766 let cookie = req.cookie("cookie1");
767 assert!(cookie.is_some());
768 let cookie = cookie.unwrap();
769 assert_eq!(cookie.name(), "cookie1");
770 assert_eq!(cookie.value(), "value1");
771
772 let cookie = req.cookie("cookie-unknown");
773 assert!(cookie.is_none());
774 }
775
776 #[test]
777 #[cfg(feature = "cookies")]
778 fn test_request_cookies_raw() {
779 let req = TestRequest::default()
780 .append_header((header::COOKIE, "cookie1=hello%20world"))
781 .append_header((header::COOKIE, "cookie2=%db"))
782 .to_http_request();
783 {
784 let cookies = req.cookies_raw().unwrap();
785 assert_eq!(cookies.len(), 2);
786 assert_eq!(cookies[0].name(), "cookie1");
787 assert_eq!(cookies[0].value(), "hello%20world");
788 assert_eq!(cookies[1].name(), "cookie2");
789 assert_eq!(cookies[1].value(), "%db");
790 }
791
792 let cookie = req.cookie_raw("cookie1");
793 assert!(cookie.is_some());
794 let cookie = cookie.unwrap();
795 assert_eq!(cookie.name(), "cookie1");
796 assert_eq!(cookie.value(), "hello%20world");
797
798 let cookie = req.cookie_raw("cookie2");
799 assert!(cookie.is_some());
800 let cookie = cookie.unwrap();
801 assert_eq!(cookie.name(), "cookie2");
802 assert_eq!(cookie.value(), "%db");
803 }
804
805 #[test]
806 #[cfg(feature = "cookies")]
807 fn test_request_cookies_raw_is_independent_from_encoded_cookies() {
808 let req = TestRequest::default()
809 .append_header((header::COOKIE, "cookie=%20"))
810 .to_http_request();
811
812 let cookie = req.cookie("cookie").unwrap();
813 assert_eq!(cookie.value(), " ");
814
815 let raw_cookie = req.cookie_raw("cookie").unwrap();
816 assert_eq!(raw_cookie.value(), "%20");
817 }
818
819 #[test]
820 #[cfg(feature = "cookies")]
821 fn test_empty_key() {
822 let req = TestRequest::default()
823 .append_header((header::COOKIE, "cookie1=value1; value2; cookie3=value3"))
824 .to_http_request();
825 {
826 let cookies = req.cookies().unwrap();
827 assert_eq!(cookies.len(), 2);
828 assert_eq!(cookies[0].name(), "cookie1");
829 assert_eq!(cookies[0].value(), "value1");
830 assert_eq!(cookies[1].name(), "cookie3");
831 assert_eq!(cookies[1].value(), "value3");
832 }
833 }
834
835 #[test]
836 fn test_request_query() {
837 let req = TestRequest::with_uri("/?id=test").to_http_request();
838 assert_eq!(req.query_string(), "id=test");
839 }
840
841 #[test]
842 fn test_url_for() {
843 let mut res = ResourceDef::new("/user/{name}.{ext}");
844 res.set_name("index");
845
846 let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
847 rmap.add(&mut res, None);
848 assert!(rmap.has_resource("/user/test.html"));
849 assert!(!rmap.has_resource("/test/unknown"));
850
851 let req = TestRequest::default()
852 .insert_header((header::HOST, "www.rust-lang.org"))
853 .rmap(rmap)
854 .to_http_request();
855
856 assert_eq!(
857 req.url_for("unknown", ["test"]),
858 Err(UrlGenerationError::ResourceNotFound)
859 );
860 assert_eq!(
861 req.url_for("index", ["test"]),
862 Err(UrlGenerationError::NotEnoughElements)
863 );
864 let url = req.url_for("index", ["test", "html"]);
865 assert_eq!(
866 url.ok().unwrap().as_str(),
867 "http://www.rust-lang.org/user/test.html"
868 );
869 }
870
871 #[test]
872 fn test_url_for_map() {
873 let mut res = ResourceDef::new("/user/{name}.{ext}");
874 res.set_name("index");
875
876 let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
877 rmap.add(&mut res, None);
878
879 let req = TestRequest::default()
880 .insert_header((header::HOST, "www.actix.rs"))
881 .rmap(rmap)
882 .to_http_request();
883
884 let mut params = HashMap::new();
885 params.insert("name", "test");
886 params.insert("ext", "html");
887
888 let url = req.url_for_map("index", ¶ms);
889 assert_eq!(
890 url.ok().unwrap().as_str(),
891 "http://www.actix.rs/user/test.html"
892 );
893
894 params.remove("ext");
895 assert_eq!(
896 req.url_for_map("index", ¶ms),
897 Err(UrlGenerationError::NotEnoughElements)
898 );
899 }
900
901 #[test]
902 fn test_url_for_iter() {
903 let mut res = ResourceDef::new("/user/{name}.{ext}");
904 res.set_name("index");
905
906 let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
907 rmap.add(&mut res, None);
908
909 let req = TestRequest::default()
910 .insert_header((header::HOST, "www.actix.rs"))
911 .rmap(rmap)
912 .to_http_request();
913
914 let url = req.url_for_iter("index", [("ext", "html"), ("name", "test")]);
915 assert_eq!(
916 url.ok().unwrap().as_str(),
917 "http://www.actix.rs/user/test.html"
918 );
919
920 let url = req.url_for_iter("index", [("name", "test")]);
921 assert_eq!(url, Err(UrlGenerationError::NotEnoughElements));
922 }
923
924 #[test]
925 fn test_url_for_static() {
926 let mut rdef = ResourceDef::new("/index.html");
927 rdef.set_name("index");
928
929 let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
930 rmap.add(&mut rdef, None);
931
932 assert!(rmap.has_resource("/index.html"));
933
934 let req = TestRequest::with_uri("/test")
935 .insert_header((header::HOST, "www.rust-lang.org"))
936 .rmap(rmap)
937 .to_http_request();
938 let url = req.url_for_static("index");
939 assert_eq!(
940 url.ok().unwrap().as_str(),
941 "http://www.rust-lang.org/index.html"
942 );
943 }
944
945 #[test]
946 fn test_match_name() {
947 let mut rdef = ResourceDef::new("/index.html");
948 rdef.set_name("index");
949
950 let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
951 rmap.add(&mut rdef, None);
952
953 assert!(rmap.has_resource("/index.html"));
954
955 let req = TestRequest::default()
956 .uri("/index.html")
957 .rmap(rmap)
958 .to_http_request();
959
960 assert_eq!(req.match_name(), Some("index"));
961 }
962
963 #[test]
964 fn test_url_for_external() {
965 let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");
966
967 rdef.set_name("youtube");
968
969 let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
970 rmap.add(&mut rdef, None);
971
972 let req = TestRequest::default().rmap(rmap).to_http_request();
973 let url = req.url_for("youtube", ["oHg5SJYRHA0"]);
974 assert_eq!(
975 url.ok().unwrap().as_str(),
976 "https://youtube.com/watch/oHg5SJYRHA0"
977 );
978 }
979
980 #[actix_rt::test]
981 async fn test_drop_http_request_pool() {
982 let srv = init_service(
983 App::new().service(web::resource("/").to(|req: HttpRequest| {
984 HttpResponse::Ok()
985 .insert_header(("pool_cap", req.app_state().pool().cap))
986 .finish()
987 })),
988 )
989 .await;
990
991 let req = TestRequest::default().to_request();
992 let resp = call_service(&srv, req).await;
993
994 drop(srv);
995
996 assert_eq!(resp.headers().get("pool_cap").unwrap(), "128");
997 }
998
999 #[actix_rt::test]
1000 async fn test_request_dropped_after_service_does_not_reenter_pool() {
1001 struct State {
1002 _data: Arc<String>,
1003 }
1004
1005 let (weak_data, app_data) = {
1006 let data = Arc::new("data".to_owned());
1007 (Arc::downgrade(&data), web::Data::new(State { _data: data }))
1008 };
1009
1010 let held_req = Rc::new(RefCell::new(None));
1011
1012 {
1013 let held_req = Rc::clone(&held_req);
1014 let srv = init_service(App::new().app_data(app_data).service(web::resource("/").to(
1015 move |req: HttpRequest| {
1016 *held_req.borrow_mut() = Some(req.clone());
1017 HttpResponse::Ok()
1018 },
1019 )))
1020 .await;
1021
1022 let resp = call_service(&srv, TestRequest::default().to_request()).await;
1023 assert_eq!(resp.status(), StatusCode::OK);
1024
1025 drop(resp);
1026 drop(srv);
1027 }
1028
1029 assert!(weak_data.upgrade().is_some());
1030 drop(held_req.borrow_mut().take());
1031 assert!(weak_data.upgrade().is_none());
1032 }
1033
1034 #[actix_rt::test]
1035 async fn test_data() {
1036 let srv = init_service(App::new().app_data(10usize).service(web::resource("/").to(
1037 |req: HttpRequest| {
1038 if req.app_data::<usize>().is_some() {
1039 HttpResponse::Ok()
1040 } else {
1041 HttpResponse::BadRequest()
1042 }
1043 },
1044 )))
1045 .await;
1046
1047 let req = TestRequest::default().to_request();
1048 let resp = call_service(&srv, req).await;
1049 assert_eq!(resp.status(), StatusCode::OK);
1050
1051 let srv = init_service(App::new().app_data(10u32).service(web::resource("/").to(
1052 |req: HttpRequest| {
1053 if req.app_data::<usize>().is_some() {
1054 HttpResponse::Ok()
1055 } else {
1056 HttpResponse::BadRequest()
1057 }
1058 },
1059 )))
1060 .await;
1061
1062 let req = TestRequest::default().to_request();
1063 let resp = call_service(&srv, req).await;
1064 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
1065 }
1066
1067 #[actix_rt::test]
1068 async fn test_cascading_data() {
1069 #[allow(dead_code)]
1070 fn echo_usize(req: HttpRequest) -> HttpResponse {
1071 let num = req.app_data::<usize>().unwrap();
1072 HttpResponse::Ok().body(num.to_string())
1073 }
1074
1075 let srv = init_service(
1076 App::new()
1077 .app_data(88usize)
1078 .service(web::resource("/").route(web::get().to(echo_usize)))
1079 .service(
1080 web::resource("/one")
1081 .app_data(1u32)
1082 .route(web::get().to(echo_usize)),
1083 ),
1084 )
1085 .await;
1086
1087 let req = TestRequest::get().uri("/").to_request();
1088 let resp = srv.call(req).await.unwrap();
1089 let body = read_body(resp).await;
1090 assert_eq!(body, Bytes::from_static(b"88"));
1091
1092 let req = TestRequest::get().uri("/one").to_request();
1093 let resp = srv.call(req).await.unwrap();
1094 let body = read_body(resp).await;
1095 assert_eq!(body, Bytes::from_static(b"88"));
1096 }
1097
1098 #[actix_rt::test]
1099 async fn test_overwrite_data() {
1100 #[allow(dead_code)]
1101 fn echo_usize(req: HttpRequest) -> HttpResponse {
1102 let num = req.app_data::<usize>().unwrap();
1103 HttpResponse::Ok().body(num.to_string())
1104 }
1105
1106 let srv = init_service(
1107 App::new()
1108 .app_data(88usize)
1109 .service(web::resource("/").route(web::get().to(echo_usize)))
1110 .service(
1111 web::resource("/one")
1112 .app_data(1usize)
1113 .route(web::get().to(echo_usize)),
1114 ),
1115 )
1116 .await;
1117
1118 let req = TestRequest::get().uri("/").to_request();
1119 let resp = srv.call(req).await.unwrap();
1120 let body = read_body(resp).await;
1121 assert_eq!(body, Bytes::from_static(b"88"));
1122
1123 let req = TestRequest::get().uri("/one").to_request();
1124 let resp = srv.call(req).await.unwrap();
1125 let body = read_body(resp).await;
1126 assert_eq!(body, Bytes::from_static(b"1"));
1127 }
1128
1129 #[actix_rt::test]
1130 async fn test_app_data_dropped() {
1131 struct Tracker {
1132 pub dropped: bool,
1133 }
1134 struct Foo {
1135 tracker: Rc<RefCell<Tracker>>,
1136 }
1137 impl Drop for Foo {
1138 fn drop(&mut self) {
1139 self.tracker.borrow_mut().dropped = true;
1140 }
1141 }
1142
1143 let tracker = Rc::new(RefCell::new(Tracker { dropped: false }));
1144 {
1145 let tracker2 = Rc::clone(&tracker);
1146 let srv = init_service(App::new().service(web::resource("/").to(
1147 move |req: HttpRequest| {
1148 req.extensions_mut().insert(Foo {
1149 tracker: Rc::clone(&tracker2),
1150 });
1151 HttpResponse::Ok()
1152 },
1153 )))
1154 .await;
1155
1156 let req = TestRequest::default().to_request();
1157 let resp = call_service(&srv, req).await;
1158 assert_eq!(resp.status(), StatusCode::OK);
1159 }
1160
1161 assert!(tracker.borrow().dropped);
1162 }
1163
1164 #[actix_rt::test]
1165 async fn extract_path_pattern() {
1166 let srv = init_service(
1167 App::new().service(
1168 web::scope("/user/{id}")
1169 .service(web::resource("/profile").route(web::get().to(
1170 move |req: HttpRequest| {
1171 assert_eq!(req.match_pattern(), Some("/user/{id}/profile".to_owned()));
1172
1173 HttpResponse::Ok().finish()
1174 },
1175 )))
1176 .default_service(web::to(move |req: HttpRequest| {
1177 assert!(req.match_pattern().is_none());
1178 HttpResponse::Ok().finish()
1179 })),
1180 ),
1181 )
1182 .await;
1183
1184 let req = TestRequest::get().uri("/user/22/profile").to_request();
1185 let res = call_service(&srv, req).await;
1186 assert_eq!(res.status(), StatusCode::OK);
1187
1188 let req = TestRequest::get().uri("/user/22/not-exist").to_request();
1189 let res = call_service(&srv, req).await;
1190 assert_eq!(res.status(), StatusCode::OK);
1191 }
1192
1193 #[actix_rt::test]
1194 async fn extract_path_pattern_with_guards() {
1195 let srv = init_service(
1196 App::new().service(
1197 web::scope("/widgets")
1198 .service(
1199 web::resource("/{id}")
1200 .name("get_widget")
1201 .guard(guard::Get())
1202 .to(|req: HttpRequest| {
1203 assert_eq!(req.match_pattern(), Some("/widgets/{id}".to_owned()));
1204 assert_eq!(req.match_name(), Some("get_widget"));
1205 HttpResponse::Ok().finish()
1206 }),
1207 )
1208 .service(
1209 web::resource("/action")
1210 .name("widget_action")
1211 .guard(guard::Post())
1212 .to(|req: HttpRequest| {
1213 assert_eq!(req.match_pattern(), Some("/widgets/action".to_owned()));
1214 assert_eq!(req.match_name(), Some("widget_action"));
1215 HttpResponse::Ok().finish()
1216 }),
1217 ),
1218 ),
1219 )
1220 .await;
1221
1222 let req = TestRequest::get().uri("/widgets/42").to_request();
1223 let res = call_service(&srv, req).await;
1224 assert_eq!(res.status(), StatusCode::OK);
1225
1226 let req = TestRequest::post().uri("/widgets/action").to_request();
1227 let res = call_service(&srv, req).await;
1228 assert_eq!(res.status(), StatusCode::OK);
1229 }
1230
1231 #[actix_rt::test]
1232 async fn extract_path_pattern_complex() {
1233 let srv = init_service(
1234 App::new()
1235 .service(web::scope("/user").service(web::scope("/{id}").service(
1236 web::resource("").to(move |req: HttpRequest| {
1237 assert_eq!(req.match_pattern(), Some("/user/{id}".to_owned()));
1238
1239 HttpResponse::Ok().finish()
1240 }),
1241 )))
1242 .service(web::resource("/").to(move |req: HttpRequest| {
1243 assert_eq!(req.match_pattern(), Some("/".to_owned()));
1244
1245 HttpResponse::Ok().finish()
1246 }))
1247 .default_service(web::to(move |req: HttpRequest| {
1248 assert!(req.match_pattern().is_none());
1249 HttpResponse::Ok().finish()
1250 })),
1251 )
1252 .await;
1253
1254 let req = TestRequest::get().uri("/user/test").to_request();
1255 let res = call_service(&srv, req).await;
1256 assert_eq!(res.status(), StatusCode::OK);
1257
1258 let req = TestRequest::get().uri("/").to_request();
1259 let res = call_service(&srv, req).await;
1260 assert_eq!(res.status(), StatusCode::OK);
1261
1262 let req = TestRequest::get().uri("/not-exist").to_request();
1263 let res = call_service(&srv, req).await;
1264 assert_eq!(res.status(), StatusCode::OK);
1265 }
1266
1267 #[actix_rt::test]
1268 async fn url_for_closest_named_resource() {
1269 let srv = test::init_service(
1271 App::new()
1272 .service(
1273 web::scope("/foo")
1274 .service(web::resource("/nested").name("nested").route(web::get().to(
1275 |req: HttpRequest| {
1276 HttpResponse::Ok()
1277 .body(format!("{}", req.url_for_static("nested").unwrap()))
1278 },
1279 )))
1280 .service(web::scope("/baz").service(web::resource("deep")))
1281 .service(web::resource("{foo_param}")),
1282 )
1283 .service(web::scope("/bar").service(
1284 web::resource("/nested").name("nested").route(web::get().to(
1285 |req: HttpRequest| {
1286 HttpResponse::Ok()
1287 .body(format!("{}", req.url_for_static("nested").unwrap()))
1288 },
1289 )),
1290 )),
1291 )
1292 .await;
1293
1294 let foo_resp =
1295 test::call_service(&srv, TestRequest::with_uri("/foo/nested").to_request()).await;
1296 assert_eq!(foo_resp.status(), StatusCode::OK);
1297 let body = read_body(foo_resp).await;
1298 assert_eq!(body, "http://localhost:8080/bar/nested");
1303
1304 let bar_resp =
1305 test::call_service(&srv, TestRequest::with_uri("/bar/nested").to_request()).await;
1306 assert_eq!(bar_resp.status(), StatusCode::OK);
1307 let body = read_body(bar_resp).await;
1308 assert_eq!(body, "http://localhost:8080/bar/nested");
1309 }
1310
1311 #[test]
1312 fn authorization_header_hidden_in_debug() {
1313 let authorization_header = "Basic bXkgdXNlcm5hbWU6bXkgcGFzc3dvcmQK";
1314 let req = TestRequest::get()
1315 .insert_header((crate::http::header::AUTHORIZATION, authorization_header))
1316 .to_http_request();
1317
1318 assert!(!format!("{:?}", req).contains(authorization_header));
1319 }
1320
1321 #[test]
1322 fn proxy_authorization_header_hidden_in_debug() {
1323 let proxy_authorization_header = "secret value";
1324 let req = TestRequest::get()
1325 .insert_header((
1326 crate::http::header::PROXY_AUTHORIZATION,
1327 proxy_authorization_header,
1328 ))
1329 .to_http_request();
1330
1331 assert!(!format!("{:?}", req).contains(proxy_authorization_header));
1332 }
1333
1334 #[test]
1335 fn cookie_header_hidden_in_debug() {
1336 let cookie_header = "secret";
1337 let req = TestRequest::get()
1338 .insert_header((crate::http::header::COOKIE, cookie_header))
1339 .to_http_request();
1340
1341 assert!(!format!("{:?}", req).contains(cookie_header));
1342 }
1343
1344 #[test]
1345 fn other_header_visible_in_debug() {
1346 let location_header = "192.0.0.1";
1347 let req = TestRequest::get()
1348 .insert_header((crate::http::header::LOCATION, location_header))
1349 .to_http_request();
1350
1351 assert!(format!("{:?}", req).contains(location_header));
1352 }
1353
1354 #[test]
1355 fn check_full_url() {
1356 let req = TestRequest::with_uri("/api?id=4&name=foo").to_http_request();
1357 assert_eq!(
1358 req.full_url().as_str(),
1359 "http://localhost:8080/api?id=4&name=foo",
1360 );
1361
1362 let req = TestRequest::with_uri("https://example.com/api?id=4&name=foo").to_http_request();
1363 assert_eq!(
1364 req.full_url().as_str(),
1365 "https://example.com/api?id=4&name=foo",
1366 );
1367
1368 let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
1369 .insert_header(("host", "example.com"))
1370 .to_http_request();
1371 assert_eq!(
1372 req.full_url().as_str(),
1373 "http://example.com/api?id=4&name=foo",
1374 );
1375 }
1376}