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