1#[cfg(any(feature = "native-tls", feature = "__rustls",))]
2use std::any::Any;
3use std::net::IpAddr;
4use std::sync::Arc;
5use std::time::Duration;
6use std::{collections::HashMap, convert::TryInto, net::SocketAddr};
7use std::{fmt, str};
8
9use bytes::Bytes;
10use 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};
14use http::uri::Scheme;
15use http::Uri;
16use hyper::client::{HttpConnector, ResponseFuture as HyperResponseFuture};
17#[cfg(feature = "native-tls-crate")]
18use native_tls_crate::TlsConnector;
19use pin_project_lite::pin_project;
20use std::future::Future;
21use std::pin::Pin;
22use std::task::{Context, Poll};
23use tokio::time::Sleep;
24
25use super::decoder::Accepts;
26use super::request::{Request, RequestBuilder};
27use super::response::Response;
28use super::Body;
29#[cfg(feature = "http3")]
30use crate::async_impl::h3_client::connect::H3Connector;
31#[cfg(feature = "http3")]
32use crate::async_impl::h3_client::{H3Client, H3ResponseFuture};
33use crate::connect::Connector;
34#[cfg(feature = "cookies")]
35use crate::cookie;
36#[cfg(feature = "hickory-dns")]
37use crate::dns::hickory::HickoryDnsResolver;
38use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
39use crate::error;
40use crate::into_url::try_uri;
41use crate::redirect::{self, remove_sensitive_headers};
42#[cfg(feature = "__tls")]
43use crate::tls::{self, TlsBackend};
44#[cfg(feature = "__tls")]
45use crate::Certificate;
46#[cfg(any(feature = "native-tls", feature = "__rustls"))]
47use crate::Identity;
48use crate::{IntoUrl, Method, Proxy, StatusCode, Url};
49use log::{debug, trace};
50#[cfg(feature = "http3")]
51use quinn::TransportConfig;
52#[cfg(feature = "http3")]
53use quinn::VarInt;
54
55/// An asynchronous `Client` to make Requests with.
56///
57/// The Client has various configuration values to tweak, but the defaults
58/// are set to what is usually the most commonly desired value. To configure a
59/// `Client`, use `Client::builder()`.
60///
61/// The `Client` holds a connection pool internally, so it is advised that
62/// you create one and **reuse** it.
63///
64/// You do **not** have to wrap the `Client` in an [`Rc`] or [`Arc`] to **reuse** it,
65/// because it already uses an [`Arc`] internally.
66///
67/// [`Rc`]: std::rc::Rc
68#[derive(Clone)]
69pub struct Client {
70 inner: Arc<ClientRef>,
71}
72
73/// A `ClientBuilder` can be used to create a `Client` with custom configuration.
74#[must_use]
75pub struct ClientBuilder {
76 config: Config,
77}
78
79enum HttpVersionPref {
80 Http1,
81 Http2,
82 #[cfg(feature = "http3")]
83 Http3,
84 All,
85}
86
87struct Config {
88 // NOTE: When adding a new field, update `fmt::Debug for ClientBuilder`
89 accepts: Accepts,
90 headers: HeaderMap,
91 #[cfg(feature = "native-tls")]
92 hostname_verification: bool,
93 #[cfg(feature = "__tls")]
94 certs_verification: bool,
95 #[cfg(feature = "__tls")]
96 tls_sni: bool,
97 connect_timeout: Option<Duration>,
98 connection_verbose: bool,
99 pool_idle_timeout: Option<Duration>,
100 pool_max_idle_per_host: usize,
101 tcp_keepalive: Option<Duration>,
102 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
103 identity: Option<Identity>,
104 proxies: Vec<Proxy>,
105 auto_sys_proxy: bool,
106 redirect_policy: redirect::Policy,
107 referer: bool,
108 timeout: Option<Duration>,
109 #[cfg(feature = "__tls")]
110 root_certs: Vec<Certificate>,
111 #[cfg(feature = "__tls")]
112 tls_built_in_root_certs: bool,
113 #[cfg(feature = "__tls")]
114 min_tls_version: Option<tls::Version>,
115 #[cfg(feature = "__tls")]
116 max_tls_version: Option<tls::Version>,
117 #[cfg(feature = "__tls")]
118 tls_info: bool,
119 #[cfg(feature = "__tls")]
120 tls: TlsBackend,
121 http_version_pref: HttpVersionPref,
122 http09_responses: bool,
123 http1_title_case_headers: bool,
124 http1_allow_obsolete_multiline_headers_in_responses: bool,
125 http1_ignore_invalid_headers_in_responses: bool,
126 http1_allow_spaces_after_header_name_in_responses: bool,
127 http2_initial_stream_window_size: Option<u32>,
128 http2_initial_connection_window_size: Option<u32>,
129 http2_adaptive_window: bool,
130 http2_max_frame_size: Option<u32>,
131 http2_keep_alive_interval: Option<Duration>,
132 http2_keep_alive_timeout: Option<Duration>,
133 http2_keep_alive_while_idle: bool,
134 local_address: Option<IpAddr>,
135 nodelay: bool,
136 #[cfg(feature = "cookies")]
137 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
138 hickory_dns: bool,
139 error: Option<crate::Error>,
140 https_only: bool,
141 #[cfg(feature = "http3")]
142 tls_enable_early_data: bool,
143 #[cfg(feature = "http3")]
144 quic_max_idle_timeout: Option<Duration>,
145 #[cfg(feature = "http3")]
146 quic_stream_receive_window: Option<VarInt>,
147 #[cfg(feature = "http3")]
148 quic_receive_window: Option<VarInt>,
149 #[cfg(feature = "http3")]
150 quic_send_window: Option<u64>,
151 dns_overrides: HashMap<String, Vec<SocketAddr>>,
152 dns_resolver: Option<Arc<dyn Resolve>>,
153}
154
155impl Default for ClientBuilder {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161impl ClientBuilder {
162 /// Constructs a new `ClientBuilder`.
163 ///
164 /// This is the same as `Client::builder()`.
165 pub fn new() -> ClientBuilder {
166 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
167 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
168
169 ClientBuilder {
170 config: Config {
171 error: None,
172 accepts: Accepts::default(),
173 headers,
174 #[cfg(feature = "native-tls")]
175 hostname_verification: true,
176 #[cfg(feature = "__tls")]
177 certs_verification: true,
178 #[cfg(feature = "__tls")]
179 tls_sni: true,
180 connect_timeout: None,
181 connection_verbose: false,
182 pool_idle_timeout: Some(Duration::from_secs(90)),
183 pool_max_idle_per_host: std::usize::MAX,
184 // TODO: Re-enable default duration once hyper's HttpConnector is fixed
185 // to no longer error when an option fails.
186 tcp_keepalive: None, //Some(Duration::from_secs(60)),
187 proxies: Vec::new(),
188 auto_sys_proxy: true,
189 redirect_policy: redirect::Policy::default(),
190 referer: true,
191 timeout: None,
192 #[cfg(feature = "__tls")]
193 root_certs: Vec::new(),
194 #[cfg(feature = "__tls")]
195 tls_built_in_root_certs: true,
196 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
197 identity: None,
198 #[cfg(feature = "__tls")]
199 min_tls_version: None,
200 #[cfg(feature = "__tls")]
201 max_tls_version: None,
202 #[cfg(feature = "__tls")]
203 tls_info: false,
204 #[cfg(feature = "__tls")]
205 tls: TlsBackend::default(),
206 http_version_pref: HttpVersionPref::All,
207 http09_responses: false,
208 http1_title_case_headers: false,
209 http1_allow_obsolete_multiline_headers_in_responses: false,
210 http1_ignore_invalid_headers_in_responses: false,
211 http1_allow_spaces_after_header_name_in_responses: false,
212 http2_initial_stream_window_size: None,
213 http2_initial_connection_window_size: None,
214 http2_adaptive_window: false,
215 http2_max_frame_size: None,
216 http2_keep_alive_interval: None,
217 http2_keep_alive_timeout: None,
218 http2_keep_alive_while_idle: false,
219 local_address: None,
220 nodelay: true,
221 hickory_dns: cfg!(feature = "hickory-dns"),
222 #[cfg(feature = "cookies")]
223 cookie_store: None,
224 https_only: false,
225 dns_overrides: HashMap::new(),
226 #[cfg(feature = "http3")]
227 tls_enable_early_data: false,
228 #[cfg(feature = "http3")]
229 quic_max_idle_timeout: None,
230 #[cfg(feature = "http3")]
231 quic_stream_receive_window: None,
232 #[cfg(feature = "http3")]
233 quic_receive_window: None,
234 #[cfg(feature = "http3")]
235 quic_send_window: None,
236 dns_resolver: None,
237 },
238 }
239 }
240
241 /// Returns a `Client` that uses this `ClientBuilder` configuration.
242 ///
243 /// # Errors
244 ///
245 /// This method fails if a TLS backend cannot be initialized, or the resolver
246 /// cannot load the system configuration.
247 pub fn build(self) -> crate::Result<Client> {
248 let config = self.config;
249
250 if let Some(err) = config.error {
251 return Err(err);
252 }
253
254 let mut proxies = config.proxies;
255 if config.auto_sys_proxy {
256 proxies.push(Proxy::system());
257 }
258 let proxies = Arc::new(proxies);
259
260 #[allow(unused)]
261 #[cfg(feature = "http3")]
262 let mut h3_connector = None;
263
264 let mut connector = {
265 #[cfg(feature = "__tls")]
266 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
267 headers.get(USER_AGENT).cloned()
268 }
269
270 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
271 false => Arc::new(GaiResolver::new()),
272 #[cfg(feature = "hickory-dns")]
273 true => Arc::new(HickoryDnsResolver::default()),
274 #[cfg(not(feature = "hickory-dns"))]
275 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
276 };
277 if let Some(dns_resolver) = config.dns_resolver {
278 resolver = dns_resolver;
279 }
280 if !config.dns_overrides.is_empty() {
281 resolver = Arc::new(DnsResolverWithOverrides::new(
282 resolver,
283 config.dns_overrides,
284 ));
285 }
286 let mut http = HttpConnector::new_with_resolver(DynResolver::new(resolver.clone()));
287 http.set_connect_timeout(config.connect_timeout);
288
289 #[cfg(all(feature = "http3", feature = "__rustls"))]
290 let build_h3_connector =
291 |resolver,
292 tls,
293 quic_max_idle_timeout: Option<Duration>,
294 quic_stream_receive_window,
295 quic_receive_window,
296 quic_send_window,
297 local_address,
298 http_version_pref: &HttpVersionPref| {
299 let mut transport_config = TransportConfig::default();
300
301 if let Some(max_idle_timeout) = quic_max_idle_timeout {
302 transport_config.max_idle_timeout(Some(
303 max_idle_timeout.try_into().map_err(error::builder)?,
304 ));
305 }
306
307 if let Some(stream_receive_window) = quic_stream_receive_window {
308 transport_config.stream_receive_window(stream_receive_window);
309 }
310
311 if let Some(receive_window) = quic_receive_window {
312 transport_config.receive_window(receive_window);
313 }
314
315 if let Some(send_window) = quic_send_window {
316 transport_config.send_window(send_window);
317 }
318
319 let res = H3Connector::new(
320 DynResolver::new(resolver),
321 tls,
322 local_address,
323 transport_config,
324 );
325
326 match res {
327 Ok(connector) => Ok(Some(connector)),
328 Err(err) => {
329 if let HttpVersionPref::Http3 = http_version_pref {
330 Err(error::builder(err))
331 } else {
332 Ok(None)
333 }
334 }
335 }
336 };
337
338 #[cfg(feature = "__tls")]
339 match config.tls {
340 #[cfg(feature = "default-tls")]
341 TlsBackend::Default => {
342 let mut tls = TlsConnector::builder();
343
344 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
345 {
346 match config.http_version_pref {
347 HttpVersionPref::Http1 => {
348 tls.request_alpns(&["http/1.1"]);
349 }
350 HttpVersionPref::Http2 => {
351 tls.request_alpns(&["h2"]);
352 }
353 HttpVersionPref::All => {
354 tls.request_alpns(&["h2", "http/1.1"]);
355 }
356 }
357 }
358
359 #[cfg(feature = "native-tls")]
360 {
361 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
362 }
363
364 tls.danger_accept_invalid_certs(!config.certs_verification);
365
366 tls.use_sni(config.tls_sni);
367
368 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
369
370 for cert in config.root_certs {
371 cert.add_to_native_tls(&mut tls);
372 }
373
374 #[cfg(feature = "native-tls")]
375 {
376 if let Some(id) = config.identity {
377 id.add_to_native_tls(&mut tls)?;
378 }
379 }
380 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
381 {
382 // Default backend + rustls Identity doesn't work.
383 if let Some(_id) = config.identity {
384 return Err(crate::error::builder("incompatible TLS identity type"));
385 }
386 }
387
388 if let Some(min_tls_version) = config.min_tls_version {
389 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
390 // TLS v1.3. This would be entirely reasonable,
391 // native-tls just doesn't support it.
392 // https://github.com/sfackler/rust-native-tls/issues/140
393 crate::error::builder("invalid minimum TLS version for backend")
394 })?;
395 tls.min_protocol_version(Some(protocol));
396 }
397
398 if let Some(max_tls_version) = config.max_tls_version {
399 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
400 // TLS v1.3.
401 // We could arguably do max_protocol_version(None), given
402 // that 1.4 does not exist yet, but that'd get messy in the
403 // future.
404 crate::error::builder("invalid maximum TLS version for backend")
405 })?;
406 tls.max_protocol_version(Some(protocol));
407 }
408
409 Connector::new_default_tls(
410 http,
411 tls,
412 proxies.clone(),
413 user_agent(&config.headers),
414 config.local_address,
415 config.nodelay,
416 config.tls_info,
417 )?
418 }
419 #[cfg(feature = "native-tls")]
420 TlsBackend::BuiltNativeTls(conn) => Connector::from_built_default_tls(
421 http,
422 conn,
423 proxies.clone(),
424 user_agent(&config.headers),
425 config.local_address,
426 config.nodelay,
427 config.tls_info,
428 ),
429 #[cfg(feature = "__rustls")]
430 TlsBackend::BuiltRustls(conn) => {
431 #[cfg(feature = "http3")]
432 {
433 h3_connector = build_h3_connector(
434 resolver,
435 conn.clone(),
436 config.quic_max_idle_timeout,
437 config.quic_stream_receive_window,
438 config.quic_receive_window,
439 config.quic_send_window,
440 config.local_address,
441 &config.http_version_pref,
442 )?;
443 }
444
445 Connector::new_rustls_tls(
446 http,
447 conn,
448 proxies.clone(),
449 user_agent(&config.headers),
450 config.local_address,
451 config.nodelay,
452 config.tls_info,
453 )
454 }
455 #[cfg(feature = "__rustls")]
456 TlsBackend::Rustls => {
457 use crate::tls::NoVerifier;
458
459 // Set root certificates.
460 let mut root_cert_store = rustls::RootCertStore::empty();
461 for cert in config.root_certs {
462 cert.add_to_rustls(&mut root_cert_store)?;
463 }
464
465 #[cfg(feature = "rustls-tls-webpki-roots")]
466 if config.tls_built_in_root_certs {
467 use rustls::OwnedTrustAnchor;
468
469 let trust_anchors =
470 webpki_roots::TLS_SERVER_ROOTS.iter().map(|trust_anchor| {
471 OwnedTrustAnchor::from_subject_spki_name_constraints(
472 trust_anchor.subject,
473 trust_anchor.spki,
474 trust_anchor.name_constraints,
475 )
476 });
477
478 root_cert_store.add_trust_anchors(trust_anchors);
479 }
480
481 #[cfg(feature = "rustls-tls-native-roots")]
482 if config.tls_built_in_root_certs {
483 let mut valid_count = 0;
484 let mut invalid_count = 0;
485 for cert in rustls_native_certs::load_native_certs()
486 .map_err(crate::error::builder)?
487 {
488 let cert = rustls::Certificate(cert.0);
489 // Continue on parsing errors, as native stores often include ancient or syntactically
490 // invalid certificates, like root certificates without any X509 extensions.
491 // Inspiration: https://github.com/rustls/rustls/blob/633bf4ba9d9521a95f68766d04c22e2b01e68318/rustls/src/anchors.rs#L105-L112
492 match root_cert_store.add(&cert) {
493 Ok(_) => valid_count += 1,
494 Err(err) => {
495 invalid_count += 1;
496 log::warn!(
497 "rustls failed to parse DER certificate {err:?} {cert:?}"
498 );
499 }
500 }
501 }
502 if valid_count == 0 && invalid_count > 0 {
503 return Err(crate::error::builder(
504 "zero valid certificates found in native root store",
505 ));
506 }
507 }
508
509 // Set TLS versions.
510 let mut versions = rustls::ALL_VERSIONS.to_vec();
511
512 if let Some(min_tls_version) = config.min_tls_version {
513 versions.retain(|&supported_version| {
514 match tls::Version::from_rustls(supported_version.version) {
515 Some(version) => version >= min_tls_version,
516 // Assume it's so new we don't know about it, allow it
517 // (as of writing this is unreachable)
518 None => true,
519 }
520 });
521 }
522
523 if let Some(max_tls_version) = config.max_tls_version {
524 versions.retain(|&supported_version| {
525 match tls::Version::from_rustls(supported_version.version) {
526 Some(version) => version <= max_tls_version,
527 None => false,
528 }
529 });
530 }
531
532 // Build TLS config
533 let config_builder = rustls::ClientConfig::builder()
534 .with_safe_default_cipher_suites()
535 .with_safe_default_kx_groups()
536 .with_protocol_versions(&versions)
537 .map_err(crate::error::builder)?
538 .with_root_certificates(root_cert_store);
539
540 // Finalize TLS config
541 let mut tls = if let Some(id) = config.identity {
542 id.add_to_rustls(config_builder)?
543 } else {
544 config_builder.with_no_client_auth()
545 };
546
547 // Certificate verifier
548 if !config.certs_verification {
549 tls.dangerous()
550 .set_certificate_verifier(Arc::new(NoVerifier));
551 }
552
553 tls.enable_sni = config.tls_sni;
554
555 // ALPN protocol
556 match config.http_version_pref {
557 HttpVersionPref::Http1 => {
558 tls.alpn_protocols = vec!["http/1.1".into()];
559 }
560 HttpVersionPref::Http2 => {
561 tls.alpn_protocols = vec!["h2".into()];
562 }
563 #[cfg(feature = "http3")]
564 HttpVersionPref::Http3 => {
565 tls.alpn_protocols = vec!["h3".into()];
566 }
567 HttpVersionPref::All => {
568 tls.alpn_protocols = vec!["h2".into(), "http/1.1".into()];
569 }
570 }
571
572 #[cfg(feature = "http3")]
573 {
574 tls.enable_early_data = config.tls_enable_early_data;
575
576 h3_connector = build_h3_connector(
577 resolver,
578 tls.clone(),
579 config.quic_max_idle_timeout,
580 config.quic_stream_receive_window,
581 config.quic_receive_window,
582 config.quic_send_window,
583 config.local_address,
584 &config.http_version_pref,
585 )?;
586 }
587
588 Connector::new_rustls_tls(
589 http,
590 tls,
591 proxies.clone(),
592 user_agent(&config.headers),
593 config.local_address,
594 config.nodelay,
595 config.tls_info,
596 )
597 }
598 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
599 TlsBackend::UnknownPreconfigured => {
600 return Err(crate::error::builder(
601 "Unknown TLS backend passed to `use_preconfigured_tls`",
602 ));
603 }
604 }
605
606 #[cfg(not(feature = "__tls"))]
607 Connector::new(http, proxies.clone(), config.local_address, config.nodelay)
608 };
609
610 connector.set_timeout(config.connect_timeout);
611 connector.set_verbose(config.connection_verbose);
612
613 let mut builder = hyper::Client::builder();
614 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
615 builder.http2_only(true);
616 }
617
618 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size {
619 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
620 }
621 if let Some(http2_initial_connection_window_size) =
622 config.http2_initial_connection_window_size
623 {
624 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
625 }
626 if config.http2_adaptive_window {
627 builder.http2_adaptive_window(true);
628 }
629 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
630 builder.http2_max_frame_size(http2_max_frame_size);
631 }
632 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
633 builder.http2_keep_alive_interval(http2_keep_alive_interval);
634 }
635 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
636 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
637 }
638 if config.http2_keep_alive_while_idle {
639 builder.http2_keep_alive_while_idle(true);
640 }
641
642 builder.pool_idle_timeout(config.pool_idle_timeout);
643 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
644 connector.set_keepalive(config.tcp_keepalive);
645
646 if config.http09_responses {
647 builder.http09_responses(true);
648 }
649
650 if config.http1_title_case_headers {
651 builder.http1_title_case_headers(true);
652 }
653
654 if config.http1_allow_obsolete_multiline_headers_in_responses {
655 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
656 }
657
658 if config.http1_ignore_invalid_headers_in_responses {
659 builder.http1_ignore_invalid_headers_in_responses(true);
660 }
661
662 if config.http1_allow_spaces_after_header_name_in_responses {
663 builder.http1_allow_spaces_after_header_name_in_responses(true);
664 }
665
666 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
667
668 Ok(Client {
669 inner: Arc::new(ClientRef {
670 accepts: config.accepts,
671 #[cfg(feature = "cookies")]
672 cookie_store: config.cookie_store,
673 // Use match instead of map since config is partially moved
674 // and it cannot be used in closure
675 #[cfg(feature = "http3")]
676 h3_client: match h3_connector {
677 Some(h3_connector) => {
678 Some(H3Client::new(h3_connector, config.pool_idle_timeout))
679 }
680 None => None,
681 },
682 hyper: builder.build(connector),
683 headers: config.headers,
684 redirect_policy: config.redirect_policy,
685 referer: config.referer,
686 request_timeout: config.timeout,
687 proxies,
688 proxies_maybe_http_auth,
689 https_only: config.https_only,
690 }),
691 })
692 }
693
694 // Higher-level options
695
696 /// Sets the `User-Agent` header to be used by this client.
697 ///
698 /// # Example
699 ///
700 /// ```rust
701 /// # async fn doc() -> Result<(), reqwest::Error> {
702 /// // Name your user agent after your app?
703 /// static APP_USER_AGENT: &str = concat!(
704 /// env!("CARGO_PKG_NAME"),
705 /// "/",
706 /// env!("CARGO_PKG_VERSION"),
707 /// );
708 ///
709 /// let client = reqwest::Client::builder()
710 /// .user_agent(APP_USER_AGENT)
711 /// .build()?;
712 /// let res = client.get("https://www.rust-lang.org").send().await?;
713 /// # Ok(())
714 /// # }
715 /// ```
716 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
717 where
718 V: TryInto<HeaderValue>,
719 V::Error: Into<http::Error>,
720 {
721 match value.try_into() {
722 Ok(value) => {
723 self.config.headers.insert(USER_AGENT, value);
724 }
725 Err(e) => {
726 self.config.error = Some(crate::error::builder(e.into()));
727 }
728 };
729 self
730 }
731 /// Sets the default headers for every request.
732 ///
733 /// # Example
734 ///
735 /// ```rust
736 /// use reqwest::header;
737 /// # async fn doc() -> Result<(), reqwest::Error> {
738 /// let mut headers = header::HeaderMap::new();
739 /// headers.insert("X-MY-HEADER", header::HeaderValue::from_static("value"));
740 ///
741 /// // Consider marking security-sensitive headers with `set_sensitive`.
742 /// let mut auth_value = header::HeaderValue::from_static("secret");
743 /// auth_value.set_sensitive(true);
744 /// headers.insert(header::AUTHORIZATION, auth_value);
745 ///
746 /// // get a client builder
747 /// let client = reqwest::Client::builder()
748 /// .default_headers(headers)
749 /// .build()?;
750 /// let res = client.get("https://www.rust-lang.org").send().await?;
751 /// # Ok(())
752 /// # }
753 /// ```
754 ///
755 /// Override the default headers:
756 ///
757 /// ```rust
758 /// use reqwest::header;
759 /// # async fn doc() -> Result<(), reqwest::Error> {
760 /// let mut headers = header::HeaderMap::new();
761 /// headers.insert("X-MY-HEADER", header::HeaderValue::from_static("value"));
762 ///
763 /// // get a client builder
764 /// let client = reqwest::Client::builder()
765 /// .default_headers(headers)
766 /// .build()?;
767 /// let res = client
768 /// .get("https://www.rust-lang.org")
769 /// .header("X-MY-HEADER", "new_value")
770 /// .send()
771 /// .await?;
772 /// # Ok(())
773 /// # }
774 /// ```
775 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
776 for (key, value) in headers.iter() {
777 self.config.headers.insert(key, value.clone());
778 }
779 self
780 }
781
782 /// Enable a persistent cookie store for the client.
783 ///
784 /// Cookies received in responses will be preserved and included in
785 /// additional requests.
786 ///
787 /// By default, no cookie store is used. Enabling the cookie store
788 /// with `cookie_store(true)` will set the store to a default implementation.
789 /// It is **not** necessary to call [cookie_store(true)](crate::ClientBuilder::cookie_store) if [cookie_provider(my_cookie_store)](crate::ClientBuilder::cookie_provider)
790 /// is used; calling [cookie_store(true)](crate::ClientBuilder::cookie_store) _after_ [cookie_provider(my_cookie_store)](crate::ClientBuilder::cookie_provider) will result
791 /// in the provided `my_cookie_store` being **overridden** with a default implementation.
792 ///
793 /// # Optional
794 ///
795 /// This requires the optional `cookies` feature to be enabled.
796 #[cfg(feature = "cookies")]
797 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
798 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
799 if enable {
800 self.cookie_provider(Arc::new(cookie::Jar::default()))
801 } else {
802 self.config.cookie_store = None;
803 self
804 }
805 }
806
807 /// Set the persistent cookie store for the client.
808 ///
809 /// Cookies received in responses will be passed to this store, and
810 /// additional requests will query this store for cookies.
811 ///
812 /// By default, no cookie store is used. It is **not** necessary to also call
813 /// [cookie_store(true)](crate::ClientBuilder::cookie_store) if [cookie_provider(my_cookie_store)](crate::ClientBuilder::cookie_provider) is used; calling
814 /// [cookie_store(true)](crate::ClientBuilder::cookie_store) _after_ [cookie_provider(my_cookie_store)](crate::ClientBuilder::cookie_provider) will result
815 /// in the provided `my_cookie_store` being **overridden** with a default implementation.
816 ///
817 /// # Optional
818 ///
819 /// This requires the optional `cookies` feature to be enabled.
820 #[cfg(feature = "cookies")]
821 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
822 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
823 mut self,
824 cookie_store: Arc<C>,
825 ) -> ClientBuilder {
826 self.config.cookie_store = Some(cookie_store as _);
827 self
828 }
829
830 /// Enable auto gzip decompression by checking the `Content-Encoding` response header.
831 ///
832 /// If auto gzip decompression is turned on:
833 ///
834 /// - When sending a request and if the request's headers do not already contain
835 /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `gzip`.
836 /// The request body is **not** automatically compressed.
837 /// - When receiving a response, if its headers contain a `Content-Encoding` value of
838 /// `gzip`, both `Content-Encoding` and `Content-Length` are removed from the
839 /// headers' set. The response body is automatically decompressed.
840 ///
841 /// If the `gzip` feature is turned on, the default option is enabled.
842 ///
843 /// # Optional
844 ///
845 /// This requires the optional `gzip` feature to be enabled
846 #[cfg(feature = "gzip")]
847 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
848 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
849 self.config.accepts.gzip = enable;
850 self
851 }
852
853 /// Enable auto brotli decompression by checking the `Content-Encoding` response header.
854 ///
855 /// If auto brotli decompression is turned on:
856 ///
857 /// - When sending a request and if the request's headers do not already contain
858 /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `br`.
859 /// The request body is **not** automatically compressed.
860 /// - When receiving a response, if its headers contain a `Content-Encoding` value of
861 /// `br`, both `Content-Encoding` and `Content-Length` are removed from the
862 /// headers' set. The response body is automatically decompressed.
863 ///
864 /// If the `brotli` feature is turned on, the default option is enabled.
865 ///
866 /// # Optional
867 ///
868 /// This requires the optional `brotli` feature to be enabled
869 #[cfg(feature = "brotli")]
870 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
871 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
872 self.config.accepts.brotli = enable;
873 self
874 }
875
876 /// Enable auto deflate decompression by checking the `Content-Encoding` response header.
877 ///
878 /// If auto deflate decompression is turned on:
879 ///
880 /// - When sending a request and if the request's headers do not already contain
881 /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `deflate`.
882 /// The request body is **not** automatically compressed.
883 /// - When receiving a response, if it's headers contain a `Content-Encoding` value that
884 /// equals to `deflate`, both values `Content-Encoding` and `Content-Length` are removed from the
885 /// headers' set. The response body is automatically decompressed.
886 ///
887 /// If the `deflate` feature is turned on, the default option is enabled.
888 ///
889 /// # Optional
890 ///
891 /// This requires the optional `deflate` feature to be enabled
892 #[cfg(feature = "deflate")]
893 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
894 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
895 self.config.accepts.deflate = enable;
896 self
897 }
898
899 /// Disable auto response body gzip decompression.
900 ///
901 /// This method exists even if the optional `gzip` feature is not enabled.
902 /// This can be used to ensure a `Client` doesn't use gzip decompression
903 /// even if another dependency were to enable the optional `gzip` feature.
904 pub fn no_gzip(self) -> ClientBuilder {
905 #[cfg(feature = "gzip")]
906 {
907 self.gzip(false)
908 }
909
910 #[cfg(not(feature = "gzip"))]
911 {
912 self
913 }
914 }
915
916 /// Disable auto response body brotli decompression.
917 ///
918 /// This method exists even if the optional `brotli` feature is not enabled.
919 /// This can be used to ensure a `Client` doesn't use brotli decompression
920 /// even if another dependency were to enable the optional `brotli` feature.
921 pub fn no_brotli(self) -> ClientBuilder {
922 #[cfg(feature = "brotli")]
923 {
924 self.brotli(false)
925 }
926
927 #[cfg(not(feature = "brotli"))]
928 {
929 self
930 }
931 }
932
933 /// Disable auto response body deflate decompression.
934 ///
935 /// This method exists even if the optional `deflate` feature is not enabled.
936 /// This can be used to ensure a `Client` doesn't use deflate decompression
937 /// even if another dependency were to enable the optional `deflate` feature.
938 pub fn no_deflate(self) -> ClientBuilder {
939 #[cfg(feature = "deflate")]
940 {
941 self.deflate(false)
942 }
943
944 #[cfg(not(feature = "deflate"))]
945 {
946 self
947 }
948 }
949
950 // Redirect options
951
952 /// Set a `RedirectPolicy` for this client.
953 ///
954 /// Default will follow redirects up to a maximum of 10.
955 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
956 self.config.redirect_policy = policy;
957 self
958 }
959
960 /// Enable or disable automatic setting of the `Referer` header.
961 ///
962 /// Default is `true`.
963 pub fn referer(mut self, enable: bool) -> ClientBuilder {
964 self.config.referer = enable;
965 self
966 }
967
968 // Proxy options
969
970 /// Add a `Proxy` to the list of proxies the `Client` will use.
971 ///
972 /// # Note
973 ///
974 /// Adding a proxy will disable the automatic usage of the "system" proxy.
975 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
976 self.config.proxies.push(proxy);
977 self.config.auto_sys_proxy = false;
978 self
979 }
980
981 /// Clear all `Proxies`, so `Client` will use no proxy anymore.
982 ///
983 /// # Note
984 /// To add a proxy exclusion list, use [crate::proxy::Proxy::no_proxy()]
985 /// on all desired proxies instead.
986 ///
987 /// This also disables the automatic usage of the "system" proxy.
988 pub fn no_proxy(mut self) -> ClientBuilder {
989 self.config.proxies.clear();
990 self.config.auto_sys_proxy = false;
991 self
992 }
993
994 // Timeout options
995
996 /// Enables a request timeout.
997 ///
998 /// The timeout is applied from when the request starts connecting until the
999 /// response body has finished.
1000 ///
1001 /// Default is no timeout.
1002 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1003 self.config.timeout = Some(timeout);
1004 self
1005 }
1006
1007 /// Set a timeout for only the connect phase of a `Client`.
1008 ///
1009 /// Default is `None`.
1010 ///
1011 /// # Note
1012 ///
1013 /// This **requires** the futures be executed in a tokio runtime with
1014 /// a tokio timer enabled.
1015 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1016 self.config.connect_timeout = Some(timeout);
1017 self
1018 }
1019
1020 /// Set whether connections should emit verbose logs.
1021 ///
1022 /// Enabling this option will emit [log][] messages at the `TRACE` level
1023 /// for read and write operations on connections.
1024 ///
1025 /// [log]: https://crates.io/crates/log
1026 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1027 self.config.connection_verbose = verbose;
1028 self
1029 }
1030
1031 // HTTP options
1032
1033 /// Set an optional timeout for idle sockets being kept-alive.
1034 ///
1035 /// Pass `None` to disable timeout.
1036 ///
1037 /// Default is 90 seconds.
1038 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1039 where
1040 D: Into<Option<Duration>>,
1041 {
1042 self.config.pool_idle_timeout = val.into();
1043 self
1044 }
1045
1046 /// Sets the maximum idle connection per host allowed in the pool.
1047 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1048 self.config.pool_max_idle_per_host = max;
1049 self
1050 }
1051
1052 /// Send headers as title case instead of lowercase.
1053 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1054 self.config.http1_title_case_headers = true;
1055 self
1056 }
1057
1058 /// Set whether HTTP/1 connections will accept obsolete line folding for
1059 /// header values.
1060 ///
1061 /// Newline codepoints (`\r` and `\n`) will be transformed to spaces when
1062 /// parsing.
1063 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1064 mut self,
1065 value: bool,
1066 ) -> ClientBuilder {
1067 self.config
1068 .http1_allow_obsolete_multiline_headers_in_responses = value;
1069 self
1070 }
1071
1072 /// Sets whether invalid header lines should be silently ignored in HTTP/1 responses.
1073 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1074 self.config.http1_ignore_invalid_headers_in_responses = value;
1075 self
1076 }
1077
1078 /// Set whether HTTP/1 connections will accept spaces between header
1079 /// names and the colon that follow them in responses.
1080 ///
1081 /// Newline codepoints (`\r` and `\n`) will be transformed to spaces when
1082 /// parsing.
1083 pub fn http1_allow_spaces_after_header_name_in_responses(
1084 mut self,
1085 value: bool,
1086 ) -> ClientBuilder {
1087 self.config
1088 .http1_allow_spaces_after_header_name_in_responses = value;
1089 self
1090 }
1091
1092 /// Only use HTTP/1.
1093 pub fn http1_only(mut self) -> ClientBuilder {
1094 self.config.http_version_pref = HttpVersionPref::Http1;
1095 self
1096 }
1097
1098 /// Allow HTTP/0.9 responses
1099 pub fn http09_responses(mut self) -> ClientBuilder {
1100 self.config.http09_responses = true;
1101 self
1102 }
1103
1104 /// Only use HTTP/2.
1105 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1106 self.config.http_version_pref = HttpVersionPref::Http2;
1107 self
1108 }
1109
1110 /// Only use HTTP/3.
1111 #[cfg(feature = "http3")]
1112 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1113 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1114 self.config.http_version_pref = HttpVersionPref::Http3;
1115 self
1116 }
1117
1118 /// Sets the `SETTINGS_INITIAL_WINDOW_SIZE` option for HTTP2 stream-level flow control.
1119 ///
1120 /// Default is currently 65,535 but may change internally to optimize for common uses.
1121 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1122 self.config.http2_initial_stream_window_size = sz.into();
1123 self
1124 }
1125
1126 /// Sets the max connection-level flow control for HTTP2
1127 ///
1128 /// Default is currently 65,535 but may change internally to optimize for common uses.
1129 pub fn http2_initial_connection_window_size(
1130 mut self,
1131 sz: impl Into<Option<u32>>,
1132 ) -> ClientBuilder {
1133 self.config.http2_initial_connection_window_size = sz.into();
1134 self
1135 }
1136
1137 /// Sets whether to use an adaptive flow control.
1138 ///
1139 /// Enabling this will override the limits set in `http2_initial_stream_window_size` and
1140 /// `http2_initial_connection_window_size`.
1141 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1142 self.config.http2_adaptive_window = enabled;
1143 self
1144 }
1145
1146 /// Sets the maximum frame size to use for HTTP2.
1147 ///
1148 /// Default is currently 16,384 but may change internally to optimize for common uses.
1149 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1150 self.config.http2_max_frame_size = sz.into();
1151 self
1152 }
1153
1154 /// Sets an interval for HTTP2 Ping frames should be sent to keep a connection alive.
1155 ///
1156 /// Pass `None` to disable HTTP2 keep-alive.
1157 /// Default is currently disabled.
1158 pub fn http2_keep_alive_interval(
1159 mut self,
1160 interval: impl Into<Option<Duration>>,
1161 ) -> ClientBuilder {
1162 self.config.http2_keep_alive_interval = interval.into();
1163 self
1164 }
1165
1166 /// Sets a timeout for receiving an acknowledgement of the keep-alive ping.
1167 ///
1168 /// If the ping is not acknowledged within the timeout, the connection will be closed.
1169 /// Does nothing if `http2_keep_alive_interval` is disabled.
1170 /// Default is currently disabled.
1171 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1172 self.config.http2_keep_alive_timeout = Some(timeout);
1173 self
1174 }
1175
1176 /// Sets whether HTTP2 keep-alive should apply while the connection is idle.
1177 ///
1178 /// If disabled, keep-alive pings are only sent while there are open request/responses streams.
1179 /// If enabled, pings are also sent when no streams are active.
1180 /// Does nothing if `http2_keep_alive_interval` is disabled.
1181 /// Default is `false`.
1182 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1183 self.config.http2_keep_alive_while_idle = enabled;
1184 self
1185 }
1186
1187 // TCP options
1188
1189 /// Set whether sockets have `TCP_NODELAY` enabled.
1190 ///
1191 /// Default is `true`.
1192 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1193 self.config.nodelay = enabled;
1194 self
1195 }
1196
1197 /// Bind to a local IP Address.
1198 ///
1199 /// # Example
1200 ///
1201 /// ```
1202 /// use std::net::IpAddr;
1203 /// let local_addr = IpAddr::from([12, 4, 1, 8]);
1204 /// let client = reqwest::Client::builder()
1205 /// .local_address(local_addr)
1206 /// .build().unwrap();
1207 /// ```
1208 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1209 where
1210 T: Into<Option<IpAddr>>,
1211 {
1212 self.config.local_address = addr.into();
1213 self
1214 }
1215
1216 /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration.
1217 ///
1218 /// If `None`, the option will not be set.
1219 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1220 where
1221 D: Into<Option<Duration>>,
1222 {
1223 self.config.tcp_keepalive = val.into();
1224 self
1225 }
1226
1227 // TLS options
1228
1229 /// Add a custom root certificate.
1230 ///
1231 /// This can be used to connect to a server that has a self-signed
1232 /// certificate for example.
1233 ///
1234 /// # Optional
1235 ///
1236 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
1237 /// feature to be enabled.
1238 #[cfg(feature = "__tls")]
1239 #[cfg_attr(
1240 docsrs,
1241 doc(cfg(any(
1242 feature = "default-tls",
1243 feature = "native-tls",
1244 feature = "rustls-tls"
1245 )))
1246 )]
1247 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1248 self.config.root_certs.push(cert);
1249 self
1250 }
1251
1252 /// Controls the use of built-in/preloaded certificates during certificate validation.
1253 ///
1254 /// Defaults to `true` -- built-in system certs will be used.
1255 ///
1256 /// # Optional
1257 ///
1258 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
1259 /// feature to be enabled.
1260 #[cfg(feature = "__tls")]
1261 #[cfg_attr(
1262 docsrs,
1263 doc(cfg(any(
1264 feature = "default-tls",
1265 feature = "native-tls",
1266 feature = "rustls-tls"
1267 )))
1268 )]
1269 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1270 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1271 self
1272 }
1273
1274 /// Sets the identity to be used for client certificate authentication.
1275 ///
1276 /// # Optional
1277 ///
1278 /// This requires the optional `native-tls` or `rustls-tls(-...)` feature to be
1279 /// enabled.
1280 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1281 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1282 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1283 self.config.identity = Some(identity);
1284 self
1285 }
1286
1287 /// Controls the use of hostname verification.
1288 ///
1289 /// Defaults to `false`.
1290 ///
1291 /// # Warning
1292 ///
1293 /// You should think very carefully before you use this method. If
1294 /// hostname verification is not used, any valid certificate for any
1295 /// site will be trusted for use from any other. This introduces a
1296 /// significant vulnerability to man-in-the-middle attacks.
1297 ///
1298 /// # Optional
1299 ///
1300 /// This requires the optional `native-tls` feature to be enabled.
1301 #[cfg(feature = "native-tls")]
1302 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1303 pub fn danger_accept_invalid_hostnames(
1304 mut self,
1305 accept_invalid_hostname: bool,
1306 ) -> ClientBuilder {
1307 self.config.hostname_verification = !accept_invalid_hostname;
1308 self
1309 }
1310
1311 /// Controls the use of certificate validation.
1312 ///
1313 /// Defaults to `false`.
1314 ///
1315 /// # Warning
1316 ///
1317 /// You should think very carefully before using this method. If
1318 /// invalid certificates are trusted, *any* certificate for *any* site
1319 /// will be trusted for use. This includes expired certificates. This
1320 /// introduces significant vulnerabilities, and should only be used
1321 /// as a last resort.
1322 ///
1323 /// # Optional
1324 ///
1325 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
1326 /// feature to be enabled.
1327 #[cfg(feature = "__tls")]
1328 #[cfg_attr(
1329 docsrs,
1330 doc(cfg(any(
1331 feature = "default-tls",
1332 feature = "native-tls",
1333 feature = "rustls-tls"
1334 )))
1335 )]
1336 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1337 self.config.certs_verification = !accept_invalid_certs;
1338 self
1339 }
1340
1341 /// Controls the use of TLS server name indication.
1342 ///
1343 /// Defaults to `true`.
1344 ///
1345 /// # Optional
1346 ///
1347 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
1348 /// feature to be enabled.
1349 #[cfg(feature = "__tls")]
1350 #[cfg_attr(
1351 docsrs,
1352 doc(cfg(any(
1353 feature = "default-tls",
1354 feature = "native-tls",
1355 feature = "rustls-tls"
1356 )))
1357 )]
1358 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1359 self.config.tls_sni = tls_sni;
1360 self
1361 }
1362
1363 /// Set the minimum required TLS version for connections.
1364 ///
1365 /// By default the TLS backend's own default is used.
1366 ///
1367 /// # Errors
1368 ///
1369 /// A value of `tls::Version::TLS_1_3` will cause an error with the
1370 /// `native-tls`/`default-tls` backend. This does not mean the version
1371 /// isn't supported, just that it can't be set as a minimum due to
1372 /// technical limitations.
1373 ///
1374 /// # Optional
1375 ///
1376 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
1377 /// feature to be enabled.
1378 #[cfg(feature = "__tls")]
1379 #[cfg_attr(
1380 docsrs,
1381 doc(cfg(any(
1382 feature = "default-tls",
1383 feature = "native-tls",
1384 feature = "rustls-tls"
1385 )))
1386 )]
1387 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1388 self.config.min_tls_version = Some(version);
1389 self
1390 }
1391
1392 /// Set the maximum allowed TLS version for connections.
1393 ///
1394 /// By default there's no maximum.
1395 ///
1396 /// # Errors
1397 ///
1398 /// A value of `tls::Version::TLS_1_3` will cause an error with the
1399 /// `native-tls`/`default-tls` backend. This does not mean the version
1400 /// isn't supported, just that it can't be set as a maximum due to
1401 /// technical limitations.
1402 ///
1403 /// # Optional
1404 ///
1405 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
1406 /// feature to be enabled.
1407 #[cfg(feature = "__tls")]
1408 #[cfg_attr(
1409 docsrs,
1410 doc(cfg(any(
1411 feature = "default-tls",
1412 feature = "native-tls",
1413 feature = "rustls-tls"
1414 )))
1415 )]
1416 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1417 self.config.max_tls_version = Some(version);
1418 self
1419 }
1420
1421 /// Force using the native TLS backend.
1422 ///
1423 /// Since multiple TLS backends can be optionally enabled, this option will
1424 /// force the `native-tls` backend to be used for this `Client`.
1425 ///
1426 /// # Optional
1427 ///
1428 /// This requires the optional `native-tls` feature to be enabled.
1429 #[cfg(feature = "native-tls")]
1430 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1431 pub fn use_native_tls(mut self) -> ClientBuilder {
1432 self.config.tls = TlsBackend::Default;
1433 self
1434 }
1435
1436 /// Force using the Rustls TLS backend.
1437 ///
1438 /// Since multiple TLS backends can be optionally enabled, this option will
1439 /// force the `rustls` backend to be used for this `Client`.
1440 ///
1441 /// # Optional
1442 ///
1443 /// This requires the optional `rustls-tls(-...)` feature to be enabled.
1444 #[cfg(feature = "__rustls")]
1445 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1446 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1447 self.config.tls = TlsBackend::Rustls;
1448 self
1449 }
1450
1451 /// Use a preconfigured TLS backend.
1452 ///
1453 /// If the passed `Any` argument is not a TLS backend that reqwest
1454 /// understands, the `ClientBuilder` will error when calling `build`.
1455 ///
1456 /// # Advanced
1457 ///
1458 /// This is an advanced option, and can be somewhat brittle. Usage requires
1459 /// keeping the preconfigured TLS argument version in sync with reqwest,
1460 /// since version mismatches will result in an "unknown" TLS backend.
1461 ///
1462 /// If possible, it's preferable to use the methods on `ClientBuilder`
1463 /// to configure reqwest's TLS.
1464 ///
1465 /// # Optional
1466 ///
1467 /// This requires one of the optional features `native-tls` or
1468 /// `rustls-tls(-...)` to be enabled.
1469 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
1470 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1471 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
1472 let mut tls = Some(tls);
1473 #[cfg(feature = "native-tls")]
1474 {
1475 if let Some(conn) =
1476 (&mut tls as &mut dyn Any).downcast_mut::<Option<native_tls_crate::TlsConnector>>()
1477 {
1478 let tls = conn.take().expect("is definitely Some");
1479 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
1480 self.config.tls = tls;
1481 return self;
1482 }
1483 }
1484 #[cfg(feature = "__rustls")]
1485 {
1486 if let Some(conn) =
1487 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
1488 {
1489 let tls = conn.take().expect("is definitely Some");
1490 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
1491 self.config.tls = tls;
1492 return self;
1493 }
1494 }
1495
1496 // Otherwise, we don't recognize the TLS backend!
1497 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
1498 self
1499 }
1500
1501 /// Add TLS information as `TlsInfo` extension to responses.
1502 ///
1503 /// # Optional
1504 ///
1505 /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
1506 /// feature to be enabled.
1507 #[cfg(feature = "__tls")]
1508 #[cfg_attr(
1509 docsrs,
1510 doc(cfg(any(
1511 feature = "default-tls",
1512 feature = "native-tls",
1513 feature = "rustls-tls"
1514 )))
1515 )]
1516 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
1517 self.config.tls_info = tls_info;
1518 self
1519 }
1520
1521 /// Restrict the Client to be used with HTTPS only requests.
1522 ///
1523 /// Defaults to false.
1524 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
1525 self.config.https_only = enabled;
1526 self
1527 }
1528
1529 /// Enables the [hickory-dns](hickory_resolver) async resolver instead of a default threadpool
1530 /// using `getaddrinfo`.
1531 ///
1532 /// If the `hickory-dns` feature is turned on, the default option is enabled.
1533 ///
1534 /// # Optional
1535 ///
1536 /// This requires the optional `hickory-dns` feature to be enabled
1537 #[cfg(feature = "hickory-dns")]
1538 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1539 #[deprecated(note = "use `hickory_dns` instead")]
1540 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
1541 self.config.hickory_dns = enable;
1542 self
1543 }
1544
1545 /// Enables the [hickory-dns](hickory_resolver) async resolver instead of a default threadpool
1546 /// using `getaddrinfo`.
1547 ///
1548 /// If the `hickory-dns` feature is turned on, the default option is enabled.
1549 ///
1550 /// # Optional
1551 ///
1552 /// This requires the optional `hickory-dns` feature to be enabled
1553 #[cfg(feature = "hickory-dns")]
1554 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1555 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
1556 self.config.hickory_dns = enable;
1557 self
1558 }
1559
1560 /// Disables the hickory-dns async resolver.
1561 ///
1562 /// This method exists even if the optional `hickory-dns` feature is not enabled.
1563 /// This can be used to ensure a `Client` doesn't use the hickory-dns async resolver
1564 /// even if another dependency were to enable the optional `hickory-dns` feature.
1565 #[deprecated(note = "use `no_hickory_dns` instead")]
1566 pub fn no_trust_dns(self) -> ClientBuilder {
1567 #[cfg(feature = "hickory-dns")]
1568 {
1569 self.hickory_dns(false)
1570 }
1571
1572 #[cfg(not(feature = "hickory-dns"))]
1573 {
1574 self
1575 }
1576 }
1577
1578 /// Disables the hickory-dns async resolver.
1579 ///
1580 /// This method exists even if the optional `hickory-dns` feature is not enabled.
1581 /// This can be used to ensure a `Client` doesn't use the hickory-dns async resolver
1582 /// even if another dependency were to enable the optional `hickory-dns` feature.
1583 pub fn no_hickory_dns(self) -> ClientBuilder {
1584 #[cfg(feature = "hickory-dns")]
1585 {
1586 self.hickory_dns(false)
1587 }
1588
1589 #[cfg(not(feature = "hickory-dns"))]
1590 {
1591 self
1592 }
1593 }
1594
1595 /// Override DNS resolution for specific domains to a particular IP address.
1596 ///
1597 /// Warning
1598 ///
1599 /// Since the DNS protocol has no notion of ports, if you wish to send
1600 /// traffic to a particular port you must include this port in the URL
1601 /// itself, any port in the overridden addr will be ignored and traffic sent
1602 /// to the conventional port for the given scheme (e.g. 80 for http).
1603 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
1604 self.resolve_to_addrs(domain, &[addr])
1605 }
1606
1607 /// Override DNS resolution for specific domains to particular IP addresses.
1608 ///
1609 /// Warning
1610 ///
1611 /// Since the DNS protocol has no notion of ports, if you wish to send
1612 /// traffic to a particular port you must include this port in the URL
1613 /// itself, any port in the overridden addresses will be ignored and traffic sent
1614 /// to the conventional port for the given scheme (e.g. 80 for http).
1615 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
1616 self.config
1617 .dns_overrides
1618 .insert(domain.to_string(), addrs.to_vec());
1619 self
1620 }
1621
1622 /// Override the DNS resolver implementation.
1623 ///
1624 /// Pass an `Arc` wrapping a trait object implementing `Resolve`.
1625 /// Overrides for specific names passed to `resolve` and `resolve_to_addrs` will
1626 /// still be applied on top of this resolver.
1627 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
1628 self.config.dns_resolver = Some(resolver as _);
1629 self
1630 }
1631
1632 /// Whether to send data on the first flight ("early data") in TLS 1.3 handshakes
1633 /// for HTTP/3 connections.
1634 ///
1635 /// The default is false.
1636 #[cfg(feature = "http3")]
1637 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1638 pub fn set_tls_enable_early_data(mut self, enabled: bool) -> ClientBuilder {
1639 self.config.tls_enable_early_data = enabled;
1640 self
1641 }
1642
1643 /// Maximum duration of inactivity to accept before timing out the QUIC connection.
1644 ///
1645 /// Please see docs in [`TransportConfig`] in [`quinn`].
1646 ///
1647 /// [`TransportConfig`]: https://docs.rs/quinn/latest/quinn/struct.TransportConfig.html
1648 #[cfg(feature = "http3")]
1649 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1650 pub fn set_quic_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
1651 self.config.quic_max_idle_timeout = Some(value);
1652 self
1653 }
1654
1655 /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream
1656 /// before becoming blocked.
1657 ///
1658 /// Please see docs in [`TransportConfig`] in [`quinn`].
1659 ///
1660 /// [`TransportConfig`]: https://docs.rs/quinn/latest/quinn/struct.TransportConfig.html
1661 #[cfg(feature = "http3")]
1662 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1663 pub fn set_quic_stream_receive_window(mut self, value: VarInt) -> ClientBuilder {
1664 self.config.quic_stream_receive_window = Some(value);
1665 self
1666 }
1667
1668 /// Maximum number of bytes the peer may transmit across all streams of a connection before
1669 /// becoming blocked.
1670 ///
1671 /// Please see docs in [`TransportConfig`] in [`quinn`].
1672 ///
1673 /// [`TransportConfig`]: https://docs.rs/quinn/latest/quinn/struct.TransportConfig.html
1674 #[cfg(feature = "http3")]
1675 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1676 pub fn set_quic_receive_window(mut self, value: VarInt) -> ClientBuilder {
1677 self.config.quic_receive_window = Some(value);
1678 self
1679 }
1680
1681 /// Maximum number of bytes to transmit to a peer without acknowledgment
1682 ///
1683 /// Please see docs in [`TransportConfig`] in [`quinn`].
1684 ///
1685 /// [`TransportConfig`]: https://docs.rs/quinn/latest/quinn/struct.TransportConfig.html
1686 #[cfg(feature = "http3")]
1687 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1688 pub fn set_quic_send_window(mut self, value: u64) -> ClientBuilder {
1689 self.config.quic_send_window = Some(value);
1690 self
1691 }
1692}
1693
1694type HyperClient = hyper::Client<Connector, super::body::ImplStream>;
1695
1696impl Default for Client {
1697 fn default() -> Self {
1698 Self::new()
1699 }
1700}
1701
1702impl Client {
1703 /// Constructs a new `Client`.
1704 ///
1705 /// # Panics
1706 ///
1707 /// This method panics if a TLS backend cannot be initialized, or the resolver
1708 /// cannot load the system configuration.
1709 ///
1710 /// Use `Client::builder()` if you wish to handle the failure as an `Error`
1711 /// instead of panicking.
1712 pub fn new() -> Client {
1713 ClientBuilder::new().build().expect("Client::new()")
1714 }
1715
1716 /// Creates a `ClientBuilder` to configure a `Client`.
1717 ///
1718 /// This is the same as `ClientBuilder::new()`.
1719 pub fn builder() -> ClientBuilder {
1720 ClientBuilder::new()
1721 }
1722
1723 /// Convenience method to make a `GET` request to a URL.
1724 ///
1725 /// # Errors
1726 ///
1727 /// This method fails whenever the supplied `Url` cannot be parsed.
1728 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1729 self.request(Method::GET, url)
1730 }
1731
1732 /// Convenience method to make a `POST` request to a URL.
1733 ///
1734 /// # Errors
1735 ///
1736 /// This method fails whenever the supplied `Url` cannot be parsed.
1737 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1738 self.request(Method::POST, url)
1739 }
1740
1741 /// Convenience method to make a `PUT` request to a URL.
1742 ///
1743 /// # Errors
1744 ///
1745 /// This method fails whenever the supplied `Url` cannot be parsed.
1746 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1747 self.request(Method::PUT, url)
1748 }
1749
1750 /// Convenience method to make a `PATCH` request to a URL.
1751 ///
1752 /// # Errors
1753 ///
1754 /// This method fails whenever the supplied `Url` cannot be parsed.
1755 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1756 self.request(Method::PATCH, url)
1757 }
1758
1759 /// Convenience method to make a `DELETE` request to a URL.
1760 ///
1761 /// # Errors
1762 ///
1763 /// This method fails whenever the supplied `Url` cannot be parsed.
1764 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1765 self.request(Method::DELETE, url)
1766 }
1767
1768 /// Convenience method to make a `HEAD` request to a URL.
1769 ///
1770 /// # Errors
1771 ///
1772 /// This method fails whenever the supplied `Url` cannot be parsed.
1773 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1774 self.request(Method::HEAD, url)
1775 }
1776
1777 /// Start building a `Request` with the `Method` and `Url`.
1778 ///
1779 /// Returns a `RequestBuilder`, which will allow setting headers and
1780 /// the request body before sending.
1781 ///
1782 /// # Errors
1783 ///
1784 /// This method fails whenever the supplied `Url` cannot be parsed.
1785 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
1786 let req = url.into_url().map(move |url| Request::new(method, url));
1787 RequestBuilder::new(self.clone(), req)
1788 }
1789
1790 /// Executes a `Request`.
1791 ///
1792 /// A `Request` can be built manually with `Request::new()` or obtained
1793 /// from a RequestBuilder with `RequestBuilder::build()`.
1794 ///
1795 /// You should prefer to use the `RequestBuilder` and
1796 /// `RequestBuilder::send()`.
1797 ///
1798 /// # Errors
1799 ///
1800 /// This method fails if there was an error while sending request,
1801 /// redirect loop was detected or redirect limit was exhausted.
1802 pub fn execute(
1803 &self,
1804 request: Request,
1805 ) -> impl Future<Output = Result<Response, crate::Error>> {
1806 self.execute_request(request)
1807 }
1808
1809 pub(super) fn execute_request(&self, req: Request) -> Pending {
1810 let (method, url, mut headers, body, timeout, version) = req.pieces();
1811 if url.scheme() != "http" && url.scheme() != "https" {
1812 return Pending::new_err(error::url_bad_scheme(url));
1813 }
1814
1815 // check if we're in https_only mode and check the scheme of the current URL
1816 if self.inner.https_only && url.scheme() != "https" {
1817 return Pending::new_err(error::url_bad_scheme(url));
1818 }
1819
1820 // insert default headers in the request headers
1821 // without overwriting already appended headers.
1822 for (key, value) in &self.inner.headers {
1823 if let Entry::Vacant(entry) = headers.entry(key) {
1824 entry.insert(value.clone());
1825 }
1826 }
1827
1828 // Add cookies from the cookie store.
1829 #[cfg(feature = "cookies")]
1830 {
1831 if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
1832 if headers.get(crate::header::COOKIE).is_none() {
1833 add_cookie_header(&mut headers, &**cookie_store, &url);
1834 }
1835 }
1836 }
1837
1838 let accept_encoding = self.inner.accepts.as_str();
1839
1840 if let Some(accept_encoding) = accept_encoding {
1841 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
1842 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
1843 }
1844 }
1845
1846 let uri = match try_uri(&url) {
1847 Ok(uri) => uri,
1848 _ => return Pending::new_err(error::url_invalid_uri(url)),
1849 };
1850
1851 let (reusable, body) = match body {
1852 Some(body) => {
1853 let (reusable, body) = body.try_reuse();
1854 (Some(reusable), body)
1855 }
1856 None => (None, Body::empty()),
1857 };
1858
1859 self.proxy_auth(&uri, &mut headers);
1860
1861 let builder = hyper::Request::builder()
1862 .method(method.clone())
1863 .uri(uri)
1864 .version(version);
1865
1866 let in_flight = match version {
1867 #[cfg(feature = "http3")]
1868 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
1869 let mut req = builder.body(body).expect("valid request parts");
1870 *req.headers_mut() = headers.clone();
1871 ResponseFuture::H3(self.inner.h3_client.as_ref().unwrap().request(req))
1872 }
1873 _ => {
1874 let mut req = builder
1875 .body(body.into_stream())
1876 .expect("valid request parts");
1877 *req.headers_mut() = headers.clone();
1878 ResponseFuture::Default(self.inner.hyper.request(req))
1879 }
1880 };
1881
1882 let timeout = timeout
1883 .or(self.inner.request_timeout)
1884 .map(tokio::time::sleep)
1885 .map(Box::pin);
1886
1887 Pending {
1888 inner: PendingInner::Request(PendingRequest {
1889 method,
1890 url,
1891 headers,
1892 body: reusable,
1893
1894 urls: Vec::new(),
1895
1896 retry_count: 0,
1897
1898 client: self.inner.clone(),
1899
1900 in_flight,
1901 timeout,
1902 }),
1903 }
1904 }
1905
1906 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
1907 if !self.inner.proxies_maybe_http_auth {
1908 return;
1909 }
1910
1911 // Only set the header here if the destination scheme is 'http',
1912 // since otherwise, the header will be included in the CONNECT tunnel
1913 // request instead.
1914 if dst.scheme() != Some(&Scheme::HTTP) {
1915 return;
1916 }
1917
1918 if headers.contains_key(PROXY_AUTHORIZATION) {
1919 return;
1920 }
1921
1922 for proxy in self.inner.proxies.iter() {
1923 if proxy.is_match(dst) {
1924 if let Some(header) = proxy.http_basic_auth(dst) {
1925 headers.insert(PROXY_AUTHORIZATION, header);
1926 }
1927
1928 break;
1929 }
1930 }
1931 }
1932}
1933
1934impl fmt::Debug for Client {
1935 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1936 let mut builder: DebugStruct<'_, '_> = f.debug_struct(name:"Client");
1937 self.inner.fmt_fields(&mut builder);
1938 builder.finish()
1939 }
1940}
1941
1942impl tower_service::Service<Request> for Client {
1943 type Response = Response;
1944 type Error = crate::Error;
1945 type Future = Pending;
1946
1947 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1948 Poll::Ready(Ok(()))
1949 }
1950
1951 fn call(&mut self, req: Request) -> Self::Future {
1952 self.execute_request(req)
1953 }
1954}
1955
1956impl tower_service::Service<Request> for &'_ Client {
1957 type Response = Response;
1958 type Error = crate::Error;
1959 type Future = Pending;
1960
1961 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1962 Poll::Ready(Ok(()))
1963 }
1964
1965 fn call(&mut self, req: Request) -> Self::Future {
1966 self.execute_request(req)
1967 }
1968}
1969
1970impl fmt::Debug for ClientBuilder {
1971 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1972 let mut builder: DebugStruct<'_, '_> = f.debug_struct(name:"ClientBuilder");
1973 self.config.fmt_fields(&mut builder);
1974 builder.finish()
1975 }
1976}
1977
1978impl Config {
1979 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
1980 // Instead of deriving Debug, only print fields when their output
1981 // would provide relevant or interesting data.
1982
1983 #[cfg(feature = "cookies")]
1984 {
1985 if let Some(_) = self.cookie_store {
1986 f.field("cookie_store", &true);
1987 }
1988 }
1989
1990 f.field("accepts", &self.accepts);
1991
1992 if !self.proxies.is_empty() {
1993 f.field("proxies", &self.proxies);
1994 }
1995
1996 if !self.redirect_policy.is_default() {
1997 f.field("redirect_policy", &self.redirect_policy);
1998 }
1999
2000 if self.referer {
2001 f.field("referer", &true);
2002 }
2003
2004 f.field("default_headers", &self.headers);
2005
2006 if self.http1_title_case_headers {
2007 f.field("http1_title_case_headers", &true);
2008 }
2009
2010 if self.http1_allow_obsolete_multiline_headers_in_responses {
2011 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2012 }
2013
2014 if self.http1_ignore_invalid_headers_in_responses {
2015 f.field("http1_ignore_invalid_headers_in_responses", &true);
2016 }
2017
2018 if self.http1_allow_spaces_after_header_name_in_responses {
2019 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2020 }
2021
2022 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2023 f.field("http1_only", &true);
2024 }
2025
2026 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2027 f.field("http2_prior_knowledge", &true);
2028 }
2029
2030 if let Some(ref d) = self.connect_timeout {
2031 f.field("connect_timeout", d);
2032 }
2033
2034 if let Some(ref d) = self.timeout {
2035 f.field("timeout", d);
2036 }
2037
2038 if let Some(ref v) = self.local_address {
2039 f.field("local_address", v);
2040 }
2041
2042 if self.nodelay {
2043 f.field("tcp_nodelay", &true);
2044 }
2045
2046 #[cfg(feature = "native-tls")]
2047 {
2048 if !self.hostname_verification {
2049 f.field("danger_accept_invalid_hostnames", &true);
2050 }
2051 }
2052
2053 #[cfg(feature = "__tls")]
2054 {
2055 if !self.certs_verification {
2056 f.field("danger_accept_invalid_certs", &true);
2057 }
2058
2059 if let Some(ref min_tls_version) = self.min_tls_version {
2060 f.field("min_tls_version", min_tls_version);
2061 }
2062
2063 if let Some(ref max_tls_version) = self.max_tls_version {
2064 f.field("max_tls_version", max_tls_version);
2065 }
2066
2067 f.field("tls_sni", &self.tls_sni);
2068
2069 f.field("tls_info", &self.tls_info);
2070 }
2071
2072 #[cfg(all(feature = "native-tls-crate", feature = "__rustls"))]
2073 {
2074 f.field("tls_backend", &self.tls);
2075 }
2076
2077 if !self.dns_overrides.is_empty() {
2078 f.field("dns_overrides", &self.dns_overrides);
2079 }
2080
2081 #[cfg(feature = "http3")]
2082 {
2083 if self.tls_enable_early_data {
2084 f.field("tls_enable_early_data", &true);
2085 }
2086 }
2087 }
2088}
2089
2090struct ClientRef {
2091 accepts: Accepts,
2092 #[cfg(feature = "cookies")]
2093 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2094 headers: HeaderMap,
2095 hyper: HyperClient,
2096 #[cfg(feature = "http3")]
2097 h3_client: Option<H3Client>,
2098 redirect_policy: redirect::Policy,
2099 referer: bool,
2100 request_timeout: Option<Duration>,
2101 proxies: Arc<Vec<Proxy>>,
2102 proxies_maybe_http_auth: bool,
2103 https_only: bool,
2104}
2105
2106impl ClientRef {
2107 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2108 // Instead of deriving Debug, only print fields when their output
2109 // would provide relevant or interesting data.
2110
2111 #[cfg(feature = "cookies")]
2112 {
2113 if let Some(_) = self.cookie_store {
2114 f.field("cookie_store", &true);
2115 }
2116 }
2117
2118 f.field("accepts", &self.accepts);
2119
2120 if !self.proxies.is_empty() {
2121 f.field("proxies", &self.proxies);
2122 }
2123
2124 if !self.redirect_policy.is_default() {
2125 f.field("redirect_policy", &self.redirect_policy);
2126 }
2127
2128 if self.referer {
2129 f.field("referer", &true);
2130 }
2131
2132 f.field("default_headers", &self.headers);
2133
2134 if let Some(ref d) = self.request_timeout {
2135 f.field("timeout", d);
2136 }
2137 }
2138}
2139
2140pin_project! {
2141 pub struct Pending {
2142 #[pin]
2143 inner: PendingInner,
2144 }
2145}
2146
2147enum PendingInner {
2148 Request(PendingRequest),
2149 Error(Option<crate::Error>),
2150}
2151
2152pin_project! {
2153 struct PendingRequest {
2154 method: Method,
2155 url: Url,
2156 headers: HeaderMap,
2157 body: Option<Option<Bytes>>,
2158
2159 urls: Vec<Url>,
2160
2161 retry_count: usize,
2162
2163 client: Arc<ClientRef>,
2164
2165 #[pin]
2166 in_flight: ResponseFuture,
2167 #[pin]
2168 timeout: Option<Pin<Box<Sleep>>>,
2169 }
2170}
2171
2172enum ResponseFuture {
2173 Default(HyperResponseFuture),
2174 #[cfg(feature = "http3")]
2175 H3(H3ResponseFuture),
2176}
2177
2178impl PendingRequest {
2179 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2180 self.project().in_flight
2181 }
2182
2183 fn timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2184 self.project().timeout
2185 }
2186
2187 fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
2188 self.project().urls
2189 }
2190
2191 fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
2192 self.project().headers
2193 }
2194
2195 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2196 if !is_retryable_error(err) {
2197 return false;
2198 }
2199
2200 trace!("can retry {err:?}");
2201
2202 let body = match self.body {
2203 Some(Some(ref body)) => Body::reusable(body.clone()),
2204 Some(None) => {
2205 debug!("error was retryable, but body not reusable");
2206 return false;
2207 }
2208 None => Body::empty(),
2209 };
2210
2211 if self.retry_count >= 2 {
2212 trace!("retry count too high");
2213 return false;
2214 }
2215 self.retry_count += 1;
2216
2217 // If it parsed once, it should parse again
2218 let uri = try_uri(&self.url).expect("URL was already validated as URI");
2219
2220 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
2221 #[cfg(feature = "http3")]
2222 ResponseFuture::H3(_) => {
2223 let mut req = hyper::Request::builder()
2224 .method(self.method.clone())
2225 .uri(uri)
2226 .body(body)
2227 .expect("valid request parts");
2228 *req.headers_mut() = self.headers.clone();
2229 ResponseFuture::H3(
2230 self.client
2231 .h3_client
2232 .as_ref()
2233 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2234 .request(req),
2235 )
2236 }
2237 _ => {
2238 let mut req = hyper::Request::builder()
2239 .method(self.method.clone())
2240 .uri(uri)
2241 .body(body.into_stream())
2242 .expect("valid request parts");
2243 *req.headers_mut() = self.headers.clone();
2244 ResponseFuture::Default(self.client.hyper.request(req))
2245 }
2246 };
2247
2248 true
2249 }
2250}
2251
2252fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
2253 #[cfg(feature = "http3")]
2254 if let Some(cause) = err.source() {
2255 if let Some(err) = cause.downcast_ref::<h3::Error>() {
2256 debug!("determining if HTTP/3 error {err} can be retried");
2257 // TODO: Does h3 provide an API for checking the error?
2258 return err.to_string().as_str() == "timeout";
2259 }
2260 }
2261
2262 if let Some(cause) = err.source() {
2263 if let Some(err) = cause.downcast_ref::<h2::Error>() {
2264 // They sent us a graceful shutdown, try with a new connection!
2265 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
2266 return true;
2267 }
2268
2269 // REFUSED_STREAM was sent from the server, which is safe to retry.
2270 // https://www.rfc-editor.org/rfc/rfc9113.html#section-8.7-3.2
2271 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
2272 {
2273 return true;
2274 }
2275 }
2276 }
2277 false
2278}
2279
2280impl Pending {
2281 pub(super) fn new_err(err: crate::Error) -> Pending {
2282 Pending {
2283 inner: PendingInner::Error(Some(err)),
2284 }
2285 }
2286
2287 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2288 self.project().inner
2289 }
2290}
2291
2292impl Future for Pending {
2293 type Output = Result<Response, crate::Error>;
2294
2295 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2296 let inner: Pin<&mut PendingInner> = self.inner();
2297 match inner.get_mut() {
2298 PendingInner::Request(ref mut req: &mut PendingRequest) => Pin::new(pointer:req).poll(cx),
2299 PendingInner::Error(ref mut err: &mut Option) => Poll::Ready(Err(err
2300 .take()
2301 .expect(msg:"Pending error polled more than once"))),
2302 }
2303 }
2304}
2305
2306impl Future for PendingRequest {
2307 type Output = Result<Response, crate::Error>;
2308
2309 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2310 if let Some(delay) = self.as_mut().timeout().as_mut().as_pin_mut() {
2311 if let Poll::Ready(()) = delay.poll(cx) {
2312 return Poll::Ready(Err(
2313 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2314 ));
2315 }
2316 }
2317
2318 loop {
2319 let res = match self.as_mut().in_flight().get_mut() {
2320 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
2321 Poll::Ready(Err(e)) => {
2322 if self.as_mut().retry_error(&e) {
2323 continue;
2324 }
2325 return Poll::Ready(Err(
2326 crate::error::request(e).with_url(self.url.clone())
2327 ));
2328 }
2329 Poll::Ready(Ok(res)) => res,
2330 Poll::Pending => return Poll::Pending,
2331 },
2332 #[cfg(feature = "http3")]
2333 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
2334 Poll::Ready(Err(e)) => {
2335 if self.as_mut().retry_error(&e) {
2336 continue;
2337 }
2338 return Poll::Ready(Err(
2339 crate::error::request(e).with_url(self.url.clone())
2340 ));
2341 }
2342 Poll::Ready(Ok(res)) => res,
2343 Poll::Pending => return Poll::Pending,
2344 },
2345 };
2346
2347 #[cfg(feature = "cookies")]
2348 {
2349 if let Some(ref cookie_store) = self.client.cookie_store {
2350 let mut cookies =
2351 cookie::extract_response_cookie_headers(&res.headers()).peekable();
2352 if cookies.peek().is_some() {
2353 cookie_store.set_cookies(&mut cookies, &self.url);
2354 }
2355 }
2356 }
2357 let should_redirect = match res.status() {
2358 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
2359 self.body = None;
2360 for header in &[
2361 TRANSFER_ENCODING,
2362 CONTENT_ENCODING,
2363 CONTENT_TYPE,
2364 CONTENT_LENGTH,
2365 ] {
2366 self.headers.remove(header);
2367 }
2368
2369 match self.method {
2370 Method::GET | Method::HEAD => {}
2371 _ => {
2372 self.method = Method::GET;
2373 }
2374 }
2375 true
2376 }
2377 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
2378 match self.body {
2379 Some(Some(_)) | None => true,
2380 Some(None) => false,
2381 }
2382 }
2383 _ => false,
2384 };
2385 if should_redirect {
2386 let loc = res.headers().get(LOCATION).and_then(|val| {
2387 let loc = (|| -> Option<Url> {
2388 // Some sites may send a utf-8 Location header,
2389 // even though we're supposed to treat those bytes
2390 // as opaque, we'll check specifically for utf8.
2391 self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
2392 })();
2393
2394 // Check that the `url` is also a valid `http::Uri`.
2395 //
2396 // If not, just log it and skip the redirect.
2397 let loc = loc.and_then(|url| {
2398 if try_uri(&url).is_ok() {
2399 Some(url)
2400 } else {
2401 None
2402 }
2403 });
2404
2405 if loc.is_none() {
2406 debug!("Location header had invalid URI: {val:?}");
2407 }
2408 loc
2409 });
2410 if let Some(loc) = loc {
2411 if self.client.referer {
2412 if let Some(referer) = make_referer(&loc, &self.url) {
2413 self.headers.insert(REFERER, referer);
2414 }
2415 }
2416 let url = self.url.clone();
2417 self.as_mut().urls().push(url);
2418 let action = self
2419 .client
2420 .redirect_policy
2421 .check(res.status(), &loc, &self.urls);
2422
2423 match action {
2424 redirect::ActionKind::Follow => {
2425 debug!("redirecting '{}' to '{}'", self.url, loc);
2426
2427 if loc.scheme() != "http" && loc.scheme() != "https" {
2428 return Poll::Ready(Err(error::url_bad_scheme(loc)));
2429 }
2430
2431 if self.client.https_only && loc.scheme() != "https" {
2432 return Poll::Ready(Err(error::redirect(
2433 error::url_bad_scheme(loc.clone()),
2434 loc,
2435 )));
2436 }
2437
2438 self.url = loc;
2439 let mut headers =
2440 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
2441
2442 remove_sensitive_headers(&mut headers, &self.url, &self.urls);
2443 let uri = try_uri(&self.url)?;
2444 let body = match self.body {
2445 Some(Some(ref body)) => Body::reusable(body.clone()),
2446 _ => Body::empty(),
2447 };
2448
2449 // Add cookies from the cookie store.
2450 #[cfg(feature = "cookies")]
2451 {
2452 if let Some(ref cookie_store) = self.client.cookie_store {
2453 add_cookie_header(&mut headers, &**cookie_store, &self.url);
2454 }
2455 }
2456
2457 *self.as_mut().in_flight().get_mut() =
2458 match *self.as_mut().in_flight().as_ref() {
2459 #[cfg(feature = "http3")]
2460 ResponseFuture::H3(_) => {
2461 let mut req = hyper::Request::builder()
2462 .method(self.method.clone())
2463 .uri(uri.clone())
2464 .body(body)
2465 .expect("valid request parts");
2466 *req.headers_mut() = headers.clone();
2467 std::mem::swap(self.as_mut().headers(), &mut headers);
2468 ResponseFuture::H3(self.client.h3_client
2469 .as_ref()
2470 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2471 .request(req))
2472 }
2473 _ => {
2474 let mut req = hyper::Request::builder()
2475 .method(self.method.clone())
2476 .uri(uri.clone())
2477 .body(body.into_stream())
2478 .expect("valid request parts");
2479 *req.headers_mut() = headers.clone();
2480 std::mem::swap(self.as_mut().headers(), &mut headers);
2481 ResponseFuture::Default(self.client.hyper.request(req))
2482 }
2483 };
2484
2485 continue;
2486 }
2487 redirect::ActionKind::Stop => {
2488 debug!("redirect policy disallowed redirection to '{loc}'");
2489 }
2490 redirect::ActionKind::Error(err) => {
2491 return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
2492 }
2493 }
2494 }
2495 }
2496
2497 let res = Response::new(
2498 res,
2499 self.url.clone(),
2500 self.client.accepts,
2501 self.timeout.take(),
2502 );
2503 return Poll::Ready(Ok(res));
2504 }
2505 }
2506}
2507
2508impl fmt::Debug for Pending {
2509 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2510 match self.inner {
2511 PendingInner::Request(ref req: &PendingRequest) => f&mut DebugStruct<'_, '_>
2512 .debug_struct("Pending")
2513 .field("method", &req.method)
2514 .field(name:"url", &req.url)
2515 .finish(),
2516 PendingInner::Error(ref err: &Option) => f.debug_struct("Pending").field(name:"error", value:err).finish(),
2517 }
2518 }
2519}
2520
2521fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
2522 if next.scheme() == "http" && previous.scheme() == "https" {
2523 return None;
2524 }
2525
2526 let mut referer: Url = previous.clone();
2527 let _ = referer.set_username("");
2528 let _ = referer.set_password(None);
2529 referer.set_fragment(None);
2530 referer.as_str().parse().ok()
2531}
2532
2533#[cfg(feature = "cookies")]
2534fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
2535 if let Some(header) = cookie_store.cookies(url) {
2536 headers.insert(crate::header::COOKIE, header);
2537 }
2538}
2539
2540#[cfg(test)]
2541mod tests {
2542 #[tokio::test]
2543 async fn execute_request_rejects_invalid_urls() {
2544 let url_str = "hxxps://www.rust-lang.org/";
2545 let url = url::Url::parse(url_str).unwrap();
2546 let result = crate::get(url.clone()).await;
2547
2548 assert!(result.is_err());
2549 let err = result.err().unwrap();
2550 assert!(err.is_builder());
2551 assert_eq!(url_str, err.url().unwrap().as_str());
2552 }
2553
2554 /// https://github.com/seanmonstar/reqwest/issues/668
2555 #[tokio::test]
2556 async fn execute_request_rejects_invalid_hostname() {
2557 let url_str = "https://{{hostname}}/";
2558 let url = url::Url::parse(url_str).unwrap();
2559 let result = crate::get(url.clone()).await;
2560
2561 assert!(result.is_err());
2562 let err = result.err().unwrap();
2563 assert!(err.is_builder());
2564 assert_eq!(url_str, err.url().unwrap().as_str());
2565 }
2566}
2567