1use std::{
2 cell::{Ref, RefCell, RefMut},
3 fmt, net,
4 rc::Rc,
5 str,
6};
7
8use actix_http::{Message, RequestHead};
9use actix_router::{Path, Url};
10use actix_utils::future::{ok, Ready};
11#[cfg(feature = "cookies")]
12use cookie::{Cookie, ParseError as CookieParseError};
13use smallvec::SmallVec;
14
15use crate::{
16 app_service::AppInitServiceState,
17 config::AppConfig,
18 dev::{Extensions, Payload},
19 error::UrlGenerationError,
20 http::{header::HeaderMap, Method, Uri, Version},
21 info::ConnectionInfo,
22 rmap::ResourceMap,
23 Error, FromRequest, HttpMessage,
24};
25
26#[cfg(feature = "cookies")]
27struct Cookies(Vec<Cookie<'static>>);
28
29#[derive(Clone)]
31pub struct HttpRequest {
32 pub(crate) inner: Rc<HttpRequestInner>,
37}
38
39pub(crate) struct HttpRequestInner {
40 pub(crate) head: Message<RequestHead>,
41 pub(crate) path: Path<Url>,
42 pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,
43 pub(crate) conn_data: Option<Rc<Extensions>>,
44 pub(crate) extensions: Rc<RefCell<Extensions>>,
45 app_state: Rc<AppInitServiceState>,
46}
47
48impl HttpRequest {
49 #[inline]
50 pub(crate) fn new(
51 path: Path<Url>,
52 head: Message<RequestHead>,
53 app_state: Rc<AppInitServiceState>,
54 app_data: Rc<Extensions>,
55 conn_data: Option<Rc<Extensions>>,
56 extensions: Rc<RefCell<Extensions>>,
57 ) -> HttpRequest {
58 let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();
59 data.push(app_data);
60
61 HttpRequest {
62 inner: Rc::new(HttpRequestInner {
63 head,
64 path,
65 app_state,
66 app_data: data,
67 conn_data,
68 extensions,
69 }),
70 }
71 }
72}
73
74impl HttpRequest {
75 #[inline]
77 pub fn head(&self) -> &RequestHead {
78 &self.inner.head
79 }
80
81 #[inline]
84 pub(crate) fn head_mut(&mut self) -> &mut RequestHead {
85 &mut Rc::get_mut(&mut self.inner).unwrap().head
86 }
87
88 #[inline]
90 pub fn uri(&self) -> &Uri {
91 &self.head().uri
92 }
93
94 pub fn full_url(&self) -> url::Url {
111 let info = self.connection_info();
112 let scheme = info.scheme();
113 let host = info.host();
114 let path_and_query = self
115 .uri()
116 .path_and_query()
117 .map(|paq| paq.as_str())
118 .unwrap_or("/");
119
120 url::Url::parse(&format!("{scheme}://{host}{path_and_query}")).unwrap()
121 }
122
123 #[inline]
125 pub fn method(&self) -> &Method {
126 &self.head().method
127 }
128
129 #[inline]
131 pub fn version(&self) -> Version {
132 self.head().version
133 }
134
135 #[inline]
136 pub fn headers(&self) -> &HeaderMap {
138 &self.head().headers
139 }
140
141 #[inline]
143 pub fn path(&self) -> &str {
144 self.head().uri.path()
145 }
146
147 #[inline]
151 pub fn query_string(&self) -> &str {
152 self.uri().query().unwrap_or_default()
153 }
154
155 #[inline]
167 pub fn match_info(&self) -> &Path<Url> {
168 &self.inner.path
169 }
170
171 #[inline]
176 pub(crate) fn match_info_mut(&mut self) -> &mut Path<Url> {
177 &mut Rc::get_mut(&mut self.inner).unwrap().path
178 }
179
180 #[inline]
187 pub fn match_pattern(&self) -> Option<String> {
188 self.resource_map().match_pattern(self.path())
189 }
190
191 #[inline]
195 pub fn match_name(&self) -> Option<&str> {
196 self.resource_map().match_name(self.path())
197 }
198
199 pub fn conn_data<T: 'static>(&self) -> Option<&T> {
207 self.inner
208 .conn_data
209 .as_deref()
210 .and_then(|container| container.get::<T>())
211 }
212
213 pub fn url_for<U, I>(&self, name: &str, elements: U) -> Result<url::Url, UrlGenerationError>
238 where
239 U: IntoIterator<Item = I>,
240 I: AsRef<str>,
241 {
242 self.resource_map().url_for(self, name, elements)
243 }
244
245 pub fn url_for_static(&self, name: &str) -> Result<url::Url, UrlGenerationError> {
250 const NO_PARAMS: [&str; 0] = [];
251 self.url_for(name, NO_PARAMS)
252 }
253
254 #[inline]
256 pub fn resource_map(&self) -> &ResourceMap {
257 self.app_state().rmap()
258 }
259
260 #[inline]
274 pub fn peer_addr(&self) -> Option<net::SocketAddr> {
275 self.head().peer_addr
276 }
277
278 #[inline]
285 pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
286 if !self.extensions().contains::<ConnectionInfo>() {
287 let info = ConnectionInfo::new(self.head(), self.app_config());
288 self.extensions_mut().insert(info);
289 }
290
291 Ref::map(self.extensions(), |data| data.get().unwrap())
292 }
293
294 #[inline]
296 pub fn app_config(&self) -> &AppConfig {
297 self.app_state().config()
298 }
299
300 #[doc(alias = "state")]
328 pub fn app_data<T: 'static>(&self) -> Option<&T> {
329 for container in self.inner.app_data.iter().rev() {
330 if let Some(data) = container.get::<T>() {
331 return Some(data);
332 }
333 }
334
335 None
336 }
337
338 #[inline]
339 fn app_state(&self) -> &AppInitServiceState {
340 &self.inner.app_state
341 }
342
343 #[cfg(feature = "cookies")]
345 pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
346 use actix_http::header::COOKIE;
347
348 if self.extensions().get::<Cookies>().is_none() {
349 let mut cookies = Vec::new();
350 for hdr in self.headers().get_all(COOKIE) {
351 let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
352 for cookie_str in s.split(';').map(|s| s.trim()) {
353 if !cookie_str.is_empty() {
354 cookies.push(Cookie::parse_encoded(cookie_str)?.into_owned());
355 }
356 }
357 }
358 self.extensions_mut().insert(Cookies(cookies));
359 }
360
361 Ok(Ref::map(self.extensions(), |ext| {
362 &ext.get::<Cookies>().unwrap().0
363 }))
364 }
365
366 #[cfg(feature = "cookies")]
368 pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
369 if let Ok(cookies) = self.cookies() {
370 for cookie in cookies.iter() {
371 if cookie.name() == name {
372 return Some(cookie.to_owned());
373 }
374 }
375 }
376 None
377 }
378}
379
380impl HttpMessage for HttpRequest {
381 type Stream = ();
382
383 #[inline]
384 fn headers(&self) -> &HeaderMap {
385 &self.head().headers
386 }
387
388 #[inline]
389 fn extensions(&self) -> Ref<'_, Extensions> {
390 self.inner.extensions.borrow()
391 }
392
393 #[inline]
394 fn extensions_mut(&self) -> RefMut<'_, Extensions> {
395 self.inner.extensions.borrow_mut()
396 }
397
398 #[inline]
399 fn take_payload(&mut self) -> Payload<Self::Stream> {
400 Payload::None
401 }
402}
403
404impl Drop for HttpRequest {
405 fn drop(&mut self) {
406 if let Some(inner) = Rc::get_mut(&mut self.inner) {
410 if inner.app_state.pool().is_available() {
411 inner.app_data.truncate(1);
413
414 Rc::get_mut(&mut inner.extensions)
417 .unwrap()
418 .get_mut()
419 .clear();
420
421 inner.conn_data = None;
424
425 let req = Rc::clone(&self.inner);
427 self.app_state().pool().push(req);
428 }
429 }
430 }
431}
432
433impl FromRequest for HttpRequest {
451 type Error = Error;
452 type Future = Ready<Result<Self, Error>>;
453
454 #[inline]
455 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
456 ok(req.clone())
457 }
458}
459
460impl fmt::Debug for HttpRequest {
461 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
462 writeln!(
463 f,
464 "\nHttpRequest {:?} {}:{}",
465 self.inner.head.version,
466 self.inner.head.method,
467 self.path()
468 )?;
469
470 if !self.query_string().is_empty() {
471 writeln!(f, " query: ?{:?}", self.query_string())?;
472 }
473
474 if !self.match_info().is_empty() {
475 writeln!(f, " params: {:?}", self.match_info())?;
476 }
477
478 writeln!(f, " headers:")?;
479
480 for (key, val) in self.headers().iter() {
481 match key {
482 &crate::http::header::AUTHORIZATION
484 | &crate::http::header::PROXY_AUTHORIZATION
485 | &crate::http::header::COOKIE => writeln!(f, " {:?}: {:?}", key, "*redacted*")?,
486
487 _ => writeln!(f, " {:?}: {:?}", key, val)?,
488 }
489 }
490
491 Ok(())
492 }
493}
494
495pub(crate) struct HttpRequestPool {
506 inner: RefCell<Vec<Rc<HttpRequestInner>>>,
507 cap: usize,
508}
509
510impl Default for HttpRequestPool {
511 fn default() -> Self {
512 Self::with_capacity(128)
513 }
514}
515
516impl HttpRequestPool {
517 pub(crate) fn with_capacity(cap: usize) -> Self {
518 HttpRequestPool {
519 inner: RefCell::new(Vec::with_capacity(cap)),
520 cap,
521 }
522 }
523
524 #[inline]
526 pub(crate) fn pop(&self) -> Option<HttpRequest> {
527 self.inner
528 .borrow_mut()
529 .pop()
530 .map(|inner| HttpRequest { inner })
531 }
532
533 #[inline]
535 pub(crate) fn is_available(&self) -> bool {
536 self.inner.borrow_mut().len() < self.cap
537 }
538
539 #[inline]
541 pub(crate) fn push(&self, req: Rc<HttpRequestInner>) {
542 self.inner.borrow_mut().push(req);
543 }
544
545 pub(crate) fn clear(&self) {
547 self.inner.borrow_mut().clear()
548 }
549}
550
551#[cfg(test)]
552mod tests {
553 use bytes::Bytes;
554
555 use super::*;
556 use crate::{
557 dev::{ResourceDef, Service},
558 http::{header, StatusCode},
559 test::{self, call_service, init_service, read_body, TestRequest},
560 web, App, HttpResponse,
561 };
562
563 #[test]
564 fn test_debug() {
565 let req = TestRequest::default()
566 .insert_header(("content-type", "text/plain"))
567 .to_http_request();
568 let dbg = format!("{:?}", req);
569 assert!(dbg.contains("HttpRequest"));
570 }
571
572 #[test]
573 #[cfg(feature = "cookies")]
574 fn test_no_request_cookies() {
575 let req = TestRequest::default().to_http_request();
576 assert!(req.cookies().unwrap().is_empty());
577 }
578
579 #[test]
580 #[cfg(feature = "cookies")]
581 fn test_request_cookies() {
582 let req = TestRequest::default()
583 .append_header((header::COOKIE, "cookie1=value1"))
584 .append_header((header::COOKIE, "cookie2=value2"))
585 .to_http_request();
586 {
587 let cookies = req.cookies().unwrap();
588 assert_eq!(cookies.len(), 2);
589 assert_eq!(cookies[0].name(), "cookie1");
590 assert_eq!(cookies[0].value(), "value1");
591 assert_eq!(cookies[1].name(), "cookie2");
592 assert_eq!(cookies[1].value(), "value2");
593 }
594
595 let cookie = req.cookie("cookie1");
596 assert!(cookie.is_some());
597 let cookie = cookie.unwrap();
598 assert_eq!(cookie.name(), "cookie1");
599 assert_eq!(cookie.value(), "value1");
600
601 let cookie = req.cookie("cookie-unknown");
602 assert!(cookie.is_none());
603 }
604
605 #[test]
606 fn test_request_query() {
607 let req = TestRequest::with_uri("/?id=test").to_http_request();
608 assert_eq!(req.query_string(), "id=test");
609 }
610
611 #[test]
612 fn test_url_for() {
613 let mut res = ResourceDef::new("/user/{name}.{ext}");
614 res.set_name("index");
615
616 let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
617 rmap.add(&mut res, None);
618 assert!(rmap.has_resource("/user/test.html"));
619 assert!(!rmap.has_resource("/test/unknown"));
620
621 let req = TestRequest::default()
622 .insert_header((header::HOST, "www.rust-lang.org"))
623 .rmap(rmap)
624 .to_http_request();
625
626 assert_eq!(
627 req.url_for("unknown", ["test"]),
628 Err(UrlGenerationError::ResourceNotFound)
629 );
630 assert_eq!(
631 req.url_for("index", ["test"]),
632 Err(UrlGenerationError::NotEnoughElements)
633 );
634 let url = req.url_for("index", ["test", "html"]);
635 assert_eq!(
636 url.ok().unwrap().as_str(),
637 "http://www.rust-lang.org/user/test.html"
638 );
639 }
640
641 #[test]
642 fn test_url_for_static() {
643 let mut rdef = ResourceDef::new("/index.html");
644 rdef.set_name("index");
645
646 let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
647 rmap.add(&mut rdef, None);
648
649 assert!(rmap.has_resource("/index.html"));
650
651 let req = TestRequest::with_uri("/test")
652 .insert_header((header::HOST, "www.rust-lang.org"))
653 .rmap(rmap)
654 .to_http_request();
655 let url = req.url_for_static("index");
656 assert_eq!(
657 url.ok().unwrap().as_str(),
658 "http://www.rust-lang.org/index.html"
659 );
660 }
661
662 #[test]
663 fn test_match_name() {
664 let mut rdef = ResourceDef::new("/index.html");
665 rdef.set_name("index");
666
667 let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
668 rmap.add(&mut rdef, None);
669
670 assert!(rmap.has_resource("/index.html"));
671
672 let req = TestRequest::default()
673 .uri("/index.html")
674 .rmap(rmap)
675 .to_http_request();
676
677 assert_eq!(req.match_name(), Some("index"));
678 }
679
680 #[test]
681 fn test_url_for_external() {
682 let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");
683
684 rdef.set_name("youtube");
685
686 let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
687 rmap.add(&mut rdef, None);
688
689 let req = TestRequest::default().rmap(rmap).to_http_request();
690 let url = req.url_for("youtube", ["oHg5SJYRHA0"]);
691 assert_eq!(
692 url.ok().unwrap().as_str(),
693 "https://youtube.com/watch/oHg5SJYRHA0"
694 );
695 }
696
697 #[actix_rt::test]
698 async fn test_drop_http_request_pool() {
699 let srv = init_service(
700 App::new().service(web::resource("/").to(|req: HttpRequest| {
701 HttpResponse::Ok()
702 .insert_header(("pool_cap", req.app_state().pool().cap))
703 .finish()
704 })),
705 )
706 .await;
707
708 let req = TestRequest::default().to_request();
709 let resp = call_service(&srv, req).await;
710
711 drop(srv);
712
713 assert_eq!(resp.headers().get("pool_cap").unwrap(), "128");
714 }
715
716 #[actix_rt::test]
717 async fn test_data() {
718 let srv = init_service(App::new().app_data(10usize).service(web::resource("/").to(
719 |req: HttpRequest| {
720 if req.app_data::<usize>().is_some() {
721 HttpResponse::Ok()
722 } else {
723 HttpResponse::BadRequest()
724 }
725 },
726 )))
727 .await;
728
729 let req = TestRequest::default().to_request();
730 let resp = call_service(&srv, req).await;
731 assert_eq!(resp.status(), StatusCode::OK);
732
733 let srv = init_service(App::new().app_data(10u32).service(web::resource("/").to(
734 |req: HttpRequest| {
735 if req.app_data::<usize>().is_some() {
736 HttpResponse::Ok()
737 } else {
738 HttpResponse::BadRequest()
739 }
740 },
741 )))
742 .await;
743
744 let req = TestRequest::default().to_request();
745 let resp = call_service(&srv, req).await;
746 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
747 }
748
749 #[actix_rt::test]
750 async fn test_cascading_data() {
751 #[allow(dead_code)]
752 fn echo_usize(req: HttpRequest) -> HttpResponse {
753 let num = req.app_data::<usize>().unwrap();
754 HttpResponse::Ok().body(num.to_string())
755 }
756
757 let srv = init_service(
758 App::new()
759 .app_data(88usize)
760 .service(web::resource("/").route(web::get().to(echo_usize)))
761 .service(
762 web::resource("/one")
763 .app_data(1u32)
764 .route(web::get().to(echo_usize)),
765 ),
766 )
767 .await;
768
769 let req = TestRequest::get().uri("/").to_request();
770 let resp = srv.call(req).await.unwrap();
771 let body = read_body(resp).await;
772 assert_eq!(body, Bytes::from_static(b"88"));
773
774 let req = TestRequest::get().uri("/one").to_request();
775 let resp = srv.call(req).await.unwrap();
776 let body = read_body(resp).await;
777 assert_eq!(body, Bytes::from_static(b"88"));
778 }
779
780 #[actix_rt::test]
781 async fn test_overwrite_data() {
782 #[allow(dead_code)]
783 fn echo_usize(req: HttpRequest) -> HttpResponse {
784 let num = req.app_data::<usize>().unwrap();
785 HttpResponse::Ok().body(num.to_string())
786 }
787
788 let srv = init_service(
789 App::new()
790 .app_data(88usize)
791 .service(web::resource("/").route(web::get().to(echo_usize)))
792 .service(
793 web::resource("/one")
794 .app_data(1usize)
795 .route(web::get().to(echo_usize)),
796 ),
797 )
798 .await;
799
800 let req = TestRequest::get().uri("/").to_request();
801 let resp = srv.call(req).await.unwrap();
802 let body = read_body(resp).await;
803 assert_eq!(body, Bytes::from_static(b"88"));
804
805 let req = TestRequest::get().uri("/one").to_request();
806 let resp = srv.call(req).await.unwrap();
807 let body = read_body(resp).await;
808 assert_eq!(body, Bytes::from_static(b"1"));
809 }
810
811 #[actix_rt::test]
812 async fn test_app_data_dropped() {
813 struct Tracker {
814 pub dropped: bool,
815 }
816 struct Foo {
817 tracker: Rc<RefCell<Tracker>>,
818 }
819 impl Drop for Foo {
820 fn drop(&mut self) {
821 self.tracker.borrow_mut().dropped = true;
822 }
823 }
824
825 let tracker = Rc::new(RefCell::new(Tracker { dropped: false }));
826 {
827 let tracker2 = Rc::clone(&tracker);
828 let srv = init_service(App::new().service(web::resource("/").to(
829 move |req: HttpRequest| {
830 req.extensions_mut().insert(Foo {
831 tracker: Rc::clone(&tracker2),
832 });
833 HttpResponse::Ok()
834 },
835 )))
836 .await;
837
838 let req = TestRequest::default().to_request();
839 let resp = call_service(&srv, req).await;
840 assert_eq!(resp.status(), StatusCode::OK);
841 }
842
843 assert!(tracker.borrow().dropped);
844 }
845
846 #[actix_rt::test]
847 async fn extract_path_pattern() {
848 let srv = init_service(
849 App::new().service(
850 web::scope("/user/{id}")
851 .service(web::resource("/profile").route(web::get().to(
852 move |req: HttpRequest| {
853 assert_eq!(req.match_pattern(), Some("/user/{id}/profile".to_owned()));
854
855 HttpResponse::Ok().finish()
856 },
857 )))
858 .default_service(web::to(move |req: HttpRequest| {
859 assert!(req.match_pattern().is_none());
860 HttpResponse::Ok().finish()
861 })),
862 ),
863 )
864 .await;
865
866 let req = TestRequest::get().uri("/user/22/profile").to_request();
867 let res = call_service(&srv, req).await;
868 assert_eq!(res.status(), StatusCode::OK);
869
870 let req = TestRequest::get().uri("/user/22/not-exist").to_request();
871 let res = call_service(&srv, req).await;
872 assert_eq!(res.status(), StatusCode::OK);
873 }
874
875 #[actix_rt::test]
876 async fn extract_path_pattern_complex() {
877 let srv = init_service(
878 App::new()
879 .service(web::scope("/user").service(web::scope("/{id}").service(
880 web::resource("").to(move |req: HttpRequest| {
881 assert_eq!(req.match_pattern(), Some("/user/{id}".to_owned()));
882
883 HttpResponse::Ok().finish()
884 }),
885 )))
886 .service(web::resource("/").to(move |req: HttpRequest| {
887 assert_eq!(req.match_pattern(), Some("/".to_owned()));
888
889 HttpResponse::Ok().finish()
890 }))
891 .default_service(web::to(move |req: HttpRequest| {
892 assert!(req.match_pattern().is_none());
893 HttpResponse::Ok().finish()
894 })),
895 )
896 .await;
897
898 let req = TestRequest::get().uri("/user/test").to_request();
899 let res = call_service(&srv, req).await;
900 assert_eq!(res.status(), StatusCode::OK);
901
902 let req = TestRequest::get().uri("/").to_request();
903 let res = call_service(&srv, req).await;
904 assert_eq!(res.status(), StatusCode::OK);
905
906 let req = TestRequest::get().uri("/not-exist").to_request();
907 let res = call_service(&srv, req).await;
908 assert_eq!(res.status(), StatusCode::OK);
909 }
910
911 #[actix_rt::test]
912 async fn url_for_closest_named_resource() {
913 let srv = test::init_service(
915 App::new()
916 .service(
917 web::scope("/foo")
918 .service(web::resource("/nested").name("nested").route(web::get().to(
919 |req: HttpRequest| {
920 HttpResponse::Ok()
921 .body(format!("{}", req.url_for_static("nested").unwrap()))
922 },
923 )))
924 .service(web::scope("/baz").service(web::resource("deep")))
925 .service(web::resource("{foo_param}")),
926 )
927 .service(web::scope("/bar").service(
928 web::resource("/nested").name("nested").route(web::get().to(
929 |req: HttpRequest| {
930 HttpResponse::Ok()
931 .body(format!("{}", req.url_for_static("nested").unwrap()))
932 },
933 )),
934 )),
935 )
936 .await;
937
938 let foo_resp =
939 test::call_service(&srv, TestRequest::with_uri("/foo/nested").to_request()).await;
940 assert_eq!(foo_resp.status(), StatusCode::OK);
941 let body = read_body(foo_resp).await;
942 assert_eq!(body, "http://localhost:8080/bar/nested");
947
948 let bar_resp =
949 test::call_service(&srv, TestRequest::with_uri("/bar/nested").to_request()).await;
950 assert_eq!(bar_resp.status(), StatusCode::OK);
951 let body = read_body(bar_resp).await;
952 assert_eq!(body, "http://localhost:8080/bar/nested");
953 }
954
955 #[test]
956 fn authorization_header_hidden_in_debug() {
957 let authorization_header = "Basic bXkgdXNlcm5hbWU6bXkgcGFzc3dvcmQK";
958 let req = TestRequest::get()
959 .insert_header((crate::http::header::AUTHORIZATION, authorization_header))
960 .to_http_request();
961
962 assert!(!format!("{:?}", req).contains(authorization_header));
963 }
964
965 #[test]
966 fn proxy_authorization_header_hidden_in_debug() {
967 let proxy_authorization_header = "secret value";
968 let req = TestRequest::get()
969 .insert_header((
970 crate::http::header::PROXY_AUTHORIZATION,
971 proxy_authorization_header,
972 ))
973 .to_http_request();
974
975 assert!(!format!("{:?}", req).contains(proxy_authorization_header));
976 }
977
978 #[test]
979 fn cookie_header_hidden_in_debug() {
980 let cookie_header = "secret";
981 let req = TestRequest::get()
982 .insert_header((crate::http::header::COOKIE, cookie_header))
983 .to_http_request();
984
985 assert!(!format!("{:?}", req).contains(cookie_header));
986 }
987
988 #[test]
989 fn other_header_visible_in_debug() {
990 let location_header = "192.0.0.1";
991 let req = TestRequest::get()
992 .insert_header((crate::http::header::LOCATION, location_header))
993 .to_http_request();
994
995 assert!(format!("{:?}", req).contains(location_header));
996 }
997
998 #[test]
999 fn check_full_url() {
1000 let req = TestRequest::with_uri("/api?id=4&name=foo").to_http_request();
1001 assert_eq!(
1002 req.full_url().as_str(),
1003 "http://localhost:8080/api?id=4&name=foo",
1004 );
1005
1006 let req = TestRequest::with_uri("https://example.com/api?id=4&name=foo").to_http_request();
1007 assert_eq!(
1008 req.full_url().as_str(),
1009 "https://example.com/api?id=4&name=foo",
1010 );
1011
1012 let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
1013 .insert_header(("host", "example.com"))
1014 .to_http_request();
1015 assert_eq!(
1016 req.full_url().as_str(),
1017 "http://example.com/api?id=4&name=foo",
1018 );
1019 }
1020}