1 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
2 use std::any::Any;
3 use std::net::IpAddr;
4 use std::sync::Arc;
5 use std::time::Duration;
6 use std::{collections::HashMap, convert::TryInto, net::SocketAddr};
7 use std::{fmt, str};
8
9 use bytes::Bytes;
10 use http::header::{
11 Entry, HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH,
12 CONTENT_TYPE, LOCATION, PROXY_AUTHORIZATION, RANGE, REFERER, TRANSFER_ENCODING, USER_AGENT,
13 };
14 use http::uri::Scheme;
15 use http::Uri;
16 use hyper::client::ResponseFuture;
17 #[cfg(feature = "native-tls-crate")]
18 use native_tls_crate::TlsConnector;
19 use pin_project_lite::pin_project;
20 use std::future::Future;
21 use std::pin::Pin;
22 use std::task::{Context, Poll};
23 use tokio::time::Sleep;
24
25 use log::debug;
26
27 use super::decoder::Accepts;
28 use super::request::{Request, RequestBuilder};
29 use super::response::Response;
30 use super::Body;
31 use crate::connect::{Connector, HttpConnector};
32 #[cfg(feature = "cookies")]
33 use crate::cookie;
34 use crate::error;
35 use crate::into_url::{expect_uri, try_uri};
36 use crate::redirect::{self, remove_sensitive_headers};
37 #[cfg(feature = "__tls")]
38 use crate::tls::{self, TlsBackend};
39 #[cfg(feature = "__tls")]
40 use crate::Certificate;
41 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
42 use crate::Identity;
43 use crate::{IntoUrl, Method, Proxy, StatusCode, Url};
44
45 /// An asynchronous `Client` to make Requests with.
46 ///
47 /// The Client has various configuration values to tweak, but the defaults
48 /// are set to what is usually the most commonly desired value. To configure a
49 /// `Client`, use `Client::builder()`.
50 ///
51 /// The `Client` holds a connection pool internally, so it is advised that
52 /// you create one and **reuse** it.
53 ///
54 /// You do **not** have to wrap the `Client` in an [`Rc`] or [`Arc`] to **reuse** it,
55 /// because it already uses an [`Arc`] internally.
56 ///
57 /// [`Rc`]: std::rc::Rc
58 #[derive(Clone)]
59 pub struct Client {
60 inner: Arc<ClientRef>,
61 }
62
63 /// A `ClientBuilder` can be used to create a `Client` with custom configuration.
64 #[must_use]
65 pub struct ClientBuilder {
66 config: Config,
67 }
68
69 enum HttpVersionPref {
70 Http1,
71 Http2,
72 All,
73 }
74
75 struct Config {
76 // NOTE: When adding a new field, update `fmt::Debug for ClientBuilder`
77 accepts: Accepts,
78 headers: HeaderMap,
79 #[cfg(feature = "native-tls")]
80 hostname_verification: bool,
81 #[cfg(feature = "__tls")]
82 certs_verification: bool,
83 connect_timeout: Option<Duration>,
84 connection_verbose: bool,
85 pool_idle_timeout: Option<Duration>,
86 pool_max_idle_per_host: usize,
87 tcp_keepalive: Option<Duration>,
88 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
89 identity: Option<Identity>,
90 proxies: Vec<Proxy>,
91 auto_sys_proxy: bool,
92 redirect_policy: redirect::Policy,
93 referer: bool,
94 timeout: Option<Duration>,
95 #[cfg(feature = "__tls")]
96 root_certs: Vec<Certificate>,
97 #[cfg(feature = "__tls")]
98 tls_built_in_root_certs: bool,
99 #[cfg(feature = "__tls")]
100 min_tls_version: Option<tls::Version>,
101 #[cfg(feature = "__tls")]
102 max_tls_version: Option<tls::Version>,
103 #[cfg(feature = "__tls")]
104 tls: TlsBackend,
105 http_version_pref: HttpVersionPref,
106 http1_title_case_headers: bool,
107 http2_initial_stream_window_size: Option<u32>,
108 http2_initial_connection_window_size: Option<u32>,
109 http2_adaptive_window: bool,
110 http2_max_frame_size: Option<u32>,
111 local_address: Option<IpAddr>,
112 nodelay: bool,
113 #[cfg(feature = "cookies")]
114 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
115 trust_dns: bool,
116 error: Option<crate::Error>,
117 https_only: bool,
118 dns_overrides: HashMap<String, SocketAddr>,
119 }
120
121 impl Default for ClientBuilder {
default() -> Self122 fn default() -> Self {
123 Self::new()
124 }
125 }
126
127 impl ClientBuilder {
128 /// Constructs a new `ClientBuilder`.
129 ///
130 /// This is the same as `Client::builder()`.
new() -> ClientBuilder131 pub fn new() -> ClientBuilder {
132 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
133 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
134
135 ClientBuilder {
136 config: Config {
137 error: None,
138 accepts: Accepts::default(),
139 headers,
140 #[cfg(feature = "native-tls")]
141 hostname_verification: true,
142 #[cfg(feature = "__tls")]
143 certs_verification: true,
144 connect_timeout: None,
145 connection_verbose: false,
146 pool_idle_timeout: Some(Duration::from_secs(90)),
147 pool_max_idle_per_host: std::usize::MAX,
148 // TODO: Re-enable default duration once hyper's HttpConnector is fixed
149 // to no longer error when an option fails.
150 tcp_keepalive: None, //Some(Duration::from_secs(60)),
151 proxies: Vec::new(),
152 auto_sys_proxy: true,
153 redirect_policy: redirect::Policy::default(),
154 referer: true,
155 timeout: None,
156 #[cfg(feature = "__tls")]
157 root_certs: Vec::new(),
158 #[cfg(feature = "__tls")]
159 tls_built_in_root_certs: true,
160 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
161 identity: None,
162 #[cfg(feature = "__tls")]
163 min_tls_version: None,
164 #[cfg(feature = "__tls")]
165 max_tls_version: None,
166 #[cfg(feature = "__tls")]
167 tls: TlsBackend::default(),
168 http_version_pref: HttpVersionPref::All,
169 http1_title_case_headers: false,
170 http2_initial_stream_window_size: None,
171 http2_initial_connection_window_size: None,
172 http2_adaptive_window: false,
173 http2_max_frame_size: None,
174 local_address: None,
175 nodelay: true,
176 trust_dns: cfg!(feature = "trust-dns"),
177 #[cfg(feature = "cookies")]
178 cookie_store: None,
179 https_only: false,
180 dns_overrides: HashMap::new(),
181 },
182 }
183 }
184
185 /// Returns a `Client` that uses this `ClientBuilder` configuration.
186 ///
187 /// # Errors
188 ///
189 /// This method fails if a TLS backend cannot be initialized, or the resolver
190 /// cannot load the system configuration.
build(self) -> crate::Result<Client>191 pub fn build(self) -> crate::Result<Client> {
192 let config = self.config;
193
194 if let Some(err) = config.error {
195 return Err(err);
196 }
197
198 let mut proxies = config.proxies;
199 if config.auto_sys_proxy {
200 proxies.push(Proxy::system());
201 }
202 let proxies = Arc::new(proxies);
203
204 let mut connector = {
205 #[cfg(feature = "__tls")]
206 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
207 headers.get(USER_AGENT).cloned()
208 }
209
210 let http = match config.trust_dns {
211 false => {
212 if config.dns_overrides.is_empty() {
213 HttpConnector::new_gai()
214 } else {
215 HttpConnector::new_gai_with_overrides(config.dns_overrides)
216 }
217 }
218 #[cfg(feature = "trust-dns")]
219 true => {
220 if config.dns_overrides.is_empty() {
221 HttpConnector::new_trust_dns()?
222 } else {
223 HttpConnector::new_trust_dns_with_overrides(config.dns_overrides)?
224 }
225 }
226 #[cfg(not(feature = "trust-dns"))]
227 true => unreachable!("trust-dns shouldn't be enabled unless the feature is"),
228 };
229
230 #[cfg(feature = "__tls")]
231 match config.tls {
232 #[cfg(feature = "default-tls")]
233 TlsBackend::Default => {
234 let mut tls = TlsConnector::builder();
235
236 #[cfg(feature = "native-tls-alpn")]
237 {
238 match config.http_version_pref {
239 HttpVersionPref::Http1 => {
240 tls.request_alpns(&["http/1.1"]);
241 }
242 HttpVersionPref::Http2 => {
243 tls.request_alpns(&["h2"]);
244 }
245 HttpVersionPref::All => {
246 tls.request_alpns(&["h2", "http/1.1"]);
247 }
248 }
249 }
250
251 #[cfg(feature = "native-tls")]
252 {
253 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
254 }
255
256 tls.danger_accept_invalid_certs(!config.certs_verification);
257
258 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
259
260 for cert in config.root_certs {
261 cert.add_to_native_tls(&mut tls);
262 }
263
264 #[cfg(feature = "native-tls")]
265 {
266 if let Some(id) = config.identity {
267 id.add_to_native_tls(&mut tls)?;
268 }
269 }
270
271 if let Some(min_tls_version) = config.min_tls_version {
272 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
273 // TLS v1.3. This would be entirely reasonable,
274 // native-tls just doesn't support it.
275 // https://github.com/sfackler/rust-native-tls/issues/140
276 crate::error::builder("invalid minimum TLS version for backend")
277 })?;
278 tls.min_protocol_version(Some(protocol));
279 }
280
281 if let Some(max_tls_version) = config.max_tls_version {
282 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
283 // TLS v1.3.
284 // We could arguably do max_protocol_version(None), given
285 // that 1.4 does not exist yet, but that'd get messy in the
286 // future.
287 crate::error::builder("invalid maximum TLS version for backend")
288 })?;
289 tls.max_protocol_version(Some(protocol));
290 }
291
292 Connector::new_default_tls(
293 http,
294 tls,
295 proxies.clone(),
296 user_agent(&config.headers),
297 config.local_address,
298 config.nodelay,
299 )?
300 }
301 #[cfg(feature = "native-tls")]
302 TlsBackend::BuiltNativeTls(conn) => Connector::from_built_default_tls(
303 http,
304 conn,
305 proxies.clone(),
306 user_agent(&config.headers),
307 config.local_address,
308 config.nodelay,
309 ),
310 #[cfg(feature = "__rustls")]
311 TlsBackend::BuiltRustls(conn) => Connector::new_rustls_tls(
312 http,
313 conn,
314 proxies.clone(),
315 user_agent(&config.headers),
316 config.local_address,
317 config.nodelay,
318 ),
319 #[cfg(feature = "__rustls")]
320 TlsBackend::Rustls => {
321 use crate::tls::NoVerifier;
322
323 // Set root certificates.
324 let mut root_cert_store = rustls::RootCertStore::empty();
325 for cert in config.root_certs {
326 cert.add_to_rustls(&mut root_cert_store)?;
327 }
328
329 #[cfg(feature = "rustls-tls-webpki-roots")]
330 if config.tls_built_in_root_certs {
331 use rustls::OwnedTrustAnchor;
332
333 let trust_anchors =
334 webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|trust_anchor| {
335 OwnedTrustAnchor::from_subject_spki_name_constraints(
336 trust_anchor.subject,
337 trust_anchor.spki,
338 trust_anchor.name_constraints,
339 )
340 });
341
342 root_cert_store.add_server_trust_anchors(trust_anchors);
343 }
344
345 #[cfg(feature = "rustls-tls-native-roots")]
346 if config.tls_built_in_root_certs {
347 for cert in rustls_native_certs::load_native_certs()
348 .map_err(crate::error::builder)?
349 {
350 root_cert_store
351 .add(&rustls::Certificate(cert.0))
352 .map_err(crate::error::builder)?
353 }
354 }
355
356 // Set TLS versions.
357 let mut versions = rustls::ALL_VERSIONS.to_vec();
358
359 if let Some(min_tls_version) = config.min_tls_version {
360 versions.retain(|&supported_version| {
361 match tls::Version::from_rustls(supported_version.version) {
362 Some(version) => version >= min_tls_version,
363 // Assume it's so new we don't know about it, allow it
364 // (as of writing this is unreachable)
365 None => true,
366 }
367 });
368 }
369
370 if let Some(max_tls_version) = config.max_tls_version {
371 versions.retain(|&supported_version| {
372 match tls::Version::from_rustls(supported_version.version) {
373 Some(version) => version <= max_tls_version,
374 None => false,
375 }
376 });
377 }
378
379 // Build TLS config
380 let config_builder = rustls::ClientConfig::builder()
381 .with_safe_default_cipher_suites()
382 .with_safe_default_kx_groups()
383 .with_protocol_versions(&versions)
384 .map_err(crate::error::builder)?
385 .with_root_certificates(root_cert_store);
386
387 // Finalize TLS config
388 let mut tls = if let Some(id) = config.identity {
389 id.add_to_rustls(config_builder)?
390 } else {
391 config_builder.with_no_client_auth()
392 };
393
394 // Certificate verifier
395 if !config.certs_verification {
396 tls.dangerous()
397 .set_certificate_verifier(Arc::new(NoVerifier));
398 }
399
400 // ALPN protocol
401 match config.http_version_pref {
402 HttpVersionPref::Http1 => {
403 tls.alpn_protocols = vec!["http/1.1".into()];
404 }
405 HttpVersionPref::Http2 => {
406 tls.alpn_protocols = vec!["h2".into()];
407 }
408 HttpVersionPref::All => {
409 tls.alpn_protocols = vec!["h2".into(), "http/1.1".into()];
410 }
411 }
412
413 Connector::new_rustls_tls(
414 http,
415 tls,
416 proxies.clone(),
417 user_agent(&config.headers),
418 config.local_address,
419 config.nodelay,
420 )
421 }
422 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
423 TlsBackend::UnknownPreconfigured => {
424 return Err(crate::error::builder(
425 "Unknown TLS backend passed to `use_preconfigured_tls`",
426 ));
427 }
428 }
429
430 #[cfg(not(feature = "__tls"))]
431 Connector::new(http, proxies.clone(), config.local_address, config.nodelay)
432 };
433
434 connector.set_timeout(config.connect_timeout);
435 connector.set_verbose(config.connection_verbose);
436
437 let mut builder = hyper::Client::builder();
438 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
439 builder.http2_only(true);
440 }
441
442 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size {
443 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
444 }
445 if let Some(http2_initial_connection_window_size) =
446 config.http2_initial_connection_window_size
447 {
448 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
449 }
450 if config.http2_adaptive_window {
451 builder.http2_adaptive_window(true);
452 }
453 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
454 builder.http2_max_frame_size(http2_max_frame_size);
455 }
456
457 builder.pool_idle_timeout(config.pool_idle_timeout);
458 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
459 connector.set_keepalive(config.tcp_keepalive);
460
461 if config.http1_title_case_headers {
462 builder.http1_title_case_headers(true);
463 }
464
465 let hyper_client = builder.build(connector);
466
467 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
468
469 Ok(Client {
470 inner: Arc::new(ClientRef {
471 accepts: config.accepts,
472 #[cfg(feature = "cookies")]
473 cookie_store: config.cookie_store,
474 hyper: hyper_client,
475 headers: config.headers,
476 redirect_policy: config.redirect_policy,
477 referer: config.referer,
478 request_timeout: config.timeout,
479 proxies,
480 proxies_maybe_http_auth,
481 https_only: config.https_only,
482 }),
483 })
484 }
485
486 // Higher-level options
487
488 /// Sets the `User-Agent` header to be used by this client.
489 ///
490 /// # Example
491 ///
492 /// ```rust
493 /// # async fn doc() -> Result<(), reqwest::Error> {
494 /// // Name your user agent after your app?
495 /// static APP_USER_AGENT: &str = concat!(
496 /// env!("CARGO_PKG_NAME"),
497 /// "/",
498 /// env!("CARGO_PKG_VERSION"),
499 /// );
500 ///
501 /// let client = reqwest::Client::builder()
502 /// .user_agent(APP_USER_AGENT)
503 /// .build()?;
504 /// let res = client.get("https://www.rust-lang.org").send().await?;
505 /// # Ok(())
506 /// # }
507 /// ```
user_agent<V>(mut self, value: V) -> ClientBuilder where V: TryInto<HeaderValue>, V::Error: Into<http::Error>,508 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
509 where
510 V: TryInto<HeaderValue>,
511 V::Error: Into<http::Error>,
512 {
513 match value.try_into() {
514 Ok(value) => {
515 self.config.headers.insert(USER_AGENT, value);
516 }
517 Err(e) => {
518 self.config.error = Some(crate::error::builder(e.into()));
519 }
520 };
521 self
522 }
523 /// Sets the default headers for every request.
524 ///
525 /// # Example
526 ///
527 /// ```rust
528 /// use reqwest::header;
529 /// # async fn doc() -> Result<(), reqwest::Error> {
530 /// let mut headers = header::HeaderMap::new();
531 /// headers.insert("X-MY-HEADER", header::HeaderValue::from_static("value"));
532 ///
533 /// // Consider marking security-sensitive headers with `set_sensitive`.
534 /// let mut auth_value = header::HeaderValue::from_static("secret");
535 /// auth_value.set_sensitive(true);
536 /// headers.insert(header::AUTHORIZATION, auth_value);
537 ///
538 /// // get a client builder
539 /// let client = reqwest::Client::builder()
540 /// .default_headers(headers)
541 /// .build()?;
542 /// let res = client.get("https://www.rust-lang.org").send().await?;
543 /// # Ok(())
544 /// # }
545 /// ```
546 ///
547 /// Override the default headers:
548 ///
549 /// ```rust
550 /// use reqwest::header;
551 /// # async fn doc() -> Result<(), reqwest::Error> {
552 /// let mut headers = header::HeaderMap::new();
553 /// headers.insert("X-MY-HEADER", header::HeaderValue::from_static("value"));
554 ///
555 /// // get a client builder
556 /// let client = reqwest::Client::builder()
557 /// .default_headers(headers)
558 /// .build()?;
559 /// let res = client
560 /// .get("https://www.rust-lang.org")
561 /// .header("X-MY-HEADER", "new_value")
562 /// .send()
563 /// .await?;
564 /// # Ok(())
565 /// # }
566 /// ```
default_headers(mut self, headers: HeaderMap) -> ClientBuilder567 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
568 for (key, value) in headers.iter() {
569 self.config.headers.insert(key, value.clone());
570 }
571 self
572 }
573
574 /// Enable a persistent cookie store for the client.
575 ///
576 /// Cookies received in responses will be preserved and included in
577 /// additional requests.
578 ///
579 /// By default, no cookie store is used.
580 ///
581 /// # Optional
582 ///
583 /// This requires the optional `cookies` feature to be enabled.
584 #[cfg(feature = "cookies")]
585 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
cookie_store(mut self, enable: bool) -> ClientBuilder586 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
587 if enable {
588 self.cookie_provider(Arc::new(cookie::Jar::default()))
589 } else {
590 self.config.cookie_store = None;
591 self
592 }
593 }
594
595 /// Set the persistent cookie store for the client.
596 ///
597 /// Cookies received in responses will be passed to this store, and
598 /// additional requests will query this store for cookies.
599 ///
600 /// By default, no cookie store is used.
601 ///
602 /// # Optional
603 ///
604 /// This requires the optional `cookies` feature to be enabled.
605 #[cfg(feature = "cookies")]
606 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
cookie_provider<C: cookie::CookieStore + 'static>( mut self, cookie_store: Arc<C>, ) -> ClientBuilder607 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
608 mut self,
609 cookie_store: Arc<C>,
610 ) -> ClientBuilder {
611 self.config.cookie_store = Some(cookie_store as _);
612 self
613 }
614
615 /// Enable auto gzip decompression by checking the `Content-Encoding` response header.
616 ///
617 /// If auto gzip decompression is turned on:
618 ///
619 /// - When sending a request and if the request's headers do not already contain
620 /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `gzip`.
621 /// The request body is **not** automatically compressed.
622 /// - When receiving a response, if its headers contain a `Content-Encoding` value of
623 /// `gzip`, both `Content-Encoding` and `Content-Length` are removed from the
624 /// headers' set. The response body is automatically decompressed.
625 ///
626 /// If the `gzip` feature is turned on, the default option is enabled.
627 ///
628 /// # Optional
629 ///
630 /// This requires the optional `gzip` feature to be enabled
631 #[cfg(feature = "gzip")]
632 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
gzip(mut self, enable: bool) -> ClientBuilder633 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
634 self.config.accepts.gzip = enable;
635 self
636 }
637
638 /// Enable auto brotli decompression by checking the `Content-Encoding` response header.
639 ///
640 /// If auto brotli decompression is turned on:
641 ///
642 /// - When sending a request and if the request's headers do not already contain
643 /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `br`.
644 /// The request body is **not** automatically compressed.
645 /// - When receiving a response, if its headers contain a `Content-Encoding` value of
646 /// `br`, both `Content-Encoding` and `Content-Length` are removed from the
647 /// headers' set. The response body is automatically decompressed.
648 ///
649 /// If the `brotli` feature is turned on, the default option is enabled.
650 ///
651 /// # Optional
652 ///
653 /// This requires the optional `brotli` feature to be enabled
654 #[cfg(feature = "brotli")]
655 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
brotli(mut self, enable: bool) -> ClientBuilder656 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
657 self.config.accepts.brotli = enable;
658 self
659 }
660
661 /// Enable auto deflate decompression by checking the `Content-Encoding` response header.
662 ///
663 /// If auto deflate decompression is turned on:
664 ///
665 /// - When sending a request and if the request's headers do not already contain
666 /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `deflate`.
667 /// The request body is **not** automatically compressed.
668 /// - When receiving a response, if it's headers contain a `Content-Encoding` value that
669 /// equals to `deflate`, both values `Content-Encoding` and `Content-Length` are removed from the
670 /// headers' set. The response body is automatically decompressed.
671 ///
672 /// If the `deflate` feature is turned on, the default option is enabled.
673 ///
674 /// # Optional
675 ///
676 /// This requires the optional `deflate` feature to be enabled
677 #[cfg(feature = "deflate")]
678 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
deflate(mut self, enable: bool) -> ClientBuilder679 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
680 self.config.accepts.deflate = enable;
681 self
682 }
683
684 /// Disable auto response body gzip decompression.
685 ///
686 /// This method exists even if the optional `gzip` feature is not enabled.
687 /// This can be used to ensure a `Client` doesn't use gzip decompression
688 /// even if another dependency were to enable the optional `gzip` feature.
no_gzip(self) -> ClientBuilder689 pub fn no_gzip(self) -> ClientBuilder {
690 #[cfg(feature = "gzip")]
691 {
692 self.gzip(false)
693 }
694
695 #[cfg(not(feature = "gzip"))]
696 {
697 self
698 }
699 }
700
701 /// Disable auto response body brotli decompression.
702 ///
703 /// This method exists even if the optional `brotli` feature is not enabled.
704 /// This can be used to ensure a `Client` doesn't use brotli decompression
705 /// even if another dependency were to enable the optional `brotli` feature.
no_brotli(self) -> ClientBuilder706 pub fn no_brotli(self) -> ClientBuilder {
707 #[cfg(feature = "brotli")]
708 {
709 self.brotli(false)
710 }
711
712 #[cfg(not(feature = "brotli"))]
713 {
714 self
715 }
716 }
717
718 /// Disable auto response body deflate decompression.
719 ///
720 /// This method exists even if the optional `deflate` feature is not enabled.
721 /// This can be used to ensure a `Client` doesn't use deflate decompression
722 /// even if another dependency were to enable the optional `deflate` feature.
no_deflate(self) -> ClientBuilder723 pub fn no_deflate(self) -> ClientBuilder {
724 #[cfg(feature = "deflate")]
725 {
726 self.deflate(false)
727 }
728
729 #[cfg(not(feature = "deflate"))]
730 {
731 self
732 }
733 }
734
735 // Redirect options
736
737 /// Set a `RedirectPolicy` for this client.
738 ///
739 /// Default will follow redirects up to a maximum of 10.
redirect(mut self, policy: redirect::Policy) -> ClientBuilder740 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
741 self.config.redirect_policy = policy;
742 self
743 }
744
745 /// Enable or disable automatic setting of the `Referer` header.
746 ///
747 /// Default is `true`.
referer(mut self, enable: bool) -> ClientBuilder748 pub fn referer(mut self, enable: bool) -> ClientBuilder {
749 self.config.referer = enable;
750 self
751 }
752
753 // Proxy options
754
755 /// Add a `Proxy` to the list of proxies the `Client` will use.
756 ///
757 /// # Note
758 ///
759 /// Adding a proxy will disable the automatic usage of the "system" proxy.
proxy(mut self, proxy: Proxy) -> ClientBuilder760 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
761 self.config.proxies.push(proxy);
762 self.config.auto_sys_proxy = false;
763 self
764 }
765
766 /// Clear all `Proxies`, so `Client` will use no proxy anymore.
767 ///
768 /// This also disables the automatic usage of the "system" proxy.
no_proxy(mut self) -> ClientBuilder769 pub fn no_proxy(mut self) -> ClientBuilder {
770 self.config.proxies.clear();
771 self.config.auto_sys_proxy = false;
772 self
773 }
774
775 // Timeout options
776
777 /// Enables a request timeout.
778 ///
779 /// The timeout is applied from when the request starts connecting until the
780 /// response body has finished.
781 ///
782 /// Default is no timeout.
timeout(mut self, timeout: Duration) -> ClientBuilder783 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
784 self.config.timeout = Some(timeout);
785 self
786 }
787
788 /// Set a timeout for only the connect phase of a `Client`.
789 ///
790 /// Default is `None`.
791 ///
792 /// # Note
793 ///
794 /// This **requires** the futures be executed in a tokio runtime with
795 /// a tokio timer enabled.
connect_timeout(mut self, timeout: Duration) -> ClientBuilder796 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
797 self.config.connect_timeout = Some(timeout);
798 self
799 }
800
801 /// Set whether connections should emit verbose logs.
802 ///
803 /// Enabling this option will emit [log][] messages at the `TRACE` level
804 /// for read and write operations on connections.
805 ///
806 /// [log]: https://crates.io/crates/log
connection_verbose(mut self, verbose: bool) -> ClientBuilder807 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
808 self.config.connection_verbose = verbose;
809 self
810 }
811
812 // HTTP options
813
814 /// Set an optional timeout for idle sockets being kept-alive.
815 ///
816 /// Pass `None` to disable timeout.
817 ///
818 /// Default is 90 seconds.
pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder where D: Into<Option<Duration>>,819 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
820 where
821 D: Into<Option<Duration>>,
822 {
823 self.config.pool_idle_timeout = val.into();
824 self
825 }
826
827 /// Sets the maximum idle connection per host allowed in the pool.
pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder828 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
829 self.config.pool_max_idle_per_host = max;
830 self
831 }
832
833 /// Send headers as title case instead of lowercase.
http1_title_case_headers(mut self) -> ClientBuilder834 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
835 self.config.http1_title_case_headers = true;
836 self
837 }
838
839 /// Only use HTTP/1.
http1_only(mut self) -> ClientBuilder840 pub fn http1_only(mut self) -> ClientBuilder {
841 self.config.http_version_pref = HttpVersionPref::Http1;
842 self
843 }
844
845 /// Only use HTTP/2.
http2_prior_knowledge(mut self) -> ClientBuilder846 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
847 self.config.http_version_pref = HttpVersionPref::Http2;
848 self
849 }
850
851 /// Sets the `SETTINGS_INITIAL_WINDOW_SIZE` option for HTTP2 stream-level flow control.
852 ///
853 /// 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>>) -> ClientBuilder854 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
855 self.config.http2_initial_stream_window_size = sz.into();
856 self
857 }
858
859 /// Sets the max connection-level flow control for HTTP2
860 ///
861 /// 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>>, ) -> ClientBuilder862 pub fn http2_initial_connection_window_size(
863 mut self,
864 sz: impl Into<Option<u32>>,
865 ) -> ClientBuilder {
866 self.config.http2_initial_connection_window_size = sz.into();
867 self
868 }
869
870 /// Sets whether to use an adaptive flow control.
871 ///
872 /// Enabling this will override the limits set in `http2_initial_stream_window_size` and
873 /// `http2_initial_connection_window_size`.
http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder874 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
875 self.config.http2_adaptive_window = enabled;
876 self
877 }
878
879 /// Sets the maximum frame size to use for HTTP2.
880 ///
881 /// Default is currently 16,384 but may change internally to optimize for common uses.
http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder882 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
883 self.config.http2_max_frame_size = sz.into();
884 self
885 }
886
887 // TCP options
888
889 /// Set whether sockets have `SO_NODELAY` enabled.
890 ///
891 /// Default is `true`.
tcp_nodelay(mut self, enabled: bool) -> ClientBuilder892 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
893 self.config.nodelay = enabled;
894 self
895 }
896
897 /// Bind to a local IP Address.
898 ///
899 /// # Example
900 ///
901 /// ```
902 /// use std::net::IpAddr;
903 /// let local_addr = IpAddr::from([12, 4, 1, 8]);
904 /// let client = reqwest::Client::builder()
905 /// .local_address(local_addr)
906 /// .build().unwrap();
907 /// ```
local_address<T>(mut self, addr: T) -> ClientBuilder where T: Into<Option<IpAddr>>,908 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
909 where
910 T: Into<Option<IpAddr>>,
911 {
912 self.config.local_address = addr.into();
913 self
914 }
915
916 /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration.
917 ///
918 /// If `None`, the option will not be set.
tcp_keepalive<D>(mut self, val: D) -> ClientBuilder where D: Into<Option<Duration>>,919 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
920 where
921 D: Into<Option<Duration>>,
922 {
923 self.config.tcp_keepalive = val.into();
924 self
925 }
926
927 // TLS options
928
929 /// Add a custom root certificate.
930 ///
931 /// This can be used to connect to a server that has a self-signed
932 /// certificate for example.
933 ///
934 /// # Optional
935 ///
936 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
937 /// feature to be enabled.
938 #[cfg(feature = "__tls")]
939 #[cfg_attr(
940 docsrs,
941 doc(cfg(any(
942 feature = "default-tls",
943 feature = "native-tls",
944 feature = "rustls-tls"
945 )))
946 )]
add_root_certificate(mut self, cert: Certificate) -> ClientBuilder947 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
948 self.config.root_certs.push(cert);
949 self
950 }
951
952 /// Controls the use of built-in/preloaded certificates during certificate validation.
953 ///
954 /// Defaults to `true` -- built-in system certs will be used.
955 ///
956 /// # Optional
957 ///
958 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
959 /// feature to be enabled.
960 #[cfg(feature = "__tls")]
961 #[cfg_attr(
962 docsrs,
963 doc(cfg(any(
964 feature = "default-tls",
965 feature = "native-tls",
966 feature = "rustls-tls"
967 )))
968 )]
tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder969 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
970 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
971 self
972 }
973
974 /// Sets the identity to be used for client certificate authentication.
975 ///
976 /// # Optional
977 ///
978 /// This requires the optional `native-tls` or `rustls-tls(-...)` feature to be
979 /// enabled.
980 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
981 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
identity(mut self, identity: Identity) -> ClientBuilder982 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
983 self.config.identity = Some(identity);
984 self
985 }
986
987 /// Controls the use of hostname verification.
988 ///
989 /// Defaults to `false`.
990 ///
991 /// # Warning
992 ///
993 /// You should think very carefully before you use this method. If
994 /// hostname verification is not used, any valid certificate for any
995 /// site will be trusted for use from any other. This introduces a
996 /// significant vulnerability to man-in-the-middle attacks.
997 ///
998 /// # Optional
999 ///
1000 /// This requires the optional `native-tls` feature to be enabled.
1001 #[cfg(feature = "native-tls")]
1002 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
danger_accept_invalid_hostnames( mut self, accept_invalid_hostname: bool, ) -> ClientBuilder1003 pub fn danger_accept_invalid_hostnames(
1004 mut self,
1005 accept_invalid_hostname: bool,
1006 ) -> ClientBuilder {
1007 self.config.hostname_verification = !accept_invalid_hostname;
1008 self
1009 }
1010
1011 /// Controls the use of certificate validation.
1012 ///
1013 /// Defaults to `false`.
1014 ///
1015 /// # Warning
1016 ///
1017 /// You should think very carefully before using this method. If
1018 /// invalid certificates are trusted, *any* certificate for *any* site
1019 /// will be trusted for use. This includes expired certificates. This
1020 /// introduces significant vulnerabilities, and should only be used
1021 /// as a last resort.
1022 ///
1023 /// # Optional
1024 ///
1025 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
1026 /// feature to be enabled.
1027 #[cfg(feature = "__tls")]
1028 #[cfg_attr(
1029 docsrs,
1030 doc(cfg(any(
1031 feature = "default-tls",
1032 feature = "native-tls",
1033 feature = "rustls-tls"
1034 )))
1035 )]
danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder1036 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1037 self.config.certs_verification = !accept_invalid_certs;
1038 self
1039 }
1040
1041 /// Set the minimum required TLS version for connections.
1042 ///
1043 /// By default the TLS backend's own default is used.
1044 ///
1045 /// # Errors
1046 ///
1047 /// A value of `tls::Version::TLS_1_3` will cause an error with the
1048 /// `native-tls`/`default-tls` backend. This does not mean the version
1049 /// isn't supported, just that it can't be set as a minimum due to
1050 /// technical limitations.
1051 ///
1052 /// # Optional
1053 ///
1054 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
1055 /// feature to be enabled.
1056 #[cfg(feature = "__tls")]
1057 #[cfg_attr(
1058 docsrs,
1059 doc(cfg(any(
1060 feature = "default-tls",
1061 feature = "native-tls",
1062 feature = "rustls-tls"
1063 )))
1064 )]
min_tls_version(mut self, version: tls::Version) -> ClientBuilder1065 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1066 self.config.min_tls_version = Some(version);
1067 self
1068 }
1069
1070 /// Set the maximum allowed TLS version for connections.
1071 ///
1072 /// By default there's no maximum.
1073 ///
1074 /// # Errors
1075 ///
1076 /// A value of `tls::Version::TLS_1_3` will cause an error with the
1077 /// `native-tls`/`default-tls` backend. This does not mean the version
1078 /// isn't supported, just that it can't be set as a maximum due to
1079 /// technical limitations.
1080 ///
1081 /// # Optional
1082 ///
1083 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
1084 /// feature to be enabled.
1085 #[cfg(feature = "__tls")]
1086 #[cfg_attr(
1087 docsrs,
1088 doc(cfg(any(
1089 feature = "default-tls",
1090 feature = "native-tls",
1091 feature = "rustls-tls"
1092 )))
1093 )]
max_tls_version(mut self, version: tls::Version) -> ClientBuilder1094 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1095 self.config.max_tls_version = Some(version);
1096 self
1097 }
1098
1099 /// Force using the native TLS backend.
1100 ///
1101 /// Since multiple TLS backends can be optionally enabled, this option will
1102 /// force the `native-tls` backend to be used for this `Client`.
1103 ///
1104 /// # Optional
1105 ///
1106 /// This requires the optional `native-tls` feature to be enabled.
1107 #[cfg(feature = "native-tls")]
1108 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
use_native_tls(mut self) -> ClientBuilder1109 pub fn use_native_tls(mut self) -> ClientBuilder {
1110 self.config.tls = TlsBackend::Default;
1111 self
1112 }
1113
1114 /// Force using the Rustls TLS backend.
1115 ///
1116 /// Since multiple TLS backends can be optionally enabled, this option will
1117 /// force the `rustls` backend to be used for this `Client`.
1118 ///
1119 /// # Optional
1120 ///
1121 /// This requires the optional `rustls-tls(-...)` feature to be enabled.
1122 #[cfg(feature = "__rustls")]
1123 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
use_rustls_tls(mut self) -> ClientBuilder1124 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1125 self.config.tls = TlsBackend::Rustls;
1126 self
1127 }
1128
1129 /// Use a preconfigured TLS backend.
1130 ///
1131 /// If the passed `Any` argument is not a TLS backend that reqwest
1132 /// understands, the `ClientBuilder` will error when calling `build`.
1133 ///
1134 /// # Advanced
1135 ///
1136 /// This is an advanced option, and can be somewhat brittle. Usage requires
1137 /// keeping the preconfigured TLS argument version in sync with reqwest,
1138 /// since version mismatches will result in an "unknown" TLS backend.
1139 ///
1140 /// If possible, it's preferable to use the methods on `ClientBuilder`
1141 /// to configure reqwest's TLS.
1142 ///
1143 /// # Optional
1144 ///
1145 /// This requires one of the optional features `native-tls` or
1146 /// `rustls-tls(-...)` to be enabled.
1147 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
1148 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder1149 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
1150 let mut tls = Some(tls);
1151 #[cfg(feature = "native-tls")]
1152 {
1153 if let Some(conn) =
1154 (&mut tls as &mut dyn Any).downcast_mut::<Option<native_tls_crate::TlsConnector>>()
1155 {
1156 let tls = conn.take().expect("is definitely Some");
1157 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
1158 self.config.tls = tls;
1159 return self;
1160 }
1161 }
1162 #[cfg(feature = "__rustls")]
1163 {
1164 if let Some(conn) =
1165 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
1166 {
1167 let tls = conn.take().expect("is definitely Some");
1168 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
1169 self.config.tls = tls;
1170 return self;
1171 }
1172 }
1173
1174 // Otherwise, we don't recognize the TLS backend!
1175 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
1176 self
1177 }
1178
1179 /// Enables the [trust-dns](trust_dns_resolver) async resolver instead of a default threadpool using `getaddrinfo`.
1180 ///
1181 /// If the `trust-dns` feature is turned on, the default option is enabled.
1182 ///
1183 /// # Optional
1184 ///
1185 /// This requires the optional `trust-dns` feature to be enabled
1186 #[cfg(feature = "trust-dns")]
1187 #[cfg_attr(docsrs, doc(cfg(feature = "trust-dns")))]
trust_dns(mut self, enable: bool) -> ClientBuilder1188 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
1189 self.config.trust_dns = enable;
1190 self
1191 }
1192
1193 /// Disables the trust-dns async resolver.
1194 ///
1195 /// This method exists even if the optional `trust-dns` feature is not enabled.
1196 /// This can be used to ensure a `Client` doesn't use the trust-dns async resolver
1197 /// even if another dependency were to enable the optional `trust-dns` feature.
no_trust_dns(self) -> ClientBuilder1198 pub fn no_trust_dns(self) -> ClientBuilder {
1199 #[cfg(feature = "trust-dns")]
1200 {
1201 self.trust_dns(false)
1202 }
1203
1204 #[cfg(not(feature = "trust-dns"))]
1205 {
1206 self
1207 }
1208 }
1209
1210 /// Restrict the Client to be used with HTTPS only requests.
1211 ///
1212 /// Defaults to false.
https_only(mut self, enabled: bool) -> ClientBuilder1213 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
1214 self.config.https_only = enabled;
1215 self
1216 }
1217
1218 /// Override DNS resolution for specific domains to particular IP addresses.
1219 ///
1220 /// Warning
1221 ///
1222 /// Since the DNS protocol has no notion of ports, if you wish to send
1223 /// traffic to a particular port you must include this port in the URL
1224 /// itself, any port in the overridden addr will be ignored and traffic sent
1225 /// to the conventional port for the given scheme (e.g. 80 for http).
resolve(mut self, domain: &str, addr: SocketAddr) -> ClientBuilder1226 pub fn resolve(mut self, domain: &str, addr: SocketAddr) -> ClientBuilder {
1227 self.config.dns_overrides.insert(domain.to_string(), addr);
1228 self
1229 }
1230 }
1231
1232 type HyperClient = hyper::Client<Connector, super::body::ImplStream>;
1233
1234 impl Default for Client {
default() -> Self1235 fn default() -> Self {
1236 Self::new()
1237 }
1238 }
1239
1240 impl Client {
1241 /// Constructs a new `Client`.
1242 ///
1243 /// # Panics
1244 ///
1245 /// This method panics if a TLS backend cannot initialized, or the resolver
1246 /// cannot load the system configuration.
1247 ///
1248 /// Use `Client::builder()` if you wish to handle the failure as an `Error`
1249 /// instead of panicking.
new() -> Client1250 pub fn new() -> Client {
1251 ClientBuilder::new().build().expect("Client::new()")
1252 }
1253
1254 /// Creates a `ClientBuilder` to configure a `Client`.
1255 ///
1256 /// This is the same as `ClientBuilder::new()`.
builder() -> ClientBuilder1257 pub fn builder() -> ClientBuilder {
1258 ClientBuilder::new()
1259 }
1260
1261 /// Convenience method to make a `GET` request to a URL.
1262 ///
1263 /// # Errors
1264 ///
1265 /// This method fails whenever the supplied `Url` cannot be parsed.
get<U: IntoUrl>(&self, url: U) -> RequestBuilder1266 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1267 self.request(Method::GET, url)
1268 }
1269
1270 /// Convenience method to make a `POST` request to a URL.
1271 ///
1272 /// # Errors
1273 ///
1274 /// This method fails whenever the supplied `Url` cannot be parsed.
post<U: IntoUrl>(&self, url: U) -> RequestBuilder1275 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1276 self.request(Method::POST, url)
1277 }
1278
1279 /// Convenience method to make a `PUT` request to a URL.
1280 ///
1281 /// # Errors
1282 ///
1283 /// This method fails whenever the supplied `Url` cannot be parsed.
put<U: IntoUrl>(&self, url: U) -> RequestBuilder1284 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1285 self.request(Method::PUT, url)
1286 }
1287
1288 /// Convenience method to make a `PATCH` request to a URL.
1289 ///
1290 /// # Errors
1291 ///
1292 /// This method fails whenever the supplied `Url` cannot be parsed.
patch<U: IntoUrl>(&self, url: U) -> RequestBuilder1293 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1294 self.request(Method::PATCH, url)
1295 }
1296
1297 /// Convenience method to make a `DELETE` request to a URL.
1298 ///
1299 /// # Errors
1300 ///
1301 /// This method fails whenever the supplied `Url` cannot be parsed.
delete<U: IntoUrl>(&self, url: U) -> RequestBuilder1302 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1303 self.request(Method::DELETE, url)
1304 }
1305
1306 /// Convenience method to make a `HEAD` request to a URL.
1307 ///
1308 /// # Errors
1309 ///
1310 /// This method fails whenever the supplied `Url` cannot be parsed.
head<U: IntoUrl>(&self, url: U) -> RequestBuilder1311 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1312 self.request(Method::HEAD, url)
1313 }
1314
1315 /// Start building a `Request` with the `Method` and `Url`.
1316 ///
1317 /// Returns a `RequestBuilder`, which will allow setting headers and
1318 /// the request body before sending.
1319 ///
1320 /// # Errors
1321 ///
1322 /// This method fails whenever the supplied `Url` cannot be parsed.
request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder1323 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
1324 let req = url.into_url().map(move |url| Request::new(method, url));
1325 RequestBuilder::new(self.clone(), req)
1326 }
1327
1328 /// Executes a `Request`.
1329 ///
1330 /// A `Request` can be built manually with `Request::new()` or obtained
1331 /// from a RequestBuilder with `RequestBuilder::build()`.
1332 ///
1333 /// You should prefer to use the `RequestBuilder` and
1334 /// `RequestBuilder::send()`.
1335 ///
1336 /// # Errors
1337 ///
1338 /// This method fails if there was an error while sending request,
1339 /// redirect loop was detected or redirect limit was exhausted.
execute( &self, request: Request, ) -> impl Future<Output = Result<Response, crate::Error>>1340 pub fn execute(
1341 &self,
1342 request: Request,
1343 ) -> impl Future<Output = Result<Response, crate::Error>> {
1344 self.execute_request(request)
1345 }
1346
execute_request(&self, req: Request) -> Pending1347 pub(super) fn execute_request(&self, req: Request) -> Pending {
1348 let (method, url, mut headers, body, timeout, version) = req.pieces();
1349 if url.scheme() != "http" && url.scheme() != "https" {
1350 return Pending::new_err(error::url_bad_scheme(url));
1351 }
1352
1353 // check if we're in https_only mode and check the scheme of the current URL
1354 if self.inner.https_only && url.scheme() != "https" {
1355 return Pending::new_err(error::url_bad_scheme(url));
1356 }
1357
1358 // insert default headers in the request headers
1359 // without overwriting already appended headers.
1360 for (key, value) in &self.inner.headers {
1361 if let Entry::Vacant(entry) = headers.entry(key) {
1362 entry.insert(value.clone());
1363 }
1364 }
1365
1366 // Add cookies from the cookie store.
1367 #[cfg(feature = "cookies")]
1368 {
1369 if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
1370 if headers.get(crate::header::COOKIE).is_none() {
1371 add_cookie_header(&mut headers, &**cookie_store, &url);
1372 }
1373 }
1374 }
1375
1376 let accept_encoding = self.inner.accepts.as_str();
1377
1378 if let Some(accept_encoding) = accept_encoding {
1379 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
1380 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
1381 }
1382 }
1383
1384 let uri = expect_uri(&url);
1385
1386 let (reusable, body) = match body {
1387 Some(body) => {
1388 let (reusable, body) = body.try_reuse();
1389 (Some(reusable), body)
1390 }
1391 None => (None, Body::empty()),
1392 };
1393
1394 self.proxy_auth(&uri, &mut headers);
1395
1396 let mut req = hyper::Request::builder()
1397 .method(method.clone())
1398 .uri(uri)
1399 .version(version)
1400 .body(body.into_stream())
1401 .expect("valid request parts");
1402
1403 let timeout = timeout
1404 .or(self.inner.request_timeout)
1405 .map(tokio::time::sleep)
1406 .map(Box::pin);
1407
1408 *req.headers_mut() = headers.clone();
1409
1410 let in_flight = self.inner.hyper.request(req);
1411
1412 Pending {
1413 inner: PendingInner::Request(PendingRequest {
1414 method,
1415 url,
1416 headers,
1417 body: reusable,
1418
1419 urls: Vec::new(),
1420
1421 client: self.inner.clone(),
1422
1423 in_flight,
1424 timeout,
1425 }),
1426 }
1427 }
1428
proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap)1429 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
1430 if !self.inner.proxies_maybe_http_auth {
1431 return;
1432 }
1433
1434 // Only set the header here if the destination scheme is 'http',
1435 // since otherwise, the header will be included in the CONNECT tunnel
1436 // request instead.
1437 if dst.scheme() != Some(&Scheme::HTTP) {
1438 return;
1439 }
1440
1441 if headers.contains_key(PROXY_AUTHORIZATION) {
1442 return;
1443 }
1444
1445 for proxy in self.inner.proxies.iter() {
1446 if proxy.is_match(dst) {
1447 if let Some(header) = proxy.http_basic_auth(dst) {
1448 headers.insert(PROXY_AUTHORIZATION, header);
1449 }
1450
1451 break;
1452 }
1453 }
1454 }
1455 }
1456
1457 impl fmt::Debug for Client {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1458 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1459 let mut builder = f.debug_struct("Client");
1460 self.inner.fmt_fields(&mut builder);
1461 builder.finish()
1462 }
1463 }
1464
1465 impl fmt::Debug for ClientBuilder {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1466 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1467 let mut builder = f.debug_struct("ClientBuilder");
1468 self.config.fmt_fields(&mut builder);
1469 builder.finish()
1470 }
1471 }
1472
1473 impl Config {
fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>)1474 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
1475 // Instead of deriving Debug, only print fields when their output
1476 // would provide relevant or interesting data.
1477
1478 #[cfg(feature = "cookies")]
1479 {
1480 if let Some(_) = self.cookie_store {
1481 f.field("cookie_store", &true);
1482 }
1483 }
1484
1485 f.field("accepts", &self.accepts);
1486
1487 if !self.proxies.is_empty() {
1488 f.field("proxies", &self.proxies);
1489 }
1490
1491 if !self.redirect_policy.is_default() {
1492 f.field("redirect_policy", &self.redirect_policy);
1493 }
1494
1495 if self.referer {
1496 f.field("referer", &true);
1497 }
1498
1499 f.field("default_headers", &self.headers);
1500
1501 if self.http1_title_case_headers {
1502 f.field("http1_title_case_headers", &true);
1503 }
1504
1505 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
1506 f.field("http1_only", &true);
1507 }
1508
1509 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
1510 f.field("http2_prior_knowledge", &true);
1511 }
1512
1513 if let Some(ref d) = self.connect_timeout {
1514 f.field("connect_timeout", d);
1515 }
1516
1517 if let Some(ref d) = self.timeout {
1518 f.field("timeout", d);
1519 }
1520
1521 if let Some(ref v) = self.local_address {
1522 f.field("local_address", v);
1523 }
1524
1525 if self.nodelay {
1526 f.field("tcp_nodelay", &true);
1527 }
1528
1529 #[cfg(feature = "native-tls")]
1530 {
1531 if !self.hostname_verification {
1532 f.field("danger_accept_invalid_hostnames", &true);
1533 }
1534 }
1535
1536 #[cfg(feature = "__tls")]
1537 {
1538 if !self.certs_verification {
1539 f.field("danger_accept_invalid_certs", &true);
1540 }
1541
1542 if let Some(ref min_tls_version) = self.min_tls_version {
1543 f.field("min_tls_version", min_tls_version);
1544 }
1545
1546 if let Some(ref max_tls_version) = self.max_tls_version {
1547 f.field("max_tls_version", max_tls_version);
1548 }
1549 }
1550
1551 #[cfg(all(feature = "native-tls-crate", feature = "__rustls"))]
1552 {
1553 f.field("tls_backend", &self.tls);
1554 }
1555
1556 if !self.dns_overrides.is_empty() {
1557 f.field("dns_overrides", &self.dns_overrides);
1558 }
1559 }
1560 }
1561
1562 struct ClientRef {
1563 accepts: Accepts,
1564 #[cfg(feature = "cookies")]
1565 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
1566 headers: HeaderMap,
1567 hyper: HyperClient,
1568 redirect_policy: redirect::Policy,
1569 referer: bool,
1570 request_timeout: Option<Duration>,
1571 proxies: Arc<Vec<Proxy>>,
1572 proxies_maybe_http_auth: bool,
1573 https_only: bool,
1574 }
1575
1576 impl ClientRef {
fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>)1577 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
1578 // Instead of deriving Debug, only print fields when their output
1579 // would provide relevant or interesting data.
1580
1581 #[cfg(feature = "cookies")]
1582 {
1583 if let Some(_) = self.cookie_store {
1584 f.field("cookie_store", &true);
1585 }
1586 }
1587
1588 f.field("accepts", &self.accepts);
1589
1590 if !self.proxies.is_empty() {
1591 f.field("proxies", &self.proxies);
1592 }
1593
1594 if !self.redirect_policy.is_default() {
1595 f.field("redirect_policy", &self.redirect_policy);
1596 }
1597
1598 if self.referer {
1599 f.field("referer", &true);
1600 }
1601
1602 f.field("default_headers", &self.headers);
1603
1604 if let Some(ref d) = self.request_timeout {
1605 f.field("timeout", d);
1606 }
1607 }
1608 }
1609
1610 pin_project! {
1611 pub(super) struct Pending {
1612 #[pin]
1613 inner: PendingInner,
1614 }
1615 }
1616
1617 enum PendingInner {
1618 Request(PendingRequest),
1619 Error(Option<crate::Error>),
1620 }
1621
1622 pin_project! {
1623 struct PendingRequest {
1624 method: Method,
1625 url: Url,
1626 headers: HeaderMap,
1627 body: Option<Option<Bytes>>,
1628
1629 urls: Vec<Url>,
1630
1631 client: Arc<ClientRef>,
1632
1633 #[pin]
1634 in_flight: ResponseFuture,
1635 #[pin]
1636 timeout: Option<Pin<Box<Sleep>>>,
1637 }
1638 }
1639
1640 impl PendingRequest {
in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture>1641 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
1642 self.project().in_flight
1643 }
1644
timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>>1645 fn timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
1646 self.project().timeout
1647 }
1648
urls(self: Pin<&mut Self>) -> &mut Vec<Url>1649 fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
1650 self.project().urls
1651 }
1652
headers(self: Pin<&mut Self>) -> &mut HeaderMap1653 fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
1654 self.project().headers
1655 }
1656 }
1657
1658 impl Pending {
new_err(err: crate::Error) -> Pending1659 pub(super) fn new_err(err: crate::Error) -> Pending {
1660 Pending {
1661 inner: PendingInner::Error(Some(err)),
1662 }
1663 }
1664
inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner>1665 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
1666 self.project().inner
1667 }
1668 }
1669
1670 impl Future for Pending {
1671 type Output = Result<Response, crate::Error>;
1672
poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>1673 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1674 let inner = self.inner();
1675 match inner.get_mut() {
1676 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
1677 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
1678 .take()
1679 .expect("Pending error polled more than once"))),
1680 }
1681 }
1682 }
1683
1684 impl Future for PendingRequest {
1685 type Output = Result<Response, crate::Error>;
1686
poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>1687 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1688 if let Some(delay) = self.as_mut().timeout().as_mut().as_pin_mut() {
1689 if let Poll::Ready(()) = delay.poll(cx) {
1690 return Poll::Ready(Err(
1691 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
1692 ));
1693 }
1694 }
1695
1696 loop {
1697 let res = match self.as_mut().in_flight().as_mut().poll(cx) {
1698 Poll::Ready(Err(e)) => {
1699 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
1700 }
1701 Poll::Ready(Ok(res)) => res,
1702 Poll::Pending => return Poll::Pending,
1703 };
1704
1705 #[cfg(feature = "cookies")]
1706 {
1707 if let Some(ref cookie_store) = self.client.cookie_store {
1708 let mut cookies =
1709 cookie::extract_response_cookie_headers(&res.headers()).peekable();
1710 if cookies.peek().is_some() {
1711 cookie_store.set_cookies(&mut cookies, &self.url);
1712 }
1713 }
1714 }
1715 let should_redirect = match res.status() {
1716 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
1717 self.body = None;
1718 for header in &[
1719 TRANSFER_ENCODING,
1720 CONTENT_ENCODING,
1721 CONTENT_TYPE,
1722 CONTENT_LENGTH,
1723 ] {
1724 self.headers.remove(header);
1725 }
1726
1727 match self.method {
1728 Method::GET | Method::HEAD => {}
1729 _ => {
1730 self.method = Method::GET;
1731 }
1732 }
1733 true
1734 }
1735 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
1736 match self.body {
1737 Some(Some(_)) | None => true,
1738 Some(None) => false,
1739 }
1740 }
1741 _ => false,
1742 };
1743 if should_redirect {
1744 let loc = res.headers().get(LOCATION).and_then(|val| {
1745 let loc = (|| -> Option<Url> {
1746 // Some sites may send a utf-8 Location header,
1747 // even though we're supposed to treat those bytes
1748 // as opaque, we'll check specifically for utf8.
1749 self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
1750 })();
1751
1752 // Check that the `url` is also a valid `http::Uri`.
1753 //
1754 // If not, just log it and skip the redirect.
1755 let loc = loc.and_then(|url| {
1756 if try_uri(&url).is_some() {
1757 Some(url)
1758 } else {
1759 None
1760 }
1761 });
1762
1763 if loc.is_none() {
1764 debug!("Location header had invalid URI: {:?}", val);
1765 }
1766 loc
1767 });
1768 if let Some(loc) = loc {
1769 if self.client.referer {
1770 if let Some(referer) = make_referer(&loc, &self.url) {
1771 self.headers.insert(REFERER, referer);
1772 }
1773 }
1774 let url = self.url.clone();
1775 self.as_mut().urls().push(url);
1776 let action = self
1777 .client
1778 .redirect_policy
1779 .check(res.status(), &loc, &self.urls);
1780
1781 match action {
1782 redirect::ActionKind::Follow => {
1783 debug!("redirecting '{}' to '{}'", self.url, loc);
1784
1785 if self.client.https_only && loc.scheme() != "https" {
1786 return Poll::Ready(Err(error::redirect(
1787 error::url_bad_scheme(loc.clone()),
1788 loc,
1789 )));
1790 }
1791
1792 self.url = loc;
1793 let mut headers =
1794 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
1795
1796 remove_sensitive_headers(&mut headers, &self.url, &self.urls);
1797 let uri = expect_uri(&self.url);
1798 let body = match self.body {
1799 Some(Some(ref body)) => Body::reusable(body.clone()),
1800 _ => Body::empty(),
1801 };
1802 let mut req = hyper::Request::builder()
1803 .method(self.method.clone())
1804 .uri(uri.clone())
1805 .body(body.into_stream())
1806 .expect("valid request parts");
1807
1808 // Add cookies from the cookie store.
1809 #[cfg(feature = "cookies")]
1810 {
1811 if let Some(ref cookie_store) = self.client.cookie_store {
1812 add_cookie_header(&mut headers, &**cookie_store, &self.url);
1813 }
1814 }
1815
1816 *req.headers_mut() = headers.clone();
1817 std::mem::swap(self.as_mut().headers(), &mut headers);
1818 *self.as_mut().in_flight().get_mut() = self.client.hyper.request(req);
1819 continue;
1820 }
1821 redirect::ActionKind::Stop => {
1822 debug!("redirect policy disallowed redirection to '{}'", loc);
1823 }
1824 redirect::ActionKind::Error(err) => {
1825 return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
1826 }
1827 }
1828 }
1829 }
1830
1831 debug!("response '{}' for {}", res.status(), self.url);
1832 let res = Response::new(
1833 res,
1834 self.url.clone(),
1835 self.client.accepts,
1836 self.timeout.take(),
1837 );
1838 return Poll::Ready(Ok(res));
1839 }
1840 }
1841 }
1842
1843 impl fmt::Debug for Pending {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1844 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1845 match self.inner {
1846 PendingInner::Request(ref req) => f
1847 .debug_struct("Pending")
1848 .field("method", &req.method)
1849 .field("url", &req.url)
1850 .finish(),
1851 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
1852 }
1853 }
1854 }
1855
make_referer(next: &Url, previous: &Url) -> Option<HeaderValue>1856 fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
1857 if next.scheme() == "http" && previous.scheme() == "https" {
1858 return None;
1859 }
1860
1861 let mut referer = previous.clone();
1862 let _ = referer.set_username("");
1863 let _ = referer.set_password(None);
1864 referer.set_fragment(None);
1865 referer.as_str().parse().ok()
1866 }
1867
1868 #[cfg(feature = "cookies")]
add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url)1869 fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
1870 if let Some(header) = cookie_store.cookies(url) {
1871 headers.insert(crate::header::COOKIE, header);
1872 }
1873 }
1874
1875 #[cfg(test)]
1876 mod tests {
1877 #[tokio::test]
execute_request_rejects_invald_urls()1878 async fn execute_request_rejects_invald_urls() {
1879 let url_str = "hxxps://www.rust-lang.org/";
1880 let url = url::Url::parse(url_str).unwrap();
1881 let result = crate::get(url.clone()).await;
1882
1883 assert!(result.is_err());
1884 let err = result.err().unwrap();
1885 assert!(err.is_builder());
1886 assert_eq!(url_str, err.url().unwrap().as_str());
1887 }
1888 }
1889