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#[derive(Clone)]
93pub struct Client {
94 inner: Arc<ClientRef>,
95}
96
97#[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 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 http1_max_headers: Option<usize>,
212 #[cfg(feature = "http2")]
213 http2_initial_stream_window_size: Option<u32>,
214 #[cfg(feature = "http2")]
215 http2_initial_connection_window_size: Option<u32>,
216 #[cfg(feature = "http2")]
217 http2_adaptive_window: bool,
218 #[cfg(feature = "http2")]
219 http2_max_frame_size: Option<u32>,
220 #[cfg(feature = "http2")]
221 http2_max_header_list_size: Option<u32>,
222 #[cfg(feature = "http2")]
223 http2_keep_alive_interval: Option<Duration>,
224 #[cfg(feature = "http2")]
225 http2_keep_alive_timeout: Option<Duration>,
226 #[cfg(feature = "http2")]
227 http2_keep_alive_while_idle: bool,
228 local_address: Option<IpAddr>,
229 #[cfg(any(
230 target_os = "android",
231 target_os = "fuchsia",
232 target_os = "illumos",
233 target_os = "ios",
234 target_os = "linux",
235 target_os = "macos",
236 target_os = "solaris",
237 target_os = "tvos",
238 target_os = "visionos",
239 target_os = "watchos",
240 ))]
241 interface: Option<String>,
242 nodelay: bool,
243 #[cfg(feature = "cookies")]
244 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
245 hickory_dns: bool,
246 error: Option<crate::Error>,
247 https_only: bool,
248 #[cfg(feature = "http3")]
249 tls_enable_early_data: bool,
250 #[cfg(feature = "http3")]
251 quic_max_idle_timeout: Option<Duration>,
252 #[cfg(feature = "http3")]
253 quic_stream_receive_window: Option<VarInt>,
254 #[cfg(feature = "http3")]
255 quic_receive_window: Option<VarInt>,
256 #[cfg(feature = "http3")]
257 quic_send_window: Option<u64>,
258 #[cfg(feature = "http3")]
259 quic_congestion_bbr: bool,
260 #[cfg(feature = "http3")]
261 h3_max_field_section_size: Option<u64>,
262 #[cfg(feature = "http3")]
263 h3_send_grease: Option<bool>,
264 dns_overrides: HashMap<String, Vec<SocketAddr>>,
265 dns_resolver: Option<Arc<dyn Resolve>>,
266
267 #[cfg(unix)]
268 unix_socket: Option<Arc<std::path::Path>>,
269 #[cfg(target_os = "windows")]
270 windows_named_pipe: Option<Arc<std::ffi::OsStr>>,
271}
272
273impl Default for ClientBuilder {
274 fn default() -> Self {
275 Self::new()
276 }
277}
278
279impl ClientBuilder {
280 pub fn new() -> Self {
284 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
285 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
286
287 ClientBuilder {
288 config: Config {
289 error: None,
290 accepts: Accepts::default(),
291 headers,
292 #[cfg(feature = "__tls")]
293 hostname_verification: true,
294 #[cfg(feature = "__tls")]
295 certs_verification: true,
296 #[cfg(feature = "__tls")]
297 tls_sni: true,
298 #[cfg(feature = "__rustls")]
299 tls_sslkeylogfile: false,
300 connect_timeout: None,
301 connection_verbose: false,
302 pool_idle_timeout: Some(Duration::from_secs(90)),
303 pool_max_idle_per_host: usize::MAX,
304 tcp_keepalive: Some(Duration::from_secs(15)),
305 tcp_keepalive_interval: Some(Duration::from_secs(15)),
306 tcp_keepalive_retries: Some(3),
307 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
308 tcp_user_timeout: Some(Duration::from_secs(30)),
309 proxies: Vec::new(),
310 auto_sys_proxy: true,
311 redirect_policy: redirect::Policy::default(),
312 retry_policy: crate::retry::Builder::default(),
313 referer: true,
314 read_timeout: None,
315 timeout: None,
316 #[cfg(feature = "__tls")]
317 root_certs: Vec::new(),
318 #[cfg(feature = "__tls")]
319 tls_certs_only: false,
320 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
321 identity: None,
322 #[cfg(feature = "__rustls")]
323 crls: vec![],
324 #[cfg(feature = "__tls")]
325 min_tls_version: None,
326 #[cfg(feature = "__tls")]
327 max_tls_version: None,
328 #[cfg(feature = "__tls")]
329 tls_info: false,
330 #[cfg(feature = "__tls")]
331 tls: TlsBackend::default(),
332 connector_layers: Vec::new(),
333 http_version_pref: HttpVersionPref::All,
334 http09_responses: false,
335 http1_title_case_headers: false,
336 http1_allow_obsolete_multiline_headers_in_responses: false,
337 http1_ignore_invalid_headers_in_responses: false,
338 http1_allow_spaces_after_header_name_in_responses: false,
339 http1_max_headers: None,
340 #[cfg(feature = "http2")]
341 http2_initial_stream_window_size: None,
342 #[cfg(feature = "http2")]
343 http2_initial_connection_window_size: None,
344 #[cfg(feature = "http2")]
345 http2_adaptive_window: false,
346 #[cfg(feature = "http2")]
347 http2_max_frame_size: None,
348 #[cfg(feature = "http2")]
349 http2_max_header_list_size: None,
350 #[cfg(feature = "http2")]
351 http2_keep_alive_interval: None,
352 #[cfg(feature = "http2")]
353 http2_keep_alive_timeout: None,
354 #[cfg(feature = "http2")]
355 http2_keep_alive_while_idle: false,
356 local_address: None,
357 #[cfg(any(
358 target_os = "android",
359 target_os = "fuchsia",
360 target_os = "illumos",
361 target_os = "ios",
362 target_os = "linux",
363 target_os = "macos",
364 target_os = "solaris",
365 target_os = "tvos",
366 target_os = "visionos",
367 target_os = "watchos",
368 ))]
369 interface: None,
370 nodelay: true,
371 hickory_dns: cfg!(feature = "hickory-dns"),
372 #[cfg(feature = "cookies")]
373 cookie_store: None,
374 https_only: false,
375 dns_overrides: HashMap::new(),
376 #[cfg(feature = "http3")]
377 tls_enable_early_data: false,
378 #[cfg(feature = "http3")]
379 quic_max_idle_timeout: None,
380 #[cfg(feature = "http3")]
381 quic_stream_receive_window: None,
382 #[cfg(feature = "http3")]
383 quic_receive_window: None,
384 #[cfg(feature = "http3")]
385 quic_send_window: None,
386 #[cfg(feature = "http3")]
387 quic_congestion_bbr: false,
388 #[cfg(feature = "http3")]
389 h3_max_field_section_size: None,
390 #[cfg(feature = "http3")]
391 h3_send_grease: None,
392 dns_resolver: None,
393 #[cfg(unix)]
394 unix_socket: None,
395 #[cfg(target_os = "windows")]
396 windows_named_pipe: None,
397 },
398 }
399 }
400}
401
402impl ClientBuilder {
403 pub fn build(self) -> crate::Result<Client> {
410 let config = self.config;
411
412 if let Some(err) = config.error {
413 return Err(err);
414 }
415
416 let mut proxies = config.proxies;
417 if config.auto_sys_proxy {
418 proxies.push(ProxyMatcher::system());
419 }
420 let proxies = Arc::new(proxies);
421
422 #[allow(unused)]
423 #[cfg(feature = "http3")]
424 let mut h3_connector = None;
425
426 let resolver = {
427 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
428 false => Arc::new(GaiResolver::new()),
429 #[cfg(feature = "hickory-dns")]
430 true => Arc::new(HickoryDnsResolver::default()),
431 #[cfg(not(feature = "hickory-dns"))]
432 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
433 };
434 if let Some(dns_resolver) = config.dns_resolver {
435 resolver = dns_resolver;
436 }
437 if !config.dns_overrides.is_empty() {
438 resolver = Arc::new(DnsResolverWithOverrides::new(
439 resolver,
440 config.dns_overrides,
441 ));
442 }
443 DynResolver::new(resolver)
444 };
445
446 let mut connector_builder = {
447 #[cfg(feature = "__tls")]
448 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
449 headers.get(USER_AGENT).cloned()
450 }
451
452 let mut http = HttpConnector::new_with_resolver(resolver.clone());
453 http.set_connect_timeout(config.connect_timeout);
454
455 #[cfg(all(feature = "http3", feature = "__rustls"))]
456 let build_h3_connector =
457 |resolver,
458 tls,
459 quic_max_idle_timeout: Option<Duration>,
460 quic_stream_receive_window,
461 quic_receive_window,
462 quic_send_window,
463 quic_congestion_bbr,
464 h3_max_field_section_size,
465 h3_send_grease,
466 local_address,
467 http_version_pref: &HttpVersionPref| {
468 let mut transport_config = TransportConfig::default();
469
470 if let Some(max_idle_timeout) = quic_max_idle_timeout {
471 transport_config.max_idle_timeout(Some(
472 max_idle_timeout.try_into().map_err(error::builder)?,
473 ));
474 }
475
476 if let Some(stream_receive_window) = quic_stream_receive_window {
477 transport_config.stream_receive_window(stream_receive_window);
478 }
479
480 if let Some(receive_window) = quic_receive_window {
481 transport_config.receive_window(receive_window);
482 }
483
484 if let Some(send_window) = quic_send_window {
485 transport_config.send_window(send_window);
486 }
487
488 if quic_congestion_bbr {
489 let factory = Arc::new(quinn::congestion::BbrConfig::default());
490 transport_config.congestion_controller_factory(factory);
491 }
492
493 let mut h3_client_config = H3ClientConfig::default();
494
495 if let Some(max_field_section_size) = h3_max_field_section_size {
496 h3_client_config.max_field_section_size = Some(max_field_section_size);
497 }
498
499 if let Some(send_grease) = h3_send_grease {
500 h3_client_config.send_grease = Some(send_grease);
501 }
502
503 let res = H3Connector::new(
504 resolver,
505 tls,
506 local_address,
507 transport_config,
508 h3_client_config,
509 );
510
511 match res {
512 Ok(connector) => Ok(Some(connector)),
513 Err(err) => {
514 if let HttpVersionPref::Http3 = http_version_pref {
515 Err(error::builder(err))
516 } else {
517 Ok(None)
518 }
519 }
520 }
521 };
522
523 #[cfg(feature = "__tls")]
524 match config.tls {
525 #[cfg(feature = "__native-tls")]
526 TlsBackend::NativeTls => {
527 let mut tls = TlsConnector::builder();
528
529 #[cfg(all(feature = "__native-tls-alpn", not(feature = "http3")))]
530 {
531 match config.http_version_pref {
532 HttpVersionPref::Http1 => {
533 tls.request_alpns(&["http/1.1"]);
534 }
535 #[cfg(feature = "http2")]
536 HttpVersionPref::Http2 => {
537 tls.request_alpns(&["h2"]);
538 }
539 HttpVersionPref::All => {
540 tls.request_alpns(&[
541 #[cfg(feature = "http2")]
542 "h2",
543 "http/1.1",
544 ]);
545 }
546 }
547 }
548
549 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
550
551 tls.danger_accept_invalid_certs(!config.certs_verification);
552
553 tls.use_sni(config.tls_sni);
554
555 tls.disable_built_in_roots(config.tls_certs_only);
556
557 for cert in config.root_certs {
558 cert.add_to_native_tls(&mut tls);
559 }
560
561 #[cfg(feature = "__native-tls")]
562 {
563 if let Some(id) = config.identity {
564 id.add_to_native_tls(&mut tls)?;
565 }
566 }
567 #[cfg(all(feature = "__rustls", not(feature = "__native-tls")))]
568 {
569 if let Some(_id) = config.identity {
571 return Err(crate::error::builder("incompatible TLS identity type"));
572 }
573 }
574
575 if let Some(min_tls_version) = config.min_tls_version {
576 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
577 crate::error::builder("invalid minimum TLS version for backend")
583 })?;
584 tls.min_protocol_version(Some(protocol));
585 }
586
587 if let Some(max_tls_version) = config.max_tls_version {
588 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
589 crate::error::builder("invalid maximum TLS version for backend")
593 })?;
594 tls.max_protocol_version(Some(protocol));
595 }
596
597 ConnectorBuilder::new_native_tls(
598 http,
599 tls,
600 proxies.clone(),
601 user_agent(&config.headers),
602 config.local_address,
603 #[cfg(any(
604 target_os = "android",
605 target_os = "fuchsia",
606 target_os = "illumos",
607 target_os = "ios",
608 target_os = "linux",
609 target_os = "macos",
610 target_os = "solaris",
611 target_os = "tvos",
612 target_os = "visionos",
613 target_os = "watchos",
614 ))]
615 config.interface.as_deref(),
616 config.nodelay,
617 config.tls_info,
618 )?
619 }
620 #[cfg(feature = "__native-tls")]
621 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_native_tls(
622 http,
623 conn,
624 proxies.clone(),
625 user_agent(&config.headers),
626 config.local_address,
627 #[cfg(any(
628 target_os = "android",
629 target_os = "fuchsia",
630 target_os = "illumos",
631 target_os = "ios",
632 target_os = "linux",
633 target_os = "macos",
634 target_os = "solaris",
635 target_os = "tvos",
636 target_os = "visionos",
637 target_os = "watchos",
638 ))]
639 config.interface.as_deref(),
640 config.nodelay,
641 config.tls_info,
642 ),
643 #[cfg(feature = "__rustls")]
644 TlsBackend::BuiltRustls(conn) => {
645 #[cfg(feature = "http3")]
646 {
647 let mut h3_tls = conn.clone();
648 h3_tls.alpn_protocols = vec!["h3".into()];
649
650 h3_connector = build_h3_connector(
651 resolver.clone(),
652 h3_tls,
653 config.quic_max_idle_timeout,
654 config.quic_stream_receive_window,
655 config.quic_receive_window,
656 config.quic_send_window,
657 config.quic_congestion_bbr,
658 config.h3_max_field_section_size,
659 config.h3_send_grease,
660 config.local_address,
661 &config.http_version_pref,
662 )?;
663 }
664
665 ConnectorBuilder::new_rustls_tls(
666 http,
667 conn,
668 proxies.clone(),
669 user_agent(&config.headers),
670 config.local_address,
671 #[cfg(any(
672 target_os = "android",
673 target_os = "fuchsia",
674 target_os = "illumos",
675 target_os = "ios",
676 target_os = "linux",
677 target_os = "macos",
678 target_os = "solaris",
679 target_os = "tvos",
680 target_os = "visionos",
681 target_os = "watchos",
682 ))]
683 config.interface.as_deref(),
684 config.nodelay,
685 config.tls_info,
686 )
687 }
688 #[cfg(feature = "__rustls")]
689 TlsBackend::Rustls => {
690 use crate::tls::{IgnoreHostname, NoVerifier};
691
692 let mut versions = rustls::ALL_VERSIONS.to_vec();
694
695 if let Some(min_tls_version) = config.min_tls_version {
696 versions.retain(|&supported_version| {
697 match tls::Version::from_rustls(supported_version.version) {
698 Some(version) => version >= min_tls_version,
699 None => true,
702 }
703 });
704 }
705
706 if let Some(max_tls_version) = config.max_tls_version {
707 versions.retain(|&supported_version| {
708 match tls::Version::from_rustls(supported_version.version) {
709 Some(version) => version <= max_tls_version,
710 None => false,
711 }
712 });
713 }
714
715 if versions.is_empty() {
716 return Err(crate::error::builder("empty supported tls versions"));
717 }
718
719 let provider = rustls::crypto::CryptoProvider::get_default()
722 .map(|arc| arc.clone())
723 .unwrap_or_else(default_rustls_crypto_provider);
724
725 let signature_algorithms = provider.signature_verification_algorithms;
727 let config_builder =
728 rustls::ClientConfig::builder_with_provider(provider.clone())
729 .with_protocol_versions(&versions)
730 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
731
732 let config_builder = if !config.certs_verification {
733 config_builder
734 .dangerous()
735 .with_custom_certificate_verifier(Arc::new(NoVerifier))
736 } else if !config.hostname_verification {
737 if !config.tls_certs_only {
738 return Err(crate::error::builder(
740 "disabling rustls hostname verification only allowed with tls_certs_only()"
741 ));
742 }
743
744 config_builder
745 .dangerous()
746 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
747 crate::tls::rustls_store(config.root_certs)?,
748 signature_algorithms,
749 )))
750 } else if !config.tls_certs_only {
751 if !config.crls.is_empty() {
753 return Err(crate::error::builder(
754 "CRLs only allowed with tls_certs_only()",
755 ));
756 }
757
758 let verifier = if config.root_certs.is_empty() {
759 rustls_platform_verifier::Verifier::new(provider)
760 .map_err(crate::error::builder)?
761 } else {
762 #[cfg(any(
763 all(unix, not(target_os = "android")),
764 target_os = "windows"
765 ))]
766 {
767 rustls_platform_verifier::Verifier::new_with_extra_roots(
768 crate::tls::rustls_der(config.root_certs)?,
769 provider,
770 )
771 .map_err(crate::error::builder)?
772 }
773
774 #[cfg(not(any(
775 all(unix, not(target_os = "android")),
776 target_os = "windows"
777 )))]
778 return Err(crate::error::builder(
779 "rustls-platform-verifier could not load extra certs",
780 ));
781 };
782
783 config_builder
784 .dangerous()
785 .with_custom_certificate_verifier(Arc::new(verifier))
786 } else {
787 if config.crls.is_empty() {
788 config_builder.with_root_certificates(crate::tls::rustls_store(
789 config.root_certs,
790 )?)
791 } else {
792 let crls = config
793 .crls
794 .iter()
795 .map(|e| e.as_rustls_crl())
796 .collect::<Vec<_>>();
797 let verifier =
798 rustls::client::WebPkiServerVerifier::builder_with_provider(
799 Arc::new(crate::tls::rustls_store(config.root_certs)?),
800 provider,
801 )
802 .with_crls(crls)
803 .build()
804 .map_err(|_| {
805 crate::error::builder("invalid TLS verification settings")
806 })?;
807 config_builder.with_webpki_verifier(verifier)
808 }
809 };
810
811 let mut tls = if let Some(id) = config.identity {
813 id.add_to_rustls(config_builder)?
814 } else {
815 config_builder.with_no_client_auth()
816 };
817
818 tls.enable_sni = config.tls_sni;
819
820 if config.tls_sslkeylogfile {
821 tls.key_log = Arc::new(rustls::KeyLogFile::new());
822 }
823
824 match config.http_version_pref {
826 HttpVersionPref::Http1 => {
827 tls.alpn_protocols = vec!["http/1.1".into()];
828 }
829 #[cfg(feature = "http2")]
830 HttpVersionPref::Http2 => {
831 tls.alpn_protocols = vec!["h2".into()];
832 }
833 #[cfg(feature = "http3")]
834 HttpVersionPref::Http3 => {
835 }
837 HttpVersionPref::All => {
838 tls.alpn_protocols = vec![
839 #[cfg(feature = "http2")]
840 "h2".into(),
841 "http/1.1".into(),
842 ];
843 }
844 }
845
846 #[cfg(feature = "http3")]
847 {
848 let mut h3_tls = tls.clone();
849 h3_tls.enable_early_data = config.tls_enable_early_data;
850
851 h3_tls.alpn_protocols = vec!["h3".into()];
853
854 h3_connector = build_h3_connector(
855 resolver.clone(),
856 h3_tls,
857 config.quic_max_idle_timeout,
858 config.quic_stream_receive_window,
859 config.quic_receive_window,
860 config.quic_send_window,
861 config.quic_congestion_bbr,
862 config.h3_max_field_section_size,
863 config.h3_send_grease,
864 config.local_address,
865 &config.http_version_pref,
866 )?;
867 }
868
869 ConnectorBuilder::new_rustls_tls(
870 http,
871 tls,
872 proxies.clone(),
873 user_agent(&config.headers),
874 config.local_address,
875 #[cfg(any(
876 target_os = "android",
877 target_os = "fuchsia",
878 target_os = "illumos",
879 target_os = "ios",
880 target_os = "linux",
881 target_os = "macos",
882 target_os = "solaris",
883 target_os = "tvos",
884 target_os = "visionos",
885 target_os = "watchos",
886 ))]
887 config.interface.as_deref(),
888 config.nodelay,
889 config.tls_info,
890 )
891 }
892 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
893 TlsBackend::UnknownPreconfigured => {
894 return Err(crate::error::builder(
895 "Unknown TLS backend passed to `use_preconfigured_tls`",
896 ));
897 }
898 }
899
900 #[cfg(not(feature = "__tls"))]
901 ConnectorBuilder::new(
902 http,
903 proxies.clone(),
904 config.local_address,
905 #[cfg(any(
906 target_os = "android",
907 target_os = "fuchsia",
908 target_os = "illumos",
909 target_os = "ios",
910 target_os = "linux",
911 target_os = "macos",
912 target_os = "solaris",
913 target_os = "tvos",
914 target_os = "visionos",
915 target_os = "watchos",
916 ))]
917 config.interface.as_deref(),
918 config.nodelay,
919 )
920 };
921
922 connector_builder.set_timeout(config.connect_timeout);
923 connector_builder.set_verbose(config.connection_verbose);
924 connector_builder.set_keepalive(config.tcp_keepalive);
925 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
926 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
927 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
928 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
929
930 #[cfg(feature = "socks")]
931 connector_builder.set_socks_resolver(resolver);
932
933 #[cfg(unix)]
937 connector_builder.set_unix_socket(config.unix_socket);
938 #[cfg(target_os = "windows")]
939 connector_builder.set_windows_named_pipe(config.windows_named_pipe.clone());
940
941 let mut builder =
942 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
943 #[cfg(feature = "http2")]
944 {
945 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
946 builder.http2_only(true);
947 }
948
949 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
950 {
951 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
952 }
953 if let Some(http2_initial_connection_window_size) =
954 config.http2_initial_connection_window_size
955 {
956 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
957 }
958 if config.http2_adaptive_window {
959 builder.http2_adaptive_window(true);
960 }
961 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
962 builder.http2_max_frame_size(http2_max_frame_size);
963 }
964 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
965 builder.http2_max_header_list_size(http2_max_header_list_size);
966 }
967 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
968 builder.http2_keep_alive_interval(http2_keep_alive_interval);
969 }
970 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
971 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
972 }
973 if config.http2_keep_alive_while_idle {
974 builder.http2_keep_alive_while_idle(true);
975 }
976 }
977
978 builder.timer(hyper_util::rt::TokioTimer::new());
979 builder.pool_timer(hyper_util::rt::TokioTimer::new());
980 builder.pool_idle_timeout(config.pool_idle_timeout);
981 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
982
983 if config.http09_responses {
984 builder.http09_responses(true);
985 }
986
987 if config.http1_title_case_headers {
988 builder.http1_title_case_headers(true);
989 }
990
991 if config.http1_allow_obsolete_multiline_headers_in_responses {
992 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
993 }
994
995 if config.http1_ignore_invalid_headers_in_responses {
996 builder.http1_ignore_invalid_headers_in_responses(true);
997 }
998
999 if config.http1_allow_spaces_after_header_name_in_responses {
1000 builder.http1_allow_spaces_after_header_name_in_responses(true);
1001 }
1002
1003 if let Some(http1_max_headers) = config.http1_max_headers {
1004 builder.http1_max_headers(http1_max_headers);
1005 }
1006
1007 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
1008 let proxies_maybe_http_custom_headers =
1009 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
1010
1011 let redirect_policy_desc = if config.redirect_policy.is_default() {
1012 None
1013 } else {
1014 Some(format!("{:?}", &config.redirect_policy))
1015 };
1016
1017 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
1018 let hyper_service = HyperService {
1019 hyper: hyper_client,
1020 };
1021
1022 let redirect_policy = {
1023 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
1024 p.with_referer(config.referer)
1025 .with_https_only(config.https_only);
1026 p
1027 };
1028
1029 let retry_policy = config.retry_policy.into_policy();
1030
1031 let svc = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
1032
1033 #[cfg(feature = "cookies")]
1034 let svc = CookieService::new(svc, config.cookie_store.clone());
1035 let hyper = FollowRedirect::with_policy(svc, redirect_policy.clone());
1036 #[cfg(any(
1037 feature = "gzip",
1038 feature = "brotli",
1039 feature = "zstd",
1040 feature = "deflate"
1041 ))]
1042 let hyper = Decompression::new(hyper)
1043 .no_gzip()
1046 .no_deflate()
1047 .no_br()
1048 .no_zstd();
1049 #[cfg(feature = "gzip")]
1050 let hyper = hyper.gzip(config.accepts.gzip);
1051 #[cfg(feature = "brotli")]
1052 let hyper = hyper.br(config.accepts.brotli);
1053 #[cfg(feature = "zstd")]
1054 let hyper = hyper.zstd(config.accepts.zstd);
1055 #[cfg(feature = "deflate")]
1056 let hyper = hyper.deflate(config.accepts.deflate);
1057
1058 Ok(Client {
1059 inner: Arc::new(ClientRef {
1060 accepts: config.accepts,
1061 #[cfg(feature = "cookies")]
1062 cookie_store: config.cookie_store.clone(),
1063 #[cfg(feature = "http3")]
1066 h3_client: match h3_connector {
1067 Some(h3_connector) => {
1068 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1069 let svc = tower::retry::Retry::new(retry_policy, h3_service);
1070 #[cfg(feature = "cookies")]
1071 let svc = CookieService::new(svc, config.cookie_store);
1072 let svc = FollowRedirect::with_policy(svc, redirect_policy);
1073 #[cfg(any(
1074 feature = "gzip",
1075 feature = "brotli",
1076 feature = "zstd",
1077 feature = "deflate"
1078 ))]
1079 let svc = Decompression::new(svc)
1080 .no_gzip()
1083 .no_deflate()
1084 .no_br()
1085 .no_zstd();
1086 #[cfg(feature = "gzip")]
1087 let svc = svc.gzip(config.accepts.gzip);
1088 #[cfg(feature = "brotli")]
1089 let svc = svc.br(config.accepts.brotli);
1090 #[cfg(feature = "zstd")]
1091 let svc = svc.zstd(config.accepts.zstd);
1092 #[cfg(feature = "deflate")]
1093 let svc = svc.deflate(config.accepts.deflate);
1094 Some(svc)
1095 }
1096 None => None,
1097 },
1098 headers: config.headers,
1099 referer: config.referer,
1100 read_timeout: config.read_timeout,
1101 total_timeout: RequestConfig::new(config.timeout),
1102 hyper,
1103 proxies,
1104 proxies_maybe_http_auth,
1105 proxies_maybe_http_custom_headers,
1106 https_only: config.https_only,
1107 redirect_policy_desc,
1108 }),
1109 })
1110 }
1111
1112 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1135 where
1136 V: TryInto<HeaderValue>,
1137 V::Error: Into<http::Error>,
1138 {
1139 match value.try_into() {
1140 Ok(value) => {
1141 self.config.headers.insert(USER_AGENT, value);
1142 }
1143 Err(e) => {
1144 self.config.error = Some(crate::error::builder(e.into()));
1145 }
1146 };
1147 self
1148 }
1149 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1173 for (key, value) in headers.iter() {
1174 self.config.headers.insert(key, value.clone());
1175 }
1176 self
1177 }
1178
1179 #[cfg(feature = "cookies")]
1194 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1195 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1196 if enable {
1197 self.cookie_provider(Arc::new(cookie::Jar::default()))
1198 } else {
1199 self.config.cookie_store = None;
1200 self
1201 }
1202 }
1203
1204 #[cfg(feature = "cookies")]
1218 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1219 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1220 mut self,
1221 cookie_store: Arc<C>,
1222 ) -> ClientBuilder {
1223 self.config.cookie_store = Some(cookie_store as _);
1224 self
1225 }
1226
1227 #[cfg(feature = "gzip")]
1244 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1245 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1246 self.config.accepts.gzip = enable;
1247 self
1248 }
1249
1250 #[cfg(feature = "brotli")]
1267 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1268 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1269 self.config.accepts.brotli = enable;
1270 self
1271 }
1272
1273 #[cfg(feature = "zstd")]
1290 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1291 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1292 self.config.accepts.zstd = enable;
1293 self
1294 }
1295
1296 #[cfg(feature = "deflate")]
1313 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1314 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1315 self.config.accepts.deflate = enable;
1316 self
1317 }
1318
1319 pub fn no_gzip(self) -> ClientBuilder {
1325 #[cfg(feature = "gzip")]
1326 {
1327 self.gzip(false)
1328 }
1329
1330 #[cfg(not(feature = "gzip"))]
1331 {
1332 self
1333 }
1334 }
1335
1336 pub fn no_brotli(self) -> ClientBuilder {
1342 #[cfg(feature = "brotli")]
1343 {
1344 self.brotli(false)
1345 }
1346
1347 #[cfg(not(feature = "brotli"))]
1348 {
1349 self
1350 }
1351 }
1352
1353 pub fn no_zstd(self) -> ClientBuilder {
1359 #[cfg(feature = "zstd")]
1360 {
1361 self.zstd(false)
1362 }
1363
1364 #[cfg(not(feature = "zstd"))]
1365 {
1366 self
1367 }
1368 }
1369
1370 pub fn no_deflate(self) -> ClientBuilder {
1376 #[cfg(feature = "deflate")]
1377 {
1378 self.deflate(false)
1379 }
1380
1381 #[cfg(not(feature = "deflate"))]
1382 {
1383 self
1384 }
1385 }
1386
1387 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1393 self.config.redirect_policy = policy;
1394 self
1395 }
1396
1397 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1401 self.config.referer = enable;
1402 self
1403 }
1404
1405 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1412 self.config.retry_policy = policy;
1413 self
1414 }
1415
1416 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1424 self.config.proxies.push(proxy.into_matcher());
1425 self.config.auto_sys_proxy = false;
1426 self
1427 }
1428
1429 pub fn no_proxy(mut self) -> ClientBuilder {
1437 self.config.proxies.clear();
1438 self.config.auto_sys_proxy = false;
1439 self
1440 }
1441
1442 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1451 self.config.timeout = Some(timeout);
1452 self
1453 }
1454
1455 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1463 self.config.read_timeout = Some(timeout);
1464 self
1465 }
1466
1467 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1476 self.config.connect_timeout = Some(timeout);
1477 self
1478 }
1479
1480 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1487 self.config.connection_verbose = verbose;
1488 self
1489 }
1490
1491 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1499 where
1500 D: Into<Option<Duration>>,
1501 {
1502 self.config.pool_idle_timeout = val.into();
1503 self
1504 }
1505
1506 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1510 self.config.pool_max_idle_per_host = max;
1511 self
1512 }
1513
1514 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1516 self.config.http1_title_case_headers = true;
1517 self
1518 }
1519
1520 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1526 mut self,
1527 value: bool,
1528 ) -> ClientBuilder {
1529 self.config
1530 .http1_allow_obsolete_multiline_headers_in_responses = value;
1531 self
1532 }
1533
1534 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1536 self.config.http1_ignore_invalid_headers_in_responses = value;
1537 self
1538 }
1539
1540 pub fn http1_allow_spaces_after_header_name_in_responses(
1546 mut self,
1547 value: bool,
1548 ) -> ClientBuilder {
1549 self.config
1550 .http1_allow_spaces_after_header_name_in_responses = value;
1551 self
1552 }
1553
1554 pub fn http1_max_headers(mut self, max: usize) -> ClientBuilder {
1561 self.config.http1_max_headers = Some(max);
1562 self
1563 }
1564
1565 pub fn http1_only(mut self) -> ClientBuilder {
1567 self.config.http_version_pref = HttpVersionPref::Http1;
1568 self
1569 }
1570
1571 pub fn http09_responses(mut self) -> ClientBuilder {
1573 self.config.http09_responses = true;
1574 self
1575 }
1576
1577 #[cfg(feature = "http2")]
1579 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1580 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1581 self.config.http_version_pref = HttpVersionPref::Http2;
1582 self
1583 }
1584
1585 #[cfg(feature = "http3")]
1587 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1588 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1589 self.config.http_version_pref = HttpVersionPref::Http3;
1590 self
1591 }
1592
1593 #[cfg(feature = "http2")]
1597 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1598 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1599 self.config.http2_initial_stream_window_size = sz.into();
1600 self
1601 }
1602
1603 #[cfg(feature = "http2")]
1607 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1608 pub fn http2_initial_connection_window_size(
1609 mut self,
1610 sz: impl Into<Option<u32>>,
1611 ) -> ClientBuilder {
1612 self.config.http2_initial_connection_window_size = sz.into();
1613 self
1614 }
1615
1616 #[cfg(feature = "http2")]
1621 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1622 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1623 self.config.http2_adaptive_window = enabled;
1624 self
1625 }
1626
1627 #[cfg(feature = "http2")]
1631 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1632 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1633 self.config.http2_max_frame_size = sz.into();
1634 self
1635 }
1636
1637 #[cfg(feature = "http2")]
1641 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1642 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1643 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1644 self
1645 }
1646
1647 #[cfg(feature = "http2")]
1652 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1653 pub fn http2_keep_alive_interval(
1654 mut self,
1655 interval: impl Into<Option<Duration>>,
1656 ) -> ClientBuilder {
1657 self.config.http2_keep_alive_interval = interval.into();
1658 self
1659 }
1660
1661 #[cfg(feature = "http2")]
1667 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1668 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1669 self.config.http2_keep_alive_timeout = Some(timeout);
1670 self
1671 }
1672
1673 #[cfg(feature = "http2")]
1680 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1681 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1682 self.config.http2_keep_alive_while_idle = enabled;
1683 self
1684 }
1685
1686 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1692 self.config.nodelay = enabled;
1693 self
1694 }
1695
1696 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1711 where
1712 T: Into<Option<IpAddr>>,
1713 {
1714 self.config.local_address = addr.into();
1715 self
1716 }
1717
1718 #[cfg(any(
1751 target_os = "android",
1752 target_os = "fuchsia",
1753 target_os = "illumos",
1754 target_os = "ios",
1755 target_os = "linux",
1756 target_os = "macos",
1757 target_os = "solaris",
1758 target_os = "tvos",
1759 target_os = "visionos",
1760 target_os = "watchos",
1761 ))]
1762 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1763 self.config.interface = Some(interface.to_string());
1764 self
1765 }
1766
1767 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1771 where
1772 D: Into<Option<Duration>>,
1773 {
1774 self.config.tcp_keepalive = val.into();
1775 self
1776 }
1777
1778 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1782 where
1783 D: Into<Option<Duration>>,
1784 {
1785 self.config.tcp_keepalive_interval = val.into();
1786 self
1787 }
1788
1789 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1793 where
1794 C: Into<Option<u32>>,
1795 {
1796 self.config.tcp_keepalive_retries = retries.into();
1797 self
1798 }
1799
1800 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1807 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1808 where
1809 D: Into<Option<Duration>>,
1810 {
1811 self.config.tcp_user_timeout = val.into();
1812 self
1813 }
1814
1815 #[cfg(unix)]
1829 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1830 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1831 self
1832 }
1833
1834 #[cfg(target_os = "windows")]
1846 pub fn windows_named_pipe(mut self, pipe: impl WindowsNamedPipeProvider) -> ClientBuilder {
1847 self.config.windows_named_pipe = Some(
1848 pipe.reqwest_windows_named_pipe_path(crate::connect::windows_named_pipe::Internal)
1849 .into(),
1850 );
1851 self
1852 }
1853
1854 #[cfg(feature = "__tls")]
1873 #[cfg_attr(
1874 docsrs,
1875 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1876 )]
1877 pub fn tls_certs_merge(
1878 mut self,
1879 certs: impl IntoIterator<Item = Certificate>,
1880 ) -> ClientBuilder {
1881 self.config.root_certs.extend(certs);
1882 self
1883 }
1884
1885 #[cfg(feature = "__tls")]
1898 #[cfg_attr(
1899 docsrs,
1900 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1901 )]
1902 pub fn tls_certs_only(mut self, certs: impl IntoIterator<Item = Certificate>) -> ClientBuilder {
1903 self.config.root_certs.extend(certs);
1904 self.config.tls_certs_only = true;
1905 self
1906 }
1907
1908 #[cfg(feature = "__tls")]
1911 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1912 self.config.root_certs.push(cert);
1913 self
1914 }
1915
1916 #[cfg(feature = "__rustls")]
1930 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1931 pub fn tls_crls_only(
1932 mut self,
1933 crls: impl IntoIterator<Item = CertificateRevocationList>,
1934 ) -> ClientBuilder {
1935 self.config.crls.extend(crls);
1936 self
1937 }
1938
1939 #[cfg(feature = "__rustls")]
1941 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1942 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1943 self.config.crls.push(crl);
1944 self
1945 }
1946
1947 #[cfg(feature = "__rustls")]
1949 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1950 pub fn add_crls(
1951 mut self,
1952 crls: impl IntoIterator<Item = CertificateRevocationList>,
1953 ) -> ClientBuilder {
1954 self.config.crls.extend(crls);
1955 self
1956 }
1957
1958 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
1965 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
1966 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1967 self.config.identity = Some(identity);
1968 self
1969 }
1970
1971 #[cfg(feature = "__tls")]
1992 #[cfg_attr(
1993 docsrs,
1994 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1995 )]
1996 pub fn tls_danger_accept_invalid_hostnames(
1997 mut self,
1998 accept_invalid_hostname: bool,
1999 ) -> ClientBuilder {
2000 self.config.hostname_verification = !accept_invalid_hostname;
2001 self
2002 }
2003
2004 #[cfg(feature = "__tls")]
2006 pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder {
2007 self.tls_danger_accept_invalid_hostnames(accept_invalid_hostname)
2008 }
2009
2010 #[cfg(feature = "__tls")]
2027 #[cfg_attr(
2028 docsrs,
2029 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2030 )]
2031 pub fn tls_danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
2032 self.config.certs_verification = !accept_invalid_certs;
2033 self
2034 }
2035
2036 #[cfg(feature = "__tls")]
2038 pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder {
2039 self.tls_danger_accept_invalid_certs(accept_invalid_certs)
2040 }
2041
2042 #[cfg(feature = "__tls")]
2051 #[cfg_attr(
2052 docsrs,
2053 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2054 )]
2055 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
2056 self.config.tls_sni = tls_sni;
2057 self
2058 }
2059
2060 #[cfg(feature = "__rustls")]
2072 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2073 pub fn tls_sslkeylogfile(mut self, on: bool) -> ClientBuilder {
2074 self.config.tls_sslkeylogfile = on;
2075 self
2076 }
2077
2078 #[cfg(feature = "__tls")]
2091 #[cfg_attr(
2092 docsrs,
2093 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2094 )]
2095 pub fn tls_version_min(mut self, version: tls::Version) -> ClientBuilder {
2096 self.config.min_tls_version = Some(version);
2097 self
2098 }
2099
2100 #[cfg(feature = "__tls")]
2102 pub fn min_tls_version(self, version: tls::Version) -> ClientBuilder {
2103 self.tls_version_min(version)
2104 }
2105
2106 #[cfg(feature = "__tls")]
2124 #[cfg_attr(
2125 docsrs,
2126 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2127 )]
2128 pub fn tls_version_max(mut self, version: tls::Version) -> ClientBuilder {
2129 self.config.max_tls_version = Some(version);
2130 self
2131 }
2132
2133 #[cfg(feature = "__tls")]
2135 pub fn max_tls_version(self, version: tls::Version) -> ClientBuilder {
2136 self.tls_version_max(version)
2137 }
2138
2139 #[cfg(feature = "__native-tls")]
2148 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2149 pub fn tls_backend_native(mut self) -> ClientBuilder {
2150 self.config.tls = TlsBackend::NativeTls;
2151 self
2152 }
2153
2154 #[cfg(feature = "__native-tls")]
2156 pub fn use_native_tls(self) -> ClientBuilder {
2157 self.tls_backend_native()
2158 }
2159
2160 #[cfg(feature = "__rustls")]
2169 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2170 pub fn tls_backend_rustls(mut self) -> ClientBuilder {
2171 self.config.tls = TlsBackend::Rustls;
2172 self
2173 }
2174
2175 #[cfg(feature = "__rustls")]
2177 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2178 pub fn use_rustls_tls(self) -> ClientBuilder {
2179 self.tls_backend_rustls()
2180 }
2181
2182 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2208 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
2209 pub fn tls_backend_preconfigured(mut self, tls: impl Any) -> ClientBuilder {
2210 let mut tls = Some(tls);
2211 #[cfg(feature = "__native-tls")]
2212 {
2213 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2214 let tls = conn.take().expect("is definitely Some");
2215 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2216 self.config.tls = tls;
2217 return self;
2218 }
2219 }
2220 #[cfg(feature = "__rustls")]
2221 {
2222 if let Some(conn) =
2223 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2224 {
2225 let tls = conn.take().expect("is definitely Some");
2226 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2227 self.config.tls = tls;
2228 return self;
2229 }
2230 }
2231
2232 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2234 self
2235 }
2236
2237 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2239 pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder {
2240 self.tls_backend_preconfigured(tls)
2241 }
2242
2243 #[cfg(feature = "__tls")]
2250 #[cfg_attr(
2251 docsrs,
2252 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2253 )]
2254 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2255 self.config.tls_info = tls_info;
2256 self
2257 }
2258
2259 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2263 self.config.https_only = enabled;
2264 self
2265 }
2266
2267 #[cfg(feature = "hickory-dns")]
2281 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2282 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2283 self.config.hickory_dns = enable;
2284 self
2285 }
2286
2287 pub fn no_hickory_dns(self) -> ClientBuilder {
2293 #[cfg(feature = "hickory-dns")]
2294 {
2295 self.hickory_dns(false)
2296 }
2297
2298 #[cfg(not(feature = "hickory-dns"))]
2299 {
2300 self
2301 }
2302 }
2303
2304 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2309 self.resolve_to_addrs(domain, &[addr])
2310 }
2311
2312 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2317 self.config
2318 .dns_overrides
2319 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2320 self
2321 }
2322
2323 pub fn dns_resolver<R>(mut self, resolver: R) -> ClientBuilder
2328 where
2329 R: crate::dns::resolve::IntoResolve,
2330 {
2331 self.config.dns_resolver = Some(resolver.into_resolve());
2332 self
2333 }
2334
2335 #[cfg(feature = "http3")]
2340 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2341 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2342 self.config.tls_enable_early_data = enabled;
2343 self
2344 }
2345
2346 #[cfg(feature = "http3")]
2352 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2353 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2354 self.config.quic_max_idle_timeout = Some(value);
2355 self
2356 }
2357
2358 #[cfg(feature = "http3")]
2369 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2370 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2371 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2372 self
2373 }
2374
2375 #[cfg(feature = "http3")]
2386 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2387 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2388 self.config.quic_receive_window = Some(value.try_into().unwrap());
2389 self
2390 }
2391
2392 #[cfg(feature = "http3")]
2398 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2399 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2400 self.config.quic_send_window = Some(value);
2401 self
2402 }
2403
2404 #[cfg(feature = "http3")]
2412 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2413 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2414 self.config.quic_congestion_bbr = true;
2415 self
2416 }
2417
2418 #[cfg(feature = "http3")]
2428 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2429 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2430 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2431 self
2432 }
2433
2434 #[cfg(feature = "http3")]
2446 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2447 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2448 self.config.h3_send_grease = Some(enabled);
2449 self
2450 }
2451
2452 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2476 where
2477 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2478 L::Service:
2479 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2480 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2481 {
2482 let layer = BoxCloneSyncServiceLayer::new(layer);
2483
2484 self.config.connector_layers.push(layer);
2485
2486 self
2487 }
2488}
2489
2490type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2491
2492impl Default for Client {
2493 fn default() -> Self {
2494 Self::new()
2495 }
2496}
2497
2498#[cfg(feature = "__rustls")]
2499fn default_rustls_crypto_provider() -> Arc<rustls::crypto::CryptoProvider> {
2500 #[cfg(not(feature = "__rustls-aws-lc-rs"))]
2501 panic!(
2502 "No rustls crypto provider is configured. \
2503 When using the `rustls-no-provider` feature you must install a \
2504 crypto provider before building a Client. For example: \
2505 `rustls::crypto::aws_lc_rs::default_provider().install_default().unwrap();` \
2506 See https://docs.rs/rustls/latest/rustls/#cryptography-providers for details."
2507 );
2508
2509 #[cfg(feature = "__rustls-aws-lc-rs")]
2510 Arc::new(rustls::crypto::aws_lc_rs::default_provider())
2511}
2512
2513impl Client {
2514 pub fn new() -> Client {
2524 ClientBuilder::new().build().expect("Client::new()")
2525 }
2526
2527 pub fn builder() -> ClientBuilder {
2531 ClientBuilder::new()
2532 }
2533
2534 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2540 self.request(Method::GET, url)
2541 }
2542
2543 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2549 self.request(Method::POST, url)
2550 }
2551
2552 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2558 self.request(Method::PUT, url)
2559 }
2560
2561 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2567 self.request(Method::PATCH, url)
2568 }
2569
2570 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2576 self.request(Method::DELETE, url)
2577 }
2578
2579 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2585 self.request(Method::HEAD, url)
2586 }
2587
2588 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2597 let req = url.into_url().map(move |url| Request::new(method, url));
2598 RequestBuilder::new(self.clone(), req)
2599 }
2600
2601 pub fn execute(
2614 &self,
2615 request: Request,
2616 ) -> impl Future<Output = Result<Response, crate::Error>> {
2617 self.execute_request(request)
2618 }
2619
2620 pub(super) fn execute_request(&self, req: Request) -> Pending {
2621 let (method, url, mut headers, body, version, extensions) = req.pieces();
2622 if url.scheme() != "http" && url.scheme() != "https" {
2623 return Pending::new_err(error::url_bad_scheme(url));
2624 }
2625
2626 if self.inner.https_only && url.scheme() != "https" {
2628 return Pending::new_err(error::url_bad_scheme(url));
2629 }
2630
2631 for (key, value) in &self.inner.headers {
2634 if let Entry::Vacant(entry) = headers.entry(key) {
2635 entry.insert(value.clone());
2636 }
2637 }
2638
2639 let uri = match try_uri(&url) {
2640 Ok(uri) => uri,
2641 _ => return Pending::new_err(error::url_invalid_uri(url)),
2642 };
2643
2644 let body = body.unwrap_or_else(Body::empty);
2645
2646 self.proxy_auth(&uri, &mut headers);
2647 self.proxy_custom_headers(&uri, &mut headers);
2648
2649 let builder = hyper::Request::builder()
2650 .method(method.clone())
2651 .uri(uri)
2652 .version(version);
2653
2654 let in_flight = match version {
2655 #[cfg(feature = "http3")]
2656 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2657 let mut req = builder.body(body).expect("valid request parts");
2658 *req.headers_mut() = headers.clone();
2659 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2660 ResponseFuture::H3(h3.call(req))
2661 }
2662 _ => {
2663 let mut req = builder.body(body).expect("valid request parts");
2664 *req.headers_mut() = headers.clone();
2665 let mut hyper = self.inner.hyper.clone();
2666 ResponseFuture::Default(hyper.call(req))
2667 }
2668 };
2669
2670 let total_timeout = self
2671 .inner
2672 .total_timeout
2673 .fetch(&extensions)
2674 .copied()
2675 .map(tokio::time::sleep)
2676 .map(Box::pin);
2677
2678 let read_timeout_fut = self
2679 .inner
2680 .read_timeout
2681 .map(tokio::time::sleep)
2682 .map(Box::pin);
2683
2684 Pending {
2685 inner: PendingInner::Request(Box::pin(PendingRequest {
2686 method,
2687 url,
2688 headers,
2689
2690 client: self.inner.clone(),
2691
2692 in_flight,
2693 total_timeout,
2694 read_timeout_fut,
2695 read_timeout: self.inner.read_timeout,
2696 })),
2697 }
2698 }
2699
2700 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2701 if !self.inner.proxies_maybe_http_auth {
2702 return;
2703 }
2704
2705 if dst.scheme() != Some(&Scheme::HTTP) {
2709 return;
2710 }
2711
2712 if headers.contains_key(PROXY_AUTHORIZATION) {
2713 return;
2714 }
2715
2716 for proxy in self.inner.proxies.iter() {
2717 if let Some(proxy) = proxy.intercept(dst) {
2718 if let Some(header) = proxy.http_non_tunnel_basic_auth() {
2719 headers.insert(PROXY_AUTHORIZATION, header);
2720 }
2721 break;
2723 }
2724 }
2725 }
2726
2727 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2728 if !self.inner.proxies_maybe_http_custom_headers {
2729 return;
2730 }
2731
2732 if dst.scheme() != Some(&Scheme::HTTP) {
2733 return;
2734 }
2735
2736 for proxy in self.inner.proxies.iter() {
2737 if let Some(proxy) = proxy.intercept(dst) {
2738 if let Some(iter) = proxy.http_non_tunnel_custom_headers() {
2739 iter.iter().for_each(|(key, value)| {
2740 headers.insert(key, value.clone());
2741 });
2742 }
2743 break;
2745 }
2746 }
2747 }
2748}
2749
2750impl fmt::Debug for Client {
2751 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2752 let mut builder = f.debug_struct("Client");
2753 self.inner.fmt_fields(&mut builder);
2754 builder.finish()
2755 }
2756}
2757
2758impl tower_service::Service<Request> for Client {
2759 type Response = Response;
2760 type Error = crate::Error;
2761 type Future = Pending;
2762
2763 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2764 Poll::Ready(Ok(()))
2765 }
2766
2767 fn call(&mut self, req: Request) -> Self::Future {
2768 self.execute_request(req)
2769 }
2770}
2771
2772impl tower_service::Service<Request> for &'_ Client {
2773 type Response = Response;
2774 type Error = crate::Error;
2775 type Future = Pending;
2776
2777 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2778 Poll::Ready(Ok(()))
2779 }
2780
2781 fn call(&mut self, req: Request) -> Self::Future {
2782 self.execute_request(req)
2783 }
2784}
2785
2786impl fmt::Debug for ClientBuilder {
2787 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2788 let mut builder = f.debug_struct("ClientBuilder");
2789 self.config.fmt_fields(&mut builder);
2790 builder.finish()
2791 }
2792}
2793
2794impl Config {
2795 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2796 #[cfg(feature = "cookies")]
2800 {
2801 if let Some(_) = self.cookie_store {
2802 f.field("cookie_store", &true);
2803 }
2804 }
2805
2806 f.field("accepts", &self.accepts);
2807
2808 if !self.proxies.is_empty() {
2809 f.field("proxies", &self.proxies);
2810 }
2811
2812 if !self.redirect_policy.is_default() {
2813 f.field("redirect_policy", &self.redirect_policy);
2814 }
2815
2816 if self.referer {
2817 f.field("referer", &true);
2818 }
2819
2820 f.field("default_headers", &self.headers);
2821
2822 if self.http1_title_case_headers {
2823 f.field("http1_title_case_headers", &true);
2824 }
2825
2826 if self.http1_allow_obsolete_multiline_headers_in_responses {
2827 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2828 }
2829
2830 if self.http1_ignore_invalid_headers_in_responses {
2831 f.field("http1_ignore_invalid_headers_in_responses", &true);
2832 }
2833
2834 if self.http1_allow_spaces_after_header_name_in_responses {
2835 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2836 }
2837
2838 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2839 f.field("http1_only", &true);
2840 }
2841
2842 #[cfg(feature = "http2")]
2843 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2844 f.field("http2_prior_knowledge", &true);
2845 }
2846
2847 if let Some(ref d) = self.connect_timeout {
2848 f.field("connect_timeout", d);
2849 }
2850
2851 if let Some(ref d) = self.timeout {
2852 f.field("timeout", d);
2853 }
2854
2855 if let Some(ref v) = self.local_address {
2856 f.field("local_address", v);
2857 }
2858
2859 #[cfg(any(
2860 target_os = "android",
2861 target_os = "fuchsia",
2862 target_os = "illumos",
2863 target_os = "ios",
2864 target_os = "linux",
2865 target_os = "macos",
2866 target_os = "solaris",
2867 target_os = "tvos",
2868 target_os = "visionos",
2869 target_os = "watchos",
2870 ))]
2871 if let Some(ref v) = self.interface {
2872 f.field("interface", v);
2873 }
2874
2875 if self.nodelay {
2876 f.field("tcp_nodelay", &true);
2877 }
2878
2879 #[cfg(feature = "__tls")]
2880 {
2881 if !self.hostname_verification {
2882 f.field("tls_danger_accept_invalid_hostnames", &true);
2883 }
2884 }
2885
2886 #[cfg(feature = "__tls")]
2887 {
2888 if !self.certs_verification {
2889 f.field("tls_danger_accept_invalid_certs", &true);
2890 }
2891
2892 if let Some(ref min_tls_version) = self.min_tls_version {
2893 f.field("tls_version_min", min_tls_version);
2894 }
2895
2896 if let Some(ref max_tls_version) = self.max_tls_version {
2897 f.field("tls_version_max", max_tls_version);
2898 }
2899
2900 f.field("tls_sni", &self.tls_sni);
2901
2902 f.field("tls_info", &self.tls_info);
2903 }
2904
2905 #[cfg(feature = "__rustls")]
2906 {
2907 f.field("tls_sslkeylogfile", &self.tls_sslkeylogfile);
2908 }
2909
2910 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2911 {
2912 f.field("tls_backend", &self.tls);
2913 }
2914
2915 if !self.dns_overrides.is_empty() {
2916 f.field("dns_overrides", &self.dns_overrides);
2917 }
2918
2919 #[cfg(feature = "http3")]
2920 {
2921 if self.tls_enable_early_data {
2922 f.field("tls_enable_early_data", &true);
2923 }
2924 }
2925
2926 #[cfg(unix)]
2927 if let Some(ref p) = self.unix_socket {
2928 f.field("unix_socket", p);
2929 }
2930 }
2931}
2932
2933#[cfg(not(feature = "cookies"))]
2934type MaybeCookieService<T> = T;
2935
2936#[cfg(feature = "cookies")]
2937type MaybeCookieService<T> = CookieService<T>;
2938
2939#[cfg(not(any(
2940 feature = "gzip",
2941 feature = "brotli",
2942 feature = "zstd",
2943 feature = "deflate"
2944)))]
2945type MaybeDecompression<T> = T;
2946
2947#[cfg(any(
2948 feature = "gzip",
2949 feature = "brotli",
2950 feature = "zstd",
2951 feature = "deflate"
2952))]
2953type MaybeDecompression<T> = Decompression<T>;
2954
2955type LayeredService<T> = MaybeDecompression<
2956 FollowRedirect<
2957 MaybeCookieService<tower::retry::Retry<crate::retry::Policy, T>>,
2958 TowerRedirectPolicy,
2959 >,
2960>;
2961type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
2962
2963struct ClientRef {
2964 accepts: Accepts,
2965 #[cfg(feature = "cookies")]
2966 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2967 headers: HeaderMap,
2968 hyper: LayeredService<HyperService>,
2969 #[cfg(feature = "http3")]
2970 h3_client: Option<LayeredService<H3Client>>,
2971 referer: bool,
2972 total_timeout: RequestConfig<TotalTimeout>,
2973 read_timeout: Option<Duration>,
2974 proxies: Arc<Vec<ProxyMatcher>>,
2975 proxies_maybe_http_auth: bool,
2976 proxies_maybe_http_custom_headers: bool,
2977 https_only: bool,
2978 redirect_policy_desc: Option<String>,
2979}
2980
2981impl ClientRef {
2982 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2983 #[cfg(feature = "cookies")]
2987 {
2988 if let Some(_) = self.cookie_store {
2989 f.field("cookie_store", &true);
2990 }
2991 }
2992
2993 f.field("accepts", &self.accepts);
2994
2995 if !self.proxies.is_empty() {
2996 f.field("proxies", &self.proxies);
2997 }
2998
2999 if let Some(s) = &self.redirect_policy_desc {
3000 f.field("redirect_policy", s);
3001 }
3002
3003 if self.referer {
3004 f.field("referer", &true);
3005 }
3006
3007 f.field("default_headers", &self.headers);
3008
3009 self.total_timeout.fmt_as_field(f);
3010
3011 if let Some(ref d) = self.read_timeout {
3012 f.field("read_timeout", d);
3013 }
3014 }
3015}
3016
3017pin_project! {
3018 pub struct Pending {
3019 #[pin]
3020 inner: PendingInner,
3021 }
3022}
3023
3024enum PendingInner {
3025 Request(Pin<Box<PendingRequest>>),
3026 Error(Option<crate::Error>),
3027}
3028
3029pin_project! {
3030 struct PendingRequest {
3031 method: Method,
3032 url: Url,
3033 headers: HeaderMap,
3034
3035 client: Arc<ClientRef>,
3036
3037 #[pin]
3038 in_flight: ResponseFuture,
3039 #[pin]
3040 total_timeout: Option<Pin<Box<Sleep>>>,
3041 #[pin]
3042 read_timeout_fut: Option<Pin<Box<Sleep>>>,
3043 read_timeout: Option<Duration>,
3044 }
3045}
3046
3047enum ResponseFuture {
3048 Default(LayeredFuture<HyperService>),
3049 #[cfg(feature = "http3")]
3050 H3(LayeredFuture<H3Client>),
3051}
3052
3053impl PendingRequest {
3054 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
3055 self.project().in_flight
3056 }
3057
3058 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3059 self.project().total_timeout
3060 }
3061
3062 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3063 self.project().read_timeout_fut
3064 }
3065}
3066
3067impl Pending {
3068 pub(super) fn new_err(err: crate::Error) -> Pending {
3069 Pending {
3070 inner: PendingInner::Error(Some(err)),
3071 }
3072 }
3073
3074 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
3075 self.project().inner
3076 }
3077}
3078
3079impl Future for Pending {
3080 type Output = Result<Response, crate::Error>;
3081
3082 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3083 let inner = self.inner();
3084 match inner.get_mut() {
3085 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
3086 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
3087 .take()
3088 .expect("Pending error polled more than once"))),
3089 }
3090 }
3091}
3092
3093impl Future for PendingRequest {
3094 type Output = Result<Response, crate::Error>;
3095
3096 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3097 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
3098 if let Poll::Ready(()) = delay.poll(cx) {
3099 return Poll::Ready(Err(
3100 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3101 ));
3102 }
3103 }
3104
3105 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
3106 if let Poll::Ready(()) = delay.poll(cx) {
3107 return Poll::Ready(Err(
3108 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3109 ));
3110 }
3111 }
3112
3113 let res = match self.as_mut().in_flight().get_mut() {
3114 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
3115 Err(e) => {
3116 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
3117 }
3118 Ok(res) => res.map(super::body::boxed),
3119 },
3120 #[cfg(feature = "http3")]
3121 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
3122 Err(e) => {
3123 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
3124 }
3125 Ok(res) => res.map(super::body::boxed),
3126 },
3127 };
3128
3129 if let Some(url) = &res
3130 .extensions()
3131 .get::<tower_http::follow_redirect::RequestUri>()
3132 {
3133 self.url = match Url::parse(&url.0.to_string()) {
3134 Ok(url) => url,
3135 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3136 }
3137 };
3138
3139 let res = Response::new(
3140 res,
3141 self.url.clone(),
3142 self.total_timeout.take(),
3143 self.read_timeout,
3144 );
3145 Poll::Ready(Ok(res))
3146 }
3147}
3148
3149impl fmt::Debug for Pending {
3150 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3151 match self.inner {
3152 PendingInner::Request(ref req) => f
3153 .debug_struct("Pending")
3154 .field("method", &req.method)
3155 .field("url", &req.url)
3156 .finish(),
3157 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3158 }
3159 }
3160}
3161
3162#[cfg(test)]
3163mod tests {
3164 #![cfg(not(feature = "rustls-no-provider"))]
3165
3166 #[tokio::test]
3167 async fn execute_request_rejects_invalid_urls() {
3168 let url_str = "hxxps://www.rust-lang.org/";
3169 let url = url::Url::parse(url_str).unwrap();
3170 let result = crate::get(url.clone()).await;
3171
3172 assert!(result.is_err());
3173 let err = result.err().unwrap();
3174 assert!(err.is_builder());
3175 assert_eq!(url_str, err.url().unwrap().as_str());
3176 }
3177
3178 #[tokio::test]
3180 async fn execute_request_rejects_invalid_hostname() {
3181 let url_str = "https://{{hostname}}/";
3182 let url = url::Url::parse(url_str).unwrap();
3183 let result = crate::get(url.clone()).await;
3184
3185 assert!(result.is_err());
3186 let err = result.err().unwrap();
3187 assert!(err.is_builder());
3188 assert_eq!(url_str, err.url().unwrap().as_str());
3189 }
3190
3191 #[test]
3192 fn test_future_size() {
3193 let s = std::mem::size_of::<super::Pending>();
3194 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3195 }
3196}