1 #[cfg(any(
2     feature = "native-tls",
3     feature = "__rustls",
4 ))]
5 use std::any::Any;
6 use std::convert::TryInto;
7 use std::net::IpAddr;
8 use std::sync::Arc;
9 #[cfg(feature = "cookies")]
10 use std::sync::RwLock;
11 use std::time::Duration;
12 use std::{fmt, str};
13 
14 use bytes::Bytes;
15 use http::header::{
16     Entry, HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH,
17     CONTENT_TYPE, LOCATION, PROXY_AUTHORIZATION, RANGE, REFERER, TRANSFER_ENCODING, USER_AGENT,
18 };
19 use http::uri::Scheme;
20 use http::Uri;
21 use hyper::client::ResponseFuture;
22 #[cfg(feature = "native-tls-crate")]
23 use native_tls_crate::TlsConnector;
24 #[cfg(feature = "rustls-tls-native-roots")]
25 use rustls::RootCertStore;
26 use std::future::Future;
27 use std::pin::Pin;
28 use std::task::{Context, Poll};
29 use tokio::time::Delay;
30 use pin_project_lite::pin_project;
31 
32 use log::debug;
33 
34 use super::decoder::Accepts;
35 use super::request::{Request, RequestBuilder};
36 use super::response::Response;
37 use super::Body;
38 use crate::connect::{Connector, HttpConnector};
39 #[cfg(feature = "cookies")]
40 use crate::cookie;
41 use crate::error;
42 use crate::into_url::{expect_uri, try_uri};
43 use crate::redirect::{self, remove_sensitive_headers};
44 #[cfg(feature = "__tls")]
45 use crate::tls::TlsBackend;
46 #[cfg(feature = "__tls")]
47 use crate::{Certificate, Identity};
48 use crate::{IntoUrl, Method, Proxy, StatusCode, Url};
49 
50 /// An asynchronous `Client` to make Requests with.
51 ///
52 /// The Client has various configuration values to tweak, but the defaults
53 /// are set to what is usually the most commonly desired value. To configure a
54 /// `Client`, use `Client::builder()`.
55 ///
56 /// The `Client` holds a connection pool internally, so it is advised that
57 /// you create one and **reuse** it.
58 ///
59 /// You do **not** have to wrap the `Client` it in an [`Rc`] or [`Arc`] to **reuse** it,
60 /// because it already uses an [`Arc`] internally.
61 ///
62 /// [`Rc`]: std::rc::Rc
63 #[derive(Clone)]
64 pub struct Client {
65     inner: Arc<ClientRef>,
66 }
67 
68 /// A `ClientBuilder` can be used to create a `Client` with  custom configuration.
69 #[must_use]
70 pub struct ClientBuilder {
71     config: Config,
72 }
73 
74 struct Config {
75     // NOTE: When adding a new field, update `fmt::Debug for ClientBuilder`
76     accepts: Accepts,
77     headers: HeaderMap,
78     #[cfg(feature = "native-tls")]
79     hostname_verification: bool,
80     #[cfg(feature = "__tls")]
81     certs_verification: bool,
82     connect_timeout: Option<Duration>,
83     connection_verbose: bool,
84     pool_idle_timeout: Option<Duration>,
85     pool_max_idle_per_host: usize,
86     tcp_keepalive: Option<Duration>,
87     #[cfg(feature = "__tls")]
88     identity: Option<Identity>,
89     proxies: Vec<Proxy>,
90     auto_sys_proxy: bool,
91     redirect_policy: redirect::Policy,
92     referer: bool,
93     timeout: Option<Duration>,
94     #[cfg(feature = "__tls")]
95     root_certs: Vec<Certificate>,
96     #[cfg(feature = "__tls")]
97     tls: TlsBackend,
98     http2_only: bool,
99     http1_writev: Option<bool>,
100     http1_title_case_headers: bool,
101     http2_initial_stream_window_size: Option<u32>,
102     http2_initial_connection_window_size: Option<u32>,
103     local_address: Option<IpAddr>,
104     nodelay: bool,
105     #[cfg(feature = "cookies")]
106     cookie_store: Option<cookie::CookieStore>,
107     trust_dns: bool,
108     error: Option<crate::Error>,
109     https_only: bool,
110 }
111 
112 impl Default for ClientBuilder {
default() -> Self113     fn default() -> Self {
114         Self::new()
115     }
116 }
117 
118 impl ClientBuilder {
119     /// Constructs a new `ClientBuilder`.
120     ///
121     /// This is the same as `Client::builder()`.
new() -> ClientBuilder122     pub fn new() -> ClientBuilder {
123         let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
124         headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
125 
126         ClientBuilder {
127             config: Config {
128                 error: None,
129                 accepts: Accepts::default(),
130                 headers,
131                 #[cfg(feature = "native-tls")]
132                 hostname_verification: true,
133                 #[cfg(feature = "__tls")]
134                 certs_verification: true,
135                 connect_timeout: None,
136                 connection_verbose: false,
137                 pool_idle_timeout: Some(Duration::from_secs(90)),
138                 pool_max_idle_per_host: std::usize::MAX,
139                 // TODO: Re-enable default duration once hyper's HttpConnector is fixed
140                 // to no longer error when an option fails.
141                 tcp_keepalive: None, //Some(Duration::from_secs(60)),
142                 proxies: Vec::new(),
143                 auto_sys_proxy: true,
144                 redirect_policy: redirect::Policy::default(),
145                 referer: true,
146                 timeout: None,
147                 #[cfg(feature = "__tls")]
148                 root_certs: Vec::new(),
149                 #[cfg(feature = "__tls")]
150                 identity: None,
151                 #[cfg(feature = "__tls")]
152                 tls: TlsBackend::default(),
153                 http2_only: false,
154                 http1_writev: None,
155                 http1_title_case_headers: false,
156                 http2_initial_stream_window_size: None,
157                 http2_initial_connection_window_size: None,
158                 local_address: None,
159                 nodelay: true,
160                 trust_dns: cfg!(feature = "trust-dns"),
161                 #[cfg(feature = "cookies")]
162                 cookie_store: None,
163                 https_only: false,
164             },
165         }
166     }
167 
168     /// Returns a `Client` that uses this `ClientBuilder` configuration.
169     ///
170     /// # Errors
171     ///
172     /// This method fails if TLS backend cannot be initialized, or the resolver
173     /// cannot load the system configuration.
build(self) -> crate::Result<Client>174     pub fn build(self) -> crate::Result<Client> {
175         let config = self.config;
176 
177         if let Some(err) = config.error {
178             return Err(err);
179         }
180 
181         let mut proxies = config.proxies;
182         if config.auto_sys_proxy {
183             proxies.push(Proxy::system());
184         }
185         let proxies = Arc::new(proxies);
186 
187         let mut connector = {
188             #[cfg(feature = "__tls")]
189             fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
190                 headers.get(USER_AGENT).cloned()
191             }
192 
193             let http = match config.trust_dns {
194                 false => HttpConnector::new_gai(),
195                 #[cfg(feature = "trust-dns")]
196                 true => HttpConnector::new_trust_dns()?,
197                 #[cfg(not(feature = "trust-dns"))]
198                 true => unreachable!("trust-dns shouldn't be enabled unless the feature is"),
199             };
200 
201             #[cfg(feature = "__tls")]
202             match config.tls {
203                 #[cfg(feature = "default-tls")]
204                 TlsBackend::Default => {
205                     let mut tls = TlsConnector::builder();
206 
207                     #[cfg(feature = "native-tls")]
208                     {
209                         tls.danger_accept_invalid_hostnames(!config.hostname_verification);
210                     }
211 
212                     tls.danger_accept_invalid_certs(!config.certs_verification);
213 
214                     for cert in config.root_certs {
215                         cert.add_to_native_tls(&mut tls);
216                     }
217 
218                     #[cfg(feature = "native-tls")]
219                     {
220                         if let Some(id) = config.identity {
221                             id.add_to_native_tls(&mut tls)?;
222                         }
223                     }
224 
225 
226                     Connector::new_default_tls(
227                         http,
228                         tls,
229                         proxies.clone(),
230                         user_agent(&config.headers),
231                         config.local_address,
232                         config.nodelay,
233                     )?
234                 },
235                 #[cfg(feature = "native-tls")]
236                 TlsBackend::BuiltNativeTls(conn) => {
237                     Connector::from_built_default_tls(
238                         http,
239                         conn,
240                         proxies.clone(),
241                         user_agent(&config.headers),
242                         config.local_address,
243                         config.nodelay)
244                 },
245                 #[cfg(feature = "__rustls")]
246                 TlsBackend::BuiltRustls(conn) => {
247                     Connector::new_rustls_tls(
248                         http,
249                         conn,
250                         proxies.clone(),
251                         user_agent(&config.headers),
252                         config.local_address,
253                         config.nodelay)
254                 },
255                 #[cfg(feature = "__rustls")]
256                 TlsBackend::Rustls => {
257                     use crate::tls::NoVerifier;
258 
259                     let mut tls = rustls::ClientConfig::new();
260                     if config.http2_only {
261                         tls.set_protocols(&["h2".into()]);
262                     } else {
263                         tls.set_protocols(&["h2".into(), "http/1.1".into()]);
264                     }
265                     #[cfg(feature = "rustls-tls-webpki-roots")]
266                     tls.root_store
267                         .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
268                     #[cfg(feature = "rustls-tls-native-roots")]
269                     {
270                         let roots_slice = NATIVE_ROOTS.as_ref().unwrap().roots.as_slice();
271                         tls.root_store.roots.extend_from_slice(roots_slice);
272                     }
273 
274                     if !config.certs_verification {
275                         tls.dangerous()
276                             .set_certificate_verifier(Arc::new(NoVerifier));
277                     }
278 
279                     for cert in config.root_certs {
280                         cert.add_to_rustls(&mut tls)?;
281                     }
282 
283                     if let Some(id) = config.identity {
284                         id.add_to_rustls(&mut tls)?;
285                     }
286 
287                     Connector::new_rustls_tls(
288                         http,
289                         tls,
290                         proxies.clone(),
291                         user_agent(&config.headers),
292                         config.local_address,
293                         config.nodelay,
294                     )
295                 },
296                 #[cfg(any(
297                     feature = "native-tls",
298                     feature = "__rustls",
299                 ))]
300                 TlsBackend::UnknownPreconfigured => {
301                     return Err(crate::error::builder(
302                         "Unknown TLS backend passed to `use_preconfigured_tls`"
303                     ));
304                 },
305             }
306 
307             #[cfg(not(feature = "__tls"))]
308             Connector::new(http, proxies.clone(), config.local_address, config.nodelay)
309         };
310 
311         connector.set_timeout(config.connect_timeout);
312         connector.set_verbose(config.connection_verbose);
313 
314         let mut builder = hyper::Client::builder();
315         if config.http2_only {
316             builder.http2_only(true);
317         }
318 
319         if let Some(http1_writev) = config.http1_writev {
320             builder.http1_writev(http1_writev);
321         }
322 
323         if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size {
324             builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
325         }
326         if let Some(http2_initial_connection_window_size) =
327             config.http2_initial_connection_window_size
328         {
329             builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
330         }
331 
332         builder.pool_idle_timeout(config.pool_idle_timeout);
333         builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
334         connector.set_keepalive(config.tcp_keepalive);
335 
336         if config.http1_title_case_headers {
337             builder.http1_title_case_headers(true);
338         }
339 
340         let hyper_client = builder.build(connector);
341 
342         let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
343 
344         Ok(Client {
345             inner: Arc::new(ClientRef {
346                 accepts: config.accepts,
347                 #[cfg(feature = "cookies")]
348                 cookie_store: config.cookie_store.map(RwLock::new),
349                 hyper: hyper_client,
350                 headers: config.headers,
351                 redirect_policy: config.redirect_policy,
352                 referer: config.referer,
353                 request_timeout: config.timeout,
354                 proxies,
355                 proxies_maybe_http_auth,
356                 https_only: config.https_only,
357             }),
358         })
359     }
360 
361     // Higher-level options
362 
363     /// Sets the `User-Agent` header to be used by this client.
364     ///
365     /// # Example
366     ///
367     /// ```rust
368     /// # async fn doc() -> Result<(), reqwest::Error> {
369     /// // Name your user agent after your app?
370     /// static APP_USER_AGENT: &str = concat!(
371     ///     env!("CARGO_PKG_NAME"),
372     ///     "/",
373     ///     env!("CARGO_PKG_VERSION"),
374     /// );
375     ///
376     /// let client = reqwest::Client::builder()
377     ///     .user_agent(APP_USER_AGENT)
378     ///     .build()?;
379     /// let res = client.get("https://www.rust-lang.org").send().await?;
380     /// # Ok(())
381     /// # }
382     /// ```
user_agent<V>(mut self, value: V) -> ClientBuilder where V: TryInto<HeaderValue>, V::Error: Into<http::Error>,383     pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
384     where
385         V: TryInto<HeaderValue>,
386         V::Error: Into<http::Error>,
387     {
388         match value.try_into() {
389             Ok(value) => {
390                 self.config.headers.insert(USER_AGENT, value);
391             }
392             Err(e) => {
393                 self.config.error = Some(crate::error::builder(e.into()));
394             }
395         };
396         self
397     }
398     /// Sets the default headers for every request.
399     ///
400     /// # Example
401     ///
402     /// ```rust
403     /// use reqwest::header;
404     /// # async fn doc() -> Result<(), reqwest::Error> {
405     /// let mut headers = header::HeaderMap::new();
406     /// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret"));
407     ///
408     /// // get a client builder
409     /// let client = reqwest::Client::builder()
410     ///     .default_headers(headers)
411     ///     .build()?;
412     /// let res = client.get("https://www.rust-lang.org").send().await?;
413     /// # Ok(())
414     /// # }
415     /// ```
416     ///
417     /// Override the default headers:
418     ///
419     /// ```rust
420     /// use reqwest::header;
421     /// # async fn doc() -> Result<(), reqwest::Error> {
422     /// let mut headers = header::HeaderMap::new();
423     /// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret"));
424     ///
425     /// // get a client builder
426     /// let client = reqwest::Client::builder()
427     ///     .default_headers(headers)
428     ///     .build()?;
429     /// let res = client
430     ///     .get("https://www.rust-lang.org")
431     ///     .header(header::AUTHORIZATION, "token")
432     ///     .send()
433     ///     .await?;
434     /// # Ok(())
435     /// # }
436     /// ```
default_headers(mut self, headers: HeaderMap) -> ClientBuilder437     pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
438         for (key, value) in headers.iter() {
439             self.config.headers.insert(key, value.clone());
440         }
441         self
442     }
443 
444     /// Enable a persistent cookie store for the client.
445     ///
446     /// Cookies received in responses will be preserved and included in
447     /// additional requests.
448     ///
449     /// By default, no cookie store is used.
450     ///
451     /// # Optional
452     ///
453     /// This requires the optional `cookies` feature to be enabled.
454     #[cfg(feature = "cookies")]
cookie_store(mut self, enable: bool) -> ClientBuilder455     pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
456         self.config.cookie_store = if enable {
457             Some(cookie::CookieStore::default())
458         } else {
459             None
460         };
461         self
462     }
463 
464     /// Enable auto gzip decompression by checking the `Content-Encoding` response header.
465     ///
466     /// If auto gzip decompression is turned on:
467     ///
468     /// - When sending a request and if the request's headers do not already contain
469     ///   an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `gzip`.
470     ///   The request body is **not** automatically compressed.
471     /// - When receiving a response, if it's headers contain a `Content-Encoding` value that
472     ///   equals to `gzip`, both values `Content-Encoding` and `Content-Length` are removed from the
473     ///   headers' set. The response body is automatically decompressed.
474     ///
475     /// If the `gzip` feature is turned on, the default option is enabled.
476     ///
477     /// # Optional
478     ///
479     /// This requires the optional `gzip` feature to be enabled
480     #[cfg(feature = "gzip")]
gzip(mut self, enable: bool) -> ClientBuilder481     pub fn gzip(mut self, enable: bool) -> ClientBuilder {
482         self.config.accepts.gzip = enable;
483         self
484     }
485 
486     /// Enable auto brotli decompression by checking the `Content-Encoding` response header.
487     ///
488     /// If auto brotli decompression is turned on:
489     ///
490     /// - When sending a request and if the request's headers do not already contain
491     ///   an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `br`.
492     ///   The request body is **not** automatically compressed.
493     /// - When receiving a response, if it's headers contain a `Content-Encoding` value that
494     ///   equals to `br`, both values `Content-Encoding` and `Content-Length` are removed from the
495     ///   headers' set. The response body is automatically decompressed.
496     ///
497     /// If the `brotli` feature is turned on, the default option is enabled.
498     ///
499     /// # Optional
500     ///
501     /// This requires the optional `brotli` feature to be enabled
502     #[cfg(feature = "brotli")]
brotli(mut self, enable: bool) -> ClientBuilder503     pub fn brotli(mut self, enable: bool) -> ClientBuilder {
504         self.config.accepts.brotli = enable;
505         self
506     }
507 
508     /// Disable auto response body gzip decompression.
509     ///
510     /// This method exists even if the optional `gzip` feature is not enabled.
511     /// This can be used to ensure a `Client` doesn't use gzip decompression
512     /// even if another dependency were to enable the optional `gzip` feature.
no_gzip(self) -> ClientBuilder513     pub fn no_gzip(self) -> ClientBuilder {
514         #[cfg(feature = "gzip")]
515         {
516             self.gzip(false)
517         }
518 
519         #[cfg(not(feature = "gzip"))]
520         {
521             self
522         }
523     }
524 
525     /// Disable auto response body brotli decompression.
526     ///
527     /// This method exists even if the optional `brotli` feature is not enabled.
528     /// This can be used to ensure a `Client` doesn't use brotli decompression
529     /// even if another dependency were to enable the optional `brotli` feature.
no_brotli(self) -> ClientBuilder530     pub fn no_brotli(self) -> ClientBuilder {
531         #[cfg(feature = "brotli")]
532         {
533             self.brotli(false)
534         }
535 
536         #[cfg(not(feature = "brotli"))]
537         {
538             self
539         }
540     }
541 
542     // Redirect options
543 
544     /// Set a `RedirectPolicy` for this client.
545     ///
546     /// Default will follow redirects up to a maximum of 10.
redirect(mut self, policy: redirect::Policy) -> ClientBuilder547     pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
548         self.config.redirect_policy = policy;
549         self
550     }
551 
552     /// Enable or disable automatic setting of the `Referer` header.
553     ///
554     /// Default is `true`.
referer(mut self, enable: bool) -> ClientBuilder555     pub fn referer(mut self, enable: bool) -> ClientBuilder {
556         self.config.referer = enable;
557         self
558     }
559 
560     // Proxy options
561 
562     /// Add a `Proxy` to the list of proxies the `Client` will use.
563     ///
564     /// # Note
565     ///
566     /// Adding a proxy will disable the automatic usage of the "system" proxy.
proxy(mut self, proxy: Proxy) -> ClientBuilder567     pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
568         self.config.proxies.push(proxy);
569         self.config.auto_sys_proxy = false;
570         self
571     }
572 
573     /// Clear all `Proxies`, so `Client` will use no proxy anymore.
574     ///
575     /// This also disables the automatic usage of the "system" proxy.
no_proxy(mut self) -> ClientBuilder576     pub fn no_proxy(mut self) -> ClientBuilder {
577         self.config.proxies.clear();
578         self.config.auto_sys_proxy = false;
579         self
580     }
581 
582     #[doc(hidden)]
583     #[deprecated(note = "the system proxy is used automatically")]
use_sys_proxy(self) -> ClientBuilder584     pub fn use_sys_proxy(self) -> ClientBuilder {
585         self
586     }
587 
588     // Timeout options
589 
590     /// Enables a request timeout.
591     ///
592     /// The timeout is applied from when the request starts connecting until the
593     /// response body has finished.
594     ///
595     /// Default is no timeout.
timeout(mut self, timeout: Duration) -> ClientBuilder596     pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
597         self.config.timeout = Some(timeout);
598         self
599     }
600 
601     /// Set a timeout for only the connect phase of a `Client`.
602     ///
603     /// Default is `None`.
604     ///
605     /// # Note
606     ///
607     /// This **requires** the futures be executed in a tokio runtime with
608     /// a tokio timer enabled.
connect_timeout(mut self, timeout: Duration) -> ClientBuilder609     pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
610         self.config.connect_timeout = Some(timeout);
611         self
612     }
613 
614     /// Set whether connections should emit verbose logs.
615     ///
616     /// Enabling this option will emit [log][] messages at the `TRACE` level
617     /// for read and write operations on connections.
618     ///
619     /// [log]: https://crates.io/crates/log
connection_verbose(mut self, verbose: bool) -> ClientBuilder620     pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
621         self.config.connection_verbose = verbose;
622         self
623     }
624 
625     // HTTP options
626 
627     /// Set an optional timeout for idle sockets being kept-alive.
628     ///
629     /// Pass `None` to disable timeout.
630     ///
631     /// Default is 90 seconds.
pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder where D: Into<Option<Duration>>,632     pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
633     where
634         D: Into<Option<Duration>>,
635     {
636         self.config.pool_idle_timeout = val.into();
637         self
638     }
639 
640     /// Sets the maximum idle connection per host allowed in the pool.
pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder641     pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
642         self.config.pool_max_idle_per_host = max;
643         self
644     }
645 
646     #[doc(hidden)]
647     #[deprecated(note = "renamed to `pool_max_idle_per_host`")]
max_idle_per_host(self, max: usize) -> ClientBuilder648     pub fn max_idle_per_host(self, max: usize) -> ClientBuilder {
649         self.pool_max_idle_per_host(max)
650     }
651 
652     /// Enable case sensitive headers.
http1_title_case_headers(mut self) -> ClientBuilder653     pub fn http1_title_case_headers(mut self) -> ClientBuilder {
654         self.config.http1_title_case_headers = true;
655         self
656     }
657 
658     /// Force hyper to use either queued(if true), or flattened(if false) write strategy
659     /// This may eliminate unnecessary cloning of buffers for some TLS backends
660     /// By default hyper will try to guess which strategy to use
http1_writev(mut self, writev: bool) -> ClientBuilder661     pub fn http1_writev(mut self, writev: bool) -> ClientBuilder {
662         self.config.http1_writev = Some(writev);
663         self
664     }
665 
666     /// Only use HTTP/2.
http2_prior_knowledge(mut self) -> ClientBuilder667     pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
668         self.config.http2_only = true;
669         self
670     }
671 
672     /// Sets the `SETTINGS_INITIAL_WINDOW_SIZE` option for HTTP2 stream-level flow control.
673     ///
674     /// Default is currently 65,535 but may change internally to optimize for common uses.
http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder675     pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
676         self.config.http2_initial_stream_window_size = sz.into();
677         self
678     }
679 
680     /// Sets the max connection-level flow control for HTTP2
681     ///
682     /// Default is currently 65,535 but may change internally to optimize for common uses.
http2_initial_connection_window_size( mut self, sz: impl Into<Option<u32>>, ) -> ClientBuilder683     pub fn http2_initial_connection_window_size(
684         mut self,
685         sz: impl Into<Option<u32>>,
686     ) -> ClientBuilder {
687         self.config.http2_initial_connection_window_size = sz.into();
688         self
689     }
690 
691     // TCP options
692 
693     #[doc(hidden)]
694     #[deprecated(note = "tcp_nodelay is enabled by default, use `tcp_nodelay_` to disable")]
tcp_nodelay(self) -> ClientBuilder695     pub fn tcp_nodelay(self) -> ClientBuilder {
696         self.tcp_nodelay_(true)
697     }
698 
699     /// Set whether sockets have `SO_NODELAY` enabled.
700     ///
701     /// Default is `true`.
702     // NOTE: Regarding naming (trailing underscore):
703     //
704     // Due to the original `tcp_nodelay()` not taking an argument, changing
705     // the default means a user has no way of *disabling* this feature.
706     //
707     // TODO(v0.11.x): Remove trailing underscore.
tcp_nodelay_(mut self, enabled: bool) -> ClientBuilder708     pub fn tcp_nodelay_(mut self, enabled: bool) -> ClientBuilder {
709         self.config.nodelay = enabled;
710         self
711     }
712 
713     /// Bind to a local IP Address.
714     ///
715     /// # Example
716     ///
717     /// ```
718     /// use std::net::IpAddr;
719     /// let local_addr = IpAddr::from([12, 4, 1, 8]);
720     /// let client = reqwest::Client::builder()
721     ///     .local_address(local_addr)
722     ///     .build().unwrap();
723     /// ```
local_address<T>(mut self, addr: T) -> ClientBuilder where T: Into<Option<IpAddr>>,724     pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
725     where
726         T: Into<Option<IpAddr>>,
727     {
728         self.config.local_address = addr.into();
729         self
730     }
731 
732     /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration.
733     ///
734     /// If `None`, the option will not be set.
tcp_keepalive<D>(mut self, val: D) -> ClientBuilder where D: Into<Option<Duration>>,735     pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
736         where
737             D: Into<Option<Duration>>,
738     {
739         self.config.tcp_keepalive = val.into();
740         self
741     }
742 
743     // TLS options
744 
745     /// Add a custom root certificate.
746     ///
747     /// This can be used to connect to a server that has a self-signed
748     /// certificate for example.
749     ///
750     /// # Optional
751     ///
752     /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
753     /// feature to be enabled.
754     #[cfg(feature = "__tls")]
add_root_certificate(mut self, cert: Certificate) -> ClientBuilder755     pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
756         self.config.root_certs.push(cert);
757         self
758     }
759 
760     /// Sets the identity to be used for client certificate authentication.
761     ///
762     /// # Optional
763     ///
764     /// This requires the optional `native-tls` or `rustls-tls(-...)` feature to be
765     /// enabled.
766     #[cfg(feature = "__tls")]
identity(mut self, identity: Identity) -> ClientBuilder767     pub fn identity(mut self, identity: Identity) -> ClientBuilder {
768         self.config.identity = Some(identity);
769         self
770     }
771 
772     /// Controls the use of hostname verification.
773     ///
774     /// Defaults to `false`.
775     ///
776     /// # Warning
777     ///
778     /// You should think very carefully before you use this method. If
779     /// hostname verification is not used, any valid certificate for any
780     /// site will be trusted for use from any other. This introduces a
781     /// significant vulnerability to man-in-the-middle attacks.
782     ///
783     /// # Optional
784     ///
785     /// This requires the optional `native-tls` feature to be enabled.
786     #[cfg(feature = "native-tls")]
danger_accept_invalid_hostnames( mut self, accept_invalid_hostname: bool, ) -> ClientBuilder787     pub fn danger_accept_invalid_hostnames(
788         mut self,
789         accept_invalid_hostname: bool,
790     ) -> ClientBuilder {
791         self.config.hostname_verification = !accept_invalid_hostname;
792         self
793     }
794 
795     /// Controls the use of certificate validation.
796     ///
797     /// Defaults to `false`.
798     ///
799     /// # Warning
800     ///
801     /// You should think very carefully before using this method. If
802     /// invalid certificates are trusted, *any* certificate for *any* site
803     /// will be trusted for use. This includes expired certificates. This
804     /// introduces significant vulnerabilities, and should only be used
805     /// as a last resort.
806     ///
807     /// # Optional
808     ///
809     /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
810     /// feature to be enabled.
811     #[cfg(feature = "__tls")]
danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder812     pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
813         self.config.certs_verification = !accept_invalid_certs;
814         self
815     }
816 
817     /// Force using the native TLS backend.
818     ///
819     /// Since multiple TLS backends can be optionally enabled, this option will
820     /// force the `native-tls` backend to be used for this `Client`.
821     ///
822     /// # Optional
823     ///
824     /// This requires the optional `native-tls` feature to be enabled.
825     #[cfg(feature = "native-tls")]
use_native_tls(mut self) -> ClientBuilder826     pub fn use_native_tls(mut self) -> ClientBuilder {
827         self.config.tls = TlsBackend::Default;
828         self
829     }
830 
831     /// Force using the Rustls TLS backend.
832     ///
833     /// Since multiple TLS backends can be optionally enabled, this option will
834     /// force the `rustls` backend to be used for this `Client`.
835     ///
836     /// # Optional
837     ///
838     /// This requires the optional `rustls-tls(-...)` feature to be enabled.
839     #[cfg(feature = "__rustls")]
use_rustls_tls(mut self) -> ClientBuilder840     pub fn use_rustls_tls(mut self) -> ClientBuilder {
841         self.config.tls = TlsBackend::Rustls;
842         self
843     }
844 
845     /// Use a preconfigured TLS backend.
846     ///
847     /// If the passed `Any` argument is not a TLS backend that reqwest
848     /// understands, the `ClientBuilder` will error when calling `build`.
849     ///
850     /// # Advanced
851     ///
852     /// This is an advanced option, and can be somewhat brittle. Usage requires
853     /// keeping the preconfigured TLS argument version in sync with reqwest,
854     /// since version mismatches will result in an "unknown" TLS backend.
855     ///
856     /// If possible, it's preferable to use the methods on `ClientBuilder`
857     /// to configure reqwest's TLS.
858     ///
859     /// # Optional
860     ///
861     /// This requires one of the optional features `native-tls` or
862     /// `rustls-tls(-...)` to be enabled.
863     #[cfg(any(
864         feature = "native-tls",
865         feature = "__rustls",
866     ))]
use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder867     pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
868         let mut tls = Some(tls);
869         #[cfg(feature = "native-tls")]
870         {
871             if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<native_tls_crate::TlsConnector>>() {
872                 let tls = conn.take().expect("is definitely Some");
873                 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
874                 self.config.tls = tls;
875                 return self;
876             }
877         }
878         #[cfg(feature = "__rustls")]
879         {
880             if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>() {
881 
882                 let tls = conn.take().expect("is definitely Some");
883                 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
884                 self.config.tls = tls;
885                 return self;
886             }
887         }
888 
889         // Otherwise, we don't recognize the TLS backend!
890         self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
891         self
892     }
893 
894     /// Enables the [trust-dns](trust_dns_resolver) async resolver instead of a default threadpool using `getaddrinfo`.
895     ///
896     /// If the `trust-dns` feature is turned on, the default option is enabled.
897     ///
898     /// # Optional
899     ///
900     /// This requires the optional `trust-dns` feature to be enabled
901     #[cfg(feature = "trust-dns")]
trust_dns(mut self, enable: bool) -> ClientBuilder902     pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
903         self.config.trust_dns = enable;
904         self
905     }
906 
907     /// Disables the trust-dns async resolver.
908     ///
909     /// This method exists even if the optional `trust-dns` feature is not enabled.
910     /// This can be used to ensure a `Client` doesn't use the trust-dns async resolver
911     /// even if another dependency were to enable the optional `trust-dns` feature.
no_trust_dns(self) -> ClientBuilder912     pub fn no_trust_dns(self) -> ClientBuilder {
913         #[cfg(feature = "trust-dns")]
914         {
915             self.trust_dns(false)
916         }
917 
918         #[cfg(not(feature = "trust-dns"))]
919         {
920             self
921         }
922     }
923 
924     /// Restrict the Client to be used with HTTPS only requests.
925     ///
926     /// Defaults to false.
https_only(mut self, enabled: bool) -> ClientBuilder927     pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
928         self.config.https_only = enabled;
929         self
930     }
931 }
932 
933 type HyperClient = hyper::Client<Connector, super::body::ImplStream>;
934 
935 impl Default for Client {
default() -> Self936     fn default() -> Self {
937         Self::new()
938     }
939 }
940 
941 impl Client {
942     /// Constructs a new `Client`.
943     ///
944     /// # Panics
945     ///
946     /// This method panics if TLS backend cannot initialized, or the resolver
947     /// cannot load the system configuration.
948     ///
949     /// Use `Client::builder()` if you wish to handle the failure as an `Error`
950     /// instead of panicking.
new() -> Client951     pub fn new() -> Client {
952         ClientBuilder::new().build().expect("Client::new()")
953     }
954 
955     /// Creates a `ClientBuilder` to configure a `Client`.
956     ///
957     /// This is the same as `ClientBuilder::new()`.
builder() -> ClientBuilder958     pub fn builder() -> ClientBuilder {
959         ClientBuilder::new()
960     }
961 
962     /// Convenience method to make a `GET` request to a URL.
963     ///
964     /// # Errors
965     ///
966     /// This method fails whenever supplied `Url` cannot be parsed.
get<U: IntoUrl>(&self, url: U) -> RequestBuilder967     pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
968         self.request(Method::GET, url)
969     }
970 
971     /// Convenience method to make a `POST` request to a URL.
972     ///
973     /// # Errors
974     ///
975     /// This method fails whenever supplied `Url` cannot be parsed.
post<U: IntoUrl>(&self, url: U) -> RequestBuilder976     pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
977         self.request(Method::POST, url)
978     }
979 
980     /// Convenience method to make a `PUT` request to a URL.
981     ///
982     /// # Errors
983     ///
984     /// This method fails whenever supplied `Url` cannot be parsed.
put<U: IntoUrl>(&self, url: U) -> RequestBuilder985     pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
986         self.request(Method::PUT, url)
987     }
988 
989     /// Convenience method to make a `PATCH` request to a URL.
990     ///
991     /// # Errors
992     ///
993     /// This method fails whenever supplied `Url` cannot be parsed.
patch<U: IntoUrl>(&self, url: U) -> RequestBuilder994     pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
995         self.request(Method::PATCH, url)
996     }
997 
998     /// Convenience method to make a `DELETE` request to a URL.
999     ///
1000     /// # Errors
1001     ///
1002     /// This method fails whenever supplied `Url` cannot be parsed.
delete<U: IntoUrl>(&self, url: U) -> RequestBuilder1003     pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1004         self.request(Method::DELETE, url)
1005     }
1006 
1007     /// Convenience method to make a `HEAD` request to a URL.
1008     ///
1009     /// # Errors
1010     ///
1011     /// This method fails whenever supplied `Url` cannot be parsed.
head<U: IntoUrl>(&self, url: U) -> RequestBuilder1012     pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1013         self.request(Method::HEAD, url)
1014     }
1015 
1016     /// Start building a `Request` with the `Method` and `Url`.
1017     ///
1018     /// Returns a `RequestBuilder`, which will allow setting headers and
1019     /// request body before sending.
1020     ///
1021     /// # Errors
1022     ///
1023     /// This method fails whenever supplied `Url` cannot be parsed.
request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder1024     pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
1025         let req = url.into_url().map(move |url| Request::new(method, url));
1026         RequestBuilder::new(self.clone(), req)
1027     }
1028 
1029     /// Executes a `Request`.
1030     ///
1031     /// A `Request` can be built manually with `Request::new()` or obtained
1032     /// from a RequestBuilder with `RequestBuilder::build()`.
1033     ///
1034     /// You should prefer to use the `RequestBuilder` and
1035     /// `RequestBuilder::send()`.
1036     ///
1037     /// # Errors
1038     ///
1039     /// This method fails if there was an error while sending request,
1040     /// redirect loop was detected or redirect limit was exhausted.
execute( &self, request: Request, ) -> impl Future<Output = Result<Response, crate::Error>>1041     pub fn execute(
1042         &self,
1043         request: Request,
1044     ) -> impl Future<Output = Result<Response, crate::Error>> {
1045         self.execute_request(request)
1046     }
1047 
execute_request(&self, req: Request) -> Pending1048     pub(super) fn execute_request(&self, req: Request) -> Pending {
1049         let (method, url, mut headers, body, timeout) = req.pieces();
1050         if url.scheme() != "http" && url.scheme() != "https" {
1051             return Pending::new_err(error::url_bad_scheme(url));
1052         }
1053 
1054         // check if we're in https_only mode and check the scheme of the current URL
1055         if self.inner.https_only && url.scheme() != "https" {
1056             return Pending::new_err(error::url_bad_scheme(url));
1057         }
1058 
1059         // insert default headers in the request headers
1060         // without overwriting already appended headers.
1061         for (key, value) in &self.inner.headers {
1062             if let Entry::Vacant(entry) = headers.entry(key) {
1063                 entry.insert(value.clone());
1064             }
1065         }
1066 
1067         // Add cookies from the cookie store.
1068         #[cfg(feature = "cookies")]
1069         {
1070             if let Some(cookie_store_wrapper) = self.inner.cookie_store.as_ref() {
1071                 if headers.get(crate::header::COOKIE).is_none() {
1072                     let cookie_store = cookie_store_wrapper.read().unwrap();
1073                     add_cookie_header(&mut headers, &cookie_store, &url);
1074                 }
1075             }
1076         }
1077 
1078         let accept_encoding = self.inner.accepts.as_str();
1079 
1080         if let Some(accept_encoding) = accept_encoding {
1081             if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
1082                 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
1083             }
1084         }
1085 
1086         let uri = expect_uri(&url);
1087 
1088         let (reusable, body) = match body {
1089             Some(body) => {
1090                 let (reusable, body) = body.try_reuse();
1091                 (Some(reusable), body)
1092             }
1093             None => (None, Body::empty()),
1094         };
1095 
1096         self.proxy_auth(&uri, &mut headers);
1097 
1098         let mut req = hyper::Request::builder()
1099             .method(method.clone())
1100             .uri(uri)
1101             .body(body.into_stream())
1102             .expect("valid request parts");
1103 
1104         let timeout = timeout
1105             .or(self.inner.request_timeout)
1106             .map(tokio::time::delay_for);
1107 
1108         *req.headers_mut() = headers.clone();
1109 
1110         let in_flight = self.inner.hyper.request(req);
1111 
1112         Pending {
1113             inner: PendingInner::Request(PendingRequest {
1114                 method,
1115                 url,
1116                 headers,
1117                 body: reusable,
1118 
1119                 urls: Vec::new(),
1120 
1121                 client: self.inner.clone(),
1122 
1123                 in_flight,
1124                 timeout,
1125             }),
1126         }
1127     }
1128 
proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap)1129     fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
1130         if !self.inner.proxies_maybe_http_auth {
1131             return;
1132         }
1133 
1134         // Only set the header here if the destination scheme is 'http',
1135         // since otherwise, the header will be included in the CONNECT tunnel
1136         // request instead.
1137         if dst.scheme() != Some(&Scheme::HTTP) {
1138             return;
1139         }
1140 
1141         if headers.contains_key(PROXY_AUTHORIZATION) {
1142             return;
1143         }
1144 
1145         for proxy in self.inner.proxies.iter() {
1146             if proxy.is_match(dst) {
1147                 if let Some(header) = proxy.http_basic_auth(dst) {
1148                     headers.insert(PROXY_AUTHORIZATION, header);
1149                 }
1150 
1151                 break;
1152             }
1153         }
1154     }
1155 }
1156 
1157 impl fmt::Debug for Client {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1158     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1159         let mut builder = f.debug_struct("Client");
1160         self.inner.fmt_fields(&mut builder);
1161         builder.finish()
1162     }
1163 }
1164 
1165 impl fmt::Debug for ClientBuilder {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1166     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1167         let mut builder = f.debug_struct("ClientBuilder");
1168         self.config.fmt_fields(&mut builder);
1169         builder.finish()
1170     }
1171 }
1172 
1173 impl Config {
fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>)1174     fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
1175         // Instead of deriving Debug, only print fields when their output
1176         // would provide relevant or interesting data.
1177 
1178         #[cfg(feature = "cookies")]
1179         {
1180             if let Some(_) = self.cookie_store {
1181                 f.field("cookie_store", &true);
1182             }
1183         }
1184 
1185         f.field("accepts", &self.accepts);
1186 
1187         if !self.proxies.is_empty() {
1188             f.field("proxies", &self.proxies);
1189         }
1190 
1191         if !self.redirect_policy.is_default() {
1192             f.field("redirect_policy", &self.redirect_policy);
1193         }
1194 
1195         if self.referer {
1196             f.field("referer", &true);
1197         }
1198 
1199         f.field("default_headers", &self.headers);
1200 
1201         if self.http1_title_case_headers {
1202             f.field("http1_title_case_headers", &true);
1203         }
1204 
1205         if self.http2_only {
1206             f.field("http2_prior_knowledge", &true);
1207         }
1208 
1209         if let Some(ref d) = self.connect_timeout {
1210             f.field("connect_timeout", d);
1211         }
1212 
1213         if let Some(ref d) = self.timeout {
1214             f.field("timeout", d);
1215         }
1216 
1217         if let Some(ref v) = self.local_address {
1218             f.field("local_address", v);
1219         }
1220 
1221         if self.nodelay {
1222             f.field("tcp_nodelay", &true);
1223         }
1224 
1225         #[cfg(feature = "native-tls")]
1226         {
1227             if !self.hostname_verification {
1228                 f.field("danger_accept_invalid_hostnames", &true);
1229             }
1230         }
1231 
1232         #[cfg(feature = "__tls")]
1233         {
1234             if !self.certs_verification {
1235                 f.field("danger_accept_invalid_certs", &true);
1236             }
1237         }
1238 
1239         #[cfg(all(feature = "native-tls-crate", feature = "__rustls"))]
1240         {
1241             f.field("tls_backend", &self.tls);
1242         }
1243     }
1244 }
1245 
1246 struct ClientRef {
1247     accepts: Accepts,
1248     #[cfg(feature = "cookies")]
1249     cookie_store: Option<RwLock<cookie::CookieStore>>,
1250     headers: HeaderMap,
1251     hyper: HyperClient,
1252     redirect_policy: redirect::Policy,
1253     referer: bool,
1254     request_timeout: Option<Duration>,
1255     proxies: Arc<Vec<Proxy>>,
1256     proxies_maybe_http_auth: bool,
1257     https_only: bool,
1258 }
1259 
1260 impl ClientRef {
fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>)1261     fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
1262         // Instead of deriving Debug, only print fields when their output
1263         // would provide relevant or interesting data.
1264 
1265         #[cfg(feature = "cookies")]
1266         {
1267             if let Some(_) = self.cookie_store {
1268                 f.field("cookie_store", &true);
1269             }
1270         }
1271 
1272         f.field("accepts", &self.accepts);
1273 
1274         if !self.proxies.is_empty() {
1275             f.field("proxies", &self.proxies);
1276         }
1277 
1278         if !self.redirect_policy.is_default() {
1279             f.field("redirect_policy", &self.redirect_policy);
1280         }
1281 
1282         if self.referer {
1283             f.field("referer", &true);
1284         }
1285 
1286         f.field("default_headers", &self.headers);
1287 
1288         if let Some(ref d) = self.request_timeout {
1289             f.field("timeout", d);
1290         }
1291     }
1292 }
1293 
1294 pin_project! {
1295     pub(super) struct Pending {
1296         #[pin]
1297         inner: PendingInner,
1298     }
1299 }
1300 
1301 enum PendingInner {
1302     Request(PendingRequest),
1303     Error(Option<crate::Error>),
1304 }
1305 
1306 pin_project! {
1307     struct PendingRequest {
1308         method: Method,
1309         url: Url,
1310         headers: HeaderMap,
1311         body: Option<Option<Bytes>>,
1312 
1313         urls: Vec<Url>,
1314 
1315         client: Arc<ClientRef>,
1316 
1317         #[pin]
1318         in_flight: ResponseFuture,
1319         #[pin]
1320         timeout: Option<Delay>,
1321     }
1322 }
1323 
1324 impl PendingRequest {
in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture>1325     fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
1326         self.project().in_flight
1327     }
1328 
timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Delay>>1329     fn timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Delay>> {
1330         self.project().timeout
1331     }
1332 
urls(self: Pin<&mut Self>) -> &mut Vec<Url>1333     fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
1334         self.project().urls
1335     }
1336 
headers(self: Pin<&mut Self>) -> &mut HeaderMap1337     fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
1338         self.project().headers
1339     }
1340 }
1341 
1342 impl Pending {
new_err(err: crate::Error) -> Pending1343     pub(super) fn new_err(err: crate::Error) -> Pending {
1344         Pending {
1345             inner: PendingInner::Error(Some(err)),
1346         }
1347     }
1348 
inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner>1349     fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
1350         self.project().inner
1351     }
1352 }
1353 
1354 impl Future for Pending {
1355     type Output = Result<Response, crate::Error>;
1356 
poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>1357     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1358         let inner = self.inner();
1359         match inner.get_mut() {
1360             PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
1361             PendingInner::Error(ref mut err) => Poll::Ready(Err(err
1362                 .take()
1363                 .expect("Pending error polled more than once"))),
1364         }
1365     }
1366 }
1367 
1368 impl Future for PendingRequest {
1369     type Output = Result<Response, crate::Error>;
1370 
poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>1371     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1372         if let Some(delay) = self.as_mut().timeout().as_mut().as_pin_mut() {
1373             if let Poll::Ready(()) = delay.poll(cx) {
1374                 return Poll::Ready(Err(
1375                     crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
1376                 ));
1377             }
1378         }
1379 
1380         loop {
1381             let res = match self.as_mut().in_flight().as_mut().poll(cx) {
1382                 Poll::Ready(Err(e)) => {
1383                     return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
1384                 }
1385                 Poll::Ready(Ok(res)) => res,
1386                 Poll::Pending => return Poll::Pending,
1387             };
1388 
1389             #[cfg(feature = "cookies")]
1390             {
1391                 if let Some(store_wrapper) = self.client.cookie_store.as_ref() {
1392                     let mut cookies = cookie::extract_response_cookies(&res.headers())
1393                         .filter_map(|res| res.ok())
1394                         .map(|cookie| cookie.into_inner().into_owned())
1395                         .peekable();
1396                     if cookies.peek().is_some() {
1397                       let mut store = store_wrapper.write().unwrap();
1398                       store.0.store_response_cookies(cookies, &self.url);
1399                     }
1400                 }
1401             }
1402             let should_redirect = match res.status() {
1403                 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
1404                     self.body = None;
1405                     for header in &[
1406                         TRANSFER_ENCODING,
1407                         CONTENT_ENCODING,
1408                         CONTENT_TYPE,
1409                         CONTENT_LENGTH,
1410                     ] {
1411                         self.headers.remove(header);
1412                     }
1413 
1414                     match self.method {
1415                         Method::GET | Method::HEAD => {}
1416                         _ => {
1417                             self.method = Method::GET;
1418                         }
1419                     }
1420                     true
1421                 }
1422                 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
1423                     match self.body {
1424                         Some(Some(_)) | None => true,
1425                         Some(None) => false,
1426                     }
1427                 }
1428                 _ => false,
1429             };
1430             if should_redirect {
1431                 let loc = res.headers().get(LOCATION).and_then(|val| {
1432                     let loc = (|| -> Option<Url> {
1433                         // Some sites may send a utf-8 Location header,
1434                         // even though we're supposed to treat those bytes
1435                         // as opaque, we'll check specifically for utf8.
1436                         self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
1437                     })();
1438 
1439                     // Check that the `url` is also a valid `http::Uri`.
1440                     //
1441                     // If not, just log it and skip the redirect.
1442                     let loc = loc.and_then(|url| {
1443                         if try_uri(&url).is_some() {
1444                             Some(url)
1445                         } else {
1446                             None
1447                         }
1448                     });
1449 
1450                     if loc.is_none() {
1451                         debug!("Location header had invalid URI: {:?}", val);
1452                     }
1453                     loc
1454                 });
1455                 if let Some(loc) = loc {
1456                     if self.client.referer {
1457                         if let Some(referer) = make_referer(&loc, &self.url) {
1458                             self.headers.insert(REFERER, referer);
1459                         }
1460                     }
1461                     let url = self.url.clone();
1462                     self.as_mut().urls().push(url);
1463                     let action = self
1464                         .client
1465                         .redirect_policy
1466                         .check(res.status(), &loc, &self.urls);
1467 
1468                     match action {
1469                         redirect::ActionKind::Follow => {
1470                             debug!("redirecting '{}' to '{}'", self.url, loc);
1471                             self.url = loc;
1472 
1473                             let mut headers =
1474                                 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
1475 
1476                             remove_sensitive_headers(&mut headers, &self.url, &self.urls);
1477                             let uri = expect_uri(&self.url);
1478                             let body = match self.body {
1479                                 Some(Some(ref body)) => Body::reusable(body.clone()),
1480                                 _ => Body::empty(),
1481                             };
1482                             let mut req = hyper::Request::builder()
1483                                 .method(self.method.clone())
1484                                 .uri(uri.clone())
1485                                 .body(body.into_stream())
1486                                 .expect("valid request parts");
1487 
1488                             // Add cookies from the cookie store.
1489                             #[cfg(feature = "cookies")]
1490                             {
1491                                 if let Some(cookie_store_wrapper) =
1492                                     self.client.cookie_store.as_ref()
1493                                 {
1494                                     let cookie_store = cookie_store_wrapper.read().unwrap();
1495                                     add_cookie_header(&mut headers, &cookie_store, &self.url);
1496                                 }
1497                             }
1498 
1499                             *req.headers_mut() = headers.clone();
1500                             std::mem::swap(self.as_mut().headers(), &mut headers);
1501                             *self.as_mut().in_flight().get_mut() = self.client.hyper.request(req);
1502                             continue;
1503                         }
1504                         redirect::ActionKind::Stop => {
1505                             debug!("redirect policy disallowed redirection to '{}'", loc);
1506                         }
1507                         redirect::ActionKind::Error(err) => {
1508                             return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
1509                         }
1510                     }
1511                 }
1512             }
1513 
1514             debug!("response '{}' for {}", res.status(), self.url);
1515             let res = Response::new(
1516                 res,
1517                 self.url.clone(),
1518                 self.client.accepts,
1519                 self.timeout.take(),
1520             );
1521             return Poll::Ready(Ok(res));
1522         }
1523     }
1524 }
1525 
1526 impl fmt::Debug for Pending {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1527     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1528         match self.inner {
1529             PendingInner::Request(ref req) => f
1530                 .debug_struct("Pending")
1531                 .field("method", &req.method)
1532                 .field("url", &req.url)
1533                 .finish(),
1534             PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
1535         }
1536     }
1537 }
1538 
make_referer(next: &Url, previous: &Url) -> Option<HeaderValue>1539 fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
1540     if next.scheme() == "http" && previous.scheme() == "https" {
1541         return None;
1542     }
1543 
1544     let mut referer = previous.clone();
1545     let _ = referer.set_username("");
1546     let _ = referer.set_password(None);
1547     referer.set_fragment(None);
1548     referer.as_str().parse().ok()
1549 }
1550 
1551 #[cfg(feature = "cookies")]
add_cookie_header(headers: &mut HeaderMap, cookie_store: &cookie::CookieStore, url: &Url)1552 fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &cookie::CookieStore, url: &Url) {
1553     let header = cookie_store
1554         .0
1555         .get_request_cookies(url)
1556         .map(|c| format!("{}={}", c.name(), c.value()))
1557         .collect::<Vec<_>>()
1558         .join("; ");
1559     if !header.is_empty() {
1560         headers.insert(
1561             crate::header::COOKIE,
1562             HeaderValue::from_bytes(header.as_bytes()).unwrap(),
1563         );
1564     }
1565 }
1566 
1567 #[cfg(feature = "rustls-tls-native-roots")]
1568 lazy_static! {
1569     static ref NATIVE_ROOTS: std::io::Result<RootCertStore> = rustls_native_certs::load_native_certs().map_err(|e| e.1);
1570 }
1571 
1572 #[cfg(test)]
1573 mod tests {
1574     #[tokio::test]
execute_request_rejects_invald_urls()1575     async fn execute_request_rejects_invald_urls() {
1576         let url_str = "hxxps://www.rust-lang.org/";
1577         let url = url::Url::parse(url_str).unwrap();
1578         let result = crate::get(url.clone()).await;
1579 
1580         assert!(result.is_err());
1581         let err = result.err().unwrap();
1582         assert!(err.is_builder());
1583         assert_eq!(url_str, err.url().unwrap().as_str());
1584     }
1585 }
1586