1use std::convert::TryFrom;
2use std::fmt;
3use std::future::Future;
4use std::io::Write;
5use std::time::Duration;
6
7use base64::write::EncoderWriter as Base64Encoder;
8use serde::Serialize;
9#[cfg(feature = "json")]
10use serde_json;
11
12use super::body::Body;
13use super::client::{Client, Pending};
14#[cfg(feature = "multipart")]
15use super::multipart;
16use super::response::Response;
17#[cfg(feature = "multipart")]
18use crate::header::CONTENT_LENGTH;
19use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
20use crate::{Method, Url};
21use http::{request::Parts, Request as HttpRequest, Version};
22
23pub struct Request {
25 method: Method,
26 url: Url,
27 headers: HeaderMap,
28 body: Option<Body>,
29 timeout: Option<Duration>,
30 version: Version,
31}
32
33#[must_use = "RequestBuilder does nothing until you 'send' it"]
37pub struct RequestBuilder {
38 client: Client,
39 request: crate::Result<Request>,
40}
41
42impl Request {
43 #[inline]
45 pub fn new(method: Method, url: Url) -> Self {
46 Request {
47 method,
48 url,
49 headers: HeaderMap::new(),
50 body: None,
51 timeout: 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 version(&self) -> Version {
119 self.version
120 }
121
122 #[inline]
124 pub fn version_mut(&mut self) -> &mut Version {
125 &mut self.version
126 }
127
128 pub fn try_clone(&self) -> Option<Request> {
132 let body = match self.body.as_ref() {
133 Some(body) => Some(body.try_clone()?),
134 None => None,
135 };
136 let mut req = Request::new(self.method().clone(), self.url().clone());
137 *req.timeout_mut() = self.timeout().cloned();
138 *req.headers_mut() = self.headers().clone();
139 *req.version_mut() = self.version();
140 req.body = body;
141 Some(req)
142 }
143
144 pub(super) fn pieces(
145 self,
146 ) -> (
147 Method,
148 Url,
149 HeaderMap,
150 Option<Body>,
151 Option<Duration>,
152 Version,
153 ) {
154 (
155 self.method,
156 self.url,
157 self.headers,
158 self.body,
159 self.timeout,
160 self.version,
161 )
162 }
163}
164
165impl RequestBuilder {
166 pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
167 let mut builder = RequestBuilder { client, request };
168
169 let auth = builder
170 .request
171 .as_mut()
172 .ok()
173 .and_then(|req| extract_authority(&mut req.url));
174
175 if let Some((username, password)) = auth {
176 builder.basic_auth(username, password)
177 } else {
178 builder
179 }
180 }
181
182 pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
184 where
185 HeaderName: TryFrom<K>,
186 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
187 HeaderValue: TryFrom<V>,
188 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
189 {
190 self.header_sensitive(key, value, false)
191 }
192
193 fn header_sensitive<K, V>(mut self, key: K, value: V, sensitive: bool) -> 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 let mut error = None;
202 if let Ok(ref mut req) = self.request {
203 match <HeaderName as TryFrom<K>>::try_from(key) {
204 Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
205 Ok(mut value) => {
206 if sensitive {
210 value.set_sensitive(true);
211 }
212 req.headers_mut().append(key, value);
213 }
214 Err(e) => error = Some(crate::error::builder(e.into())),
215 },
216 Err(e) => error = Some(crate::error::builder(e.into())),
217 };
218 }
219 if let Some(err) = error {
220 self.request = Err(err);
221 }
222 self
223 }
224
225 pub fn headers(mut self, headers: crate::header::HeaderMap) -> RequestBuilder {
229 if let Ok(ref mut req) = self.request {
230 crate::util::replace_headers(req.headers_mut(), headers);
231 }
232 self
233 }
234
235 pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
250 where
251 U: fmt::Display,
252 P: fmt::Display,
253 {
254 let mut header_value = b"Basic ".to_vec();
255 {
256 let mut encoder = Base64Encoder::new(&mut header_value, base64::STANDARD);
257 write!(encoder, "{}:", username).unwrap();
259 if let Some(password) = password {
260 write!(encoder, "{}", password).unwrap();
261 }
262 }
263
264 self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
265 }
266
267 pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
269 where
270 T: fmt::Display,
271 {
272 let header_value = format!("Bearer {}", token);
273 self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
274 }
275
276 pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
278 if let Ok(ref mut req) = self.request {
279 *req.body_mut() = Some(body.into());
280 }
281 self
282 }
283
284 pub fn timeout(mut self, timeout: Duration) -> RequestBuilder {
290 if let Ok(ref mut req) = self.request {
291 *req.timeout_mut() = Some(timeout);
292 }
293 self
294 }
295
296 #[cfg(feature = "multipart")]
316 #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
317 pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
318 let mut builder = self.header(
319 CONTENT_TYPE,
320 format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
321 );
322
323 builder = match multipart.compute_length() {
324 Some(length) => builder.header(CONTENT_LENGTH, length),
325 None => builder,
326 };
327
328 if let Ok(ref mut req) = builder.request {
329 *req.body_mut() = Some(multipart.stream())
330 }
331 builder
332 }
333
334 pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> RequestBuilder {
353 let mut error = None;
354 if let Ok(ref mut req) = self.request {
355 let url = req.url_mut();
356 let mut pairs = url.query_pairs_mut();
357 let serializer = serde_urlencoded::Serializer::new(&mut pairs);
358
359 if let Err(err) = query.serialize(serializer) {
360 error = Some(crate::error::builder(err));
361 }
362 }
363 if let Ok(ref mut req) = self.request {
364 if let Some("") = req.url().query() {
365 req.url_mut().set_query(None);
366 }
367 }
368 if let Some(err) = error {
369 self.request = Err(err);
370 }
371 self
372 }
373
374 pub fn version(mut self, version: Version) -> RequestBuilder {
376 if let Ok(ref mut req) = self.request {
377 req.version = version;
378 }
379 self
380 }
381
382 pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> RequestBuilder {
410 let mut error = None;
411 if let Ok(ref mut req) = self.request {
412 match serde_urlencoded::to_string(form) {
413 Ok(body) => {
414 req.headers_mut().insert(
415 CONTENT_TYPE,
416 HeaderValue::from_static("application/x-www-form-urlencoded"),
417 );
418 *req.body_mut() = Some(body.into());
419 }
420 Err(err) => error = Some(crate::error::builder(err)),
421 }
422 }
423 if let Some(err) = error {
424 self.request = Err(err);
425 }
426 self
427 }
428
429 #[cfg(feature = "json")]
440 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
441 pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
442 let mut error = None;
443 if let Ok(ref mut req) = self.request {
444 match serde_json::to_vec(json) {
445 Ok(body) => {
446 req.headers_mut()
447 .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
448 *req.body_mut() = Some(body.into());
449 }
450 Err(err) => error = Some(crate::error::builder(err)),
451 }
452 }
453 if let Some(err) = error {
454 self.request = Err(err);
455 }
456 self
457 }
458
459 pub fn fetch_mode_no_cors(self) -> RequestBuilder {
469 self
470 }
471
472 pub fn build(self) -> crate::Result<Request> {
475 self.request
476 }
477
478 pub fn send(self) -> impl Future<Output = Result<Response, crate::Error>> {
500 match self.request {
501 Ok(req) => self.client.execute_request(req),
502 Err(err) => Pending::new_err(err),
503 }
504 }
505
506 pub fn try_clone(&self) -> Option<RequestBuilder> {
526 self.request
527 .as_ref()
528 .ok()
529 .and_then(|req| req.try_clone())
530 .map(|req| RequestBuilder {
531 client: self.client.clone(),
532 request: Ok(req),
533 })
534 }
535}
536
537impl fmt::Debug for Request {
538 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
539 fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
540 }
541}
542
543impl fmt::Debug for RequestBuilder {
544 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
545 let mut builder = f.debug_struct("RequestBuilder");
546 match self.request {
547 Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
548 Err(ref err) => builder.field("error", err).finish(),
549 }
550 }
551}
552
553fn fmt_request_fields<'a, 'b>(
554 f: &'a mut fmt::DebugStruct<'a, 'b>,
555 req: &Request,
556) -> &'a mut fmt::DebugStruct<'a, 'b> {
557 f.field("method", &req.method)
558 .field("url", &req.url)
559 .field("headers", &req.headers)
560}
561
562pub(crate) fn extract_authority(url: &mut Url) -> Option<(String, Option<String>)> {
565 use percent_encoding::percent_decode;
566
567 if url.has_authority() {
568 let username: String = percent_decode(url.username().as_bytes())
569 .decode_utf8()
570 .ok()?
571 .into();
572 let password = url.password().and_then(|pass| {
573 percent_decode(pass.as_bytes())
574 .decode_utf8()
575 .ok()
576 .map(String::from)
577 });
578 if !username.is_empty() || password.is_some() {
579 url.set_username("")
580 .expect("has_authority means set_username shouldn't fail");
581 url.set_password(None)
582 .expect("has_authority means set_password shouldn't fail");
583 return Some((username, password));
584 }
585 }
586
587 None
588}
589
590impl<T> TryFrom<HttpRequest<T>> for Request
591where
592 T: Into<Body>,
593{
594 type Error = crate::Error;
595
596 fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
597 let (parts, body) = req.into_parts();
598 let Parts {
599 method,
600 uri,
601 headers,
602 version,
603 ..
604 } = parts;
605 let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
606 Ok(Request {
607 method,
608 url,
609 headers,
610 body: Some(body.into()),
611 timeout: None,
612 version,
613 })
614 }
615}
616
617impl TryFrom<Request> for HttpRequest<Body> {
618 type Error = crate::Error;
619
620 fn try_from(req: Request) -> crate::Result<Self> {
621 let Request {
622 method,
623 url,
624 headers,
625 body,
626 version,
627 ..
628 } = req;
629
630 let mut req = HttpRequest::builder()
631 .version(version)
632 .method(method)
633 .uri(url.as_str())
634 .body(body.unwrap_or_else(Body::empty))
635 .map_err(crate::error::builder)?;
636
637 *req.headers_mut() = headers;
638 Ok(req)
639 }
640}
641
642#[cfg(test)]
643mod tests {
644 use super::{Client, HttpRequest, Request, Version};
645 use crate::Method;
646 use serde::Serialize;
647 use std::collections::BTreeMap;
648 use std::convert::TryFrom;
649
650 #[test]
651 fn add_query_append() {
652 let client = Client::new();
653 let some_url = "https://google.com/";
654 let r = client.get(some_url);
655
656 let r = r.query(&[("foo", "bar")]);
657 let r = r.query(&[("qux", 3)]);
658
659 let req = r.build().expect("request is valid");
660 assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
661 }
662
663 #[test]
664 fn add_query_append_same() {
665 let client = Client::new();
666 let some_url = "https://google.com/";
667 let r = client.get(some_url);
668
669 let r = r.query(&[("foo", "a"), ("foo", "b")]);
670
671 let req = r.build().expect("request is valid");
672 assert_eq!(req.url().query(), Some("foo=a&foo=b"));
673 }
674
675 #[test]
676 fn add_query_struct() {
677 #[derive(Serialize)]
678 struct Params {
679 foo: String,
680 qux: i32,
681 }
682
683 let client = Client::new();
684 let some_url = "https://google.com/";
685 let r = client.get(some_url);
686
687 let params = Params {
688 foo: "bar".into(),
689 qux: 3,
690 };
691
692 let r = r.query(¶ms);
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_map() {
700 let mut params = BTreeMap::new();
701 params.insert("foo", "bar");
702 params.insert("qux", "three");
703
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(¶ms);
709
710 let req = r.build().expect("request is valid");
711 assert_eq!(req.url().query(), Some("foo=bar&qux=three"));
712 }
713
714 #[test]
715 fn test_replace_headers() {
716 use http::HeaderMap;
717
718 let mut headers = HeaderMap::new();
719 headers.insert("foo", "bar".parse().unwrap());
720 headers.append("foo", "baz".parse().unwrap());
721
722 let client = Client::new();
723 let req = client
724 .get("https://hyper.rs")
725 .header("im-a", "keeper")
726 .header("foo", "pop me")
727 .headers(headers)
728 .build()
729 .expect("request build");
730
731 assert_eq!(req.headers()["im-a"], "keeper");
732
733 let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>();
734 assert_eq!(foo.len(), 2);
735 assert_eq!(foo[0], "bar");
736 assert_eq!(foo[1], "baz");
737 }
738
739 #[test]
740 fn normalize_empty_query() {
741 let client = Client::new();
742 let some_url = "https://google.com/";
743 let empty_query: &[(&str, &str)] = &[];
744
745 let req = client
746 .get(some_url)
747 .query(empty_query)
748 .build()
749 .expect("request build");
750
751 assert_eq!(req.url().query(), None);
752 assert_eq!(req.url().as_str(), "https://google.com/");
753 }
754
755 #[test]
756 fn try_clone_reusable() {
757 let client = Client::new();
758 let builder = client
759 .post("http://httpbin.org/post")
760 .header("foo", "bar")
761 .body("from a &str!");
762 let req = builder
763 .try_clone()
764 .expect("clone successful")
765 .build()
766 .expect("request is valid");
767 assert_eq!(req.url().as_str(), "http://httpbin.org/post");
768 assert_eq!(req.method(), Method::POST);
769 assert_eq!(req.headers()["foo"], "bar");
770 }
771
772 #[test]
773 fn try_clone_no_body() {
774 let client = Client::new();
775 let builder = client.get("http://httpbin.org/get");
776 let req = builder
777 .try_clone()
778 .expect("clone successful")
779 .build()
780 .expect("request is valid");
781 assert_eq!(req.url().as_str(), "http://httpbin.org/get");
782 assert_eq!(req.method(), Method::GET);
783 assert!(req.body().is_none());
784 }
785
786 #[test]
787 #[cfg(feature = "stream")]
788 fn try_clone_stream() {
789 let chunks: Vec<Result<_, ::std::io::Error>> = vec![Ok("hello"), Ok(" "), Ok("world")];
790 let stream = futures_util::stream::iter(chunks);
791 let client = Client::new();
792 let builder = client
793 .get("http://httpbin.org/get")
794 .body(super::Body::wrap_stream(stream));
795 let clone = builder.try_clone();
796 assert!(clone.is_none());
797 }
798
799 #[test]
800 fn convert_url_authority_into_basic_auth() {
801 let client = Client::new();
802 let some_url = "https://Aladdin:open sesame@localhost/";
803
804 let req = client.get(some_url).build().expect("request build");
805
806 assert_eq!(req.url().as_str(), "https://localhost/");
807 assert_eq!(
808 req.headers()["authorization"],
809 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
810 );
811 }
812
813 #[test]
814 fn test_basic_auth_sensitive_header() {
815 let client = Client::new();
816 let some_url = "https://localhost/";
817
818 let req = client
819 .get(some_url)
820 .basic_auth("Aladdin", Some("open sesame"))
821 .build()
822 .expect("request build");
823
824 assert_eq!(req.url().as_str(), "https://localhost/");
825 assert_eq!(
826 req.headers()["authorization"],
827 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
828 );
829 assert!(req.headers()["authorization"].is_sensitive());
830 }
831
832 #[test]
833 fn test_bearer_auth_sensitive_header() {
834 let client = Client::new();
835 let some_url = "https://localhost/";
836
837 let req = client
838 .get(some_url)
839 .bearer_auth("Hold my bear")
840 .build()
841 .expect("request build");
842
843 assert_eq!(req.url().as_str(), "https://localhost/");
844 assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
845 assert!(req.headers()["authorization"].is_sensitive());
846 }
847
848 #[test]
849 fn test_explicit_sensitive_header() {
850 let client = Client::new();
851 let some_url = "https://localhost/";
852
853 let mut header = http::HeaderValue::from_static("in plain sight");
854 header.set_sensitive(true);
855
856 let req = client
857 .get(some_url)
858 .header("hiding", header)
859 .build()
860 .expect("request build");
861
862 assert_eq!(req.url().as_str(), "https://localhost/");
863 assert_eq!(req.headers()["hiding"], "in plain sight");
864 assert!(req.headers()["hiding"].is_sensitive());
865 }
866
867 #[test]
868 fn convert_from_http_request() {
869 let http_request = HttpRequest::builder()
870 .method("GET")
871 .uri("http://localhost/")
872 .header("User-Agent", "my-awesome-agent/1.0")
873 .body("test test test")
874 .unwrap();
875 let req: Request = Request::try_from(http_request).unwrap();
876 assert!(req.body().is_some());
877 let test_data = b"test test test";
878 assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
879 let headers = req.headers();
880 assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
881 assert_eq!(req.method(), Method::GET);
882 assert_eq!(req.url().as_str(), "http://localhost/");
883 }
884
885 #[test]
886 fn set_http_request_version() {
887 let http_request = HttpRequest::builder()
888 .method("GET")
889 .uri("http://localhost/")
890 .header("User-Agent", "my-awesome-agent/1.0")
891 .version(Version::HTTP_11)
892 .body("test test test")
893 .unwrap();
894 let req: Request = Request::try_from(http_request).unwrap();
895 assert!(req.body().is_some());
896 let test_data = b"test test test";
897 assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
898 let headers = req.headers();
899 assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
900 assert_eq!(req.method(), Method::GET);
901 assert_eq!(req.url().as_str(), "http://localhost/");
902 assert_eq!(req.version(), Version::HTTP_11);
903 }
904
905 }