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;
15use crate::config::{RequestConfig, RequestTimeout};
16#[cfg(feature = "multipart")]
17use crate::header::CONTENT_LENGTH;
18use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
19use crate::{Method, Url};
20use http::{request::Parts, Extensions, Request as HttpRequest, Version};
21
22pub struct Request {
24 method: Method,
25 url: Url,
26 headers: HeaderMap,
27 body: Option<Body>,
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 version: Version::default(),
51 extensions: Extensions::new(),
52 }
53 }
54
55 #[inline]
57 pub fn method(&self) -> &Method {
58 &self.method
59 }
60
61 #[inline]
63 pub fn method_mut(&mut self) -> &mut Method {
64 &mut self.method
65 }
66
67 #[inline]
69 pub fn url(&self) -> &Url {
70 &self.url
71 }
72
73 #[inline]
75 pub fn url_mut(&mut self) -> &mut Url {
76 &mut self.url
77 }
78
79 #[inline]
81 pub fn headers(&self) -> &HeaderMap {
82 &self.headers
83 }
84
85 #[inline]
87 pub fn headers_mut(&mut self) -> &mut HeaderMap {
88 &mut self.headers
89 }
90
91 #[inline]
93 pub fn body(&self) -> Option<&Body> {
94 self.body.as_ref()
95 }
96
97 #[inline]
99 pub fn body_mut(&mut self) -> &mut Option<Body> {
100 &mut self.body
101 }
102
103 #[inline]
105 pub(crate) fn extensions(&self) -> &Extensions {
106 &self.extensions
107 }
108
109 #[inline]
111 pub(crate) fn extensions_mut(&mut self) -> &mut Extensions {
112 &mut self.extensions
113 }
114
115 #[inline]
117 pub fn timeout(&self) -> Option<&Duration> {
118 RequestConfig::<RequestTimeout>::get(&self.extensions)
119 }
120
121 #[inline]
123 pub fn timeout_mut(&mut self) -> &mut Option<Duration> {
124 RequestConfig::<RequestTimeout>::get_mut(&mut self.extensions)
125 }
126
127 #[inline]
129 pub fn version(&self) -> Version {
130 self.version
131 }
132
133 #[inline]
135 pub fn version_mut(&mut self) -> &mut Version {
136 &mut self.version
137 }
138
139 pub fn try_clone(&self) -> Option<Request> {
143 let body = match self.body.as_ref() {
144 Some(body) => Some(body.try_clone()?),
145 None => None,
146 };
147 let mut req = Request::new(self.method().clone(), self.url().clone());
148 *req.timeout_mut() = self.timeout().copied();
149 *req.headers_mut() = self.headers().clone();
150 *req.version_mut() = self.version();
151 *req.extensions_mut() = self.extensions().clone();
152 req.body = body;
153 Some(req)
154 }
155
156 pub(super) fn pieces(self) -> (Method, Url, HeaderMap, Option<Body>, Version, Extensions) {
157 (
158 self.method,
159 self.url,
160 self.headers,
161 self.body,
162 self.version,
163 self.extensions,
164 )
165 }
166}
167
168impl RequestBuilder {
169 pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
170 let mut builder = RequestBuilder { client, request };
171
172 let auth = builder
173 .request
174 .as_mut()
175 .ok()
176 .and_then(|req| extract_authority(&mut req.url));
177
178 if let Some((username, password)) = auth {
179 builder.basic_auth(username, password)
180 } else {
181 builder
182 }
183 }
184
185 pub fn from_parts(client: Client, request: Request) -> RequestBuilder {
187 RequestBuilder {
188 client,
189 request: crate::Result::Ok(request),
190 }
191 }
192
193 pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
195 where
196 HeaderName: TryFrom<K>,
197 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
198 HeaderValue: TryFrom<V>,
199 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
200 {
201 self.header_sensitive(key, value, false)
202 }
203
204 fn header_sensitive<K, V>(mut self, key: K, value: V, sensitive: bool) -> 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 let mut error = None;
213 if let Ok(ref mut req) = self.request {
214 match <HeaderName as TryFrom<K>>::try_from(key) {
215 Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
216 Ok(mut value) => {
217 if sensitive {
221 value.set_sensitive(true);
222 }
223 req.headers_mut().append(key, value);
224 }
225 Err(e) => error = Some(crate::error::builder(e.into())),
226 },
227 Err(e) => error = Some(crate::error::builder(e.into())),
228 };
229 }
230 if let Some(err) = error {
231 self.request = Err(err);
232 }
233 self
234 }
235
236 pub fn headers(mut self, headers: crate::header::HeaderMap) -> RequestBuilder {
240 if let Ok(ref mut req) = self.request {
241 crate::util::replace_headers(req.headers_mut(), headers);
242 }
243 self
244 }
245
246 pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
261 where
262 U: fmt::Display,
263 P: fmt::Display,
264 {
265 let header_value = crate::util::basic_auth(username, password);
266 self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
267 }
268
269 pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
271 where
272 T: fmt::Display,
273 {
274 let header_value = format!("Bearer {token}");
275 self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
276 }
277
278 pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
280 if let Ok(ref mut req) = self.request {
281 *req.body_mut() = Some(body.into());
282 }
283 self
284 }
285
286 pub fn timeout(mut self, timeout: Duration) -> RequestBuilder {
292 if let Ok(ref mut req) = self.request {
293 *req.timeout_mut() = Some(timeout);
294 }
295 self
296 }
297
298 #[cfg(feature = "multipart")]
321 #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
322 pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
323 let mut builder = self.header(
324 CONTENT_TYPE,
325 format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
326 );
327
328 builder = match multipart.compute_length() {
329 Some(length) => builder.header(CONTENT_LENGTH, length),
330 None => builder,
331 };
332
333 if let Ok(ref mut req) = builder.request {
334 *req.body_mut() = Some(multipart.stream())
335 }
336 builder
337 }
338
339 pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> RequestBuilder {
358 let mut error = None;
359 if let Ok(ref mut req) = self.request {
360 let url = req.url_mut();
361 let mut pairs = url.query_pairs_mut();
362 let serializer = serde_urlencoded::Serializer::new(&mut pairs);
363
364 if let Err(err) = query.serialize(serializer) {
365 error = Some(crate::error::builder(err));
366 }
367 }
368 if let Ok(ref mut req) = self.request {
369 if let Some("") = req.url().query() {
370 req.url_mut().set_query(None);
371 }
372 }
373 if let Some(err) = error {
374 self.request = Err(err);
375 }
376 self
377 }
378
379 pub fn version(mut self, version: Version) -> RequestBuilder {
381 if let Ok(ref mut req) = self.request {
382 req.version = version;
383 }
384 self
385 }
386
387 pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> RequestBuilder {
415 let mut error = None;
416 if let Ok(ref mut req) = self.request {
417 match serde_urlencoded::to_string(form) {
418 Ok(body) => {
419 req.headers_mut()
420 .entry(CONTENT_TYPE)
421 .or_insert(HeaderValue::from_static(
422 "application/x-www-form-urlencoded",
423 ));
424 *req.body_mut() = Some(body.into());
425 }
426 Err(err) => error = Some(crate::error::builder(err)),
427 }
428 }
429 if let Some(err) = error {
430 self.request = Err(err);
431 }
432 self
433 }
434
435 #[cfg(feature = "json")]
446 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
447 pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
448 let mut error = None;
449 if let Ok(ref mut req) = self.request {
450 match serde_json::to_vec(json) {
451 Ok(body) => {
452 if !req.headers().contains_key(CONTENT_TYPE) {
453 req.headers_mut()
454 .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
455 }
456 *req.body_mut() = Some(body.into());
457 }
458 Err(err) => error = Some(crate::error::builder(err)),
459 }
460 }
461 if let Some(err) = error {
462 self.request = Err(err);
463 }
464 self
465 }
466
467 #[doc(hidden)]
475 #[cfg_attr(target_arch = "wasm32", deprecated)]
476 pub fn fetch_mode_no_cors(self) -> RequestBuilder {
477 self
478 }
479
480 pub fn build(self) -> crate::Result<Request> {
483 self.request
484 }
485
486 pub fn build_split(self) -> (Client, crate::Result<Request>) {
492 (self.client, self.request)
493 }
494
495 pub fn send(self) -> impl Future<Output = Result<Response, crate::Error>> {
517 match self.request {
518 Ok(req) => self.client.execute_request(req),
519 Err(err) => Pending::new_err(err),
520 }
521 }
522
523 pub fn try_clone(&self) -> Option<RequestBuilder> {
543 self.request
544 .as_ref()
545 .ok()
546 .and_then(|req| req.try_clone())
547 .map(|req| RequestBuilder {
548 client: self.client.clone(),
549 request: Ok(req),
550 })
551 }
552}
553
554impl fmt::Debug for Request {
555 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
556 fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
557 }
558}
559
560impl fmt::Debug for RequestBuilder {
561 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
562 let mut builder = f.debug_struct("RequestBuilder");
563 match self.request {
564 Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
565 Err(ref err) => builder.field("error", err).finish(),
566 }
567 }
568}
569
570fn fmt_request_fields<'a, 'b>(
571 f: &'a mut fmt::DebugStruct<'a, 'b>,
572 req: &Request,
573) -> &'a mut fmt::DebugStruct<'a, 'b> {
574 f.field("method", &req.method)
575 .field("url", &req.url)
576 .field("headers", &req.headers)
577}
578
579pub(crate) fn extract_authority(url: &mut Url) -> Option<(String, Option<String>)> {
582 use percent_encoding::percent_decode;
583
584 if url.has_authority() {
585 let username: String = percent_decode(url.username().as_bytes())
586 .decode_utf8()
587 .ok()?
588 .into();
589 let password = url.password().and_then(|pass| {
590 percent_decode(pass.as_bytes())
591 .decode_utf8()
592 .ok()
593 .map(String::from)
594 });
595 if !username.is_empty() || password.is_some() {
596 url.set_username("")
597 .expect("has_authority means set_username shouldn't fail");
598 url.set_password(None)
599 .expect("has_authority means set_password shouldn't fail");
600 return Some((username, password));
601 }
602 }
603
604 None
605}
606
607impl<T> TryFrom<HttpRequest<T>> for Request
608where
609 T: Into<Body>,
610{
611 type Error = crate::Error;
612
613 fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
614 let (parts, body) = req.into_parts();
615 let Parts {
616 method,
617 uri,
618 headers,
619 version,
620 extensions,
621 ..
622 } = parts;
623 let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
624 Ok(Request {
625 method,
626 url,
627 headers,
628 body: Some(body.into()),
629 version,
630 extensions,
631 })
632 }
633}
634
635impl TryFrom<Request> for HttpRequest<Body> {
636 type Error = crate::Error;
637
638 fn try_from(req: Request) -> crate::Result<Self> {
639 let Request {
640 method,
641 url,
642 headers,
643 body,
644 version,
645 extensions,
646 ..
647 } = req;
648
649 let mut req = HttpRequest::builder()
650 .version(version)
651 .method(method)
652 .uri(url.as_str())
653 .body(body.unwrap_or_else(Body::empty))
654 .map_err(crate::error::builder)?;
655
656 *req.headers_mut() = headers;
657 *req.extensions_mut() = extensions;
658 Ok(req)
659 }
660}
661
662#[cfg(test)]
663mod tests {
664 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
665
666 use super::{Client, HttpRequest, Request, RequestBuilder, Version};
667 use crate::Method;
668 use serde::Serialize;
669 use std::collections::BTreeMap;
670 use std::convert::TryFrom;
671
672 #[test]
673 fn add_query_append() {
674 let client = Client::new();
675 let some_url = "https://google.com/";
676 let r = client.get(some_url);
677
678 let r = r.query(&[("foo", "bar")]);
679 let r = r.query(&[("qux", 3)]);
680
681 let req = r.build().expect("request is valid");
682 assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
683 }
684
685 #[test]
686 fn add_query_append_same() {
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", "a"), ("foo", "b")]);
692
693 let req = r.build().expect("request is valid");
694 assert_eq!(req.url().query(), Some("foo=a&foo=b"));
695 }
696
697 #[test]
698 fn add_query_struct() {
699 #[derive(Serialize)]
700 struct Params {
701 foo: String,
702 qux: i32,
703 }
704
705 let client = Client::new();
706 let some_url = "https://google.com/";
707 let r = client.get(some_url);
708
709 let params = Params {
710 foo: "bar".into(),
711 qux: 3,
712 };
713
714 let r = r.query(¶ms);
715
716 let req = r.build().expect("request is valid");
717 assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
718 }
719
720 #[test]
721 fn add_query_map() {
722 let mut params = BTreeMap::new();
723 params.insert("foo", "bar");
724 params.insert("qux", "three");
725
726 let client = Client::new();
727 let some_url = "https://google.com/";
728 let r = client.get(some_url);
729
730 let r = r.query(¶ms);
731
732 let req = r.build().expect("request is valid");
733 assert_eq!(req.url().query(), Some("foo=bar&qux=three"));
734 }
735
736 #[test]
737 fn test_replace_headers() {
738 use http::HeaderMap;
739
740 let mut headers = HeaderMap::new();
741 headers.insert("foo", "bar".parse().unwrap());
742 headers.append("foo", "baz".parse().unwrap());
743
744 let client = Client::new();
745 let req = client
746 .get("https://hyper.rs")
747 .header("im-a", "keeper")
748 .header("foo", "pop me")
749 .headers(headers)
750 .build()
751 .expect("request build");
752
753 assert_eq!(req.headers()["im-a"], "keeper");
754
755 let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>();
756 assert_eq!(foo.len(), 2);
757 assert_eq!(foo[0], "bar");
758 assert_eq!(foo[1], "baz");
759 }
760
761 #[test]
762 fn normalize_empty_query() {
763 let client = Client::new();
764 let some_url = "https://google.com/";
765 let empty_query: &[(&str, &str)] = &[];
766
767 let req = client
768 .get(some_url)
769 .query(empty_query)
770 .build()
771 .expect("request build");
772
773 assert_eq!(req.url().query(), None);
774 assert_eq!(req.url().as_str(), "https://google.com/");
775 }
776
777 #[test]
778 fn try_clone_reusable() {
779 let client = Client::new();
780 let builder = client
781 .post("http://httpbin.org/post")
782 .header("foo", "bar")
783 .body("from a &str!");
784 let req = builder
785 .try_clone()
786 .expect("clone successful")
787 .build()
788 .expect("request is valid");
789 assert_eq!(req.url().as_str(), "http://httpbin.org/post");
790 assert_eq!(req.method(), Method::POST);
791 assert_eq!(req.headers()["foo"], "bar");
792 }
793
794 #[test]
795 fn try_clone_no_body() {
796 let client = Client::new();
797 let builder = client.get("http://httpbin.org/get");
798 let req = builder
799 .try_clone()
800 .expect("clone successful")
801 .build()
802 .expect("request is valid");
803 assert_eq!(req.url().as_str(), "http://httpbin.org/get");
804 assert_eq!(req.method(), Method::GET);
805 assert!(req.body().is_none());
806 }
807
808 #[test]
809 #[cfg(feature = "stream")]
810 fn try_clone_stream() {
811 let chunks: Vec<Result<_, ::std::io::Error>> = vec![Ok("hello"), Ok(" "), Ok("world")];
812 let stream = futures_util::stream::iter(chunks);
813 let client = Client::new();
814 let builder = client
815 .get("http://httpbin.org/get")
816 .body(super::Body::wrap_stream(stream));
817 let clone = builder.try_clone();
818 assert!(clone.is_none());
819 }
820
821 #[test]
822 fn convert_url_authority_into_basic_auth() {
823 let client = Client::new();
824 let some_url = "https://Aladdin:open sesame@localhost/";
825
826 let req = client.get(some_url).build().expect("request build");
827
828 assert_eq!(req.url().as_str(), "https://localhost/");
829 assert_eq!(
830 req.headers()["authorization"],
831 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
832 );
833 }
834
835 #[test]
836 fn test_basic_auth_sensitive_header() {
837 let client = Client::new();
838 let some_url = "https://localhost/";
839
840 let req = client
841 .get(some_url)
842 .basic_auth("Aladdin", Some("open sesame"))
843 .build()
844 .expect("request build");
845
846 assert_eq!(req.url().as_str(), "https://localhost/");
847 assert_eq!(
848 req.headers()["authorization"],
849 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
850 );
851 assert!(req.headers()["authorization"].is_sensitive());
852 }
853
854 #[test]
855 fn test_bearer_auth_sensitive_header() {
856 let client = Client::new();
857 let some_url = "https://localhost/";
858
859 let req = client
860 .get(some_url)
861 .bearer_auth("Hold my bear")
862 .build()
863 .expect("request build");
864
865 assert_eq!(req.url().as_str(), "https://localhost/");
866 assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
867 assert!(req.headers()["authorization"].is_sensitive());
868 }
869
870 #[test]
871 fn test_explicit_sensitive_header() {
872 let client = Client::new();
873 let some_url = "https://localhost/";
874
875 let mut header = http::HeaderValue::from_static("in plain sight");
876 header.set_sensitive(true);
877
878 let req = client
879 .get(some_url)
880 .header("hiding", header)
881 .build()
882 .expect("request build");
883
884 assert_eq!(req.url().as_str(), "https://localhost/");
885 assert_eq!(req.headers()["hiding"], "in plain sight");
886 assert!(req.headers()["hiding"].is_sensitive());
887 }
888
889 #[test]
890 fn convert_from_http_request() {
891 let http_request = HttpRequest::builder()
892 .method("GET")
893 .uri("http://localhost/")
894 .header("User-Agent", "my-awesome-agent/1.0")
895 .body("test test test")
896 .unwrap();
897 let req: Request = Request::try_from(http_request).unwrap();
898 assert!(req.body().is_some());
899 let test_data = b"test test test";
900 assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
901 let headers = req.headers();
902 assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
903 assert_eq!(req.method(), Method::GET);
904 assert_eq!(req.url().as_str(), "http://localhost/");
905 }
906
907 #[test]
908 fn set_http_request_version() {
909 let http_request = HttpRequest::builder()
910 .method("GET")
911 .uri("http://localhost/")
912 .header("User-Agent", "my-awesome-agent/1.0")
913 .version(Version::HTTP_11)
914 .body("test test test")
915 .unwrap();
916 let req: Request = Request::try_from(http_request).unwrap();
917 assert!(req.body().is_some());
918 let test_data = b"test test test";
919 assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
920 let headers = req.headers();
921 assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
922 assert_eq!(req.method(), Method::GET);
923 assert_eq!(req.url().as_str(), "http://localhost/");
924 assert_eq!(req.version(), Version::HTTP_11);
925 }
926
927 #[test]
928 fn builder_split_reassemble() {
929 let builder = {
930 let client = Client::new();
931 client.get("http://example.com")
932 };
933 let (client, inner) = builder.build_split();
934 let request = inner.unwrap();
935 let builder = RequestBuilder::from_parts(client, request);
936 builder.build().unwrap();
937 }
938
939 }