1use std::convert::TryFrom;
2use std::fmt;
3use std::future::Future;
4use std::time::Duration;
5
6#[cfg(any(feature = "query", feature = "form", feature = "json"))]
7use serde::Serialize;
8#[cfg(feature = "json")]
9use serde_json;
10
11use super::body::Body;
12use super::client::{Client, Pending};
13#[cfg(feature = "multipart")]
14use super::multipart;
15use super::response::Response;
16use crate::config::{RequestConfig, TotalTimeout};
17#[cfg(feature = "multipart")]
18use crate::header::CONTENT_LENGTH;
19#[cfg(any(feature = "multipart", feature = "form", feature = "json"))]
20use crate::header::CONTENT_TYPE;
21use crate::header::{HeaderMap, HeaderName, HeaderValue};
22use crate::{Method, Url};
23use http::{request::Parts, Extensions, Request as HttpRequest, Version};
24
25pub struct Request {
27 method: Method,
28 url: Url,
29 headers: HeaderMap,
30 body: Option<Body>,
31 version: Version,
32 extensions: Extensions,
33}
34
35#[must_use = "RequestBuilder does nothing until you 'send' it"]
39pub struct RequestBuilder {
40 client: Client,
41 request: crate::Result<Request>,
42}
43
44impl Request {
45 #[inline]
47 pub fn new(method: Method, url: Url) -> Self {
48 Request {
49 method,
50 url,
51 headers: HeaderMap::new(),
52 body: None,
53 version: Version::default(),
54 extensions: Extensions::new(),
55 }
56 }
57
58 #[inline]
60 pub fn method(&self) -> &Method {
61 &self.method
62 }
63
64 #[inline]
66 pub fn method_mut(&mut self) -> &mut Method {
67 &mut self.method
68 }
69
70 #[inline]
72 pub fn url(&self) -> &Url {
73 &self.url
74 }
75
76 #[inline]
78 pub fn url_mut(&mut self) -> &mut Url {
79 &mut self.url
80 }
81
82 #[inline]
84 pub fn headers(&self) -> &HeaderMap {
85 &self.headers
86 }
87
88 #[inline]
90 pub fn headers_mut(&mut self) -> &mut HeaderMap {
91 &mut self.headers
92 }
93
94 #[inline]
96 pub fn body(&self) -> Option<&Body> {
97 self.body.as_ref()
98 }
99
100 #[inline]
102 pub fn body_mut(&mut self) -> &mut Option<Body> {
103 &mut self.body
104 }
105
106 #[inline]
108 pub(crate) fn extensions(&self) -> &Extensions {
109 &self.extensions
110 }
111
112 #[inline]
114 pub(crate) fn extensions_mut(&mut self) -> &mut Extensions {
115 &mut self.extensions
116 }
117
118 #[inline]
120 pub fn timeout(&self) -> Option<&Duration> {
121 RequestConfig::<TotalTimeout>::get(&self.extensions)
122 }
123
124 #[inline]
126 pub fn timeout_mut(&mut self) -> &mut Option<Duration> {
127 RequestConfig::<TotalTimeout>::get_mut(&mut self.extensions)
128 }
129
130 #[inline]
132 pub fn version(&self) -> Version {
133 self.version
134 }
135
136 #[inline]
138 pub fn version_mut(&mut self) -> &mut Version {
139 &mut self.version
140 }
141
142 pub fn try_clone(&self) -> Option<Request> {
146 let body = match self.body.as_ref() {
147 Some(body) => Some(body.try_clone()?),
148 None => None,
149 };
150 let mut req = Request::new(self.method().clone(), self.url().clone());
151 *req.timeout_mut() = self.timeout().copied();
152 *req.headers_mut() = self.headers().clone();
153 *req.version_mut() = self.version();
154 *req.extensions_mut() = self.extensions().clone();
155 req.body = body;
156 Some(req)
157 }
158
159 pub(super) fn pieces(self) -> (Method, Url, HeaderMap, Option<Body>, Version, Extensions) {
160 (
161 self.method,
162 self.url,
163 self.headers,
164 self.body,
165 self.version,
166 self.extensions,
167 )
168 }
169}
170
171impl RequestBuilder {
172 pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
173 let mut builder = RequestBuilder { client, request };
174
175 let auth = builder
176 .request
177 .as_mut()
178 .ok()
179 .and_then(|req| extract_authority(&mut req.url));
180
181 if let Some((username, password)) = auth {
182 builder.basic_auth(username, password)
183 } else {
184 builder
185 }
186 }
187
188 pub fn from_parts(client: Client, request: Request) -> RequestBuilder {
190 RequestBuilder {
191 client,
192 request: crate::Result::Ok(request),
193 }
194 }
195
196 pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
198 where
199 HeaderName: TryFrom<K>,
200 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
201 HeaderValue: TryFrom<V>,
202 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
203 {
204 self.header_sensitive(key, value, false)
205 }
206
207 fn header_sensitive<K, V>(mut self, key: K, value: V, sensitive: bool) -> RequestBuilder
209 where
210 HeaderName: TryFrom<K>,
211 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
212 HeaderValue: TryFrom<V>,
213 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
214 {
215 let mut error = None;
216 if let Ok(ref mut req) = self.request {
217 match <HeaderName as TryFrom<K>>::try_from(key) {
218 Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
219 Ok(mut value) => {
220 if sensitive {
224 value.set_sensitive(true);
225 }
226 req.headers_mut().append(key, value);
227 }
228 Err(e) => error = Some(crate::error::builder(e.into())),
229 },
230 Err(e) => error = Some(crate::error::builder(e.into())),
231 };
232 }
233 if let Some(err) = error {
234 self.request = Err(err);
235 }
236 self
237 }
238
239 pub fn headers(mut self, headers: crate::header::HeaderMap) -> RequestBuilder {
243 if let Ok(ref mut req) = self.request {
244 crate::util::replace_headers(req.headers_mut(), headers);
245 }
246 self
247 }
248
249 pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
264 where
265 U: fmt::Display,
266 P: fmt::Display,
267 {
268 let header_value = crate::util::basic_auth(username, password);
269 self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
270 }
271
272 pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
274 where
275 T: fmt::Display,
276 {
277 let header_value = format!("Bearer {token}");
278 self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
279 }
280
281 pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
283 if let Ok(ref mut req) = self.request {
284 *req.body_mut() = Some(body.into());
285 }
286 self
287 }
288
289 pub fn timeout(mut self, timeout: Duration) -> RequestBuilder {
295 if let Ok(ref mut req) = self.request {
296 *req.timeout_mut() = Some(timeout);
297 }
298 self
299 }
300
301 #[cfg(feature = "multipart")]
324 #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
325 pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
326 let mut builder = self.header(
327 CONTENT_TYPE,
328 format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
329 );
330
331 builder = match multipart.compute_length() {
332 Some(length) => builder.header(CONTENT_LENGTH, length),
333 None => builder,
334 };
335
336 if let Ok(ref mut req) = builder.request {
337 *req.body_mut() = Some(multipart.stream())
338 }
339 builder
340 }
341
342 #[cfg(feature = "query")]
365 #[cfg_attr(docsrs, doc(cfg(feature = "query")))]
366 pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> RequestBuilder {
367 let mut error = None;
368 if let Ok(ref mut req) = self.request {
369 let url = req.url_mut();
370 let mut pairs = url.query_pairs_mut();
371 let serializer = serde_urlencoded::Serializer::new(&mut pairs);
372
373 if let Err(err) = query.serialize(serializer) {
374 error = Some(crate::error::builder(err));
375 }
376 }
377 if let Ok(ref mut req) = self.request {
378 if let Some("") = req.url().query() {
379 req.url_mut().set_query(None);
380 }
381 }
382 if let Some(err) = error {
383 self.request = Err(err);
384 }
385 self
386 }
387
388 pub fn version(mut self, version: Version) -> RequestBuilder {
390 if let Ok(ref mut req) = self.request {
391 req.version = version;
392 }
393 self
394 }
395
396 #[cfg(feature = "form")]
428 #[cfg_attr(docsrs, doc(cfg(feature = "form")))]
429 pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> RequestBuilder {
430 let mut error = None;
431 if let Ok(ref mut req) = self.request {
432 match serde_urlencoded::to_string(form) {
433 Ok(body) => {
434 req.headers_mut()
435 .entry(CONTENT_TYPE)
436 .or_insert(HeaderValue::from_static(
437 "application/x-www-form-urlencoded",
438 ));
439 *req.body_mut() = Some(body.into());
440 }
441 Err(err) => error = Some(crate::error::builder(err)),
442 }
443 }
444 if let Some(err) = error {
445 self.request = Err(err);
446 }
447 self
448 }
449
450 #[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 req.headers_mut()
472 .entry(CONTENT_TYPE)
473 .or_insert_with(|| HeaderValue::from_static("application/json"));
474 *req.body_mut() = Some(body.into());
475 }
476 Err(err) => error = Some(crate::error::builder(err)),
477 }
478 }
479 if let Some(err) = error {
480 self.request = Err(err);
481 }
482 self
483 }
484
485 pub fn build(self) -> crate::Result<Request> {
488 self.request
489 }
490
491 pub fn build_split(self) -> (Client, crate::Result<Request>) {
497 (self.client, self.request)
498 }
499
500 pub fn send(self) -> impl Future<Output = Result<Response, crate::Error>> {
522 match self.request {
523 Ok(req) => self.client.execute_request(req),
524 Err(err) => Pending::new_err(err),
525 }
526 }
527
528 pub fn try_clone(&self) -> Option<RequestBuilder> {
548 self.request
549 .as_ref()
550 .ok()
551 .and_then(|req| req.try_clone())
552 .map(|req| RequestBuilder {
553 client: self.client.clone(),
554 request: Ok(req),
555 })
556 }
557}
558
559impl fmt::Debug for Request {
560 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
561 fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
562 }
563}
564
565impl fmt::Debug for RequestBuilder {
566 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
567 let mut builder = f.debug_struct("RequestBuilder");
568 match self.request {
569 Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
570 Err(ref err) => builder.field("error", err).finish(),
571 }
572 }
573}
574
575fn fmt_request_fields<'a, 'b>(
576 f: &'a mut fmt::DebugStruct<'a, 'b>,
577 req: &Request,
578) -> &'a mut fmt::DebugStruct<'a, 'b> {
579 f.field("method", &req.method)
580 .field("url", &req.url)
581 .field("headers", &req.headers)
582}
583
584pub(crate) fn extract_authority(url: &mut Url) -> Option<(String, Option<String>)> {
587 use percent_encoding::percent_decode;
588
589 if url.has_authority() {
590 let username: String = percent_decode(url.username().as_bytes())
591 .decode_utf8()
592 .ok()?
593 .into();
594 let password = url.password().and_then(|pass| {
595 percent_decode(pass.as_bytes())
596 .decode_utf8()
597 .ok()
598 .map(String::from)
599 });
600 if !username.is_empty() || password.is_some() {
601 url.set_username("")
602 .expect("has_authority means set_username shouldn't fail");
603 url.set_password(None)
604 .expect("has_authority means set_password shouldn't fail");
605 return Some((username, password));
606 }
607 }
608
609 None
610}
611
612impl<T> TryFrom<HttpRequest<T>> for Request
613where
614 T: Into<Body>,
615{
616 type Error = crate::Error;
617
618 fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
619 let (parts, body) = req.into_parts();
620 let Parts {
621 method,
622 uri,
623 headers,
624 version,
625 extensions,
626 ..
627 } = parts;
628 let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
629 Ok(Request {
630 method,
631 url,
632 headers,
633 body: Some(body.into()),
634 version,
635 extensions,
636 })
637 }
638}
639
640impl TryFrom<Request> for HttpRequest<Body> {
641 type Error = crate::Error;
642
643 fn try_from(req: Request) -> crate::Result<Self> {
644 let Request {
645 method,
646 url,
647 headers,
648 body,
649 version,
650 extensions,
651 ..
652 } = req;
653
654 let mut req = HttpRequest::builder()
655 .version(version)
656 .method(method)
657 .uri(url.as_str())
658 .body(body.unwrap_or_else(Body::empty))
659 .map_err(crate::error::builder)?;
660
661 *req.headers_mut() = headers;
662 *req.extensions_mut() = extensions;
663 Ok(req)
664 }
665}
666
667#[cfg(test)]
668mod tests {
669 #![cfg(not(feature = "rustls-no-provider"))]
670
671 use super::*;
672 #[cfg(feature = "query")]
673 use std::collections::BTreeMap;
674
675 #[test]
676 #[cfg(feature = "query")]
677 fn add_query_append() {
678 let client = Client::new();
679 let some_url = "https://google.com/";
680 let r = client.get(some_url);
681
682 let r = r.query(&[("foo", "bar")]);
683 let r = r.query(&[("qux", 3)]);
684
685 let req = r.build().expect("request is valid");
686 assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
687 }
688
689 #[test]
690 #[cfg(feature = "query")]
691 fn add_query_append_same() {
692 let client = Client::new();
693 let some_url = "https://google.com/";
694 let r = client.get(some_url);
695
696 let r = r.query(&[("foo", "a"), ("foo", "b")]);
697
698 let req = r.build().expect("request is valid");
699 assert_eq!(req.url().query(), Some("foo=a&foo=b"));
700 }
701
702 #[test]
703 #[cfg(feature = "query")]
704 fn add_query_struct() {
705 #[derive(Serialize)]
706 struct Params {
707 foo: String,
708 qux: i32,
709 }
710
711 let client = Client::new();
712 let some_url = "https://google.com/";
713 let r = client.get(some_url);
714
715 let params = Params {
716 foo: "bar".into(),
717 qux: 3,
718 };
719
720 let r = r.query(¶ms);
721
722 let req = r.build().expect("request is valid");
723 assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
724 }
725
726 #[test]
727 #[cfg(feature = "query")]
728 fn add_query_map() {
729 let mut params = BTreeMap::new();
730 params.insert("foo", "bar");
731 params.insert("qux", "three");
732
733 let client = Client::new();
734 let some_url = "https://google.com/";
735 let r = client.get(some_url);
736
737 let r = r.query(¶ms);
738
739 let req = r.build().expect("request is valid");
740 assert_eq!(req.url().query(), Some("foo=bar&qux=three"));
741 }
742
743 #[test]
744 fn test_replace_headers() {
745 use http::HeaderMap;
746
747 let mut headers = HeaderMap::new();
748 headers.insert("foo", "bar".parse().unwrap());
749 headers.append("foo", "baz".parse().unwrap());
750
751 let client = Client::new();
752 let req = client
753 .get("https://hyper.rs")
754 .header("im-a", "keeper")
755 .header("foo", "pop me")
756 .headers(headers)
757 .build()
758 .expect("request build");
759
760 assert_eq!(req.headers()["im-a"], "keeper");
761
762 let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>();
763 assert_eq!(foo.len(), 2);
764 assert_eq!(foo[0], "bar");
765 assert_eq!(foo[1], "baz");
766 }
767
768 #[test]
769 #[cfg(feature = "query")]
770 fn normalize_empty_query() {
771 let client = Client::new();
772 let some_url = "https://google.com/";
773 let empty_query: &[(&str, &str)] = &[];
774
775 let req = client
776 .get(some_url)
777 .query(empty_query)
778 .build()
779 .expect("request build");
780
781 assert_eq!(req.url().query(), None);
782 assert_eq!(req.url().as_str(), "https://google.com/");
783 }
784
785 #[test]
786 fn try_clone_reusable() {
787 let client = Client::new();
788 let builder = client
789 .post("http://httpbin.org/post")
790 .header("foo", "bar")
791 .body("from a &str!");
792 let req = builder
793 .try_clone()
794 .expect("clone successful")
795 .build()
796 .expect("request is valid");
797 assert_eq!(req.url().as_str(), "http://httpbin.org/post");
798 assert_eq!(req.method(), Method::POST);
799 assert_eq!(req.headers()["foo"], "bar");
800 }
801
802 #[test]
803 fn try_clone_no_body() {
804 let client = Client::new();
805 let builder = client.get("http://httpbin.org/get");
806 let req = builder
807 .try_clone()
808 .expect("clone successful")
809 .build()
810 .expect("request is valid");
811 assert_eq!(req.url().as_str(), "http://httpbin.org/get");
812 assert_eq!(req.method(), Method::GET);
813 assert!(req.body().is_none());
814 }
815
816 #[test]
817 #[cfg(feature = "stream")]
818 fn try_clone_stream() {
819 let chunks: Vec<Result<_, ::std::io::Error>> = vec![Ok("hello"), Ok(" "), Ok("world")];
820 let stream = futures_util::stream::iter(chunks);
821 let client = Client::new();
822 let builder = client
823 .get("http://httpbin.org/get")
824 .body(super::Body::wrap_stream(stream));
825 let clone = builder.try_clone();
826 assert!(clone.is_none());
827 }
828
829 #[test]
830 fn convert_url_authority_into_basic_auth() {
831 let client = Client::new();
832 let some_url = "https://Aladdin:open sesame@localhost/";
833
834 let req = client.get(some_url).build().expect("request build");
835
836 assert_eq!(req.url().as_str(), "https://localhost/");
837 assert_eq!(
838 req.headers()["authorization"],
839 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
840 );
841 }
842
843 #[test]
844 fn test_basic_auth_sensitive_header() {
845 let client = Client::new();
846 let some_url = "https://localhost/";
847
848 let req = client
849 .get(some_url)
850 .basic_auth("Aladdin", Some("open sesame"))
851 .build()
852 .expect("request build");
853
854 assert_eq!(req.url().as_str(), "https://localhost/");
855 assert_eq!(
856 req.headers()["authorization"],
857 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
858 );
859 assert!(req.headers()["authorization"].is_sensitive());
860 }
861
862 #[test]
863 fn test_bearer_auth_sensitive_header() {
864 let client = Client::new();
865 let some_url = "https://localhost/";
866
867 let req = client
868 .get(some_url)
869 .bearer_auth("Hold my bear")
870 .build()
871 .expect("request build");
872
873 assert_eq!(req.url().as_str(), "https://localhost/");
874 assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
875 assert!(req.headers()["authorization"].is_sensitive());
876 }
877
878 #[test]
879 fn test_explicit_sensitive_header() {
880 let client = Client::new();
881 let some_url = "https://localhost/";
882
883 let mut header = http::HeaderValue::from_static("in plain sight");
884 header.set_sensitive(true);
885
886 let req = client
887 .get(some_url)
888 .header("hiding", header)
889 .build()
890 .expect("request build");
891
892 assert_eq!(req.url().as_str(), "https://localhost/");
893 assert_eq!(req.headers()["hiding"], "in plain sight");
894 assert!(req.headers()["hiding"].is_sensitive());
895 }
896
897 #[test]
898 fn convert_from_http_request() {
899 let http_request = HttpRequest::builder()
900 .method("GET")
901 .uri("http://localhost/")
902 .header("User-Agent", "my-awesome-agent/1.0")
903 .body("test test test")
904 .unwrap();
905 let req: Request = Request::try_from(http_request).unwrap();
906 assert!(req.body().is_some());
907 let test_data = b"test test test";
908 assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
909 let headers = req.headers();
910 assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
911 assert_eq!(req.method(), Method::GET);
912 assert_eq!(req.url().as_str(), "http://localhost/");
913 }
914
915 #[test]
916 fn set_http_request_version() {
917 let http_request = HttpRequest::builder()
918 .method("GET")
919 .uri("http://localhost/")
920 .header("User-Agent", "my-awesome-agent/1.0")
921 .version(Version::HTTP_11)
922 .body("test test test")
923 .unwrap();
924 let req: Request = Request::try_from(http_request).unwrap();
925 assert!(req.body().is_some());
926 let test_data = b"test test test";
927 assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
928 let headers = req.headers();
929 assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
930 assert_eq!(req.method(), Method::GET);
931 assert_eq!(req.url().as_str(), "http://localhost/");
932 assert_eq!(req.version(), Version::HTTP_11);
933 }
934
935 #[test]
936 fn builder_split_reassemble() {
937 let builder = {
938 let client = Client::new();
939 client.get("http://example.com")
940 };
941 let (client, inner) = builder.build_split();
942 let request = inner.unwrap();
943 let builder = RequestBuilder::from_parts(client, request);
944 builder.build().unwrap();
945 }
946
947 }