Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_text/tests/annealer_corpus/reqwest_client.rs

113 KiB, 1 run

created by r1870400018:11796, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1#[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2use std::any::Any;
3use std::future::Future;
4use std::net::IpAddr;
5use std::pin::Pin;
6use std::sync::Arc;
7use std::task::{ready, Context, Poll};
8use std::time::Duration;
9use std::{collections::HashMap, convert::TryInto, net::SocketAddr};
10use std::{fmt, str};
11
12use super::request::{Request, RequestBuilder};
13use super::response::Response;
14use super::Body;
15#[cfg(feature = "http3")]
16use crate::async_impl::h3_client::connect::{H3ClientConfig, H3Connector};
17#[cfg(feature = "http3")]
18use crate::async_impl::h3_client::H3Client;
19use crate::config::{RequestConfig, TotalTimeout};
20#[cfg(unix)]
21use crate::connect::uds::UnixSocketProvider;
22#[cfg(target_os = "windows")]
23use crate::connect::windows_named_pipe::WindowsNamedPipeProvider;
24use crate::connect::{
25 sealed::{Conn, Unnameable},
26 BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
27};
28#[cfg(feature = "cookies")]
29use crate::cookie;
30#[cfg(feature = "cookies")]
31use crate::cookie::service::CookieService;
32#[cfg(feature = "hickory-dns")]
33use crate::dns::hickory::HickoryDnsResolver;
34use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
35use crate::error::{self, BoxError};
36use crate::into_url::try_uri;
37use crate::proxy::Matcher as ProxyMatcher;
38use crate::redirect::{self, TowerRedirectPolicy};
39#[cfg(feature = "__rustls")]
40use crate::tls::CertificateRevocationList;
41#[cfg(feature = "__tls")]
42use crate::tls::{self, TlsBackend};
43#[cfg(feature = "__tls")]
44use crate::Certificate;
45#[cfg(any(feature = "__native-tls", feature = "__rustls"))]
46use crate::Identity;
47use crate::{IntoUrl, Method, Proxy, Url};
48
49use http::header::{Entry, HeaderMap, HeaderValue, ACCEPT, PROXY_AUTHORIZATION, USER_AGENT};
50use http::uri::Scheme;
51use http::Uri;
52use hyper_util::client::legacy::connect::HttpConnector;
53#[cfg(feature = "__native-tls")]
54use native_tls_crate::TlsConnector;
55use pin_project_lite::pin_project;
56#[cfg(feature = "http3")]
57use quinn::TransportConfig;
58#[cfg(feature = "http3")]
59use quinn::VarInt;
60use tokio::time::Sleep;
61use tower::util::BoxCloneSyncServiceLayer;
62use tower::{Layer, Service};
63#[cfg(any(
64 feature = "gzip",
65 feature = "brotli",
66 feature = "zstd",
67 feature = "deflate"
68))]
69use tower_http::decompression::Decompression;
70use tower_http::follow_redirect::FollowRedirect;
71
72/// An asynchronous `Client` to make Requests with.
73///
74/// The Client has various configuration values to tweak, but the defaults
75/// are set to what is usually the most commonly desired value. To configure a
76/// `Client`, use `Client::builder()`.
77///
78/// The `Client` holds a connection pool internally to improve performance
79/// by reusing connections and avoiding setup overhead, so it is advised that
80/// you create one and **reuse** it.
81///
82/// You do **not** have to wrap the `Client` in an [`Rc`] or [`Arc`] to **reuse** it,
83/// because it already uses an [`Arc`] internally.
84///
85/// # Connection Pooling
86///
87/// The connection pool can be configured using [`ClientBuilder`] methods
88/// with the `pool_` prefix, such as [`ClientBuilder::pool_idle_timeout`]
89/// and [`ClientBuilder::pool_max_idle_per_host`].
90///
91/// [`Rc`]: std::rc::Rc
92#[derive(Clone)]
93pub struct Client {
94 inner: Arc<ClientRef>,
95}
96
97/// A `ClientBuilder` can be used to create a `Client` with custom configuration.
98#[must_use]
99pub struct ClientBuilder {
100 config: Config,
101}
102
103enum HttpVersionPref {
104 Http1,
105 #[cfg(feature = "http2")]
106 Http2,
107 #[cfg(feature = "http3")]
108 Http3,
109 All,
110}
111
112#[derive(Clone, Copy, Debug)]
113struct Accepts {
114 #[cfg(feature = "gzip")]
115 gzip: bool,
116 #[cfg(feature = "brotli")]
117 brotli: bool,
118 #[cfg(feature = "zstd")]
119 zstd: bool,
120 #[cfg(feature = "deflate")]
121 deflate: bool,
122}
123
124impl Default for Accepts {
125 fn default() -> Accepts {
126 Accepts {
127 #[cfg(feature = "gzip")]
128 gzip: true,
129 #[cfg(feature = "brotli")]
130 brotli: true,
131 #[cfg(feature = "zstd")]
132 zstd: true,
133 #[cfg(feature = "deflate")]
134 deflate: true,
135 }
136 }
137}
138
139#[derive(Clone)]
140struct HyperService {
141 hyper: HyperClient,
142}
143
144impl Service<hyper::Request<crate::async_impl::body::Body>> for HyperService {
145 type Error = crate::Error;
146 type Response = http::Response<hyper::body::Incoming>;
147 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + Sync>>;
148
149 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
150 self.hyper.poll_ready(cx).map_err(crate::error::request)
151 }
152
153 fn call(&mut self, req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
154 let clone = self.hyper.clone();
155 let mut inner = std::mem::replace(&mut self.hyper, clone);
156 Box::pin(async move { inner.call(req).await.map_err(crate::error::request) })
157 }
158}
159
160struct Config {
161 // NOTE: When adding a new field, update `fmt::Debug for ClientBuilder`
162 accepts: Accepts,
163 headers: HeaderMap,
164 #[cfg(feature = "__tls")]
165 hostname_verification: bool,
166 #[cfg(feature = "__tls")]
167 certs_verification: bool,
168 #[cfg(feature = "__tls")]
169 tls_sni: bool,
170 #[cfg(feature = "__rustls")]
171 tls_sslkeylogfile: bool,
172 connect_timeout: Option<Duration>,
173 connection_verbose: bool,
174 pool_idle_timeout: Option<Duration>,
175 pool_max_idle_per_host: usize,
176 tcp_keepalive: Option<Duration>,
177 tcp_keepalive_interval: Option<Duration>,
178 tcp_keepalive_retries: Option<u32>,
179 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
180 tcp_user_timeout: Option<Duration>,
181 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
182 identity: Option<Identity>,
183 proxies: Vec<ProxyMatcher>,
184 auto_sys_proxy: bool,
185 redirect_policy: redirect::Policy,
186 retry_policy: crate::retry::Builder,
187 referer: bool,
188 read_timeout: Option<Duration>,
189 timeout: Option<Duration>,
190 #[cfg(feature = "__tls")]
191 root_certs: Vec<Certificate>,
192 #[cfg(feature = "__tls")]
193 tls_certs_only: bool,
194 #[cfg(feature = "__rustls")]
195 crls: Vec<CertificateRevocationList>,
196 #[cfg(feature = "__tls")]
197 min_tls_version: Option<tls::Version>,
198 #[cfg(feature = "__tls")]
199 max_tls_version: Option<tls::Version>,
200 #[cfg(feature = "__tls")]
201 tls_info: bool,
202 #[cfg(feature = "__tls")]
203 tls: TlsBackend,
204 connector_layers: Vec<BoxedConnectorLayer>,
205 http_version_pref: HttpVersionPref,
206 http09_responses: bool,
207 http1_title_case_headers: bool,
208 http1_allow_obsolete_multiline_headers_in_responses: bool,
209 http1_ignore_invalid_headers_in_responses: bool,
210 http1_allow_spaces_after_header_name_in_responses: bool,
211 #[cfg(feature = "http2")]
212 http2_initial_stream_window_size: Option<u32>,
213 #[cfg(feature = "http2")]
214 http2_initial_connection_window_size: Option<u32>,
215 #[cfg(feature = "http2")]
216 http2_adaptive_window: bool,
217 #[cfg(feature = "http2")]
218 http2_max_frame_size: Option<u32>,
219 #[cfg(feature = "http2")]
220 http2_max_header_list_size: Option<u32>,
221 #[cfg(feature = "http2")]
222 http2_keep_alive_interval: Option<Duration>,
223 #[cfg(feature = "http2")]
224 http2_keep_alive_timeout: Option<Duration>,
225 #[cfg(feature = "http2")]
226 http2_keep_alive_while_idle: bool,
227 local_address: Option<IpAddr>,
228 #[cfg(any(
229 target_os = "android",
230 target_os = "fuchsia",
231 target_os = "illumos",
232 target_os = "ios",
233 target_os = "linux",
234 target_os = "macos",
235 target_os = "solaris",
236 target_os = "tvos",
237 target_os = "visionos",
238 target_os = "watchos",
239 ))]
240 interface: Option<String>,
241 nodelay: bool,
242 #[cfg(feature = "cookies")]
243 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
244 hickory_dns: bool,
245 error: Option<crate::Error>,
246 https_only: bool,
247 #[cfg(feature = "http3")]
248 tls_enable_early_data: bool,
249 #[cfg(feature = "http3")]
250 quic_max_idle_timeout: Option<Duration>,
251 #[cfg(feature = "http3")]
252 quic_stream_receive_window: Option<VarInt>,
253 #[cfg(feature = "http3")]
254 quic_receive_window: Option<VarInt>,
255 #[cfg(feature = "http3")]
256 quic_send_window: Option<u64>,
257 #[cfg(feature = "http3")]
258 quic_congestion_bbr: bool,
259 #[cfg(feature = "http3")]
260 h3_max_field_section_size: Option<u64>,
261 #[cfg(feature = "http3")]
262 h3_send_grease: Option<bool>,
263 dns_overrides: HashMap<String, Vec<SocketAddr>>,
264 dns_resolver: Option<Arc<dyn Resolve>>,
265
266 #[cfg(unix)]
267 unix_socket: Option<Arc<std::path::Path>>,
268 #[cfg(target_os = "windows")]
269 windows_named_pipe: Option<Arc<std::ffi::OsStr>>,
270}
271
272impl Default for ClientBuilder {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278impl ClientBuilder {
279 /// Constructs a new `ClientBuilder`.
280 ///
281 /// This is the same as `Client::builder()`.
282 pub fn new() -> Self {
283 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
284 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
285
286 ClientBuilder {
287 config: Config {
288 error: None,
289 accepts: Accepts::default(),
290 headers,
291 #[cfg(feature = "__tls")]
292 hostname_verification: true,
293 #[cfg(feature = "__tls")]
294 certs_verification: true,
295 #[cfg(feature = "__tls")]
296 tls_sni: true,
297 #[cfg(feature = "__rustls")]
298 tls_sslkeylogfile: false,
299 connect_timeout: None,
300 connection_verbose: false,
301 pool_idle_timeout: Some(Duration::from_secs(90)),
302 pool_max_idle_per_host: usize::MAX,
303 tcp_keepalive: Some(Duration::from_secs(15)),
304 tcp_keepalive_interval: Some(Duration::from_secs(15)),
305 tcp_keepalive_retries: Some(3),
306 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
307 tcp_user_timeout: Some(Duration::from_secs(30)),
308 proxies: Vec::new(),
309 auto_sys_proxy: true,
310 redirect_policy: redirect::Policy::default(),
311 retry_policy: crate::retry::Builder::default(),
312 referer: true,
313 read_timeout: None,
314 timeout: None,
315 #[cfg(feature = "__tls")]
316 root_certs: Vec::new(),
317 #[cfg(feature = "__tls")]
318 tls_certs_only: false,
319 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
320 identity: None,
321 #[cfg(feature = "__rustls")]
322 crls: vec![],
323 #[cfg(feature = "__tls")]
324 min_tls_version: None,
325 #[cfg(feature = "__tls")]
326 max_tls_version: None,
327 #[cfg(feature = "__tls")]
328 tls_info: false,
329 #[cfg(feature = "__tls")]
330 tls: TlsBackend::default(),
331 connector_layers: Vec::new(),
332 http_version_pref: HttpVersionPref::All,
333 http09_responses: false,
334 http1_title_case_headers: false,
335 http1_allow_obsolete_multiline_headers_in_responses: false,
336 http1_ignore_invalid_headers_in_responses: false,
337 http1_allow_spaces_after_header_name_in_responses: false,
338 #[cfg(feature = "http2")]
339 http2_initial_stream_window_size: None,
340 #[cfg(feature = "http2")]
341 http2_initial_connection_window_size: None,
342 #[cfg(feature = "http2")]
343 http2_adaptive_window: false,
344 #[cfg(feature = "http2")]
345 http2_max_frame_size: None,
346 #[cfg(feature = "http2")]
347 http2_max_header_list_size: None,
348 #[cfg(feature = "http2")]
349 http2_keep_alive_interval: None,
350 #[cfg(feature = "http2")]
351 http2_keep_alive_timeout: None,
352 #[cfg(feature = "http2")]
353 http2_keep_alive_while_idle: false,
354 local_address: None,
355 #[cfg(any(
356 target_os = "android",
357 target_os = "fuchsia",
358 target_os = "illumos",
359 target_os = "ios",
360 target_os = "linux",
361 target_os = "macos",
362 target_os = "solaris",
363 target_os = "tvos",
364 target_os = "visionos",
365 target_os = "watchos",
366 ))]
367 interface: None,
368 nodelay: true,
369 hickory_dns: cfg!(feature = "hickory-dns"),
370 #[cfg(feature = "cookies")]
371 cookie_store: None,
372 https_only: false,
373 dns_overrides: HashMap::new(),
374 #[cfg(feature = "http3")]
375 tls_enable_early_data: false,
376 #[cfg(feature = "http3")]
377 quic_max_idle_timeout: None,
378 #[cfg(feature = "http3")]
379 quic_stream_receive_window: None,
380 #[cfg(feature = "http3")]
381 quic_receive_window: None,
382 #[cfg(feature = "http3")]
383 quic_send_window: None,
384 #[cfg(feature = "http3")]
385 quic_congestion_bbr: false,
386 #[cfg(feature = "http3")]
387 h3_max_field_section_size: None,
388 #[cfg(feature = "http3")]
389 h3_send_grease: None,
390 dns_resolver: None,
391 #[cfg(unix)]
392 unix_socket: None,
393 #[cfg(target_os = "windows")]
394 windows_named_pipe: None,
395 },
396 }
397 }
398}
399
400impl ClientBuilder {
401 /// Returns a `Client` that uses this `ClientBuilder` configuration.
402 ///
403 /// # Errors
404 ///
405 /// This method fails if a TLS backend cannot be initialized, or the resolver
406 /// cannot load the system configuration.
407 pub fn build(self) -> crate::Result<Client> {
408 let config = self.config;
409
410 if let Some(err) = config.error {
411 return Err(err);
412 }
413
414 let mut proxies = config.proxies;
415 if config.auto_sys_proxy {
416 proxies.push(ProxyMatcher::system());
417 }
418 let proxies = Arc::new(proxies);
419
420 #[allow(unused)]
421 #[cfg(feature = "http3")]
422 let mut h3_connector = None;
423
424 let resolver = {
425 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
426 false => Arc::new(GaiResolver::new()),
427 #[cfg(feature = "hickory-dns")]
428 true => Arc::new(HickoryDnsResolver::default()),
429 #[cfg(not(feature = "hickory-dns"))]
430 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
431 };
432 if let Some(dns_resolver) = config.dns_resolver {
433 resolver = dns_resolver;
434 }
435 if !config.dns_overrides.is_empty() {
436 resolver = Arc::new(DnsResolverWithOverrides::new(
437 resolver,
438 config.dns_overrides,
439 ));
440 }
441 DynResolver::new(resolver)
442 };
443
444 let mut connector_builder = {
445 #[cfg(feature = "__tls")]
446 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
447 headers.get(USER_AGENT).cloned()
448 }
449
450 let mut http = HttpConnector::new_with_resolver(resolver.clone());
451 http.set_connect_timeout(config.connect_timeout);
452
453 #[cfg(all(feature = "http3", feature = "__rustls"))]
454 let build_h3_connector =
455 |resolver,
456 tls,
457 quic_max_idle_timeout: Option<Duration>,
458 quic_stream_receive_window,
459 quic_receive_window,
460 quic_send_window,
461 quic_congestion_bbr,
462 h3_max_field_section_size,
463 h3_send_grease,
464 local_address,
465 http_version_pref: &HttpVersionPref| {
466 let mut transport_config = TransportConfig::default();
467
468 if let Some(max_idle_timeout) = quic_max_idle_timeout {
469 transport_config.max_idle_timeout(Some(
470 max_idle_timeout.try_into().map_err(error::builder)?,
471 ));
472 }
473
474 if let Some(stream_receive_window) = quic_stream_receive_window {
475 transport_config.stream_receive_window(stream_receive_window);
476 }
477
478 if let Some(receive_window) = quic_receive_window {
479 transport_config.receive_window(receive_window);
480 }
481
482 if let Some(send_window) = quic_send_window {
483 transport_config.send_window(send_window);
484 }
485
486 if quic_congestion_bbr {
487 let factory = Arc::new(quinn::congestion::BbrConfig::default());
488 transport_config.congestion_controller_factory(factory);
489 }
490
491 let mut h3_client_config = H3ClientConfig::default();
492
493 if let Some(max_field_section_size) = h3_max_field_section_size {
494 h3_client_config.max_field_section_size = Some(max_field_section_size);
495 }
496
497 if let Some(send_grease) = h3_send_grease {
498 h3_client_config.send_grease = Some(send_grease);
499 }
500
501 let res = H3Connector::new(
502 resolver,
503 tls,
504 local_address,
505 transport_config,
506 h3_client_config,
507 );
508
509 match res {
510 Ok(connector) => Ok(Some(connector)),
511 Err(err) => {
512 if let HttpVersionPref::Http3 = http_version_pref {
513 Err(error::builder(err))
514 } else {
515 Ok(None)
516 }
517 }
518 }
519 };
520
521 #[cfg(feature = "__tls")]
522 match config.tls {
523 #[cfg(feature = "__native-tls")]
524 TlsBackend::NativeTls => {
525 let mut tls = TlsConnector::builder();
526
527 #[cfg(all(feature = "__native-tls-alpn", not(feature = "http3")))]
528 {
529 match config.http_version_pref {
530 HttpVersionPref::Http1 => {
531 tls.request_alpns(&["http/1.1"]);
532 }
533 #[cfg(feature = "http2")]
534 HttpVersionPref::Http2 => {
535 tls.request_alpns(&["h2"]);
536 }
537 HttpVersionPref::All => {
538 tls.request_alpns(&[
539 #[cfg(feature = "http2")]
540 "h2",
541 "http/1.1",
542 ]);
543 }
544 }
545 }
546
547 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
548
549 tls.danger_accept_invalid_certs(!config.certs_verification);
550
551 tls.use_sni(config.tls_sni);
552
553 tls.disable_built_in_roots(config.tls_certs_only);
554
555 for cert in config.root_certs {
556 cert.add_to_native_tls(&mut tls);
557 }
558
559 #[cfg(feature = "__native-tls")]
560 {
561 if let Some(id) = config.identity {
562 id.add_to_native_tls(&mut tls)?;
563 }
564 }
565 #[cfg(all(feature = "__rustls", not(feature = "__native-tls")))]
566 {
567 // Default backend + rustls Identity doesn't work.
568 if let Some(_id) = config.identity {
569 return Err(crate::error::builder("incompatible TLS identity type"));
570 }
571 }
572
573 if let Some(min_tls_version) = config.min_tls_version {
574 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
575 // TLS v1.3. This would be entirely reasonable,
576 // native-tls just doesn't support it.
577 // https://github.com/sfackler/rust-native-tls/issues/140
578 crate::error::builder("invalid minimum TLS version for backend")
579 })?;
580 tls.min_protocol_version(Some(protocol));
581 }
582
583 if let Some(max_tls_version) = config.max_tls_version {
584 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
585 // TLS v1.3.
586 // We could arguably do max_protocol_version(None), given
587 // that 1.4 does not exist yet, but that'd get messy in the
588 // future.
589 crate::error::builder("invalid maximum TLS version for backend")
590 })?;
591 tls.max_protocol_version(Some(protocol));
592 }
593
594 ConnectorBuilder::new_native_tls(
595 http,
596 tls,
597 proxies.clone(),
598 user_agent(&config.headers),
599 config.local_address,
600 #[cfg(any(
601 target_os = "android",
602 target_os = "fuchsia",
603 target_os = "illumos",
604 target_os = "ios",
605 target_os = "linux",
606 target_os = "macos",
607 target_os = "solaris",
608 target_os = "tvos",
609 target_os = "visionos",
610 target_os = "watchos",
611 ))]
612 config.interface.as_deref(),
613 config.nodelay,
614 config.tls_info,
615 )?
616 }
617 #[cfg(feature = "__native-tls")]
618 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_native_tls(
619 http,
620 conn,
621 proxies.clone(),
622 user_agent(&config.headers),
623 config.local_address,
624 #[cfg(any(
625 target_os = "android",
626 target_os = "fuchsia",
627 target_os = "illumos",
628 target_os = "ios",
629 target_os = "linux",
630 target_os = "macos",
631 target_os = "solaris",
632 target_os = "tvos",
633 target_os = "visionos",
634 target_os = "watchos",
635 ))]
636 config.interface.as_deref(),
637 config.nodelay,
638 config.tls_info,
639 ),
640 #[cfg(feature = "__rustls")]
641 TlsBackend::BuiltRustls(conn) => {
642 #[cfg(feature = "http3")]
643 {
644 let mut h3_tls = conn.clone();
645 h3_tls.alpn_protocols = vec!["h3".into()];
646
647 h3_connector = build_h3_connector(
648 resolver.clone(),
649 h3_tls,
650 config.quic_max_idle_timeout,
651 config.quic_stream_receive_window,
652 config.quic_receive_window,
653 config.quic_send_window,
654 config.quic_congestion_bbr,
655 config.h3_max_field_section_size,
656 config.h3_send_grease,
657 config.local_address,
658 &config.http_version_pref,
659 )?;
660 }
661
662 ConnectorBuilder::new_rustls_tls(
663 http,
664 conn,
665 proxies.clone(),
666 user_agent(&config.headers),
667 config.local_address,
668 #[cfg(any(
669 target_os = "android",
670 target_os = "fuchsia",
671 target_os = "illumos",
672 target_os = "ios",
673 target_os = "linux",
674 target_os = "macos",
675 target_os = "solaris",
676 target_os = "tvos",
677 target_os = "visionos",
678 target_os = "watchos",
679 ))]
680 config.interface.as_deref(),
681 config.nodelay,
682 config.tls_info,
683 )
684 }
685 #[cfg(feature = "__rustls")]
686 TlsBackend::Rustls => {
687 use crate::tls::{IgnoreHostname, NoVerifier};
688
689 // Set TLS versions.
690 let mut versions = rustls::ALL_VERSIONS.to_vec();
691
692 if let Some(min_tls_version) = config.min_tls_version {
693 versions.retain(|&supported_version| {
694 match tls::Version::from_rustls(supported_version.version) {
695 Some(version) => version >= min_tls_version,
696 // Assume it's so new we don't know about it, allow it
697 // (as of writing this is unreachable)
698 None => true,
699 }
700 });
701 }
702
703 if let Some(max_tls_version) = config.max_tls_version {
704 versions.retain(|&supported_version| {
705 match tls::Version::from_rustls(supported_version.version) {
706 Some(version) => version <= max_tls_version,
707 None => false,
708 }
709 });
710 }
711
712 if versions.is_empty() {
713 return Err(crate::error::builder("empty supported tls versions"));
714 }
715
716 // Allow user to have installed a runtime default.
717 // If not, we ship with _our_ recommended default.
718 let provider = rustls::crypto::CryptoProvider::get_default()
719 .map(|arc| arc.clone())
720 .unwrap_or_else(default_rustls_crypto_provider);
721
722 // Build TLS config
723 let signature_algorithms = provider.signature_verification_algorithms;
724 let config_builder =
725 rustls::ClientConfig::builder_with_provider(provider.clone())
726 .with_protocol_versions(&versions)
727 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
728
729 let config_builder = if !config.certs_verification {
730 config_builder
731 .dangerous()
732 .with_custom_certificate_verifier(Arc::new(NoVerifier))
733 } else if !config.hostname_verification {
734 if !config.tls_certs_only {
735 // Should this just warn? Error for now...
736 return Err(crate::error::builder(
737 "disabling rustls hostname verification only allowed with tls_certs_only()"
738 ));
739 }
740
741 config_builder
742 .dangerous()
743 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
744 crate::tls::rustls_store(config.root_certs)?,
745 signature_algorithms,
746 )))
747 } else if !config.tls_certs_only {
748 // Check for some misconfigurations and report them.
749 if !config.crls.is_empty() {
750 return Err(crate::error::builder(
751 "CRLs only allowed with tls_certs_only()",
752 ));
753 }
754
755 let verifier = if config.root_certs.is_empty() {
756 rustls_platform_verifier::Verifier::new(provider.clone())
757 .map_err(crate::error::builder)?
758 } else {
759 #[cfg(any(
760 all(unix, not(target_os = "android")),
761 target_os = "windows"
762 ))]
763 {
764 rustls_platform_verifier::Verifier::new_with_extra_roots(
765 crate::tls::rustls_der(config.root_certs)?,
766 provider.clone(),
767 )
768 .map_err(crate::error::builder)?
769 }
770
771 #[cfg(not(any(
772 all(unix, not(target_os = "android")),
773 target_os = "windows"
774 )))]
775 return Err(crate::error::builder(
776 "rustls-platform-verifier could not load extra certs",
777 ));
778 };
779
780 config_builder
781 .dangerous()
782 .with_custom_certificate_verifier(Arc::new(verifier))
783 } else {
784 if config.crls.is_empty() {
785 config_builder.with_root_certificates(crate::tls::rustls_store(
786 config.root_certs,
787 )?)
788 } else {
789 let crls = config
790 .crls
791 .iter()
792 .map(|e| e.as_rustls_crl())
793 .collect::<Vec<_>>();
794 let verifier =
795 rustls::client::WebPkiServerVerifier::builder_with_provider(
796 Arc::new(crate::tls::rustls_store(config.root_certs)?),
797 provider,
798 )
799 .with_crls(crls)
800 .build()
801 .map_err(|_| {
802 crate::error::builder("invalid TLS verification settings")
803 })?;
804 config_builder.with_webpki_verifier(verifier)
805 }
806 };
807
808 // Finalize TLS config
809 let mut tls = if let Some(id) = config.identity {
810 id.add_to_rustls(config_builder)?
811 } else {
812 config_builder.with_no_client_auth()
813 };
814
815 tls.enable_sni = config.tls_sni;
816
817 if config.tls_sslkeylogfile {
818 tls.key_log = Arc::new(rustls::KeyLogFile::new());
819 }
820
821 // ALPN protocol
822 match config.http_version_pref {
823 HttpVersionPref::Http1 => {
824 tls.alpn_protocols = vec!["http/1.1".into()];
825 }
826 #[cfg(feature = "http2")]
827 HttpVersionPref::Http2 => {
828 tls.alpn_protocols = vec!["h2".into()];
829 }
830 #[cfg(feature = "http3")]
831 HttpVersionPref::Http3 => {
832 // h3 ALPN is not valid over TCP
833 }
834 HttpVersionPref::All => {
835 tls.alpn_protocols = vec![
836 #[cfg(feature = "http2")]
837 "h2".into(),
838 "http/1.1".into(),
839 ];
840 }
841 }
842
843 #[cfg(feature = "http3")]
844 {
845 let mut h3_tls = tls.clone();
846 h3_tls.enable_early_data = config.tls_enable_early_data;
847
848 // h3 ALPN is required over QUIC for HTTP/3
849 h3_tls.alpn_protocols = vec!["h3".into()];
850
851 h3_connector = build_h3_connector(
852 resolver.clone(),
853 h3_tls,
854 config.quic_max_idle_timeout,
855 config.quic_stream_receive_window,
856 config.quic_receive_window,
857 config.quic_send_window,
858 config.quic_congestion_bbr,
859 config.h3_max_field_section_size,
860 config.h3_send_grease,
861 config.local_address,
862 &config.http_version_pref,
863 )?;
864 }
865
866 ConnectorBuilder::new_rustls_tls(
867 http,
868 tls,
869 proxies.clone(),
870 user_agent(&config.headers),
871 config.local_address,
872 #[cfg(any(
873 target_os = "android",
874 target_os = "fuchsia",
875 target_os = "illumos",
876 target_os = "ios",
877 target_os = "linux",
878 target_os = "macos",
879 target_os = "solaris",
880 target_os = "tvos",
881 target_os = "visionos",
882 target_os = "watchos",
883 ))]
884 config.interface.as_deref(),
885 config.nodelay,
886 config.tls_info,
887 )
888 }
889 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
890 TlsBackend::UnknownPreconfigured => {
891 return Err(crate::error::builder(
892 "Unknown TLS backend passed to `use_preconfigured_tls`",
893 ));
894 }
895 }
896
897 #[cfg(not(feature = "__tls"))]
898 ConnectorBuilder::new(
899 http,
900 proxies.clone(),
901 config.local_address,
902 #[cfg(any(
903 target_os = "android",
904 target_os = "fuchsia",
905 target_os = "illumos",
906 target_os = "ios",
907 target_os = "linux",
908 target_os = "macos",
909 target_os = "solaris",
910 target_os = "tvos",
911 target_os = "visionos",
912 target_os = "watchos",
913 ))]
914 config.interface.as_deref(),
915 config.nodelay,
916 )
917 };
918
919 connector_builder.set_timeout(config.connect_timeout);
920 connector_builder.set_verbose(config.connection_verbose);
921 connector_builder.set_keepalive(config.tcp_keepalive);
922 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
923 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
924 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
925 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
926
927 #[cfg(feature = "socks")]
928 connector_builder.set_socks_resolver(resolver);
929
930 // TODO: It'd be best to refactor this so the HttpConnector is never
931 // constructed at all. But there's a lot of code for all the different
932 // ways TLS can be configured...
933 #[cfg(unix)]
934 connector_builder.set_unix_socket(config.unix_socket);
935 #[cfg(target_os = "windows")]
936 connector_builder.set_windows_named_pipe(config.windows_named_pipe.clone());
937
938 let mut builder =
939 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
940 #[cfg(feature = "http2")]
941 {
942 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
943 builder.http2_only(true);
944 }
945
946 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
947 {
948 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
949 }
950 if let Some(http2_initial_connection_window_size) =
951 config.http2_initial_connection_window_size
952 {
953 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
954 }
955 if config.http2_adaptive_window {
956 builder.http2_adaptive_window(true);
957 }
958 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
959 builder.http2_max_frame_size(http2_max_frame_size);
960 }
961 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
962 builder.http2_max_header_list_size(http2_max_header_list_size);
963 }
964 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
965 builder.http2_keep_alive_interval(http2_keep_alive_interval);
966 }
967 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
968 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
969 }
970 if config.http2_keep_alive_while_idle {
971 builder.http2_keep_alive_while_idle(true);
972 }
973 }
974
975 builder.timer(hyper_util::rt::TokioTimer::new());
976 builder.pool_timer(hyper_util::rt::TokioTimer::new());
977 builder.pool_idle_timeout(config.pool_idle_timeout);
978 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
979
980 if config.http09_responses {
981 builder.http09_responses(true);
982 }
983
984 if config.http1_title_case_headers {
985 builder.http1_title_case_headers(true);
986 }
987
988 if config.http1_allow_obsolete_multiline_headers_in_responses {
989 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
990 }
991
992 if config.http1_ignore_invalid_headers_in_responses {
993 builder.http1_ignore_invalid_headers_in_responses(true);
994 }
995
996 if config.http1_allow_spaces_after_header_name_in_responses {
997 builder.http1_allow_spaces_after_header_name_in_responses(true);
998 }
999
1000 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
1001 let proxies_maybe_http_custom_headers =
1002 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
1003
1004 let redirect_policy_desc = if config.redirect_policy.is_default() {
1005 None
1006 } else {
1007 Some(format!("{:?}", &config.redirect_policy))
1008 };
1009
1010 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
1011 let hyper_service = HyperService {
1012 hyper: hyper_client,
1013 };
1014
1015 let redirect_policy = {
1016 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
1017 p.with_referer(config.referer)
1018 .with_https_only(config.https_only);
1019 p
1020 };
1021
1022 let retry_policy = config.retry_policy.into_policy();
1023
1024 let svc = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
1025
1026 #[cfg(feature = "cookies")]
1027 let svc = CookieService::new(svc, config.cookie_store.clone());
1028 let hyper = FollowRedirect::with_policy(svc, redirect_policy.clone());
1029 #[cfg(any(
1030 feature = "gzip",
1031 feature = "brotli",
1032 feature = "zstd",
1033 feature = "deflate"
1034 ))]
1035 let hyper = Decompression::new(hyper)
1036 // set everything to NO, in case tower-http has it enabled but
1037 // reqwest does not. then set to config value if cfg allows.
1038 .no_gzip()
1039 .no_deflate()
1040 .no_br()
1041 .no_zstd();
1042 #[cfg(feature = "gzip")]
1043 let hyper = hyper.gzip(config.accepts.gzip);
1044 #[cfg(feature = "brotli")]
1045 let hyper = hyper.br(config.accepts.brotli);
1046 #[cfg(feature = "zstd")]
1047 let hyper = hyper.zstd(config.accepts.zstd);
1048 #[cfg(feature = "deflate")]
1049 let hyper = hyper.deflate(config.accepts.deflate);
1050
1051 Ok(Client {
1052 inner: Arc::new(ClientRef {
1053 accepts: config.accepts,
1054 #[cfg(feature = "cookies")]
1055 cookie_store: config.cookie_store.clone(),
1056 // Use match instead of map since config is partially moved,
1057 // and it cannot be used in closure
1058 #[cfg(feature = "http3")]
1059 h3_client: match h3_connector {
1060 Some(h3_connector) => {
1061 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1062 let svc = tower::retry::Retry::new(retry_policy, h3_service);
1063 #[cfg(feature = "cookies")]
1064 let svc = CookieService::new(svc, config.cookie_store);
1065 let svc = FollowRedirect::with_policy(svc, redirect_policy);
1066 #[cfg(any(
1067 feature = "gzip",
1068 feature = "brotli",
1069 feature = "zstd",
1070 feature = "deflate"
1071 ))]
1072 let svc = Decompression::new(svc)
1073 // set everything to NO, in case tower-http has it enabled but
1074 // reqwest does not. then set to config value if cfg allows.
1075 .no_gzip()
1076 .no_deflate()
1077 .no_br()
1078 .no_zstd();
1079 #[cfg(feature = "gzip")]
1080 let svc = svc.gzip(config.accepts.gzip);
1081 #[cfg(feature = "brotli")]
1082 let svc = svc.br(config.accepts.brotli);
1083 #[cfg(feature = "zstd")]
1084 let svc = svc.zstd(config.accepts.zstd);
1085 #[cfg(feature = "deflate")]
1086 let svc = svc.deflate(config.accepts.deflate);
1087 Some(svc)
1088 }
1089 None => None,
1090 },
1091 headers: config.headers,
1092 referer: config.referer,
1093 read_timeout: config.read_timeout,
1094 total_timeout: RequestConfig::new(config.timeout),
1095 hyper,
1096 proxies,
1097 proxies_maybe_http_auth,
1098 proxies_maybe_http_custom_headers,
1099 https_only: config.https_only,
1100 redirect_policy_desc,
1101 }),
1102 })
1103 }
1104
1105 // Higher-level options
1106
1107 /// Sets the `User-Agent` header to be used by this client.
1108 ///
1109 /// # Example
1110 ///
1111 /// ```rust
1112 /// # async fn doc() -> Result<(), reqwest::Error> {
1113 /// // Name your user agent after your app?
1114 /// static APP_USER_AGENT: &str = concat!(
1115 /// env!("CARGO_PKG_NAME"),
1116 /// "/",
1117 /// env!("CARGO_PKG_VERSION"),
1118 /// );
1119 ///
1120 /// let client = reqwest::Client::builder()
1121 /// .user_agent(APP_USER_AGENT)
1122 /// .build()?;
1123 /// let res = client.get("https://www.rust-lang.org").send().await?;
1124 /// # Ok(())
1125 /// # }
1126 /// ```
1127 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1128 where
1129 V: TryInto<HeaderValue>,
1130 V::Error: Into<http::Error>,
1131 {
1132 match value.try_into() {
1133 Ok(value) => {
1134 self.config.headers.insert(USER_AGENT, value);
1135 }
1136 Err(e) => {
1137 self.config.error = Some(crate::error::builder(e.into()));
1138 }
1139 };
1140 self
1141 }
1142 /// Sets the default headers for every request.
1143 ///
1144 /// # Example
1145 ///
1146 /// ```rust
1147 /// use reqwest::header;
1148 /// # async fn doc() -> Result<(), reqwest::Error> {
1149 /// let mut headers = header::HeaderMap::new();
1150 /// headers.insert("X-MY-HEADER", header::HeaderValue::from_static("value"));
1151 ///
1152 /// // Consider marking security-sensitive headers with `set_sensitive`.
1153 /// let mut auth_value = header::HeaderValue::from_static("secret");
1154 /// auth_value.set_sensitive(true);
1155 /// headers.insert(header::AUTHORIZATION, auth_value);
1156 ///
1157 /// // get a client builder
1158 /// let client = reqwest::Client::builder()
1159 /// .default_headers(headers)
1160 /// .build()?;
1161 /// let res = client.get("https://www.rust-lang.org").send().await?;
1162 /// # Ok(())
1163 /// # }
1164 /// ```
1165 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1166 for (key, value) in headers.iter() {
1167 self.config.headers.insert(key, value.clone());
1168 }
1169 self
1170 }
1171
1172 /// Enable a persistent cookie store for the client.
1173 ///
1174 /// Cookies received in responses will be preserved and included in
1175 /// additional requests.
1176 ///
1177 /// By default, no cookie store is used. Enabling the cookie store
1178 /// with `cookie_store(true)` will set the store to a default implementation.
1179 /// It is **not** necessary to call [cookie_store(true)](crate::ClientBuilder::cookie_store) if [cookie_provider(my_cookie_store)](crate::ClientBuilder::cookie_provider)
1180 /// is used; calling [cookie_store(true)](crate::ClientBuilder::cookie_store) _after_ [cookie_provider(my_cookie_store)](crate::ClientBuilder::cookie_provider) will result
1181 /// in the provided `my_cookie_store` being **overridden** with a default implementation.
1182 ///
1183 /// # Optional
1184 ///
1185 /// This requires the optional `cookies` feature to be enabled.
1186 #[cfg(feature = "cookies")]
1187 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1188 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1189 if enable {
1190 self.cookie_provider(Arc::new(cookie::Jar::default()))
1191 } else {
1192 self.config.cookie_store = None;
1193 self
1194 }
1195 }
1196
1197 /// Set the persistent cookie store for the client.
1198 ///
1199 /// Cookies received in responses will be passed to this store, and
1200 /// additional requests will query this store for cookies.
1201 ///
1202 /// By default, no cookie store is used. It is **not** necessary to also call
1203 /// [cookie_store(true)](crate::ClientBuilder::cookie_store) if [cookie_provider(my_cookie_store)](crate::ClientBuilder::cookie_provider) is used; calling
1204 /// [cookie_store(true)](crate::ClientBuilder::cookie_store) _after_ [cookie_provider(my_cookie_store)](crate::ClientBuilder::cookie_provider) will result
1205 /// in the provided `my_cookie_store` being **overridden** with a default implementation.
1206 ///
1207 /// # Optional
1208 ///
1209 /// This requires the optional `cookies` feature to be enabled.
1210 #[cfg(feature = "cookies")]
1211 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1212 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1213 mut self,
1214 cookie_store: Arc<C>,
1215 ) -> ClientBuilder {
1216 self.config.cookie_store = Some(cookie_store as _);
1217 self
1218 }
1219
1220 /// Enable auto gzip decompression by checking the `Content-Encoding` response header.
1221 ///
1222 /// If auto gzip decompression is turned on:
1223 ///
1224 /// - When sending a request and if the request's headers do not already contain
1225 /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `gzip`.
1226 /// The request body is **not** automatically compressed.
1227 /// - When receiving a response, if its headers contain a `Content-Encoding` value of
1228 /// `gzip`, both `Content-Encoding` and `Content-Length` are removed from the
1229 /// headers' set. The response body is automatically decompressed.
1230 ///
1231 /// If the `gzip` feature is turned on, the default option is enabled.
1232 ///
1233 /// # Optional
1234 ///
1235 /// This requires the optional `gzip` feature to be enabled
1236 #[cfg(feature = "gzip")]
1237 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1238 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1239 self.config.accepts.gzip = enable;
1240 self
1241 }
1242
1243 /// Enable auto brotli decompression by checking the `Content-Encoding` response header.
1244 ///
1245 /// If auto brotli decompression is turned on:
1246 ///
1247 /// - When sending a request and if the request's headers do not already contain
1248 /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `br`.
1249 /// The request body is **not** automatically compressed.
1250 /// - When receiving a response, if its headers contain a `Content-Encoding` value of
1251 /// `br`, both `Content-Encoding` and `Content-Length` are removed from the
1252 /// headers' set. The response body is automatically decompressed.
1253 ///
1254 /// If the `brotli` feature is turned on, the default option is enabled.
1255 ///
1256 /// # Optional
1257 ///
1258 /// This requires the optional `brotli` feature to be enabled
1259 #[cfg(feature = "brotli")]
1260 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1261 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1262 self.config.accepts.brotli = enable;
1263 self
1264 }
1265
1266 /// Enable auto zstd decompression by checking the `Content-Encoding` response header.
1267 ///
1268 /// If auto zstd decompression is turned on:
1269 ///
1270 /// - When sending a request and if the request's headers do not already contain
1271 /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `zstd`.
1272 /// The request body is **not** automatically compressed.
1273 /// - When receiving a response, if its headers contain a `Content-Encoding` value of
1274 /// `zstd`, both `Content-Encoding` and `Content-Length` are removed from the
1275 /// headers' set. The response body is automatically decompressed.
1276 ///
1277 /// If the `zstd` feature is turned on, the default option is enabled.
1278 ///
1279 /// # Optional
1280 ///
1281 /// This requires the optional `zstd` feature to be enabled
1282 #[cfg(feature = "zstd")]
1283 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1284 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1285 self.config.accepts.zstd = enable;
1286 self
1287 }
1288
1289 /// Enable auto deflate decompression by checking the `Content-Encoding` response header.
1290 ///
1291 /// If auto deflate decompression is turned on:
1292 ///
1293 /// - When sending a request and if the request's headers do not already contain
1294 /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `deflate`.
1295 /// The request body is **not** automatically compressed.
1296 /// - When receiving a response, if it's headers contain a `Content-Encoding` value that
1297 /// equals to `deflate`, both values `Content-Encoding` and `Content-Length` are removed from the
1298 /// headers' set. The response body is automatically decompressed.
1299 ///
1300 /// If the `deflate` feature is turned on, the default option is enabled.
1301 ///
1302 /// # Optional
1303 ///
1304 /// This requires the optional `deflate` feature to be enabled
1305 #[cfg(feature = "deflate")]
1306 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1307 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1308 self.config.accepts.deflate = enable;
1309 self
1310 }
1311
1312 /// Disable auto response body gzip decompression.
1313 ///
1314 /// This method exists even if the optional `gzip` feature is not enabled.
1315 /// This can be used to ensure a `Client` doesn't use gzip decompression
1316 /// even if another dependency were to enable the optional `gzip` feature.
1317 pub fn no_gzip(self) -> ClientBuilder {
1318 #[cfg(feature = "gzip")]
1319 {
1320 self.gzip(false)
1321 }
1322
1323 #[cfg(not(feature = "gzip"))]
1324 {
1325 self
1326 }
1327 }
1328
1329 /// Disable auto response body brotli decompression.
1330 ///
1331 /// This method exists even if the optional `brotli` feature is not enabled.
1332 /// This can be used to ensure a `Client` doesn't use brotli decompression
1333 /// even if another dependency were to enable the optional `brotli` feature.
1334 pub fn no_brotli(self) -> ClientBuilder {
1335 #[cfg(feature = "brotli")]
1336 {
1337 self.brotli(false)
1338 }
1339
1340 #[cfg(not(feature = "brotli"))]
1341 {
1342 self
1343 }
1344 }
1345
1346 /// Disable auto response body zstd decompression.
1347 ///
1348 /// This method exists even if the optional `zstd` feature is not enabled.
1349 /// This can be used to ensure a `Client` doesn't use zstd decompression
1350 /// even if another dependency were to enable the optional `zstd` feature.
1351 pub fn no_zstd(self) -> ClientBuilder {
1352 #[cfg(feature = "zstd")]
1353 {
1354 self.zstd(false)
1355 }
1356
1357 #[cfg(not(feature = "zstd"))]
1358 {
1359 self
1360 }
1361 }
1362
1363 /// Disable auto response body deflate decompression.
1364 ///
1365 /// This method exists even if the optional `deflate` feature is not enabled.
1366 /// This can be used to ensure a `Client` doesn't use deflate decompression
1367 /// even if another dependency were to enable the optional `deflate` feature.
1368 pub fn no_deflate(self) -> ClientBuilder {
1369 #[cfg(feature = "deflate")]
1370 {
1371 self.deflate(false)
1372 }
1373
1374 #[cfg(not(feature = "deflate"))]
1375 {
1376 self
1377 }
1378 }
1379
1380 // Redirect options
1381
1382 /// Set a `RedirectPolicy` for this client.
1383 ///
1384 /// Default will follow redirects up to a maximum of 10.
1385 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1386 self.config.redirect_policy = policy;
1387 self
1388 }
1389
1390 /// Enable or disable automatic setting of the `Referer` header.
1391 ///
1392 /// Default is `true`.
1393 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1394 self.config.referer = enable;
1395 self
1396 }
1397
1398 // Retry options
1399
1400 /// Set a request retry policy.
1401 ///
1402 /// Default behavior is to retry protocol NACKs.
1403 // XXX: accept an `impl retry::IntoPolicy` instead?
1404 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1405 self.config.retry_policy = policy;
1406 self
1407 }
1408
1409 // Proxy options
1410
1411 /// Add a `Proxy` to the list of proxies the `Client` will use.
1412 ///
1413 /// # Note
1414 ///
1415 /// Adding a proxy will disable the automatic usage of the "system" proxy.
1416 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1417 self.config.proxies.push(proxy.into_matcher());
1418 self.config.auto_sys_proxy = false;
1419 self
1420 }
1421
1422 /// Clear all `Proxies`, so `Client` will use no proxy anymore.
1423 ///
1424 /// # Note
1425 /// To add a proxy exclusion list, use [crate::proxy::Proxy::no_proxy()]
1426 /// on all desired proxies instead.
1427 ///
1428 /// This also disables the automatic usage of the "system" proxy.
1429 pub fn no_proxy(mut self) -> ClientBuilder {
1430 self.config.proxies.clear();
1431 self.config.auto_sys_proxy = false;
1432 self
1433 }
1434
1435 // Timeout options
1436
1437 /// Enables a total request timeout.
1438 ///
1439 /// The timeout is applied from when the request starts connecting until the
1440 /// response body has finished. Also considered a total deadline.
1441 ///
1442 /// Default is no timeout.
1443 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1444 self.config.timeout = Some(timeout);
1445 self
1446 }
1447
1448 /// Enables a read timeout.
1449 ///
1450 /// The timeout applies to each read operation, and resets after a
1451 /// successful read. This is more appropriate for detecting stalled
1452 /// connections when the size isn't known beforehand.
1453 ///
1454 /// Default is no timeout.
1455 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1456 self.config.read_timeout = Some(timeout);
1457 self
1458 }
1459
1460 /// Set a timeout for only the connect phase of a `Client`.
1461 ///
1462 /// Default is `None`.
1463 ///
1464 /// # Note
1465 ///
1466 /// This **requires** the futures be executed in a tokio runtime with
1467 /// a tokio timer enabled.
1468 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1469 self.config.connect_timeout = Some(timeout);
1470 self
1471 }
1472
1473 /// Set whether connections should emit verbose logs.
1474 ///
1475 /// Enabling this option will emit [log][] messages at the `TRACE` level
1476 /// for read and write operations on connections.
1477 ///
1478 /// [log]: https://crates.io/crates/log
1479 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1480 self.config.connection_verbose = verbose;
1481 self
1482 }
1483
1484 // HTTP options
1485
1486 /// Set an optional timeout for idle sockets being kept-alive.
1487 ///
1488 /// Pass `None` to disable timeout.
1489 ///
1490 /// Default is 90 seconds.
1491 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1492 where
1493 D: Into<Option<Duration>>,
1494 {
1495 self.config.pool_idle_timeout = val.into();
1496 self
1497 }
1498
1499 /// Sets the maximum idle connection per host allowed in the pool.
1500 ///
1501 /// Default is `usize::MAX` (no limit).
1502 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1503 self.config.pool_max_idle_per_host = max;
1504 self
1505 }
1506
1507 /// Send headers as title case instead of lowercase.
1508 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1509 self.config.http1_title_case_headers = true;
1510 self
1511 }
1512
1513 /// Set whether HTTP/1 connections will accept obsolete line folding for
1514 /// header values.
1515 ///
1516 /// Newline codepoints (`\r` and `\n`) will be transformed to spaces when
1517 /// parsing.
1518 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1519 mut self,
1520 value: bool,
1521 ) -> ClientBuilder {
1522 self.config
1523 .http1_allow_obsolete_multiline_headers_in_responses = value;
1524 self
1525 }
1526
1527 /// Sets whether invalid header lines should be silently ignored in HTTP/1 responses.
1528 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1529 self.config.http1_ignore_invalid_headers_in_responses = value;
1530 self
1531 }
1532
1533 /// Set whether HTTP/1 connections will accept spaces between header
1534 /// names and the colon that follow them in responses.
1535 ///
1536 /// Newline codepoints (`\r` and `\n`) will be transformed to spaces when
1537 /// parsing.
1538 pub fn http1_allow_spaces_after_header_name_in_responses(
1539 mut self,
1540 value: bool,
1541 ) -> ClientBuilder {
1542 self.config
1543 .http1_allow_spaces_after_header_name_in_responses = value;
1544 self
1545 }
1546
1547 /// Only use HTTP/1.
1548 pub fn http1_only(mut self) -> ClientBuilder {
1549 self.config.http_version_pref = HttpVersionPref::Http1;
1550 self
1551 }
1552
1553 /// Allow HTTP/0.9 responses
1554 pub fn http09_responses(mut self) -> ClientBuilder {
1555 self.config.http09_responses = true;
1556 self
1557 }
1558
1559 /// Only use HTTP/2.
1560 #[cfg(feature = "http2")]
1561 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1562 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1563 self.config.http_version_pref = HttpVersionPref::Http2;
1564 self
1565 }
1566
1567 /// Only use HTTP/3.
1568 #[cfg(feature = "http3")]
1569 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1570 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1571 self.config.http_version_pref = HttpVersionPref::Http3;
1572 self
1573 }
1574
1575 /// Sets the `SETTINGS_INITIAL_WINDOW_SIZE` option for HTTP2 stream-level flow control.
1576 ///
1577 /// Default may change internally to optimize for common uses.
1578 #[cfg(feature = "http2")]
1579 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1580 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1581 self.config.http2_initial_stream_window_size = sz.into();
1582 self
1583 }
1584
1585 /// Sets the max connection-level flow control for HTTP2
1586 ///
1587 /// Default may change internally to optimize for common uses.
1588 #[cfg(feature = "http2")]
1589 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1590 pub fn http2_initial_connection_window_size(
1591 mut self,
1592 sz: impl Into<Option<u32>>,
1593 ) -> ClientBuilder {
1594 self.config.http2_initial_connection_window_size = sz.into();
1595 self
1596 }
1597
1598 /// Sets whether to use an adaptive flow control.
1599 ///
1600 /// Enabling this will override the limits set in `http2_initial_stream_window_size` and
1601 /// `http2_initial_connection_window_size`.
1602 #[cfg(feature = "http2")]
1603 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1604 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1605 self.config.http2_adaptive_window = enabled;
1606 self
1607 }
1608
1609 /// Sets the maximum frame size to use for HTTP2.
1610 ///
1611 /// Default is currently 16,384 but may change internally to optimize for common uses.
1612 #[cfg(feature = "http2")]
1613 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1614 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1615 self.config.http2_max_frame_size = sz.into();
1616 self
1617 }
1618
1619 /// Sets the maximum size of received header frames for HTTP2.
1620 ///
1621 /// Default is currently 16KB, but can change.
1622 #[cfg(feature = "http2")]
1623 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1624 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1625 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1626 self
1627 }
1628
1629 /// Sets an interval for HTTP2 Ping frames should be sent to keep a connection alive.
1630 ///
1631 /// Pass `None` to disable HTTP2 keep-alive.
1632 /// Default is currently disabled.
1633 #[cfg(feature = "http2")]
1634 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1635 pub fn http2_keep_alive_interval(
1636 mut self,
1637 interval: impl Into<Option<Duration>>,
1638 ) -> ClientBuilder {
1639 self.config.http2_keep_alive_interval = interval.into();
1640 self
1641 }
1642
1643 /// Sets a timeout for receiving an acknowledgement of the keep-alive ping.
1644 ///
1645 /// If the ping is not acknowledged within the timeout, the connection will be closed.
1646 /// Does nothing if `http2_keep_alive_interval` is disabled.
1647 /// Default is currently disabled.
1648 #[cfg(feature = "http2")]
1649 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1650 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1651 self.config.http2_keep_alive_timeout = Some(timeout);
1652 self
1653 }
1654
1655 /// Sets whether HTTP2 keep-alive should apply while the connection is idle.
1656 ///
1657 /// If disabled, keep-alive pings are only sent while there are open request/responses streams.
1658 /// If enabled, pings are also sent when no streams are active.
1659 /// Does nothing if `http2_keep_alive_interval` is disabled.
1660 /// Default is `false`.
1661 #[cfg(feature = "http2")]
1662 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1663 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1664 self.config.http2_keep_alive_while_idle = enabled;
1665 self
1666 }
1667
1668 // TCP options
1669
1670 /// Set whether sockets have `TCP_NODELAY` enabled.
1671 ///
1672 /// Default is `true`.
1673 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1674 self.config.nodelay = enabled;
1675 self
1676 }
1677
1678 /// Bind to a local IP Address.
1679 ///
1680 /// # Example
1681 ///
1682 /// ```
1683 /// # fn doc() -> Result<(), reqwest::Error> {
1684 /// use std::net::IpAddr;
1685 /// let local_addr = IpAddr::from([12, 4, 1, 8]);
1686 /// let client = reqwest::Client::builder()
1687 /// .local_address(local_addr)
1688 /// .build()?;
1689 /// # Ok(())
1690 /// # }
1691 /// ```
1692 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1693 where
1694 T: Into<Option<IpAddr>>,
1695 {
1696 self.config.local_address = addr.into();
1697 self
1698 }
1699
1700 /// Bind connections only on the specified network interface.
1701 ///
1702 /// This option is only available on the following operating systems:
1703 ///
1704 /// - Android
1705 /// - Fuchsia
1706 /// - Linux,
1707 /// - macOS and macOS-like systems (iOS, tvOS, watchOS and visionOS)
1708 /// - Solaris and illumos
1709 ///
1710 /// On Android, Linux, and Fuchsia, this uses the
1711 /// [`SO_BINDTODEVICE`][man-7-socket] socket option. On macOS and macOS-like
1712 /// systems, Solaris, and illumos, this instead uses the [`IP_BOUND_IF` and
1713 /// `IPV6_BOUND_IF`][man-7p-ip] socket options (as appropriate).
1714 ///
1715 /// Note that connections will fail if the provided interface name is not a
1716 /// network interface that currently exists when a connection is established.
1717 ///
1718 /// # Example
1719 ///
1720 /// ```
1721 /// # fn doc() -> Result<(), reqwest::Error> {
1722 /// let interface = "lo";
1723 /// let client = reqwest::Client::builder()
1724 /// .interface(interface)
1725 /// .build()?;
1726 /// # Ok(())
1727 /// # }
1728 /// ```
1729 ///
1730 /// [man-7-socket]: https://man7.org/linux/man-pages/man7/socket.7.html
1731 /// [man-7p-ip]: https://docs.oracle.com/cd/E86824_01/html/E54777/ip-7p.html
1732 #[cfg(any(
1733 target_os = "android",
1734 target_os = "fuchsia",
1735 target_os = "illumos",
1736 target_os = "ios",
1737 target_os = "linux",
1738 target_os = "macos",
1739 target_os = "solaris",
1740 target_os = "tvos",
1741 target_os = "visionos",
1742 target_os = "watchos",
1743 ))]
1744 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1745 self.config.interface = Some(interface.to_string());
1746 self
1747 }
1748
1749 /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration.
1750 ///
1751 /// If `None`, the option will not be set.
1752 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1753 where
1754 D: Into<Option<Duration>>,
1755 {
1756 self.config.tcp_keepalive = val.into();
1757 self
1758 }
1759
1760 /// Set that all sockets have `SO_KEEPALIVE` set with the supplied interval.
1761 ///
1762 /// If `None`, the option will not be set.
1763 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1764 where
1765 D: Into<Option<Duration>>,
1766 {
1767 self.config.tcp_keepalive_interval = val.into();
1768 self
1769 }
1770
1771 /// Set that all sockets have `SO_KEEPALIVE` set with the supplied retry count.
1772 ///
1773 /// If `None`, the option will not be set.
1774 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1775 where
1776 C: Into<Option<u32>>,
1777 {
1778 self.config.tcp_keepalive_retries = retries.into();
1779 self
1780 }
1781
1782 /// Set that all sockets have `TCP_USER_TIMEOUT` set with the supplied duration.
1783 ///
1784 /// This option controls how long transmitted data may remain unacknowledged before
1785 /// the connection is force-closed.
1786 ///
1787 /// If `None`, the option will not be set.
1788 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1789 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1790 where
1791 D: Into<Option<Duration>>,
1792 {
1793 self.config.tcp_user_timeout = val.into();
1794 self
1795 }
1796
1797 // Alt Transports
1798
1799 /// Set that all connections will use this Unix socket.
1800 ///
1801 /// If a request URI uses the `https` scheme, TLS will still be used over
1802 /// the Unix socket.
1803 ///
1804 /// # Note
1805 ///
1806 /// This option is not compatible with any of the TCP or Proxy options.
1807 /// Setting this will ignore all those options previously set.
1808 ///
1809 /// Likewise, DNS resolution will not be done on the domain name.
1810 #[cfg(unix)]
1811 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1812 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1813 self
1814 }
1815
1816 /// Set that all connections will use this Windows named pipe.
1817 ///
1818 /// If a request URI uses the `https` scheme, TLS will still be used over
1819 /// the Windows named pipe.
1820 ///
1821 /// # Note
1822 ///
1823 /// This option is not compatible with any of the TCP or Proxy options.
1824 /// Setting this will ignore all those options previously set.
1825 ///
1826 /// Likewise, DNS resolution will not be done on the domain name.
1827 #[cfg(target_os = "windows")]
1828 pub fn windows_named_pipe(mut self, pipe: impl WindowsNamedPipeProvider) -> ClientBuilder {
1829 self.config.windows_named_pipe = Some(
1830 pipe.reqwest_windows_named_pipe_path(crate::connect::windows_named_pipe::Internal)
1831 .into(),
1832 );
1833 self
1834 }
1835
1836 // TLS options
1837
1838 /// Add custom certificate roots.
1839 ///
1840 /// This can be used to connect to a server that has a self-signed
1841 /// certificate for example.
1842 ///
1843 /// This optional attempts to merge with any native or built-in roots.
1844 ///
1845 /// # Errors
1846 ///
1847 /// If the selected TLS backend or verifier does not support merging
1848 /// certificates, the builder will return an error.
1849 ///
1850 /// # Optional
1851 ///
1852 /// This requires the optional `default-tls`, `native-tls`, or `rustls(-...)`
1853 /// feature to be enabled.
1854 #[cfg(feature = "__tls")]
1855 #[cfg_attr(
1856 docsrs,
1857 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1858 )]
1859 pub fn tls_certs_merge(
1860 mut self,
1861 certs: impl IntoIterator<Item = Certificate>,
1862 ) -> ClientBuilder {
1863 self.config.root_certs.extend(certs);
1864 self
1865 }
1866
1867 /// Use only the provided certificate roots.
1868 ///
1869 /// This can be used to connect to a server that has a self-signed
1870 /// certificate for example.
1871 ///
1872 /// This option disables any native or built-in roots, and **only** uses
1873 /// the roots provided to this method.
1874 ///
1875 /// # Optional
1876 ///
1877 /// This requires the optional `default-tls`, `native-tls`, or `rustls(-...)`
1878 /// feature to be enabled.
1879 #[cfg(feature = "__tls")]
1880 #[cfg_attr(
1881 docsrs,
1882 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1883 )]
1884 pub fn tls_certs_only(mut self, certs: impl IntoIterator<Item = Certificate>) -> ClientBuilder {
1885 self.config.root_certs.extend(certs);
1886 self.config.tls_certs_only = true;
1887 self
1888 }
1889
1890 /// Deprecated: use [`ClientBuilder::tls_certs_merge()`] or
1891 /// [`ClientBuilder::tls_certs_only()`] instead.
1892 #[cfg(feature = "__tls")]
1893 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1894 self.config.root_certs.push(cert);
1895 self
1896 }
1897
1898 /// Add multiple certificate revocation lists.
1899 ///
1900 /// # Errors
1901 ///
1902 /// This only works if also using only provided root certificates. This
1903 /// cannot work with the native verifier.
1904 ///
1905 /// If CRLs are added but `tls_certs_only()` is not called, the builder
1906 /// will return an error.
1907 ///
1908 /// # Optional
1909 ///
1910 /// This requires the `rustls(-...)` Cargo feature enabled.
1911 #[cfg(feature = "__rustls")]
1912 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1913 pub fn tls_crls_only(
1914 mut self,
1915 crls: impl IntoIterator<Item = CertificateRevocationList>,
1916 ) -> ClientBuilder {
1917 self.config.crls.extend(crls);
1918 self
1919 }
1920
1921 /// Deprecated: use [`ClientBuilder::tls_crls_only()`] instead.
1922 #[cfg(feature = "__rustls")]
1923 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1924 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1925 self.config.crls.push(crl);
1926 self
1927 }
1928
1929 /// Deprecated: use [`ClientBuilder::tls_crls_only()`] instead.
1930 #[cfg(feature = "__rustls")]
1931 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1932 pub fn add_crls(
1933 mut self,
1934 crls: impl IntoIterator<Item = CertificateRevocationList>,
1935 ) -> ClientBuilder {
1936 self.config.crls.extend(crls);
1937 self
1938 }
1939
1940 /// Sets the identity to be used for client certificate authentication.
1941 ///
1942 /// # Optional
1943 ///
1944 /// This requires the optional `native-tls` or `rustls(-...)` feature to be
1945 /// enabled.
1946 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
1947 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
1948 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1949 self.config.identity = Some(identity);
1950 self
1951 }
1952
1953 /// Controls the use of hostname verification.
1954 ///
1955 /// Defaults to `false`.
1956 ///
1957 /// # Warning
1958 ///
1959 /// You should think very carefully before you use this method. If
1960 /// hostname verification is not used, any valid certificate for any
1961 /// site will be trusted for use from any other. This introduces a
1962 /// significant vulnerability to man-in-the-middle attacks.
1963 ///
1964 /// # Errors
1965 ///
1966 /// Depending on the TLS backend and verifier, this might not work with
1967 /// native certificates, only those added with [`ClientBuilder::tls_certs_only()`].
1968 ///
1969 /// # Optional
1970 ///
1971 /// This requires the optional `default-tls`, `native-tls`, or `rustls(-...)`
1972 /// feature to be enabled.
1973 #[cfg(feature = "__tls")]
1974 #[cfg_attr(
1975 docsrs,
1976 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1977 )]
1978 pub fn tls_danger_accept_invalid_hostnames(
1979 mut self,
1980 accept_invalid_hostname: bool,
1981 ) -> ClientBuilder {
1982 self.config.hostname_verification = !accept_invalid_hostname;
1983 self
1984 }
1985
1986 /// Deprecated: use [`ClientBuilder::tls_danger_accept_invalid_hostnames()`] instead.
1987 #[cfg(feature = "__tls")]
1988 pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder {
1989 self.tls_danger_accept_invalid_hostnames(accept_invalid_hostname)
1990 }
1991
1992 /// Controls the use of certificate validation.
1993 ///
1994 /// Defaults to `false`.
1995 ///
1996 /// # Warning
1997 ///
1998 /// You should think very carefully before using this method. If
1999 /// invalid certificates are trusted, *any* certificate for *any* site
2000 /// will be trusted for use. This includes expired certificates. This
2001 /// introduces significant vulnerabilities, and should only be used
2002 /// as a last resort.
2003 ///
2004 /// # Optional
2005 ///
2006 /// This requires the optional `default-tls`, `native-tls`, or `rustls(-...)`
2007 /// feature to be enabled.
2008 #[cfg(feature = "__tls")]
2009 #[cfg_attr(
2010 docsrs,
2011 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2012 )]
2013 pub fn tls_danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
2014 self.config.certs_verification = !accept_invalid_certs;
2015 self
2016 }
2017
2018 /// Deprecated: use [`ClientBuilder::tls_danger_accept_invalid_certs()`] instead.
2019 #[cfg(feature = "__tls")]
2020 pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder {
2021 self.tls_danger_accept_invalid_certs(accept_invalid_certs)
2022 }
2023
2024 /// Controls the use of TLS server name indication.
2025 ///
2026 /// Defaults to `true`.
2027 ///
2028 /// # Optional
2029 ///
2030 /// This requires the optional `default-tls`, `native-tls`, or `rustls(-...)`
2031 /// feature to be enabled.
2032 #[cfg(feature = "__tls")]
2033 #[cfg_attr(
2034 docsrs,
2035 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2036 )]
2037 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
2038 self.config.tls_sni = tls_sni;
2039 self
2040 }
2041
2042 /// Controls if the SSLKEYLOGFILE environment variable is respected.
2043 ///
2044 /// When enabled, if the environment variable `SSLKEYLOGFILE` is present at runtime,
2045 /// TLS keys will be logged to the file at the path described in the variable.
2046 /// This can be used by end-users to allow debugging TLS connections.
2047 ///
2048 /// Defaults to `false`.
2049 ///
2050 /// # Optional
2051 ///
2052 /// This requires the `rustls(-...)` Cargo feature enabled.
2053 #[cfg(feature = "__rustls")]
2054 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2055 pub fn tls_sslkeylogfile(mut self, on: bool) -> ClientBuilder {
2056 self.config.tls_sslkeylogfile = on;
2057 self
2058 }
2059
2060 /// Set the minimum required TLS version for connections.
2061 ///
2062 /// By default, the TLS backend's own default is used.
2063 ///
2064 /// # Errors
2065 ///
2066 /// A value of `tls::Version::TLS_1_3` will cause an error with the
2067 /// `native-tls` backend. This does not mean the version
2068 /// isn't supported, just that it can't be set as a minimum due to
2069 /// technical limitations.
2070 ///
2071 /// # Optional
2072 ///
2073 /// This requires the optional `default-tls`, `native-tls`, or `rustls(-...)`
2074 /// feature to be enabled.
2075 #[cfg(feature = "__tls")]
2076 #[cfg_attr(
2077 docsrs,
2078 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2079 )]
2080 pub fn tls_version_min(mut self, version: tls::Version) -> ClientBuilder {
2081 self.config.min_tls_version = Some(version);
2082 self
2083 }
2084
2085 /// Deprecated: use [`ClientBuilder::tls_version_min()`] instead.
2086 #[cfg(feature = "__tls")]
2087 pub fn min_tls_version(self, version: tls::Version) -> ClientBuilder {
2088 self.tls_version_min(version)
2089 }
2090
2091 /// Set the maximum allowed TLS version for connections.
2092 ///
2093 /// By default, there's no maximum.
2094 ///
2095 /// # Errors
2096 ///
2097 /// A value of `tls::Version::TLS_1_3` will cause an error with the
2098 /// `native-tls` backend. This does not mean the version
2099 /// isn't supported, just that it can't be set as a maximum due to
2100 /// technical limitations.
2101 ///
2102 /// Cannot set a maximum outside the protocol versions supported by
2103 /// `rustls` with the `rustls` backend.
2104 ///
2105 /// # Optional
2106 ///
2107 /// This requires the optional `default-tls`, `native-tls`, or `rustls(-...)`
2108 /// feature to be enabled.
2109 #[cfg(feature = "__tls")]
2110 #[cfg_attr(
2111 docsrs,
2112 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2113 )]
2114 pub fn tls_version_max(mut self, version: tls::Version) -> ClientBuilder {
2115 self.config.max_tls_version = Some(version);
2116 self
2117 }
2118
2119 /// Deprecated: use [`ClientBuilder::tls_version_max()`] instead.
2120 #[cfg(feature = "__tls")]
2121 pub fn max_tls_version(self, version: tls::Version) -> ClientBuilder {
2122 self.tls_version_max(version)
2123 }
2124
2125 /// Force using the native TLS backend.
2126 ///
2127 /// Since multiple TLS backends can be optionally enabled, this option will
2128 /// force the `native-tls` backend to be used for this `Client`.
2129 ///
2130 /// # Optional
2131 ///
2132 /// This requires the optional `native-tls` feature to be enabled.
2133 #[cfg(feature = "__native-tls")]
2134 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2135 pub fn tls_backend_native(mut self) -> ClientBuilder {
2136 self.config.tls = TlsBackend::NativeTls;
2137 self
2138 }
2139
2140 /// Deprecated: use [`ClientBuilder::tls_backend_native()`] instead.
2141 #[cfg(feature = "__native-tls")]
2142 pub fn use_native_tls(self) -> ClientBuilder {
2143 self.tls_backend_native()
2144 }
2145
2146 /// Force using the Rustls TLS backend.
2147 ///
2148 /// Since multiple TLS backends can be optionally enabled, this option will
2149 /// force the `rustls` backend to be used for this `Client`.
2150 ///
2151 /// # Optional
2152 ///
2153 /// This requires the optional `rustls(-...)` feature to be enabled.
2154 #[cfg(feature = "__rustls")]
2155 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2156 pub fn tls_backend_rustls(mut self) -> ClientBuilder {
2157 self.config.tls = TlsBackend::Rustls;
2158 self
2159 }
2160
2161 /// Deprecated: use [`ClientBuilder::tls_backend_rustls()`] instead.
2162 #[cfg(feature = "__rustls")]
2163 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2164 pub fn use_rustls_tls(self) -> ClientBuilder {
2165 self.tls_backend_rustls()
2166 }
2167
2168 /// Use a preconfigured TLS backend.
2169 ///
2170 /// If the passed `Any` argument is not a TLS backend that reqwest
2171 /// understands, the `ClientBuilder` will error when calling `build`.
2172 ///
2173 /// # Advanced
2174 ///
2175 /// <div class="warning">
2176 ///
2177 /// There is no semver stability on the internals of this method. Use at
2178 /// your own risk.
2179 ///
2180 /// </div>
2181 ///
2182 /// This is an advanced option, and can be somewhat brittle. Usage requires
2183 /// keeping the preconfigured TLS argument version in sync with reqwest,
2184 /// since version mismatches will result in an "unknown" TLS backend.
2185 ///
2186 /// If possible, it's preferable to use the methods on `ClientBuilder`
2187 /// to configure reqwest's TLS.
2188 ///
2189 /// # Optional
2190 ///
2191 /// This requires one of the optional features `native-tls` or
2192 /// `rustls(-...)` to be enabled.
2193 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2194 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
2195 pub fn tls_backend_preconfigured(mut self, tls: impl Any) -> ClientBuilder {
2196 let mut tls = Some(tls);
2197 #[cfg(feature = "__native-tls")]
2198 {
2199 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2200 let tls = conn.take().expect("is definitely Some");
2201 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2202 self.config.tls = tls;
2203 return self;
2204 }
2205 }
2206 #[cfg(feature = "__rustls")]
2207 {
2208 if let Some(conn) =
2209 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2210 {
2211 let tls = conn.take().expect("is definitely Some");
2212 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2213 self.config.tls = tls;
2214 return self;
2215 }
2216 }
2217
2218 // Otherwise, we don't recognize the TLS backend!
2219 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2220 self
2221 }
2222
2223 /// Deprecated: use [`ClientBuilder::tls_backend_preconfigured()`] instead.
2224 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2225 pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder {
2226 self.tls_backend_preconfigured(tls)
2227 }
2228
2229 /// Add TLS information as `TlsInfo` extension to responses.
2230 ///
2231 /// # Optional
2232 ///
2233 /// This requires the optional `default-tls`, `native-tls`, or `rustls(-...)`
2234 /// feature to be enabled.
2235 #[cfg(feature = "__tls")]
2236 #[cfg_attr(
2237 docsrs,
2238 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2239 )]
2240 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2241 self.config.tls_info = tls_info;
2242 self
2243 }
2244
2245 /// Restrict the Client to be used with HTTPS only requests.
2246 ///
2247 /// Defaults to false.
2248 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2249 self.config.https_only = enabled;
2250 self
2251 }
2252
2253 /// Enables the [hickory-dns](hickory_resolver) async resolver instead of a default threadpool
2254 /// using `getaddrinfo`.
2255 ///
2256 /// If the `hickory-dns` feature is turned on, the default option is enabled.
2257 ///
2258 /// # Optional
2259 ///
2260 /// This requires the optional `hickory-dns` feature to be enabled
2261 ///
2262 /// # Warning
2263 ///
2264 /// The hickory resolver does not work exactly the same, or on all the platforms
2265 /// that the default resolver does
2266 #[cfg(feature = "hickory-dns")]
2267 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2268 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2269 self.config.hickory_dns = enable;
2270 self
2271 }
2272
2273 /// Disables the hickory-dns async resolver.
2274 ///
2275 /// This method exists even if the optional `hickory-dns` feature is not enabled.
2276 /// This can be used to ensure a `Client` doesn't use the hickory-dns async resolver
2277 /// even if another dependency were to enable the optional `hickory-dns` feature.
2278 pub fn no_hickory_dns(self) -> ClientBuilder {
2279 #[cfg(feature = "hickory-dns")]
2280 {
2281 self.hickory_dns(false)
2282 }
2283
2284 #[cfg(not(feature = "hickory-dns"))]
2285 {
2286 self
2287 }
2288 }
2289
2290 /// Override DNS resolution for specific domains to a particular IP address.
2291 ///
2292 /// Set the port to `0` to use the conventional port for the given scheme (e.g. 80 for http).
2293 /// Ports in the URL itself will always be used instead of the port in the overridden addr.
2294 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2295 self.resolve_to_addrs(domain, &[addr])
2296 }
2297
2298 /// Override DNS resolution for specific domains to particular IP addresses.
2299 ///
2300 /// Set the port to `0` to use the conventional port for the given scheme (e.g. 80 for http).
2301 /// Ports in the URL itself will always be used instead of the port in the overridden addr.
2302 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2303 self.config
2304 .dns_overrides
2305 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2306 self
2307 }
2308
2309 /// Override the DNS resolver implementation.
2310 ///
2311 /// Overrides for specific names passed to `resolve` and `resolve_to_addrs` will
2312 /// still be applied on top of this resolver.
2313 pub fn dns_resolver<R>(mut self, resolver: R) -> ClientBuilder
2314 where
2315 R: crate::dns::resolve::IntoResolve,
2316 {
2317 self.config.dns_resolver = Some(resolver.into_resolve());
2318 self
2319 }
2320
2321 /// Whether to send data on the first flight ("early data") in TLS 1.3 handshakes
2322 /// for HTTP/3 connections.
2323 ///
2324 /// The default is false.
2325 #[cfg(feature = "http3")]
2326 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2327 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2328 self.config.tls_enable_early_data = enabled;
2329 self
2330 }
2331
2332 /// Maximum duration of inactivity to accept before timing out the QUIC connection.
2333 ///
2334 /// Please see docs in [`TransportConfig`] in [`quinn`].
2335 ///
2336 /// [`TransportConfig`]: https://docs.rs/quinn/latest/quinn/struct.TransportConfig.html
2337 #[cfg(feature = "http3")]
2338 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2339 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2340 self.config.quic_max_idle_timeout = Some(value);
2341 self
2342 }
2343
2344 /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream
2345 /// before becoming blocked.
2346 ///
2347 /// Please see docs in [`TransportConfig`] in [`quinn`].
2348 ///
2349 /// [`TransportConfig`]: https://docs.rs/quinn/latest/quinn/struct.TransportConfig.html
2350 ///
2351 /// # Panics
2352 ///
2353 /// Panics if the value is over 2^62.
2354 #[cfg(feature = "http3")]
2355 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2356 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2357 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2358 self
2359 }
2360
2361 /// Maximum number of bytes the peer may transmit across all streams of a connection before
2362 /// becoming blocked.
2363 ///
2364 /// Please see docs in [`TransportConfig`] in [`quinn`].
2365 ///
2366 /// [`TransportConfig`]: https://docs.rs/quinn/latest/quinn/struct.TransportConfig.html
2367 ///
2368 /// # Panics
2369 ///
2370 /// Panics if the value is over 2^62.
2371 #[cfg(feature = "http3")]
2372 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2373 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2374 self.config.quic_receive_window = Some(value.try_into().unwrap());
2375 self
2376 }
2377
2378 /// Maximum number of bytes to transmit to a peer without acknowledgment
2379 ///
2380 /// Please see docs in [`TransportConfig`] in [`quinn`].
2381 ///
2382 /// [`TransportConfig`]: https://docs.rs/quinn/latest/quinn/struct.TransportConfig.html
2383 #[cfg(feature = "http3")]
2384 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2385 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2386 self.config.quic_send_window = Some(value);
2387 self
2388 }
2389
2390 /// Override the default congestion control algorithm to use [BBR]
2391 ///
2392 /// The current default congestion control algorithm is [CUBIC]. This method overrides the
2393 /// default.
2394 ///
2395 /// [BBR]: https://datatracker.ietf.org/doc/html/draft-ietf-ccwg-bbr
2396 /// [CUBIC]: https://datatracker.ietf.org/doc/html/rfc8312
2397 #[cfg(feature = "http3")]
2398 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2399 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2400 self.config.quic_congestion_bbr = true;
2401 self
2402 }
2403
2404 /// Set the maximum HTTP/3 header size this client is willing to accept.
2405 ///
2406 /// See [header size constraints] section of the specification for details.
2407 ///
2408 /// [header size constraints]: https://www.rfc-editor.org/rfc/rfc9114.html#name-header-size-constraints
2409 ///
2410 /// Please see docs in [`Builder`] in [`h3`].
2411 ///
2412 /// [`Builder`]: https://docs.rs/h3/latest/h3/client/struct.Builder.html#method.max_field_section_size
2413 #[cfg(feature = "http3")]
2414 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2415 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2416 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2417 self
2418 }
2419
2420 /// Enable whether to send HTTP/3 protocol grease on the connections.
2421 ///
2422 /// HTTP/3 uses the concept of "grease"
2423 ///
2424 /// to prevent potential interoperability issues in the future.
2425 /// In HTTP/3, the concept of grease is used to ensure that the protocol can evolve
2426 /// and accommodate future changes without breaking existing implementations.
2427 ///
2428 /// Please see docs in [`Builder`] in [`h3`].
2429 ///
2430 /// [`Builder`]: https://docs.rs/h3/latest/h3/client/struct.Builder.html#method.send_grease
2431 #[cfg(feature = "http3")]
2432 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2433 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2434 self.config.h3_send_grease = Some(enabled);
2435 self
2436 }
2437
2438 /// Adds a new Tower [`Layer`](https://docs.rs/tower/latest/tower/trait.Layer.html) to the
2439 /// base connector [`Service`](https://docs.rs/tower/latest/tower/trait.Service.html) which
2440 /// is responsible for connection establishment.
2441 ///
2442 /// Each subsequent invocation of this function will wrap previous layers.
2443 ///
2444 /// If configured, the `connect_timeout` will be the outermost layer.
2445 ///
2446 /// Example usage:
2447 /// ```
2448 /// use std::time::Duration;
2449 ///
2450 /// # #[cfg(not(feature = "rustls-no-provider"))]
2451 /// let client = reqwest::Client::builder()
2452 /// // resolved to outermost layer, meaning while we are waiting on concurrency limit
2453 /// .connect_timeout(Duration::from_millis(200))
2454 /// // underneath the concurrency check, so only after concurrency limit lets us through
2455 /// .connector_layer(tower::timeout::TimeoutLayer::new(Duration::from_millis(50)))
2456 /// .connector_layer(tower::limit::concurrency::ConcurrencyLimitLayer::new(2))
2457 /// .build()
2458 /// .unwrap();
2459 /// ```
2460 ///
2461 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2462 where
2463 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2464 L::Service:
2465 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2466 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2467 {
2468 let layer = BoxCloneSyncServiceLayer::new(layer);
2469
2470 self.config.connector_layers.push(layer);
2471
2472 self
2473 }
2474}
2475
2476type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2477
2478impl Default for Client {
2479 fn default() -> Self {
2480 Self::new()
2481 }
2482}
2483
2484#[cfg(feature = "__rustls")]
2485fn default_rustls_crypto_provider() -> Arc<rustls::crypto::CryptoProvider> {
2486 #[cfg(not(feature = "__rustls-aws-lc-rs"))]
2487 panic!(
2488 "No rustls crypto provider is configured. \
2489 When using the `rustls-no-provider` feature you must install a \
2490 crypto provider before building a Client. For example: \
2491 `rustls::crypto::aws_lc_rs::default_provider().install_default().unwrap();` \
2492 See https://docs.rs/rustls/latest/rustls/#cryptography-providers for details."
2493 );
2494
2495 #[cfg(feature = "__rustls-aws-lc-rs")]
2496 Arc::new(rustls::crypto::aws_lc_rs::default_provider())
2497}
2498
2499impl Client {
2500 /// Constructs a new `Client`.
2501 ///
2502 /// # Panics
2503 ///
2504 /// This method panics if a TLS backend cannot be initialized, or the resolver
2505 /// cannot load the system configuration.
2506 ///
2507 /// Use `Client::builder()` if you wish to handle the failure as an `Error`
2508 /// instead of panicking.
2509 pub fn new() -> Client {
2510 ClientBuilder::new().build().expect("Client::new()")
2511 }
2512
2513 /// Creates a `ClientBuilder` to configure a `Client`.
2514 ///
2515 /// This is the same as `ClientBuilder::new()`.
2516 pub fn builder() -> ClientBuilder {
2517 ClientBuilder::new()
2518 }
2519
2520 /// Convenience method to make a `GET` request to a URL.
2521 ///
2522 /// # Errors
2523 ///
2524 /// This method fails whenever the supplied `Url` cannot be parsed.
2525 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2526 self.request(Method::GET, url)
2527 }
2528
2529 /// Convenience method to make a `POST` request to a URL.
2530 ///
2531 /// # Errors
2532 ///
2533 /// This method fails whenever the supplied `Url` cannot be parsed.
2534 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2535 self.request(Method::POST, url)
2536 }
2537
2538 /// Convenience method to make a `PUT` request to a URL.
2539 ///
2540 /// # Errors
2541 ///
2542 /// This method fails whenever the supplied `Url` cannot be parsed.
2543 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2544 self.request(Method::PUT, url)
2545 }
2546
2547 /// Convenience method to make a `PATCH` request to a URL.
2548 ///
2549 /// # Errors
2550 ///
2551 /// This method fails whenever the supplied `Url` cannot be parsed.
2552 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2553 self.request(Method::PATCH, url)
2554 }
2555
2556 /// Convenience method to make a `DELETE` request to a URL.
2557 ///
2558 /// # Errors
2559 ///
2560 /// This method fails whenever the supplied `Url` cannot be parsed.
2561 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2562 self.request(Method::DELETE, url)
2563 }
2564
2565 /// Convenience method to make a `HEAD` request to a URL.
2566 ///
2567 /// # Errors
2568 ///
2569 /// This method fails whenever the supplied `Url` cannot be parsed.
2570 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2571 self.request(Method::HEAD, url)
2572 }
2573
2574 /// Start building a `Request` with the `Method` and `Url`.
2575 ///
2576 /// Returns a `RequestBuilder`, which will allow setting headers and
2577 /// the request body before sending.
2578 ///
2579 /// # Errors
2580 ///
2581 /// This method fails whenever the supplied `Url` cannot be parsed.
2582 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2583 let req = url.into_url().map(move |url| Request::new(method, url));
2584 RequestBuilder::new(self.clone(), req)
2585 }
2586
2587 /// Executes a `Request`.
2588 ///
2589 /// A `Request` can be built manually with `Request::new()` or obtained
2590 /// from a RequestBuilder with `RequestBuilder::build()`.
2591 ///
2592 /// You should prefer to use the `RequestBuilder` and
2593 /// `RequestBuilder::send()`.
2594 ///
2595 /// # Errors
2596 ///
2597 /// This method fails if there was an error while sending request,
2598 /// redirect loop was detected or redirect limit was exhausted.
2599 pub fn execute(
2600 &self,
2601 request: Request,
2602 ) -> impl Future<Output = Result<Response, crate::Error>> {
2603 self.execute_request(request)
2604 }
2605
2606 pub(super) fn execute_request(&self, req: Request) -> Pending {
2607 let (method, url, mut headers, body, version, extensions) = req.pieces();
2608 if url.scheme() != "http" && url.scheme() != "https" {
2609 return Pending::new_err(error::url_bad_scheme(url));
2610 }
2611
2612 // check if we're in https_only mode and check the scheme of the current URL
2613 if self.inner.https_only && url.scheme() != "https" {
2614 return Pending::new_err(error::url_bad_scheme(url));
2615 }
2616
2617 // insert default headers in the request headers
2618 // without overwriting already appended headers.
2619 for (key, value) in &self.inner.headers {
2620 if let Entry::Vacant(entry) = headers.entry(key) {
2621 entry.insert(value.clone());
2622 }
2623 }
2624
2625 let uri = match try_uri(&url) {
2626 Ok(uri) => uri,
2627 _ => return Pending::new_err(error::url_invalid_uri(url)),
2628 };
2629
2630 let body = body.unwrap_or_else(Body::empty);
2631
2632 self.proxy_auth(&uri, &mut headers);
2633 self.proxy_custom_headers(&uri, &mut headers);
2634
2635 let builder = hyper::Request::builder()
2636 .method(method.clone())
2637 .uri(uri)
2638 .version(version);
2639
2640 let in_flight = match version {
2641 #[cfg(feature = "http3")]
2642 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2643 let mut req = builder.body(body).expect("valid request parts");
2644 *req.headers_mut() = headers.clone();
2645 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2646 ResponseFuture::H3(h3.call(req))
2647 }
2648 _ => {
2649 let mut req = builder.body(body).expect("valid request parts");
2650 *req.headers_mut() = headers.clone();
2651 let mut hyper = self.inner.hyper.clone();
2652 ResponseFuture::Default(hyper.call(req))
2653 }
2654 };
2655
2656 let total_timeout = self
2657 .inner
2658 .total_timeout
2659 .fetch(&extensions)
2660 .copied()
2661 .map(tokio::time::sleep)
2662 .map(Box::pin);
2663
2664 let read_timeout_fut = self
2665 .inner
2666 .read_timeout
2667 .map(tokio::time::sleep)
2668 .map(Box::pin);
2669
2670 Pending {
2671 inner: PendingInner::Request(Box::pin(PendingRequest {
2672 method,
2673 url,
2674 headers,
2675
2676 client: self.inner.clone(),
2677
2678 in_flight,
2679 total_timeout,
2680 read_timeout_fut,
2681 read_timeout: self.inner.read_timeout,
2682 })),
2683 }
2684 }
2685
2686 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2687 if !self.inner.proxies_maybe_http_auth {
2688 return;
2689 }
2690
2691 // Only set the header here if the destination scheme is 'http',
2692 // since otherwise, the header will be included in the CONNECT tunnel
2693 // request instead.
2694 if dst.scheme() != Some(&Scheme::HTTP) {
2695 return;
2696 }
2697
2698 if headers.contains_key(PROXY_AUTHORIZATION) {
2699 return;
2700 }
2701
2702 for proxy in self.inner.proxies.iter() {
2703 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2704 headers.insert(PROXY_AUTHORIZATION, header);
2705 break;
2706 }
2707 }
2708 }
2709
2710 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2711 if !self.inner.proxies_maybe_http_custom_headers {
2712 return;
2713 }
2714
2715 if dst.scheme() != Some(&Scheme::HTTP) {
2716 return;
2717 }
2718
2719 for proxy in self.inner.proxies.iter() {
2720 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2721 iter.iter().for_each(|(key, value)| {
2722 headers.insert(key, value.clone());
2723 });
2724 break;
2725 }
2726 }
2727 }
2728}
2729
2730impl fmt::Debug for Client {
2731 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2732 let mut builder = f.debug_struct("Client");
2733 self.inner.fmt_fields(&mut builder);
2734 builder.finish()
2735 }
2736}
2737
2738impl tower_service::Service<Request> for Client {
2739 type Response = Response;
2740 type Error = crate::Error;
2741 type Future = Pending;
2742
2743 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2744 Poll::Ready(Ok(()))
2745 }
2746
2747 fn call(&mut self, req: Request) -> Self::Future {
2748 self.execute_request(req)
2749 }
2750}
2751
2752impl tower_service::Service<Request> for &'_ Client {
2753 type Response = Response;
2754 type Error = crate::Error;
2755 type Future = Pending;
2756
2757 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2758 Poll::Ready(Ok(()))
2759 }
2760
2761 fn call(&mut self, req: Request) -> Self::Future {
2762 self.execute_request(req)
2763 }
2764}
2765
2766impl fmt::Debug for ClientBuilder {
2767 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2768 let mut builder = f.debug_struct("ClientBuilder");
2769 self.config.fmt_fields(&mut builder);
2770 builder.finish()
2771 }
2772}
2773
2774impl Config {
2775 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2776 // Instead of deriving Debug, only print fields when their output
2777 // would provide relevant or interesting data.
2778
2779 #[cfg(feature = "cookies")]
2780 {
2781 if let Some(_) = self.cookie_store {
2782 f.field("cookie_store", &true);
2783 }
2784 }
2785
2786 f.field("accepts", &self.accepts);
2787
2788 if !self.proxies.is_empty() {
2789 f.field("proxies", &self.proxies);
2790 }
2791
2792 if !self.redirect_policy.is_default() {
2793 f.field("redirect_policy", &self.redirect_policy);
2794 }
2795
2796 if self.referer {
2797 f.field("referer", &true);
2798 }
2799
2800 f.field("default_headers", &self.headers);
2801
2802 if self.http1_title_case_headers {
2803 f.field("http1_title_case_headers", &true);
2804 }
2805
2806 if self.http1_allow_obsolete_multiline_headers_in_responses {
2807 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2808 }
2809
2810 if self.http1_ignore_invalid_headers_in_responses {
2811 f.field("http1_ignore_invalid_headers_in_responses", &true);
2812 }
2813
2814 if self.http1_allow_spaces_after_header_name_in_responses {
2815 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2816 }
2817
2818 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2819 f.field("http1_only", &true);
2820 }
2821
2822 #[cfg(feature = "http2")]
2823 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2824 f.field("http2_prior_knowledge", &true);
2825 }
2826
2827 if let Some(ref d) = self.connect_timeout {
2828 f.field("connect_timeout", d);
2829 }
2830
2831 if let Some(ref d) = self.timeout {
2832 f.field("timeout", d);
2833 }
2834
2835 if let Some(ref v) = self.local_address {
2836 f.field("local_address", v);
2837 }
2838
2839 #[cfg(any(
2840 target_os = "android",
2841 target_os = "fuchsia",
2842 target_os = "illumos",
2843 target_os = "ios",
2844 target_os = "linux",
2845 target_os = "macos",
2846 target_os = "solaris",
2847 target_os = "tvos",
2848 target_os = "visionos",
2849 target_os = "watchos",
2850 ))]
2851 if let Some(ref v) = self.interface {
2852 f.field("interface", v);
2853 }
2854
2855 if self.nodelay {
2856 f.field("tcp_nodelay", &true);
2857 }
2858
2859 #[cfg(feature = "__tls")]
2860 {
2861 if !self.hostname_verification {
2862 f.field("tls_danger_accept_invalid_hostnames", &true);
2863 }
2864 }
2865
2866 #[cfg(feature = "__tls")]
2867 {
2868 if !self.certs_verification {
2869 f.field("tls_danger_accept_invalid_certs", &true);
2870 }
2871
2872 if let Some(ref min_tls_version) = self.min_tls_version {
2873 f.field("tls_version_min", min_tls_version);
2874 }
2875
2876 if let Some(ref max_tls_version) = self.max_tls_version {
2877 f.field("tls_version_max", max_tls_version);
2878 }
2879
2880 f.field("tls_sni", &self.tls_sni);
2881
2882 f.field("tls_info", &self.tls_info);
2883 }
2884
2885 #[cfg(feature = "__rustls")]
2886 {
2887 f.field("tls_sslkeylogfile", &self.tls_sslkeylogfile);
2888 }
2889
2890 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2891 {
2892 f.field("tls_backend", &self.tls);
2893 }
2894
2895 if !self.dns_overrides.is_empty() {
2896 f.field("dns_overrides", &self.dns_overrides);
2897 }
2898
2899 #[cfg(feature = "http3")]
2900 {
2901 if self.tls_enable_early_data {
2902 f.field("tls_enable_early_data", &true);
2903 }
2904 }
2905
2906 #[cfg(unix)]
2907 if let Some(ref p) = self.unix_socket {
2908 f.field("unix_socket", p);
2909 }
2910 }
2911}
2912
2913#[cfg(not(feature = "cookies"))]
2914type MaybeCookieService<T> = T;
2915
2916#[cfg(feature = "cookies")]
2917type MaybeCookieService<T> = CookieService<T>;
2918
2919#[cfg(not(any(
2920 feature = "gzip",
2921 feature = "brotli",
2922 feature = "zstd",
2923 feature = "deflate"
2924)))]
2925type MaybeDecompression<T> = T;
2926
2927#[cfg(any(
2928 feature = "gzip",
2929 feature = "brotli",
2930 feature = "zstd",
2931 feature = "deflate"
2932))]
2933type MaybeDecompression<T> = Decompression<T>;
2934
2935type LayeredService<T> = MaybeDecompression<
2936 FollowRedirect<
2937 MaybeCookieService<tower::retry::Retry<crate::retry::Policy, T>>,
2938 TowerRedirectPolicy,
2939 >,
2940>;
2941type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
2942
2943struct ClientRef {
2944 accepts: Accepts,
2945 #[cfg(feature = "cookies")]
2946 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2947 headers: HeaderMap,
2948 hyper: LayeredService<HyperService>,
2949 #[cfg(feature = "http3")]
2950 h3_client: Option<LayeredService<H3Client>>,
2951 referer: bool,
2952 total_timeout: RequestConfig<TotalTimeout>,
2953 read_timeout: Option<Duration>,
2954 proxies: Arc<Vec<ProxyMatcher>>,
2955 proxies_maybe_http_auth: bool,
2956 proxies_maybe_http_custom_headers: bool,
2957 https_only: bool,
2958 redirect_policy_desc: Option<String>,
2959}
2960
2961impl ClientRef {
2962 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2963 // Instead of deriving Debug, only print fields when their output
2964 // would provide relevant or interesting data.
2965
2966 #[cfg(feature = "cookies")]
2967 {
2968 if let Some(_) = self.cookie_store {
2969 f.field("cookie_store", &true);
2970 }
2971 }
2972
2973 f.field("accepts", &self.accepts);
2974
2975 if !self.proxies.is_empty() {
2976 f.field("proxies", &self.proxies);
2977 }
2978
2979 if let Some(s) = &self.redirect_policy_desc {
2980 f.field("redirect_policy", s);
2981 }
2982
2983 if self.referer {
2984 f.field("referer", &true);
2985 }
2986
2987 f.field("default_headers", &self.headers);
2988
2989 self.total_timeout.fmt_as_field(f);
2990
2991 if let Some(ref d) = self.read_timeout {
2992 f.field("read_timeout", d);
2993 }
2994 }
2995}
2996
2997pin_project! {
2998 pub struct Pending {
2999 #[pin]
3000 inner: PendingInner,
3001 }
3002}
3003
3004enum PendingInner {
3005 Request(Pin<Box<PendingRequest>>),
3006 Error(Option<crate::Error>),
3007}
3008
3009pin_project! {
3010 struct PendingRequest {
3011 method: Method,
3012 url: Url,
3013 headers: HeaderMap,
3014
3015 client: Arc<ClientRef>,
3016
3017 #[pin]
3018 in_flight: ResponseFuture,
3019 #[pin]
3020 total_timeout: Option<Pin<Box<Sleep>>>,
3021 #[pin]
3022 read_timeout_fut: Option<Pin<Box<Sleep>>>,
3023 read_timeout: Option<Duration>,
3024 }
3025}
3026
3027enum ResponseFuture {
3028 Default(LayeredFuture<HyperService>),
3029 #[cfg(feature = "http3")]
3030 H3(LayeredFuture<H3Client>),
3031}
3032
3033impl PendingRequest {
3034 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
3035 self.project().in_flight
3036 }
3037
3038 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3039 self.project().total_timeout
3040 }
3041
3042 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3043 self.project().read_timeout_fut
3044 }
3045}
3046
3047impl Pending {
3048 pub(super) fn new_err(err: crate::Error) -> Pending {
3049 Pending {
3050 inner: PendingInner::Error(Some(err)),
3051 }
3052 }
3053
3054 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
3055 self.project().inner
3056 }
3057}
3058
3059impl Future for Pending {
3060 type Output = Result<Response, crate::Error>;
3061
3062 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3063 let inner = self.inner();
3064 match inner.get_mut() {
3065 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
3066 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
3067 .take()
3068 .expect("Pending error polled more than once"))),
3069 }
3070 }
3071}
3072
3073impl Future for PendingRequest {
3074 type Output = Result<Response, crate::Error>;
3075
3076 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3077 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
3078 if let Poll::Ready(()) = delay.poll(cx) {
3079 return Poll::Ready(Err(
3080 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3081 ));
3082 }
3083 }
3084
3085 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
3086 if let Poll::Ready(()) = delay.poll(cx) {
3087 return Poll::Ready(Err(
3088 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3089 ));
3090 }
3091 }
3092
3093 let res = match self.as_mut().in_flight().get_mut() {
3094 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
3095 Err(e) => {
3096 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
3097 }
3098 Ok(res) => res.map(super::body::boxed),
3099 },
3100 #[cfg(feature = "http3")]
3101 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
3102 Err(e) => {
3103 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
3104 }
3105 Ok(res) => res.map(super::body::boxed),
3106 },
3107 };
3108
3109 if let Some(url) = &res
3110 .extensions()
3111 .get::<tower_http::follow_redirect::RequestUri>()
3112 {
3113 self.url = match Url::parse(&url.0.to_string()) {
3114 Ok(url) => url,
3115 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3116 }
3117 };
3118
3119 let res = Response::new(
3120 res,
3121 self.url.clone(),
3122 self.total_timeout.take(),
3123 self.read_timeout,
3124 );
3125 Poll::Ready(Ok(res))
3126 }
3127}
3128
3129impl fmt::Debug for Pending {
3130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3131 match self.inner {
3132 PendingInner::Request(ref req) => f
3133 .debug_struct("Pending")
3134 .field("method", &req.method)
3135 .field("url", &req.url)
3136 .finish(),
3137 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3138 }
3139 }
3140}
3141
3142#[cfg(test)]
3143mod tests {
3144 #![cfg(not(feature = "rustls-no-provider"))]
3145
3146 #[tokio::test]
3147 async fn execute_request_rejects_invalid_urls() {
3148 let url_str = "hxxps://www.rust-lang.org/";
3149 let url = url::Url::parse(url_str).unwrap();
3150 let result = crate::get(url.clone()).await;
3151
3152 assert!(result.is_err());
3153 let err = result.err().unwrap();
3154 assert!(err.is_builder());
3155 assert_eq!(url_str, err.url().unwrap().as_str());
3156 }
3157
3158 /// https://github.com/seanmonstar/reqwest/issues/668
3159 #[tokio::test]
3160 async fn execute_request_rejects_invalid_hostname() {
3161 let url_str = "https://{{hostname}}/";
3162 let url = url::Url::parse(url_str).unwrap();
3163 let result = crate::get(url.clone()).await;
3164
3165 assert!(result.is_err());
3166 let err = result.err().unwrap();
3167 assert!(err.is_builder());
3168 assert_eq!(url_str, err.url().unwrap().as_str());
3169 }
3170
3171 #[test]
3172 fn test_future_size() {
3173 let s = std::mem::size_of::<super::Pending>();
3174 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3175 }
3176}