1use std::convert::TryFrom;
2use std::fmt;
3use std::future::Future;
4use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
5use std::time::Duration;
6
7use http::{Request as HttpRequest, Version, request::Parts};
8use serde::Serialize;
9
10use super::body::Body;
11use super::http::{Client, Pending};
12#[cfg(feature = "multipart")]
13use super::multipart;
14use super::response::Response;
15use crate::header::{CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue};
16use crate::proxy::IntoProxy;
17use crate::util::client::{NetworkScheme, NetworkSchemeBuilder};
18use crate::{Method, Url, redirect};
19
20pub struct Request {
22 method: Method,
23 url: Url,
24 headers: HeaderMap,
25 body: Option<Body>,
26 timeout: Option<Duration>,
27 read_timeout: Option<Duration>,
28 version: Option<Version>,
29 redirect: Option<redirect::Policy>,
30 allow_compression: bool,
31 network_scheme: NetworkSchemeBuilder,
32 protocol: Option<hyper2::ext::Protocol>,
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 timeout: None,
54 read_timeout: None,
55 version: None,
56 redirect: None,
57 allow_compression: cfg!(any(
58 feature = "gzip",
59 feature = "brotli",
60 feature = "deflate",
61 feature = "zstd"
62 )),
63 network_scheme: NetworkScheme::builder(),
64 protocol: None,
65 }
66 }
67
68 #[inline]
70 pub fn method(&self) -> &Method {
71 &self.method
72 }
73
74 #[inline]
76 pub fn method_mut(&mut self) -> &mut Method {
77 &mut self.method
78 }
79
80 #[inline]
82 pub fn url(&self) -> &Url {
83 &self.url
84 }
85
86 #[inline]
88 pub fn url_mut(&mut self) -> &mut Url {
89 &mut self.url
90 }
91
92 #[inline]
94 pub fn headers(&self) -> &HeaderMap {
95 &self.headers
96 }
97
98 #[inline]
100 pub fn headers_mut(&mut self) -> &mut HeaderMap {
101 &mut self.headers
102 }
103
104 #[inline]
106 pub fn redirect_mut(&mut self) -> &mut Option<redirect::Policy> {
107 &mut self.redirect
108 }
109
110 #[cfg(any(
112 feature = "gzip",
113 feature = "brotli",
114 feature = "deflate",
115 feature = "zstd"
116 ))]
117 #[inline]
118 pub fn allow_compression_mut(&mut self) -> &mut bool {
119 &mut self.allow_compression
120 }
121
122 #[inline]
124 pub fn network_scheme_mut(&mut self) -> &mut NetworkSchemeBuilder {
125 &mut self.network_scheme
126 }
127
128 #[inline]
130 pub fn body(&self) -> Option<&Body> {
131 self.body.as_ref()
132 }
133
134 #[inline]
136 pub fn body_mut(&mut self) -> &mut Option<Body> {
137 &mut self.body
138 }
139
140 #[inline]
142 pub fn timeout(&self) -> Option<&Duration> {
143 self.timeout.as_ref()
144 }
145
146 #[inline]
148 pub fn timeout_mut(&mut self) -> &mut Option<Duration> {
149 &mut self.timeout
150 }
151
152 #[inline]
154 pub fn read_timeout(&self) -> Option<&Duration> {
155 self.read_timeout.as_ref()
156 }
157
158 #[inline]
160 pub fn read_timeout_mut(&mut self) -> &mut Option<Duration> {
161 &mut self.read_timeout
162 }
163
164 #[inline]
166 pub fn version(&self) -> Option<Version> {
167 self.version
168 }
169
170 #[inline]
172 pub fn version_mut(&mut self) -> &mut Option<Version> {
173 &mut self.version
174 }
175
176 #[inline]
178 pub fn protocol_mut(&mut self) -> &mut Option<hyper2::ext::Protocol> {
179 &mut self.protocol
180 }
181
182 pub fn try_clone(&self) -> Option<Request> {
186 let body = match self.body.as_ref() {
187 Some(body) => Some(body.try_clone()?),
188 None => None,
189 };
190 let mut req = Request::new(self.method().clone(), self.url().clone());
191 *req.timeout_mut() = self.timeout().copied();
192 *req.read_timeout_mut() = self.read_timeout().copied();
193 *req.headers_mut() = self.headers().clone();
194 *req.version_mut() = self.version();
195 *req.redirect_mut() = self.redirect.clone();
196 #[cfg(any(
197 feature = "gzip",
198 feature = "brotli",
199 feature = "deflate",
200 feature = "zstd"
201 ))]
202 {
203 *req.allow_compression_mut() = self.allow_compression;
204 }
205 *req.network_scheme_mut() = self.network_scheme.clone();
206 *req.protocol_mut() = self.protocol.clone();
207 req.body = body;
208 Some(req)
209 }
210
211 #[allow(clippy::type_complexity)]
212 pub(super) fn pieces(
213 self,
214 ) -> (
215 Method,
216 Url,
217 HeaderMap,
218 Option<Body>,
219 Option<Duration>,
220 Option<Duration>,
221 Option<Version>,
222 Option<redirect::Policy>,
223 bool,
224 NetworkScheme,
225 Option<hyper2::ext::Protocol>,
226 ) {
227 (
228 self.method,
229 self.url,
230 self.headers,
231 self.body,
232 self.timeout,
233 self.read_timeout,
234 self.version,
235 self.redirect,
236 self.allow_compression,
237 self.network_scheme.build(),
238 self.protocol,
239 )
240 }
241}
242
243impl RequestBuilder {
244 pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
245 let mut builder = RequestBuilder { client, request };
246
247 let auth = builder
248 .request
249 .as_mut()
250 .ok()
251 .and_then(|req| extract_authority(&mut req.url));
252
253 if let Some((username, password)) = auth {
254 builder.basic_auth(username, password)
255 } else {
256 builder
257 }
258 }
259
260 pub fn from_parts(client: Client, request: Request) -> RequestBuilder {
262 RequestBuilder {
263 client,
264 request: crate::Result::Ok(request),
265 }
266 }
267
268 pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
272 where
273 HeaderName: TryFrom<K>,
274 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
275 HeaderValue: TryFrom<V>,
276 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
277 {
278 self.header_operation(key, value, false, true, false)
279 }
280
281 pub fn header_append<K, V>(self, key: K, value: V) -> RequestBuilder
285 where
286 HeaderName: TryFrom<K>,
287 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
288 HeaderValue: TryFrom<V>,
289 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
290 {
291 self.header_operation(key, value, false, false, false)
292 }
293
294 fn header_operation<K, V>(
300 mut self,
301 key: K,
302 value: V,
303 sensitive: bool,
304 overwrite: bool,
305 or_insert: bool,
306 ) -> RequestBuilder
307 where
308 HeaderName: TryFrom<K>,
309 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
310 HeaderValue: TryFrom<V>,
311 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
312 {
313 let mut error = None;
314 if let Ok(ref mut req) = self.request {
315 match <HeaderName as TryFrom<K>>::try_from(key) {
316 Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
317 Ok(mut value) => {
318 if sensitive {
322 value.set_sensitive(true);
323 }
324
325 if or_insert {
327 req.headers_mut().entry(key).or_insert(value);
328 } else if overwrite {
329 req.headers_mut().insert(key, value);
330 } else {
331 req.headers_mut().append(key, value);
332 }
333 }
334 Err(e) => error = Some(crate::error::builder(e.into())),
335 },
336 Err(e) => error = Some(crate::error::builder(e.into())),
337 };
338 }
339 if let Some(err) = error {
340 self.request = Err(err);
341 }
342 self
343 }
344
345 pub fn headers(mut self, headers: crate::header::HeaderMap) -> RequestBuilder {
349 if let Ok(ref mut req) = self.request {
350 crate::util::replace_headers(req.headers_mut(), headers);
351 }
352 self
353 }
354
355 pub fn auth<V>(self, value: V) -> RequestBuilder
357 where
358 HeaderValue: TryFrom<V>,
359 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
360 {
361 self.header_operation(crate::header::AUTHORIZATION, value, true, true, false)
362 }
363
364 pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
379 where
380 U: fmt::Display,
381 P: fmt::Display,
382 {
383 let header_value = crate::util::basic_auth(username, password);
384 self.header_operation(
385 crate::header::AUTHORIZATION,
386 header_value,
387 true,
388 true,
389 false,
390 )
391 }
392
393 pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
395 where
396 T: fmt::Display,
397 {
398 let header_value = format!("Bearer {}", token);
399 self.header_operation(
400 crate::header::AUTHORIZATION,
401 header_value,
402 true,
403 true,
404 false,
405 )
406 }
407
408 pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
410 if let Ok(ref mut req) = self.request {
411 *req.body_mut() = Some(body.into());
412 }
413 self
414 }
415
416 pub fn timeout(mut self, timeout: Duration) -> RequestBuilder {
422 if let Ok(ref mut req) = self.request {
423 *req.timeout_mut() = Some(timeout);
424 }
425 self
426 }
427
428 pub fn read_timeout(mut self, timeout: Duration) -> RequestBuilder {
434 if let Ok(ref mut req) = self.request {
435 *req.read_timeout_mut() = Some(timeout);
436 }
437 self
438 }
439
440 #[cfg(feature = "multipart")]
460 #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
461 pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
462 let mut builder = self.header_operation(
463 CONTENT_TYPE,
464 format!("multipart/form-data; boundary={}", multipart.boundary()),
465 false,
466 false,
467 true,
468 );
469
470 builder = match multipart.compute_length() {
471 Some(length) => builder.header(http::header::CONTENT_LENGTH, length),
472 None => builder,
473 };
474
475 if let Ok(ref mut req) = builder.request {
476 *req.body_mut() = Some(multipart.stream())
477 }
478 builder
479 }
480
481 pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> RequestBuilder {
500 let mut error = None;
501 if let Ok(ref mut req) = self.request {
502 let url = req.url_mut();
503 let mut pairs = url.query_pairs_mut();
504 let serializer = serde_urlencoded::Serializer::new(&mut pairs);
505
506 if let Err(err) = query.serialize(serializer) {
507 error = Some(crate::error::builder(err));
508 }
509 }
510 if let Ok(ref mut req) = self.request {
511 if let Some("") = req.url().query() {
512 req.url_mut().set_query(None);
513 }
514 }
515 if let Some(err) = error {
516 self.request = Err(err);
517 }
518 self
519 }
520
521 pub fn version(mut self, version: Version) -> RequestBuilder {
523 if let Ok(ref mut req) = self.request {
524 req.version = Some(version);
525 }
526 self
527 }
528
529 pub fn redirect(mut self, policy: redirect::Policy) -> RequestBuilder {
531 if let Ok(ref mut req) = self.request {
532 req.redirect = Some(policy)
533 }
534 self
535 }
536
537 #[cfg(any(
542 feature = "gzip",
543 feature = "brotli",
544 feature = "deflate",
545 feature = "zstd"
546 ))]
547 pub fn allow_compression(mut self, allow_compression: bool) -> Self {
548 if let Ok(ref mut req) = self.request {
549 req.allow_compression = allow_compression
550 }
551 self
552 }
553
554 pub fn proxy<P>(mut self, proxy: P) -> RequestBuilder
578 where
579 P: IntoProxy,
580 {
581 if let Ok(ref mut req) = self.request {
582 match proxy.into_proxy() {
583 Ok(proxy) => {
584 if let Some(proxy_scheme) = proxy.intercept(req.url()) {
585 req.network_scheme.proxy_scheme(proxy_scheme);
586 }
587 }
588 Err(err) => {
589 self.request = Err(crate::error::builder(err));
590 }
591 }
592 }
593 self
594 }
595
596 pub fn local_address<V>(mut self, local_address: V) -> RequestBuilder
598 where
599 V: Into<Option<IpAddr>>,
600 {
601 if let Ok(ref mut req) = self.request {
602 req.network_scheme.address(local_address);
603 }
604 self
605 }
606
607 pub fn local_addresses<V4, V6>(mut self, ipv4: V4, ipv6: V6) -> RequestBuilder
609 where
610 V4: Into<Option<Ipv4Addr>>,
611 V6: Into<Option<Ipv6Addr>>,
612 {
613 if let Ok(ref mut req) = self.request {
614 req.network_scheme.addresses(ipv4, ipv6);
615 }
616 self
617 }
618
619 #[cfg(any(
621 target_os = "android",
622 target_os = "fuchsia",
623 target_os = "linux",
624 all(
625 feature = "apple-network-device-binding",
626 any(
627 target_os = "ios",
628 target_os = "visionos",
629 target_os = "macos",
630 target_os = "tvos",
631 target_os = "watchos",
632 )
633 )
634 ))]
635 #[cfg_attr(docsrs, doc(cfg(feature = "apple-network-device-binding")))]
636 pub fn interface<I>(mut self, interface: I) -> RequestBuilder
637 where
638 I: Into<std::borrow::Cow<'static, str>>,
639 {
640 if let Ok(ref mut req) = self.request {
641 req.network_scheme.interface(interface);
642 }
643 self
644 }
645
646 pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> RequestBuilder {
674 if let Ok(ref mut req) = self.request {
675 match serde_urlencoded::to_string(form) {
676 Ok(body) => {
677 req.headers_mut()
678 .entry(CONTENT_TYPE)
679 .or_insert(HeaderValue::from_static(
680 "application/x-www-form-urlencoded",
681 ));
682 *req.body_mut() = Some(body.into());
683 }
684 Err(err) => self.request = Err(crate::error::builder(err)),
685 }
686 }
687 self
688 }
689
690 #[cfg(feature = "json")]
701 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
702 pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
703 if let Ok(ref mut req) = self.request {
704 match serde_json::to_vec(json) {
705 Ok(body) => {
706 req.headers_mut()
707 .entry(CONTENT_TYPE)
708 .or_insert(HeaderValue::from_static("application/json"));
709 *req.body_mut() = Some(body.into());
710 }
711 Err(err) => self.request = Err(crate::error::builder(err)),
712 }
713 }
714
715 self
716 }
717
718 pub fn build(self) -> crate::Result<Request> {
721 self.request
722 }
723
724 pub fn build_split(self) -> (Client, crate::Result<Request>) {
730 (self.client, self.request)
731 }
732
733 pub fn send(self) -> impl Future<Output = Result<Response, crate::Error>> {
755 match self.request {
756 Ok(req) => self.client.execute_request(req),
757 Err(err) => Pending::new_err(err),
758 }
759 }
760
761 pub fn try_clone(&self) -> Option<RequestBuilder> {
781 self.request
782 .as_ref()
783 .ok()
784 .and_then(|req| req.try_clone())
785 .map(|req| RequestBuilder {
786 client: self.client.clone(),
787 request: Ok(req),
788 })
789 }
790}
791
792impl fmt::Debug for Request {
793 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
794 fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
795 }
796}
797
798impl fmt::Debug for RequestBuilder {
799 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
800 let mut builder = f.debug_struct("RequestBuilder");
801 match self.request {
802 Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
803 Err(ref err) => builder.field("error", err).finish(),
804 }
805 }
806}
807
808fn fmt_request_fields<'a, 'b>(
809 f: &'a mut fmt::DebugStruct<'a, 'b>,
810 req: &Request,
811) -> &'a mut fmt::DebugStruct<'a, 'b> {
812 f.field("method", &req.method)
813 .field("url", &req.url)
814 .field("headers", &req.headers)
815}
816
817pub(crate) fn extract_authority(url: &mut Url) -> Option<(String, Option<String>)> {
820 use percent_encoding::percent_decode;
821
822 if url.has_authority() {
823 let username: String = percent_decode(url.username().as_bytes())
824 .decode_utf8()
825 .ok()?
826 .into();
827 let password = url.password().and_then(|pass| {
828 percent_decode(pass.as_bytes())
829 .decode_utf8()
830 .ok()
831 .map(String::from)
832 });
833 if !username.is_empty() || password.is_some() {
834 url.set_username("")
835 .expect("has_authority means set_username shouldn't fail");
836 url.set_password(None)
837 .expect("has_authority means set_password shouldn't fail");
838 return Some((username, password));
839 }
840 }
841
842 None
843}
844
845impl<T> TryFrom<HttpRequest<T>> for Request
846where
847 T: Into<Body>,
848{
849 type Error = crate::Error;
850
851 fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
852 let (parts, body) = req.into_parts();
853 let Parts {
854 method,
855 uri,
856 headers,
857 ..
858 } = parts;
859 let url = crate::into_url::IntoUrlSealed::into_url(uri.to_string())?;
860 Ok(Request {
861 method,
862 url,
863 headers,
864 body: Some(body.into()),
865 timeout: None,
866 read_timeout: None,
867 version: None,
869 redirect: None,
870 allow_compression: cfg!(any(
871 feature = "gzip",
872 feature = "brotli",
873 feature = "deflate",
874 feature = "zstd"
875 )),
876 network_scheme: NetworkScheme::builder(),
877 protocol: None,
878 })
879 }
880}
881
882impl TryFrom<Request> for HttpRequest<Body> {
883 type Error = crate::Error;
884
885 fn try_from(req: Request) -> crate::Result<Self> {
886 let Request {
887 method,
888 url,
889 headers,
890 body,
891 version,
892 ..
893 } = req;
894
895 let mut builder = HttpRequest::builder();
896
897 if let Some(version) = version {
898 builder = builder.version(version);
899 }
900
901 let mut req = builder
902 .method(method)
903 .uri(url.as_str())
904 .body(body.unwrap_or_else(Body::empty))
905 .map_err(crate::error::builder)?;
906
907 *req.headers_mut() = headers;
908 Ok(req)
909 }
910}