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::{Method, Url};
19use http::{request::Parts, Extensions, 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 version: Version,
29 extensions: Extensions,
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 version: Version::default(),
52 extensions: Extensions::new(),
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(crate) fn extensions(&self) -> &Extensions {
107 &self.extensions
108 }
109
110 #[inline]
112 pub(crate) fn extensions_mut(&mut self) -> &mut Extensions {
113 &mut self.extensions
114 }
115
116 #[inline]
118 pub fn timeout(&self) -> Option<&Duration> {
119 self.timeout.as_ref()
120 }
121
122 #[inline]
124 pub fn timeout_mut(&mut self) -> &mut Option<Duration> {
125 &mut self.timeout
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.extensions_mut() = self.extensions().clone();
153 req.body = body;
154 Some(req)
155 }
156
157 pub(super) fn pieces(
158 self,
159 ) -> (
160 Method,
161 Url,
162 HeaderMap,
163 Option<Body>,
164 Option<Duration>,
165 Version,
166 Extensions,
167 ) {
168 (
169 self.method,
170 self.url,
171 self.headers,
172 self.body,
173 self.timeout,
174 self.version,
175 self.extensions,
176 )
177 }
178}
179
180impl RequestBuilder {
181 pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
182 let mut builder = RequestBuilder { client, request };
183
184 let auth = builder
185 .request
186 .as_mut()
187 .ok()
188 .and_then(|req| extract_authority(&mut req.url));
189
190 if let Some((username, password)) = auth {
191 builder.basic_auth(username, password)
192 } else {
193 builder
194 }
195 }
196
197 pub fn from_parts(client: Client, request: Request) -> RequestBuilder {
199 RequestBuilder {
200 client,
201 request: crate::Result::Ok(request),
202 }
203 }
204
205 pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
207 where
208 HeaderName: TryFrom<K>,
209 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
210 HeaderValue: TryFrom<V>,
211 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
212 {
213 self.header_sensitive(key, value, false)
214 }
215
216 fn header_sensitive<K, V>(mut self, key: K, value: V, sensitive: bool) -> RequestBuilder
218 where
219 HeaderName: TryFrom<K>,
220 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
221 HeaderValue: TryFrom<V>,
222 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
223 {
224 let mut error = None;
225 if let Ok(ref mut req) = self.request {
226 match <HeaderName as TryFrom<K>>::try_from(key) {
227 Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
228 Ok(mut value) => {
229 if sensitive {
233 value.set_sensitive(true);
234 }
235 req.headers_mut().append(key, value);
236 }
237 Err(e) => error = Some(crate::error::builder(e.into())),
238 },
239 Err(e) => error = Some(crate::error::builder(e.into())),
240 };
241 }
242 if let Some(err) = error {
243 self.request = Err(err);
244 }
245 self
246 }
247
248 pub fn headers(mut self, headers: crate::header::HeaderMap) -> RequestBuilder {
252 if let Ok(ref mut req) = self.request {
253 crate::util::replace_headers(req.headers_mut(), headers);
254 }
255 self
256 }
257
258 pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
273 where
274 U: fmt::Display,
275 P: fmt::Display,
276 {
277 let header_value = crate::util::basic_auth(username, password);
278 self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
279 }
280
281 pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
283 where
284 T: fmt::Display,
285 {
286 let header_value = format!("Bearer {token}");
287 self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
288 }
289
290 pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
292 if let Ok(ref mut req) = self.request {
293 *req.body_mut() = Some(body.into());
294 }
295 self
296 }
297
298 pub fn timeout(mut self, timeout: Duration) -> RequestBuilder {
304 if let Ok(ref mut req) = self.request {
305 *req.timeout_mut() = Some(timeout);
306 }
307 self
308 }
309
310 #[cfg(feature = "multipart")]
333 #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
334 pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
335 let mut builder = self.header(
336 CONTENT_TYPE,
337 format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
338 );
339
340 builder = match multipart.compute_length() {
341 Some(length) => builder.header(CONTENT_LENGTH, length),
342 None => builder,
343 };
344
345 if let Ok(ref mut req) = builder.request {
346 *req.body_mut() = Some(multipart.stream())
347 }
348 builder
349 }
350
351 pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> RequestBuilder {
370 let mut error = None;
371 if let Ok(ref mut req) = self.request {
372 let url = req.url_mut();
373 let mut pairs = url.query_pairs_mut();
374 let serializer = serde_urlencoded::Serializer::new(&mut pairs);
375
376 if let Err(err) = query.serialize(serializer) {
377 error = Some(crate::error::builder(err));
378 }
379 }
380 if let Ok(ref mut req) = self.request {
381 if let Some("") = req.url().query() {
382 req.url_mut().set_query(None);
383 }
384 }
385 if let Some(err) = error {
386 self.request = Err(err);
387 }
388 self
389 }
390
391 pub fn version(mut self, version: Version) -> RequestBuilder {
393 if let Ok(ref mut req) = self.request {
394 req.version = version;
395 }
396 self
397 }
398
399 pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> RequestBuilder {
427 let mut error = None;
428 if let Ok(ref mut req) = self.request {
429 match serde_urlencoded::to_string(form) {
430 Ok(body) => {
431 req.headers_mut()
432 .entry(CONTENT_TYPE)
433 .or_insert(HeaderValue::from_static(
434 "application/x-www-form-urlencoded",
435 ));
436 *req.body_mut() = Some(body.into());
437 }
438 Err(err) => error = Some(crate::error::builder(err)),
439 }
440 }
441 if let Some(err) = error {
442 self.request = Err(err);
443 }
444 self
445 }
446
447 #[cfg(feature = "json")]
458 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
459 pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
460 let mut error = None;
461 if let Ok(ref mut req) = self.request {
462 match serde_json::to_vec(json) {
463 Ok(body) => {
464 if !req.headers().contains_key(CONTENT_TYPE) {
465 req.headers_mut()
466 .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
467 }
468 *req.body_mut() = Some(body.into());
469 }
470 Err(err) => error = Some(crate::error::builder(err)),
471 }
472 }
473 if let Some(err) = error {
474 self.request = Err(err);
475 }
476 self
477 }
478
479 #[doc(hidden)]
487 #[cfg_attr(target_arch = "wasm32", deprecated)]
488 pub fn fetch_mode_no_cors(self) -> RequestBuilder {
489 self
490 }
491
492 pub fn build(self) -> crate::Result<Request> {
495 self.request
496 }
497
498 pub fn build_split(self) -> (Client, crate::Result<Request>) {
504 (self.client, self.request)
505 }
506
507 pub fn send(self) -> impl Future<Output = Result<Response, crate::Error>> {
529 match self.request {
530 Ok(req) => self.client.execute_request(req),
531 Err(err) => Pending::new_err(err),
532 }
533 }
534
535 pub fn try_clone(&self) -> Option<RequestBuilder> {
555 self.request
556 .as_ref()
557 .ok()
558 .and_then(|req| req.try_clone())
559 .map(|req| RequestBuilder {
560 client: self.client.clone(),
561 request: Ok(req),
562 })
563 }
564}
565
566impl fmt::Debug for Request {
567 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
568 fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
569 }
570}
571
572impl fmt::Debug for RequestBuilder {
573 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
574 let mut builder = f.debug_struct("RequestBuilder");
575 match self.request {
576 Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
577 Err(ref err) => builder.field("error", err).finish(),
578 }
579 }
580}
581
582fn fmt_request_fields<'a, 'b>(
583 f: &'a mut fmt::DebugStruct<'a, 'b>,
584 req: &Request,
585) -> &'a mut fmt::DebugStruct<'a, 'b> {
586 f.field("method", &req.method)
587 .field("url", &req.url)
588 .field("headers", &req.headers)
589}
590
591pub(crate) fn extract_authority(url: &mut Url) -> Option<(String, Option<String>)> {
594 use percent_encoding::percent_decode;
595
596 if url.has_authority() {
597 let username: String = percent_decode(url.username().as_bytes())
598 .decode_utf8()
599 .ok()?
600 .into();
601 let password = url.password().and_then(|pass| {
602 percent_decode(pass.as_bytes())
603 .decode_utf8()
604 .ok()
605 .map(String::from)
606 });
607 if !username.is_empty() || password.is_some() {
608 url.set_username("")
609 .expect("has_authority means set_username shouldn't fail");
610 url.set_password(None)
611 .expect("has_authority means set_password shouldn't fail");
612 return Some((username, password));
613 }
614 }
615
616 None
617}
618
619impl<T> TryFrom<HttpRequest<T>> for Request
620where
621 T: Into<Body>,
622{
623 type Error = crate::Error;
624
625 fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
626 let (parts, body) = req.into_parts();
627 let Parts {
628 method,
629 uri,
630 headers,
631 version,
632 extensions,
633 ..
634 } = parts;
635 let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
636 Ok(Request {
637 method,
638 url,
639 headers,
640 body: Some(body.into()),
641 timeout: None,
642 version,
643 extensions,
644 })
645 }
646}
647
648impl TryFrom<Request> for HttpRequest<Body> {
649 type Error = crate::Error;
650
651 fn try_from(req: Request) -> crate::Result<Self> {
652 let Request {
653 method,
654 url,
655 headers,
656 body,
657 version,
658 extensions,
659 ..
660 } = req;
661
662 let mut req = HttpRequest::builder()
663 .version(version)
664 .method(method)
665 .uri(url.as_str())
666 .body(body.unwrap_or_else(Body::empty))
667 .map_err(crate::error::builder)?;
668
669 *req.headers_mut() = headers;
670 *req.extensions_mut() = extensions;
671 Ok(req)
672 }
673}
674
675#[cfg(test)]
676mod tests {
677 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
678
679 use super::{Client, HttpRequest, Request, RequestBuilder, Version};
680 use crate::Method;
681 use serde::Serialize;
682 use std::collections::BTreeMap;
683 use std::convert::TryFrom;
684
685 #[test]
686 fn add_query_append() {
687 let client = Client::new();
688 let some_url = "https://google.com/";
689 let r = client.get(some_url);
690
691 let r = r.query(&[("foo", "bar")]);
692 let r = r.query(&[("qux", 3)]);
693
694 let req = r.build().expect("request is valid");
695 assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
696 }
697
698 #[test]
699 fn add_query_append_same() {
700 let client = Client::new();
701 let some_url = "https://google.com/";
702 let r = client.get(some_url);
703
704 let r = r.query(&[("foo", "a"), ("foo", "b")]);
705
706 let req = r.build().expect("request is valid");
707 assert_eq!(req.url().query(), Some("foo=a&foo=b"));
708 }
709
710 #[test]
711 fn add_query_struct() {
712 #[derive(Serialize)]
713 struct Params {
714 foo: String,
715 qux: i32,
716 }
717
718 let client = Client::new();
719 let some_url = "https://google.com/";
720 let r = client.get(some_url);
721
722 let params = Params {
723 foo: "bar".into(),
724 qux: 3,
725 };
726
727 let r = r.query(¶ms);
728
729 let req = r.build().expect("request is valid");
730 assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
731 }
732
733 #[test]
734 fn add_query_map() {
735 let mut params = BTreeMap::new();
736 params.insert("foo", "bar");
737 params.insert("qux", "three");
738
739 let client = Client::new();
740 let some_url = "https://google.com/";
741 let r = client.get(some_url);
742
743 let r = r.query(¶ms);
744
745 let req = r.build().expect("request is valid");
746 assert_eq!(req.url().query(), Some("foo=bar&qux=three"));
747 }
748
749 #[test]
750 fn test_replace_headers() {
751 use http::HeaderMap;
752
753 let mut headers = HeaderMap::new();
754 headers.insert("foo", "bar".parse().unwrap());
755 headers.append("foo", "baz".parse().unwrap());
756
757 let client = Client::new();
758 let req = client
759 .get("https://hyper.rs")
760 .header("im-a", "keeper")
761 .header("foo", "pop me")
762 .headers(headers)
763 .build()
764 .expect("request build");
765
766 assert_eq!(req.headers()["im-a"], "keeper");
767
768 let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>();
769 assert_eq!(foo.len(), 2);
770 assert_eq!(foo[0], "bar");
771 assert_eq!(foo[1], "baz");
772 }
773
774 #[test]
775 fn normalize_empty_query() {
776 let client = Client::new();
777 let some_url = "https://google.com/";
778 let empty_query: &[(&str, &str)] = &[];
779
780 let req = client
781 .get(some_url)
782 .query(empty_query)
783 .build()
784 .expect("request build");
785
786 assert_eq!(req.url().query(), None);
787 assert_eq!(req.url().as_str(), "https://google.com/");
788 }
789
790 #[test]
791 fn try_clone_reusable() {
792 let client = Client::new();
793 let builder = client
794 .post("http://httpbin.org/post")
795 .header("foo", "bar")
796 .body("from a &str!");
797 let req = builder
798 .try_clone()
799 .expect("clone successful")
800 .build()
801 .expect("request is valid");
802 assert_eq!(req.url().as_str(), "http://httpbin.org/post");
803 assert_eq!(req.method(), Method::POST);
804 assert_eq!(req.headers()["foo"], "bar");
805 }
806
807 #[test]
808 fn try_clone_no_body() {
809 let client = Client::new();
810 let builder = client.get("http://httpbin.org/get");
811 let req = builder
812 .try_clone()
813 .expect("clone successful")
814 .build()
815 .expect("request is valid");
816 assert_eq!(req.url().as_str(), "http://httpbin.org/get");
817 assert_eq!(req.method(), Method::GET);
818 assert!(req.body().is_none());
819 }
820
821 #[test]
822 #[cfg(feature = "stream")]
823 fn try_clone_stream() {
824 let chunks: Vec<Result<_, ::std::io::Error>> = vec![Ok("hello"), Ok(" "), Ok("world")];
825 let stream = futures_util::stream::iter(chunks);
826 let client = Client::new();
827 let builder = client
828 .get("http://httpbin.org/get")
829 .body(super::Body::wrap_stream(stream));
830 let clone = builder.try_clone();
831 assert!(clone.is_none());
832 }
833
834 #[test]
835 fn convert_url_authority_into_basic_auth() {
836 let client = Client::new();
837 let some_url = "https://Aladdin:open sesame@localhost/";
838
839 let req = client.get(some_url).build().expect("request build");
840
841 assert_eq!(req.url().as_str(), "https://localhost/");
842 assert_eq!(
843 req.headers()["authorization"],
844 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
845 );
846 }
847
848 #[test]
849 fn test_basic_auth_sensitive_header() {
850 let client = Client::new();
851 let some_url = "https://localhost/";
852
853 let req = client
854 .get(some_url)
855 .basic_auth("Aladdin", Some("open sesame"))
856 .build()
857 .expect("request build");
858
859 assert_eq!(req.url().as_str(), "https://localhost/");
860 assert_eq!(
861 req.headers()["authorization"],
862 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
863 );
864 assert!(req.headers()["authorization"].is_sensitive());
865 }
866
867 #[test]
868 fn test_bearer_auth_sensitive_header() {
869 let client = Client::new();
870 let some_url = "https://localhost/";
871
872 let req = client
873 .get(some_url)
874 .bearer_auth("Hold my bear")
875 .build()
876 .expect("request build");
877
878 assert_eq!(req.url().as_str(), "https://localhost/");
879 assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
880 assert!(req.headers()["authorization"].is_sensitive());
881 }
882
883 #[test]
884 fn test_explicit_sensitive_header() {
885 let client = Client::new();
886 let some_url = "https://localhost/";
887
888 let mut header = http::HeaderValue::from_static("in plain sight");
889 header.set_sensitive(true);
890
891 let req = client
892 .get(some_url)
893 .header("hiding", header)
894 .build()
895 .expect("request build");
896
897 assert_eq!(req.url().as_str(), "https://localhost/");
898 assert_eq!(req.headers()["hiding"], "in plain sight");
899 assert!(req.headers()["hiding"].is_sensitive());
900 }
901
902 #[test]
903 fn convert_from_http_request() {
904 let http_request = HttpRequest::builder()
905 .method("GET")
906 .uri("http://localhost/")
907 .header("User-Agent", "my-awesome-agent/1.0")
908 .body("test test test")
909 .unwrap();
910 let req: Request = Request::try_from(http_request).unwrap();
911 assert!(req.body().is_some());
912 let test_data = b"test test test";
913 assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
914 let headers = req.headers();
915 assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
916 assert_eq!(req.method(), Method::GET);
917 assert_eq!(req.url().as_str(), "http://localhost/");
918 }
919
920 #[test]
921 fn set_http_request_version() {
922 let http_request = HttpRequest::builder()
923 .method("GET")
924 .uri("http://localhost/")
925 .header("User-Agent", "my-awesome-agent/1.0")
926 .version(Version::HTTP_11)
927 .body("test test test")
928 .unwrap();
929 let req: Request = Request::try_from(http_request).unwrap();
930 assert!(req.body().is_some());
931 let test_data = b"test test test";
932 assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
933 let headers = req.headers();
934 assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
935 assert_eq!(req.method(), Method::GET);
936 assert_eq!(req.url().as_str(), "http://localhost/");
937 assert_eq!(req.version(), Version::HTTP_11);
938 }
939
940 #[test]
941 fn builder_split_reassemble() {
942 let builder = {
943 let client = Client::new();
944 client.get("http://example.com")
945 };
946 let (client, inner) = builder.build_split();
947 let request = inner.unwrap();
948 let builder = RequestBuilder::from_parts(client, request);
949 builder.build().unwrap();
950 }
951
952 }