1use std::convert::TryFrom;
2use std::fmt;
3use std::future::Future;
4use std::time::Duration;
5
6use serde::Serialize;
7#[cfg(feature = "json")]
8use serde_json;
9
10use super::body::Body;
11use super::client::{Client, Pending};
12#[cfg(feature = "multipart")]
13use super::multipart;
14use super::response::Response;
15#[cfg(feature = "multipart")]
16use crate::header::CONTENT_LENGTH;
17use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
18use crate::{redirect, Method, Url};
19use http::{request::Parts, Request as HttpRequest, Version};
20
21pub struct Request {
23 method: Method,
24 url: Url,
25 headers: HeaderMap,
26 body: Option<Body>,
27 timeout: Option<Duration>,
28 redirect_policy: Option<redirect::Policy>,
29 version: Version,
30}
31
32#[must_use = "RequestBuilder does nothing until you 'send' it"]
36pub struct RequestBuilder {
37 client: Client,
38 request: crate::Result<Request>,
39}
40
41impl Request {
42 #[inline]
44 pub fn new(method: Method, url: Url) -> Self {
45 Request {
46 method,
47 url,
48 headers: HeaderMap::new(),
49 body: None,
50 timeout: None,
51 redirect_policy: None,
52 version: Version::default(),
53 }
54 }
55
56 #[inline]
58 pub fn method(&self) -> &Method {
59 &self.method
60 }
61
62 #[inline]
64 pub fn method_mut(&mut self) -> &mut Method {
65 &mut self.method
66 }
67
68 #[inline]
70 pub fn url(&self) -> &Url {
71 &self.url
72 }
73
74 #[inline]
76 pub fn url_mut(&mut self) -> &mut Url {
77 &mut self.url
78 }
79
80 #[inline]
82 pub fn headers(&self) -> &HeaderMap {
83 &self.headers
84 }
85
86 #[inline]
88 pub fn headers_mut(&mut self) -> &mut HeaderMap {
89 &mut self.headers
90 }
91
92 #[inline]
94 pub fn body(&self) -> Option<&Body> {
95 self.body.as_ref()
96 }
97
98 #[inline]
100 pub fn body_mut(&mut self) -> &mut Option<Body> {
101 &mut self.body
102 }
103
104 #[inline]
106 pub fn timeout(&self) -> Option<&Duration> {
107 self.timeout.as_ref()
108 }
109
110 #[inline]
112 pub fn timeout_mut(&mut self) -> &mut Option<Duration> {
113 &mut self.timeout
114 }
115
116 #[inline]
118 pub fn redirect_policy(&self) -> Option<&redirect::Policy> {
119 self.redirect_policy.as_ref()
120 }
121
122 #[inline]
124 pub fn redirect_policy_mut(&mut self) -> &mut Option<redirect::Policy> {
125 &mut self.redirect_policy
126 }
127
128 #[inline]
130 pub fn version(&self) -> Version {
131 self.version
132 }
133
134 #[inline]
136 pub fn version_mut(&mut self) -> &mut Version {
137 &mut self.version
138 }
139
140 pub fn try_clone(&self) -> Option<Request> {
144 let body = match self.body.as_ref() {
145 Some(body) => Some(body.try_clone()?),
146 None => None,
147 };
148 let mut req = Request::new(self.method().clone(), self.url().clone());
149 *req.timeout_mut() = self.timeout().copied();
150 *req.headers_mut() = self.headers().clone();
151 *req.version_mut() = self.version();
152 req.body = body;
153 Some(req)
154 }
155
156 pub(super) fn pieces(
157 self,
158 ) -> (
159 Method,
160 Url,
161 HeaderMap,
162 Option<Body>,
163 Option<Duration>,
164 Option<redirect::Policy>,
165 Version,
166 ) {
167 (
168 self.method,
169 self.url,
170 self.headers,
171 self.body,
172 self.timeout,
173 self.redirect_policy,
174 self.version,
175 )
176 }
177}
178
179impl RequestBuilder {
180 pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
181 let mut builder = RequestBuilder { client, request };
182
183 let auth = builder
184 .request
185 .as_mut()
186 .ok()
187 .and_then(|req| extract_authority(&mut req.url));
188
189 if let Some((username, password)) = auth {
190 builder.basic_auth(username, password)
191 } else {
192 builder
193 }
194 }
195
196 pub fn from_parts(client: Client, request: Request) -> RequestBuilder {
198 RequestBuilder {
199 client,
200 request: crate::Result::Ok(request),
201 }
202 }
203
204 pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
206 where
207 HeaderName: TryFrom<K>,
208 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
209 HeaderValue: TryFrom<V>,
210 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
211 {
212 self.header_sensitive(key, value, false)
213 }
214
215 fn header_sensitive<K, V>(mut self, key: K, value: V, sensitive: bool) -> RequestBuilder
217 where
218 HeaderName: TryFrom<K>,
219 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
220 HeaderValue: TryFrom<V>,
221 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
222 {
223 let mut error = None;
224 if let Ok(ref mut req) = self.request {
225 match <HeaderName as TryFrom<K>>::try_from(key) {
226 Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
227 Ok(mut value) => {
228 if sensitive {
232 value.set_sensitive(true);
233 }
234 req.headers_mut().append(key, value);
235 }
236 Err(e) => error = Some(crate::error::builder(e.into())),
237 },
238 Err(e) => error = Some(crate::error::builder(e.into())),
239 };
240 }
241 if let Some(err) = error {
242 self.request = Err(err);
243 }
244 self
245 }
246
247 pub fn headers(mut self, headers: crate::header::HeaderMap) -> RequestBuilder {
251 if let Ok(ref mut req) = self.request {
252 crate::util::replace_headers(req.headers_mut(), headers);
253 }
254 self
255 }
256
257 pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
272 where
273 U: fmt::Display,
274 P: fmt::Display,
275 {
276 let header_value = crate::util::basic_auth(username, password);
277 self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
278 }
279
280 pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
282 where
283 T: fmt::Display,
284 {
285 let header_value = format!("Bearer {token}");
286 self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
287 }
288
289 pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
291 if let Ok(ref mut req) = self.request {
292 *req.body_mut() = Some(body.into());
293 }
294 self
295 }
296
297 pub fn timeout(mut self, timeout: Duration) -> RequestBuilder {
303 if let Ok(ref mut req) = self.request {
304 *req.timeout_mut() = Some(timeout);
305 }
306 self
307 }
308
309 pub fn redirect_policy(mut self, policy: redirect::Policy) -> RequestBuilder {
311 if let Ok(ref mut req) = self.request {
312 *req.redirect_policy_mut() = Some(policy)
313 }
314 self
315 }
316
317 #[cfg(feature = "multipart")]
340 #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
341 pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
342 let mut builder = self.header(
343 CONTENT_TYPE,
344 format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
345 );
346
347 builder = match multipart.compute_length() {
348 Some(length) => builder.header(CONTENT_LENGTH, length),
349 None => builder,
350 };
351
352 if let Ok(ref mut req) = builder.request {
353 *req.body_mut() = Some(multipart.stream())
354 }
355 builder
356 }
357
358 pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> RequestBuilder {
377 let mut error = None;
378 if let Ok(ref mut req) = self.request {
379 let url = req.url_mut();
380 let mut pairs = url.query_pairs_mut();
381 let serializer = serde_urlencoded::Serializer::new(&mut pairs);
382
383 if let Err(err) = query.serialize(serializer) {
384 error = Some(crate::error::builder(err));
385 }
386 }
387 if let Ok(ref mut req) = self.request {
388 if let Some("") = req.url().query() {
389 req.url_mut().set_query(None);
390 }
391 }
392 if let Some(err) = error {
393 self.request = Err(err);
394 }
395 self
396 }
397
398 pub fn version(mut self, version: Version) -> RequestBuilder {
400 if let Ok(ref mut req) = self.request {
401 req.version = version;
402 }
403 self
404 }
405
406 pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> RequestBuilder {
434 let mut error = None;
435 if let Ok(ref mut req) = self.request {
436 match serde_urlencoded::to_string(form) {
437 Ok(body) => {
438 req.headers_mut()
439 .entry(CONTENT_TYPE)
440 .or_insert(HeaderValue::from_static(
441 "application/x-www-form-urlencoded",
442 ));
443 *req.body_mut() = Some(body.into());
444 }
445 Err(err) => error = Some(crate::error::builder(err)),
446 }
447 }
448 if let Some(err) = error {
449 self.request = Err(err);
450 }
451 self
452 }
453
454 #[cfg(feature = "json")]
465 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
466 pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
467 let mut error = None;
468 if let Ok(ref mut req) = self.request {
469 match serde_json::to_vec(json) {
470 Ok(body) => {
471 if !req.headers().contains_key(CONTENT_TYPE) {
472 req.headers_mut()
473 .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
474 }
475 *req.body_mut() = Some(body.into());
476 }
477 Err(err) => error = Some(crate::error::builder(err)),
478 }
479 }
480 if let Some(err) = error {
481 self.request = Err(err);
482 }
483 self
484 }
485
486 #[doc(hidden)]
494 #[cfg_attr(target_arch = "wasm32", deprecated)]
495 pub fn fetch_mode_no_cors(self) -> RequestBuilder {
496 self
497 }
498
499 pub fn build(self) -> crate::Result<Request> {
502 self.request
503 }
504
505 pub fn build_split(self) -> (Client, crate::Result<Request>) {
511 (self.client, self.request)
512 }
513
514 pub fn send(self) -> impl Future<Output = Result<Response, crate::Error>> {
536 match self.request {
537 Ok(req) => self.client.execute_request(req),
538 Err(err) => Pending::new_err(err),
539 }
540 }
541
542 pub fn try_clone(&self) -> Option<RequestBuilder> {
562 self.request
563 .as_ref()
564 .ok()
565 .and_then(|req| req.try_clone())
566 .map(|req| RequestBuilder {
567 client: self.client.clone(),
568 request: Ok(req),
569 })
570 }
571}
572
573impl fmt::Debug for Request {
574 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
575 fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
576 }
577}
578
579impl fmt::Debug for RequestBuilder {
580 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
581 let mut builder = f.debug_struct("RequestBuilder");
582 match self.request {
583 Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
584 Err(ref err) => builder.field("error", err).finish(),
585 }
586 }
587}
588
589fn fmt_request_fields<'a, 'b>(
590 f: &'a mut fmt::DebugStruct<'a, 'b>,
591 req: &Request,
592) -> &'a mut fmt::DebugStruct<'a, 'b> {
593 f.field("method", &req.method)
594 .field("url", &req.url)
595 .field("headers", &req.headers)
596}
597
598pub(crate) fn extract_authority(url: &mut Url) -> Option<(String, Option<String>)> {
601 use percent_encoding::percent_decode;
602
603 if url.has_authority() {
604 let username: String = percent_decode(url.username().as_bytes())
605 .decode_utf8()
606 .ok()?
607 .into();
608 let password = url.password().and_then(|pass| {
609 percent_decode(pass.as_bytes())
610 .decode_utf8()
611 .ok()
612 .map(String::from)
613 });
614 if !username.is_empty() || password.is_some() {
615 url.set_username("")
616 .expect("has_authority means set_username shouldn't fail");
617 url.set_password(None)
618 .expect("has_authority means set_password shouldn't fail");
619 return Some((username, password));
620 }
621 }
622
623 None
624}
625
626impl<T> TryFrom<HttpRequest<T>> for Request
627where
628 T: Into<Body>,
629{
630 type Error = crate::Error;
631
632 fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
633 let (parts, body) = req.into_parts();
634 let Parts {
635 method,
636 uri,
637 headers,
638 version,
639 ..
640 } = parts;
641 let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
642 Ok(Request {
643 method,
644 url,
645 headers,
646 body: Some(body.into()),
647 timeout: None,
648 redirect_policy: None,
649 version,
650 })
651 }
652}
653
654impl TryFrom<Request> for HttpRequest<Body> {
655 type Error = crate::Error;
656
657 fn try_from(req: Request) -> crate::Result<Self> {
658 let Request {
659 method,
660 url,
661 headers,
662 body,
663 version,
664 ..
665 } = req;
666
667 let mut req = HttpRequest::builder()
668 .version(version)
669 .method(method)
670 .uri(url.as_str())
671 .body(body.unwrap_or_else(Body::empty))
672 .map_err(crate::error::builder)?;
673
674 *req.headers_mut() = headers;
675 Ok(req)
676 }
677}
678
679#[cfg(test)]
680mod tests {
681 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
682
683 use super::{Client, HttpRequest, Request, RequestBuilder, Version};
684 use crate::Method;
685 use serde::Serialize;
686 use std::collections::BTreeMap;
687 use std::convert::TryFrom;
688
689 #[test]
690 fn add_query_append() {
691 let client = Client::new();
692 let some_url = "https://google.com/";
693 let r = client.get(some_url);
694
695 let r = r.query(&[("foo", "bar")]);
696 let r = r.query(&[("qux", 3)]);
697
698 let req = r.build().expect("request is valid");
699 assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
700 }
701
702 #[test]
703 fn add_query_append_same() {
704 let client = Client::new();
705 let some_url = "https://google.com/";
706 let r = client.get(some_url);
707
708 let r = r.query(&[("foo", "a"), ("foo", "b")]);
709
710 let req = r.build().expect("request is valid");
711 assert_eq!(req.url().query(), Some("foo=a&foo=b"));
712 }
713
714 #[test]
715 fn add_query_struct() {
716 #[derive(Serialize)]
717 struct Params {
718 foo: String,
719 qux: i32,
720 }
721
722 let client = Client::new();
723 let some_url = "https://google.com/";
724 let r = client.get(some_url);
725
726 let params = Params {
727 foo: "bar".into(),
728 qux: 3,
729 };
730
731 let r = r.query(¶ms);
732
733 let req = r.build().expect("request is valid");
734 assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
735 }
736
737 #[test]
738 fn add_query_map() {
739 let mut params = BTreeMap::new();
740 params.insert("foo", "bar");
741 params.insert("qux", "three");
742
743 let client = Client::new();
744 let some_url = "https://google.com/";
745 let r = client.get(some_url);
746
747 let r = r.query(¶ms);
748
749 let req = r.build().expect("request is valid");
750 assert_eq!(req.url().query(), Some("foo=bar&qux=three"));
751 }
752
753 #[test]
754 fn test_replace_headers() {
755 use http::HeaderMap;
756
757 let mut headers = HeaderMap::new();
758 headers.insert("foo", "bar".parse().unwrap());
759 headers.append("foo", "baz".parse().unwrap());
760
761 let client = Client::new();
762 let req = client
763 .get("https://hyper.rs")
764 .header("im-a", "keeper")
765 .header("foo", "pop me")
766 .headers(headers)
767 .build()
768 .expect("request build");
769
770 assert_eq!(req.headers()["im-a"], "keeper");
771
772 let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>();
773 assert_eq!(foo.len(), 2);
774 assert_eq!(foo[0], "bar");
775 assert_eq!(foo[1], "baz");
776 }
777
778 #[test]
779 fn normalize_empty_query() {
780 let client = Client::new();
781 let some_url = "https://google.com/";
782 let empty_query: &[(&str, &str)] = &[];
783
784 let req = client
785 .get(some_url)
786 .query(empty_query)
787 .build()
788 .expect("request build");
789
790 assert_eq!(req.url().query(), None);
791 assert_eq!(req.url().as_str(), "https://google.com/");
792 }
793
794 #[test]
795 fn try_clone_reusable() {
796 let client = Client::new();
797 let builder = client
798 .post("http://httpbin.org/post")
799 .header("foo", "bar")
800 .body("from a &str!");
801 let req = builder
802 .try_clone()
803 .expect("clone successful")
804 .build()
805 .expect("request is valid");
806 assert_eq!(req.url().as_str(), "http://httpbin.org/post");
807 assert_eq!(req.method(), Method::POST);
808 assert_eq!(req.headers()["foo"], "bar");
809 }
810
811 #[test]
812 fn try_clone_no_body() {
813 let client = Client::new();
814 let builder = client.get("http://httpbin.org/get");
815 let req = builder
816 .try_clone()
817 .expect("clone successful")
818 .build()
819 .expect("request is valid");
820 assert_eq!(req.url().as_str(), "http://httpbin.org/get");
821 assert_eq!(req.method(), Method::GET);
822 assert!(req.body().is_none());
823 }
824
825 #[test]
826 #[cfg(feature = "stream")]
827 fn try_clone_stream() {
828 let chunks: Vec<Result<_, ::std::io::Error>> = vec![Ok("hello"), Ok(" "), Ok("world")];
829 let stream = futures_util::stream::iter(chunks);
830 let client = Client::new();
831 let builder = client
832 .get("http://httpbin.org/get")
833 .body(super::Body::wrap_stream(stream));
834 let clone = builder.try_clone();
835 assert!(clone.is_none());
836 }
837
838 #[test]
839 fn convert_url_authority_into_basic_auth() {
840 let client = Client::new();
841 let some_url = "https://Aladdin:open sesame@localhost/";
842
843 let req = client.get(some_url).build().expect("request build");
844
845 assert_eq!(req.url().as_str(), "https://localhost/");
846 assert_eq!(
847 req.headers()["authorization"],
848 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
849 );
850 }
851
852 #[test]
853 fn test_basic_auth_sensitive_header() {
854 let client = Client::new();
855 let some_url = "https://localhost/";
856
857 let req = client
858 .get(some_url)
859 .basic_auth("Aladdin", Some("open sesame"))
860 .build()
861 .expect("request build");
862
863 assert_eq!(req.url().as_str(), "https://localhost/");
864 assert_eq!(
865 req.headers()["authorization"],
866 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
867 );
868 assert!(req.headers()["authorization"].is_sensitive());
869 }
870
871 #[test]
872 fn test_bearer_auth_sensitive_header() {
873 let client = Client::new();
874 let some_url = "https://localhost/";
875
876 let req = client
877 .get(some_url)
878 .bearer_auth("Hold my bear")
879 .build()
880 .expect("request build");
881
882 assert_eq!(req.url().as_str(), "https://localhost/");
883 assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
884 assert!(req.headers()["authorization"].is_sensitive());
885 }
886
887 #[test]
888 fn test_explicit_sensitive_header() {
889 let client = Client::new();
890 let some_url = "https://localhost/";
891
892 let mut header = http::HeaderValue::from_static("in plain sight");
893 header.set_sensitive(true);
894
895 let req = client
896 .get(some_url)
897 .header("hiding", header)
898 .build()
899 .expect("request build");
900
901 assert_eq!(req.url().as_str(), "https://localhost/");
902 assert_eq!(req.headers()["hiding"], "in plain sight");
903 assert!(req.headers()["hiding"].is_sensitive());
904 }
905
906 #[test]
907 fn convert_from_http_request() {
908 let http_request = HttpRequest::builder()
909 .method("GET")
910 .uri("http://localhost/")
911 .header("User-Agent", "my-awesome-agent/1.0")
912 .body("test test test")
913 .unwrap();
914 let req: Request = Request::try_from(http_request).unwrap();
915 assert!(req.body().is_some());
916 let test_data = b"test test test";
917 assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
918 let headers = req.headers();
919 assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
920 assert_eq!(req.method(), Method::GET);
921 assert_eq!(req.url().as_str(), "http://localhost/");
922 }
923
924 #[test]
925 fn set_http_request_version() {
926 let http_request = HttpRequest::builder()
927 .method("GET")
928 .uri("http://localhost/")
929 .header("User-Agent", "my-awesome-agent/1.0")
930 .version(Version::HTTP_11)
931 .body("test test test")
932 .unwrap();
933 let req: Request = Request::try_from(http_request).unwrap();
934 assert!(req.body().is_some());
935 let test_data = b"test test test";
936 assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
937 let headers = req.headers();
938 assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
939 assert_eq!(req.method(), Method::GET);
940 assert_eq!(req.url().as_str(), "http://localhost/");
941 assert_eq!(req.version(), Version::HTTP_11);
942 }
943
944 #[test]
945 fn builder_split_reassemble() {
946 let builder = {
947 let client = Client::new();
948 client.get("http://example.com")
949 };
950 let (client, inner) = builder.build_split();
951 let request = inner.unwrap();
952 let builder = RequestBuilder::from_parts(client, request);
953 builder.build().unwrap();
954 }
955
956 }