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 #[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 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 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 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 crate::error::builder("invalid minimum TLS version for backend")
581 })?;
582 tls.min_protocol_version(Some(protocol));
583 }
584
585 if let Some(max_tls_version) = config.max_tls_version {
586 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
587 crate::error::builder("invalid maximum TLS version for backend")
591 })?;
592 tls.max_protocol_version(Some(protocol));
593 }
594
595 ConnectorBuilder::new_native_tls(
596 http,
597 tls,
598 proxies.clone(),
599 user_agent(&config.headers),
600 config.local_address,
601 #[cfg(any(
602 target_os = "android",
603 target_os = "fuchsia",
604 target_os = "illumos",
605 target_os = "ios",
606 target_os = "linux",
607 target_os = "macos",
608 target_os = "solaris",
609 target_os = "tvos",
610 target_os = "visionos",
611 target_os = "watchos",
612 ))]
613 config.interface.as_deref(),
614 config.nodelay,
615 config.tls_info,
616 )?
617 }
618 #[cfg(feature = "__native-tls")]
619 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_native_tls(
620 http,
621 conn,
622 proxies.clone(),
623 user_agent(&config.headers),
624 config.local_address,
625 #[cfg(any(
626 target_os = "android",
627 target_os = "fuchsia",
628 target_os = "illumos",
629 target_os = "ios",
630 target_os = "linux",
631 target_os = "macos",
632 target_os = "solaris",
633 target_os = "tvos",
634 target_os = "visionos",
635 target_os = "watchos",
636 ))]
637 config.interface.as_deref(),
638 config.nodelay,
639 config.tls_info,
640 ),
641 #[cfg(feature = "__rustls")]
642 TlsBackend::BuiltRustls(conn) => {
643 #[cfg(feature = "http3")]
644 {
645 let mut h3_tls = conn.clone();
646 h3_tls.alpn_protocols = vec!["h3".into()];
647
648 h3_connector = build_h3_connector(
649 resolver.clone(),
650 h3_tls,
651 config.quic_max_idle_timeout,
652 config.quic_stream_receive_window,
653 config.quic_receive_window,
654 config.quic_send_window,
655 config.quic_congestion_bbr,
656 config.h3_max_field_section_size,
657 config.h3_send_grease,
658 config.local_address,
659 &config.http_version_pref,
660 )?;
661 }
662
663 ConnectorBuilder::new_rustls_tls(
664 http,
665 conn,
666 proxies.clone(),
667 user_agent(&config.headers),
668 config.local_address,
669 #[cfg(any(
670 target_os = "android",
671 target_os = "fuchsia",
672 target_os = "illumos",
673 target_os = "ios",
674 target_os = "linux",
675 target_os = "macos",
676 target_os = "solaris",
677 target_os = "tvos",
678 target_os = "visionos",
679 target_os = "watchos",
680 ))]
681 config.interface.as_deref(),
682 config.nodelay,
683 config.tls_info,
684 )
685 }
686 #[cfg(feature = "__rustls")]
687 TlsBackend::Rustls => {
688 use crate::tls::{IgnoreHostname, NoVerifier};
689
690 let mut versions = rustls::ALL_VERSIONS.to_vec();
692
693 if let Some(min_tls_version) = config.min_tls_version {
694 versions.retain(|&supported_version| {
695 match tls::Version::from_rustls(supported_version.version) {
696 Some(version) => version >= min_tls_version,
697 None => true,
700 }
701 });
702 }
703
704 if let Some(max_tls_version) = config.max_tls_version {
705 versions.retain(|&supported_version| {
706 match tls::Version::from_rustls(supported_version.version) {
707 Some(version) => version <= max_tls_version,
708 None => false,
709 }
710 });
711 }
712
713 if versions.is_empty() {
714 return Err(crate::error::builder("empty supported tls versions"));
715 }
716
717 let provider = rustls::crypto::CryptoProvider::get_default()
720 .map(|arc| arc.clone())
721 .unwrap_or_else(default_rustls_crypto_provider);
722
723 let signature_algorithms = provider.signature_verification_algorithms;
725 let config_builder =
726 rustls::ClientConfig::builder_with_provider(provider.clone())
727 .with_protocol_versions(&versions)
728 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
729
730 let config_builder = if !config.certs_verification {
731 config_builder
732 .dangerous()
733 .with_custom_certificate_verifier(Arc::new(NoVerifier))
734 } else if !config.hostname_verification {
735 if !config.tls_certs_only {
736 return Err(crate::error::builder(
738 "disabling rustls hostname verification only allowed with tls_certs_only()"
739 ));
740 }
741
742 config_builder
743 .dangerous()
744 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
745 crate::tls::rustls_store(config.root_certs)?,
746 signature_algorithms,
747 )))
748 } else if !config.tls_certs_only {
749 if !config.crls.is_empty() {
751 return Err(crate::error::builder(
752 "CRLs only allowed with tls_certs_only()",
753 ));
754 }
755
756 let verifier = if config.root_certs.is_empty() {
757 rustls_platform_verifier::Verifier::new(provider.clone())
758 .map_err(crate::error::builder)?
759 } else {
760 #[cfg(any(
761 all(unix, not(target_os = "android")),
762 target_os = "windows"
763 ))]
764 {
765 rustls_platform_verifier::Verifier::new_with_extra_roots(
766 crate::tls::rustls_der(config.root_certs)?,
767 provider.clone(),
768 )
769 .map_err(crate::error::builder)?
770 }
771
772 #[cfg(not(any(
773 all(unix, not(target_os = "android")),
774 target_os = "windows"
775 )))]
776 return Err(crate::error::builder(
777 "rustls-platform-verifier could not load extra certs",
778 ));
779 };
780
781 config_builder
782 .dangerous()
783 .with_custom_certificate_verifier(Arc::new(verifier))
784 } else {
785 if config.crls.is_empty() {
786 config_builder.with_root_certificates(crate::tls::rustls_store(
787 config.root_certs,
788 )?)
789 } else {
790 let crls = config
791 .crls
792 .iter()
793 .map(|e| e.as_rustls_crl())
794 .collect::<Vec<_>>();
795 let verifier =
796 rustls::client::WebPkiServerVerifier::builder_with_provider(
797 Arc::new(crate::tls::rustls_store(config.root_certs)?),
798 provider,
799 )
800 .with_crls(crls)
801 .build()
802 .map_err(|_| {
803 crate::error::builder("invalid TLS verification settings")
804 })?;
805 config_builder.with_webpki_verifier(verifier)
806 }
807 };
808
809 let mut tls = if let Some(id) = config.identity {
811 id.add_to_rustls(config_builder)?
812 } else {
813 config_builder.with_no_client_auth()
814 };
815
816 tls.enable_sni = config.tls_sni;
817
818 if config.tls_sslkeylogfile {
819 tls.key_log = Arc::new(rustls::KeyLogFile::new());
820 }
821
822 match config.http_version_pref {
824 HttpVersionPref::Http1 => {
825 tls.alpn_protocols = vec!["http/1.1".into()];
826 }
827 #[cfg(feature = "http2")]
828 HttpVersionPref::Http2 => {
829 tls.alpn_protocols = vec!["h2".into()];
830 }
831 #[cfg(feature = "http3")]
832 HttpVersionPref::Http3 => {
833 }
835 HttpVersionPref::All => {
836 tls.alpn_protocols = vec![
837 #[cfg(feature = "http2")]
838 "h2".into(),
839 "http/1.1".into(),
840 ];
841 }
842 }
843
844 #[cfg(feature = "http3")]
845 {
846 let mut h3_tls = tls.clone();
847 h3_tls.enable_early_data = config.tls_enable_early_data;
848
849 h3_tls.alpn_protocols = vec!["h3".into()];
851
852 h3_connector = build_h3_connector(
853 resolver.clone(),
854 h3_tls,
855 config.quic_max_idle_timeout,
856 config.quic_stream_receive_window,
857 config.quic_receive_window,
858 config.quic_send_window,
859 config.quic_congestion_bbr,
860 config.h3_max_field_section_size,
861 config.h3_send_grease,
862 config.local_address,
863 &config.http_version_pref,
864 )?;
865 }
866
867 ConnectorBuilder::new_rustls_tls(
868 http,
869 tls,
870 proxies.clone(),
871 user_agent(&config.headers),
872 config.local_address,
873 #[cfg(any(
874 target_os = "android",
875 target_os = "fuchsia",
876 target_os = "illumos",
877 target_os = "ios",
878 target_os = "linux",
879 target_os = "macos",
880 target_os = "solaris",
881 target_os = "tvos",
882 target_os = "visionos",
883 target_os = "watchos",
884 ))]
885 config.interface.as_deref(),
886 config.nodelay,
887 config.tls_info,
888 )
889 }
890 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
891 TlsBackend::UnknownPreconfigured => {
892 return Err(crate::error::builder(
893 "Unknown TLS backend passed to `use_preconfigured_tls`",
894 ));
895 }
896 }
897
898 #[cfg(not(feature = "__tls"))]
899 ConnectorBuilder::new(
900 http,
901 proxies.clone(),
902 config.local_address,
903 #[cfg(any(
904 target_os = "android",
905 target_os = "fuchsia",
906 target_os = "illumos",
907 target_os = "ios",
908 target_os = "linux",
909 target_os = "macos",
910 target_os = "solaris",
911 target_os = "tvos",
912 target_os = "visionos",
913 target_os = "watchos",
914 ))]
915 config.interface.as_deref(),
916 config.nodelay,
917 )
918 };
919
920 connector_builder.set_timeout(config.connect_timeout);
921 connector_builder.set_verbose(config.connection_verbose);
922 connector_builder.set_keepalive(config.tcp_keepalive);
923 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
924 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
925 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
926 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
927
928 #[cfg(feature = "socks")]
929 connector_builder.set_socks_resolver(resolver);
930
931 #[cfg(unix)]
935 connector_builder.set_unix_socket(config.unix_socket);
936 #[cfg(target_os = "windows")]
937 connector_builder.set_windows_named_pipe(config.windows_named_pipe.clone());
938
939 let mut builder =
940 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
941 #[cfg(feature = "http2")]
942 {
943 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
944 builder.http2_only(true);
945 }
946
947 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
948 {
949 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
950 }
951 if let Some(http2_initial_connection_window_size) =
952 config.http2_initial_connection_window_size
953 {
954 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
955 }
956 if config.http2_adaptive_window {
957 builder.http2_adaptive_window(true);
958 }
959 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
960 builder.http2_max_frame_size(http2_max_frame_size);
961 }
962 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
963 builder.http2_max_header_list_size(http2_max_header_list_size);
964 }
965 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
966 builder.http2_keep_alive_interval(http2_keep_alive_interval);
967 }
968 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
969 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
970 }
971 if config.http2_keep_alive_while_idle {
972 builder.http2_keep_alive_while_idle(true);
973 }
974 }
975
976 builder.timer(hyper_util::rt::TokioTimer::new());
977 builder.pool_timer(hyper_util::rt::TokioTimer::new());
978 builder.pool_idle_timeout(config.pool_idle_timeout);
979 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
980
981 if config.http09_responses {
982 builder.http09_responses(true);
983 }
984
985 if config.http1_title_case_headers {
986 builder.http1_title_case_headers(true);
987 }
988
989 if config.http1_allow_obsolete_multiline_headers_in_responses {
990 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
991 }
992
993 if config.http1_ignore_invalid_headers_in_responses {
994 builder.http1_ignore_invalid_headers_in_responses(true);
995 }
996
997 if config.http1_allow_spaces_after_header_name_in_responses {
998 builder.http1_allow_spaces_after_header_name_in_responses(true);
999 }
1000
1001 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
1002 let proxies_maybe_http_custom_headers =
1003 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
1004
1005 let redirect_policy_desc = if config.redirect_policy.is_default() {
1006 None
1007 } else {
1008 Some(format!("{:?}", &config.redirect_policy))
1009 };
1010
1011 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
1012 let hyper_service = HyperService {
1013 hyper: hyper_client,
1014 };
1015
1016 let redirect_policy = {
1017 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
1018 p.with_referer(config.referer)
1019 .with_https_only(config.https_only);
1020 p
1021 };
1022
1023 let retry_policy = config.retry_policy.into_policy();
1024
1025 let svc = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
1026
1027 #[cfg(feature = "cookies")]
1028 let svc = CookieService::new(svc, config.cookie_store.clone());
1029 let hyper = FollowRedirect::with_policy(svc, redirect_policy.clone());
1030 #[cfg(any(
1031 feature = "gzip",
1032 feature = "brotli",
1033 feature = "zstd",
1034 feature = "deflate"
1035 ))]
1036 let hyper = Decompression::new(hyper)
1037 .no_gzip()
1040 .no_deflate()
1041 .no_br()
1042 .no_zstd();
1043 #[cfg(feature = "gzip")]
1044 let hyper = hyper.gzip(config.accepts.gzip);
1045 #[cfg(feature = "brotli")]
1046 let hyper = hyper.br(config.accepts.brotli);
1047 #[cfg(feature = "zstd")]
1048 let hyper = hyper.zstd(config.accepts.zstd);
1049 #[cfg(feature = "deflate")]
1050 let hyper = hyper.deflate(config.accepts.deflate);
1051
1052 Ok(Client {
1053 inner: Arc::new(ClientRef {
1054 accepts: config.accepts,
1055 #[cfg(feature = "cookies")]
1056 cookie_store: config.cookie_store.clone(),
1057 #[cfg(feature = "http3")]
1060 h3_client: match h3_connector {
1061 Some(h3_connector) => {
1062 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1063 let svc = tower::retry::Retry::new(retry_policy, h3_service);
1064 #[cfg(feature = "cookies")]
1065 let svc = CookieService::new(svc, config.cookie_store);
1066 let svc = FollowRedirect::with_policy(svc, redirect_policy);
1067 #[cfg(any(
1068 feature = "gzip",
1069 feature = "brotli",
1070 feature = "zstd",
1071 feature = "deflate"
1072 ))]
1073 let svc = Decompression::new(svc)
1074 .no_gzip()
1077 .no_deflate()
1078 .no_br()
1079 .no_zstd();
1080 #[cfg(feature = "gzip")]
1081 let svc = svc.gzip(config.accepts.gzip);
1082 #[cfg(feature = "brotli")]
1083 let svc = svc.br(config.accepts.brotli);
1084 #[cfg(feature = "zstd")]
1085 let svc = svc.zstd(config.accepts.zstd);
1086 #[cfg(feature = "deflate")]
1087 let svc = svc.deflate(config.accepts.deflate);
1088 Some(svc)
1089 }
1090 None => None,
1091 },
1092 headers: config.headers,
1093 referer: config.referer,
1094 read_timeout: config.read_timeout,
1095 total_timeout: RequestConfig::new(config.timeout),
1096 hyper,
1097 proxies,
1098 proxies_maybe_http_auth,
1099 proxies_maybe_http_custom_headers,
1100 https_only: config.https_only,
1101 redirect_policy_desc,
1102 }),
1103 })
1104 }
1105
1106 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1129 where
1130 V: TryInto<HeaderValue>,
1131 V::Error: Into<http::Error>,
1132 {
1133 match value.try_into() {
1134 Ok(value) => {
1135 self.config.headers.insert(USER_AGENT, value);
1136 }
1137 Err(e) => {
1138 self.config.error = Some(crate::error::builder(e.into()));
1139 }
1140 };
1141 self
1142 }
1143 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1167 for (key, value) in headers.iter() {
1168 self.config.headers.insert(key, value.clone());
1169 }
1170 self
1171 }
1172
1173 #[cfg(feature = "cookies")]
1188 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1189 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1190 if enable {
1191 self.cookie_provider(Arc::new(cookie::Jar::default()))
1192 } else {
1193 self.config.cookie_store = None;
1194 self
1195 }
1196 }
1197
1198 #[cfg(feature = "cookies")]
1212 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1213 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1214 mut self,
1215 cookie_store: Arc<C>,
1216 ) -> ClientBuilder {
1217 self.config.cookie_store = Some(cookie_store as _);
1218 self
1219 }
1220
1221 #[cfg(feature = "gzip")]
1238 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1239 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1240 self.config.accepts.gzip = enable;
1241 self
1242 }
1243
1244 #[cfg(feature = "brotli")]
1261 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1262 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1263 self.config.accepts.brotli = enable;
1264 self
1265 }
1266
1267 #[cfg(feature = "zstd")]
1284 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1285 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1286 self.config.accepts.zstd = enable;
1287 self
1288 }
1289
1290 #[cfg(feature = "deflate")]
1307 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1308 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1309 self.config.accepts.deflate = enable;
1310 self
1311 }
1312
1313 pub fn no_gzip(self) -> ClientBuilder {
1319 #[cfg(feature = "gzip")]
1320 {
1321 self.gzip(false)
1322 }
1323
1324 #[cfg(not(feature = "gzip"))]
1325 {
1326 self
1327 }
1328 }
1329
1330 pub fn no_brotli(self) -> ClientBuilder {
1336 #[cfg(feature = "brotli")]
1337 {
1338 self.brotli(false)
1339 }
1340
1341 #[cfg(not(feature = "brotli"))]
1342 {
1343 self
1344 }
1345 }
1346
1347 pub fn no_zstd(self) -> ClientBuilder {
1353 #[cfg(feature = "zstd")]
1354 {
1355 self.zstd(false)
1356 }
1357
1358 #[cfg(not(feature = "zstd"))]
1359 {
1360 self
1361 }
1362 }
1363
1364 pub fn no_deflate(self) -> ClientBuilder {
1370 #[cfg(feature = "deflate")]
1371 {
1372 self.deflate(false)
1373 }
1374
1375 #[cfg(not(feature = "deflate"))]
1376 {
1377 self
1378 }
1379 }
1380
1381 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1387 self.config.redirect_policy = policy;
1388 self
1389 }
1390
1391 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1395 self.config.referer = enable;
1396 self
1397 }
1398
1399 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1406 self.config.retry_policy = policy;
1407 self
1408 }
1409
1410 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1418 self.config.proxies.push(proxy.into_matcher());
1419 self.config.auto_sys_proxy = false;
1420 self
1421 }
1422
1423 pub fn no_proxy(mut self) -> ClientBuilder {
1431 self.config.proxies.clear();
1432 self.config.auto_sys_proxy = false;
1433 self
1434 }
1435
1436 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1445 self.config.timeout = Some(timeout);
1446 self
1447 }
1448
1449 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1457 self.config.read_timeout = Some(timeout);
1458 self
1459 }
1460
1461 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1470 self.config.connect_timeout = Some(timeout);
1471 self
1472 }
1473
1474 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1481 self.config.connection_verbose = verbose;
1482 self
1483 }
1484
1485 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1493 where
1494 D: Into<Option<Duration>>,
1495 {
1496 self.config.pool_idle_timeout = val.into();
1497 self
1498 }
1499
1500 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1504 self.config.pool_max_idle_per_host = max;
1505 self
1506 }
1507
1508 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1510 self.config.http1_title_case_headers = true;
1511 self
1512 }
1513
1514 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1520 mut self,
1521 value: bool,
1522 ) -> ClientBuilder {
1523 self.config
1524 .http1_allow_obsolete_multiline_headers_in_responses = value;
1525 self
1526 }
1527
1528 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1530 self.config.http1_ignore_invalid_headers_in_responses = value;
1531 self
1532 }
1533
1534 pub fn http1_allow_spaces_after_header_name_in_responses(
1540 mut self,
1541 value: bool,
1542 ) -> ClientBuilder {
1543 self.config
1544 .http1_allow_spaces_after_header_name_in_responses = value;
1545 self
1546 }
1547
1548 pub fn http1_only(mut self) -> ClientBuilder {
1550 self.config.http_version_pref = HttpVersionPref::Http1;
1551 self
1552 }
1553
1554 pub fn http09_responses(mut self) -> ClientBuilder {
1556 self.config.http09_responses = true;
1557 self
1558 }
1559
1560 #[cfg(feature = "http2")]
1562 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1563 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1564 self.config.http_version_pref = HttpVersionPref::Http2;
1565 self
1566 }
1567
1568 #[cfg(feature = "http3")]
1570 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1571 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1572 self.config.http_version_pref = HttpVersionPref::Http3;
1573 self
1574 }
1575
1576 #[cfg(feature = "http2")]
1580 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1581 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1582 self.config.http2_initial_stream_window_size = sz.into();
1583 self
1584 }
1585
1586 #[cfg(feature = "http2")]
1590 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1591 pub fn http2_initial_connection_window_size(
1592 mut self,
1593 sz: impl Into<Option<u32>>,
1594 ) -> ClientBuilder {
1595 self.config.http2_initial_connection_window_size = sz.into();
1596 self
1597 }
1598
1599 #[cfg(feature = "http2")]
1604 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1605 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1606 self.config.http2_adaptive_window = enabled;
1607 self
1608 }
1609
1610 #[cfg(feature = "http2")]
1614 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1615 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1616 self.config.http2_max_frame_size = sz.into();
1617 self
1618 }
1619
1620 #[cfg(feature = "http2")]
1624 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1625 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1626 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1627 self
1628 }
1629
1630 #[cfg(feature = "http2")]
1635 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1636 pub fn http2_keep_alive_interval(
1637 mut self,
1638 interval: impl Into<Option<Duration>>,
1639 ) -> ClientBuilder {
1640 self.config.http2_keep_alive_interval = interval.into();
1641 self
1642 }
1643
1644 #[cfg(feature = "http2")]
1650 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1651 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1652 self.config.http2_keep_alive_timeout = Some(timeout);
1653 self
1654 }
1655
1656 #[cfg(feature = "http2")]
1663 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1664 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1665 self.config.http2_keep_alive_while_idle = enabled;
1666 self
1667 }
1668
1669 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1675 self.config.nodelay = enabled;
1676 self
1677 }
1678
1679 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1694 where
1695 T: Into<Option<IpAddr>>,
1696 {
1697 self.config.local_address = addr.into();
1698 self
1699 }
1700
1701 #[cfg(any(
1734 target_os = "android",
1735 target_os = "fuchsia",
1736 target_os = "illumos",
1737 target_os = "ios",
1738 target_os = "linux",
1739 target_os = "macos",
1740 target_os = "solaris",
1741 target_os = "tvos",
1742 target_os = "visionos",
1743 target_os = "watchos",
1744 ))]
1745 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1746 self.config.interface = Some(interface.to_string());
1747 self
1748 }
1749
1750 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1754 where
1755 D: Into<Option<Duration>>,
1756 {
1757 self.config.tcp_keepalive = val.into();
1758 self
1759 }
1760
1761 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1765 where
1766 D: Into<Option<Duration>>,
1767 {
1768 self.config.tcp_keepalive_interval = val.into();
1769 self
1770 }
1771
1772 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1776 where
1777 C: Into<Option<u32>>,
1778 {
1779 self.config.tcp_keepalive_retries = retries.into();
1780 self
1781 }
1782
1783 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1790 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1791 where
1792 D: Into<Option<Duration>>,
1793 {
1794 self.config.tcp_user_timeout = val.into();
1795 self
1796 }
1797
1798 #[cfg(unix)]
1812 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1813 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1814 self
1815 }
1816
1817 #[cfg(target_os = "windows")]
1829 pub fn windows_named_pipe(mut self, pipe: impl WindowsNamedPipeProvider) -> ClientBuilder {
1830 self.config.windows_named_pipe = Some(
1831 pipe.reqwest_windows_named_pipe_path(crate::connect::windows_named_pipe::Internal)
1832 .into(),
1833 );
1834 self
1835 }
1836
1837 #[cfg(feature = "__tls")]
1856 #[cfg_attr(
1857 docsrs,
1858 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1859 )]
1860 pub fn tls_certs_merge(
1861 mut self,
1862 certs: impl IntoIterator<Item = Certificate>,
1863 ) -> ClientBuilder {
1864 self.config.root_certs.extend(certs);
1865 self
1866 }
1867
1868 #[cfg(feature = "__tls")]
1881 #[cfg_attr(
1882 docsrs,
1883 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1884 )]
1885 pub fn tls_certs_only(mut self, certs: impl IntoIterator<Item = Certificate>) -> ClientBuilder {
1886 self.config.root_certs.extend(certs);
1887 self.config.tls_certs_only = true;
1888 self
1889 }
1890
1891 #[cfg(feature = "__tls")]
1894 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1895 self.config.root_certs.push(cert);
1896 self
1897 }
1898
1899 #[cfg(feature = "__rustls")]
1913 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1914 pub fn tls_crls_only(
1915 mut self,
1916 crls: impl IntoIterator<Item = CertificateRevocationList>,
1917 ) -> ClientBuilder {
1918 self.config.crls.extend(crls);
1919 self
1920 }
1921
1922 #[cfg(feature = "__rustls")]
1924 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1925 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1926 self.config.crls.push(crl);
1927 self
1928 }
1929
1930 #[cfg(feature = "__rustls")]
1932 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1933 pub fn add_crls(
1934 mut self,
1935 crls: impl IntoIterator<Item = CertificateRevocationList>,
1936 ) -> ClientBuilder {
1937 self.config.crls.extend(crls);
1938 self
1939 }
1940
1941 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
1948 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
1949 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1950 self.config.identity = Some(identity);
1951 self
1952 }
1953
1954 #[cfg(feature = "__tls")]
1975 #[cfg_attr(
1976 docsrs,
1977 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1978 )]
1979 pub fn tls_danger_accept_invalid_hostnames(
1980 mut self,
1981 accept_invalid_hostname: bool,
1982 ) -> ClientBuilder {
1983 self.config.hostname_verification = !accept_invalid_hostname;
1984 self
1985 }
1986
1987 #[cfg(feature = "__tls")]
1989 pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder {
1990 self.tls_danger_accept_invalid_hostnames(accept_invalid_hostname)
1991 }
1992
1993 #[cfg(feature = "__tls")]
2010 #[cfg_attr(
2011 docsrs,
2012 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2013 )]
2014 pub fn tls_danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
2015 self.config.certs_verification = !accept_invalid_certs;
2016 self
2017 }
2018
2019 #[cfg(feature = "__tls")]
2021 pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder {
2022 self.tls_danger_accept_invalid_certs(accept_invalid_certs)
2023 }
2024
2025 #[cfg(feature = "__tls")]
2034 #[cfg_attr(
2035 docsrs,
2036 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2037 )]
2038 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
2039 self.config.tls_sni = tls_sni;
2040 self
2041 }
2042
2043 #[cfg(feature = "__rustls")]
2055 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2056 pub fn tls_sslkeylogfile(mut self, on: bool) -> ClientBuilder {
2057 self.config.tls_sslkeylogfile = on;
2058 self
2059 }
2060
2061 #[cfg(feature = "__tls")]
2074 #[cfg_attr(
2075 docsrs,
2076 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2077 )]
2078 pub fn tls_version_min(mut self, version: tls::Version) -> ClientBuilder {
2079 self.config.min_tls_version = Some(version);
2080 self
2081 }
2082
2083 #[cfg(feature = "__tls")]
2085 pub fn min_tls_version(self, version: tls::Version) -> ClientBuilder {
2086 self.tls_version_min(version)
2087 }
2088
2089 #[cfg(feature = "__tls")]
2107 #[cfg_attr(
2108 docsrs,
2109 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2110 )]
2111 pub fn tls_version_max(mut self, version: tls::Version) -> ClientBuilder {
2112 self.config.max_tls_version = Some(version);
2113 self
2114 }
2115
2116 #[cfg(feature = "__tls")]
2118 pub fn max_tls_version(self, version: tls::Version) -> ClientBuilder {
2119 self.tls_version_max(version)
2120 }
2121
2122 #[cfg(feature = "__native-tls")]
2131 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2132 pub fn tls_backend_native(mut self) -> ClientBuilder {
2133 self.config.tls = TlsBackend::NativeTls;
2134 self
2135 }
2136
2137 #[cfg(feature = "__native-tls")]
2139 pub fn use_native_tls(self) -> ClientBuilder {
2140 self.tls_backend_native()
2141 }
2142
2143 #[cfg(feature = "__rustls")]
2152 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2153 pub fn tls_backend_rustls(mut self) -> ClientBuilder {
2154 self.config.tls = TlsBackend::Rustls;
2155 self
2156 }
2157
2158 #[cfg(feature = "__rustls")]
2160 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2161 pub fn use_rustls_tls(self) -> ClientBuilder {
2162 self.tls_backend_rustls()
2163 }
2164
2165 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2191 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
2192 pub fn tls_backend_preconfigured(mut self, tls: impl Any) -> ClientBuilder {
2193 let mut tls = Some(tls);
2194 #[cfg(feature = "__native-tls")]
2195 {
2196 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2197 let tls = conn.take().expect("is definitely Some");
2198 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2199 self.config.tls = tls;
2200 return self;
2201 }
2202 }
2203 #[cfg(feature = "__rustls")]
2204 {
2205 if let Some(conn) =
2206 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2207 {
2208 let tls = conn.take().expect("is definitely Some");
2209 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2210 self.config.tls = tls;
2211 return self;
2212 }
2213 }
2214
2215 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2217 self
2218 }
2219
2220 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2222 pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder {
2223 self.tls_backend_preconfigured(tls)
2224 }
2225
2226 #[cfg(feature = "__tls")]
2233 #[cfg_attr(
2234 docsrs,
2235 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2236 )]
2237 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2238 self.config.tls_info = tls_info;
2239 self
2240 }
2241
2242 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2246 self.config.https_only = enabled;
2247 self
2248 }
2249
2250 #[cfg(feature = "hickory-dns")]
2264 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2265 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2266 self.config.hickory_dns = enable;
2267 self
2268 }
2269
2270 pub fn no_hickory_dns(self) -> ClientBuilder {
2276 #[cfg(feature = "hickory-dns")]
2277 {
2278 self.hickory_dns(false)
2279 }
2280
2281 #[cfg(not(feature = "hickory-dns"))]
2282 {
2283 self
2284 }
2285 }
2286
2287 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2292 self.resolve_to_addrs(domain, &[addr])
2293 }
2294
2295 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2300 self.config
2301 .dns_overrides
2302 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2303 self
2304 }
2305
2306 pub fn dns_resolver<R>(mut self, resolver: R) -> ClientBuilder
2311 where
2312 R: crate::dns::resolve::IntoResolve,
2313 {
2314 self.config.dns_resolver = Some(resolver.into_resolve());
2315 self
2316 }
2317
2318 #[cfg(feature = "http3")]
2323 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2324 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2325 self.config.tls_enable_early_data = enabled;
2326 self
2327 }
2328
2329 #[cfg(feature = "http3")]
2335 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2336 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2337 self.config.quic_max_idle_timeout = Some(value);
2338 self
2339 }
2340
2341 #[cfg(feature = "http3")]
2352 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2353 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2354 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2355 self
2356 }
2357
2358 #[cfg(feature = "http3")]
2369 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2370 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2371 self.config.quic_receive_window = Some(value.try_into().unwrap());
2372 self
2373 }
2374
2375 #[cfg(feature = "http3")]
2381 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2382 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2383 self.config.quic_send_window = Some(value);
2384 self
2385 }
2386
2387 #[cfg(feature = "http3")]
2395 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2396 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2397 self.config.quic_congestion_bbr = true;
2398 self
2399 }
2400
2401 #[cfg(feature = "http3")]
2411 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2412 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2413 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2414 self
2415 }
2416
2417 #[cfg(feature = "http3")]
2429 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2430 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2431 self.config.h3_send_grease = Some(enabled);
2432 self
2433 }
2434
2435 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2459 where
2460 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2461 L::Service:
2462 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2463 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2464 {
2465 let layer = BoxCloneSyncServiceLayer::new(layer);
2466
2467 self.config.connector_layers.push(layer);
2468
2469 self
2470 }
2471}
2472
2473type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2474
2475impl Default for Client {
2476 fn default() -> Self {
2477 Self::new()
2478 }
2479}
2480
2481#[cfg(feature = "__rustls")]
2482fn default_rustls_crypto_provider() -> Arc<rustls::crypto::CryptoProvider> {
2483 #[cfg(not(feature = "__rustls-aws-lc-rs"))]
2484 panic!(
2485 "No rustls crypto provider is configured. \
2486 When using the `rustls-no-provider` feature you must install a \
2487 crypto provider before building a Client. For example: \
2488 `rustls::crypto::aws_lc_rs::default_provider().install_default().unwrap();` \
2489 See https://docs.rs/rustls/latest/rustls/#cryptography-providers for details."
2490 );
2491
2492 #[cfg(feature = "__rustls-aws-lc-rs")]
2493 Arc::new(rustls::crypto::aws_lc_rs::default_provider())
2494}
2495
2496impl Client {
2497 pub fn new() -> Client {
2507 ClientBuilder::new().build().expect("Client::new()")
2508 }
2509
2510 pub fn builder() -> ClientBuilder {
2514 ClientBuilder::new()
2515 }
2516
2517 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2523 self.request(Method::GET, url)
2524 }
2525
2526 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2532 self.request(Method::POST, url)
2533 }
2534
2535 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2541 self.request(Method::PUT, url)
2542 }
2543
2544 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2550 self.request(Method::PATCH, url)
2551 }
2552
2553 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2559 self.request(Method::DELETE, url)
2560 }
2561
2562 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2568 self.request(Method::HEAD, url)
2569 }
2570
2571 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2580 let req = url.into_url().map(move |url| Request::new(method, url));
2581 RequestBuilder::new(self.clone(), req)
2582 }
2583
2584 pub fn execute(
2597 &self,
2598 request: Request,
2599 ) -> impl Future<Output = Result<Response, crate::Error>> {
2600 self.execute_request(request)
2601 }
2602
2603 pub(super) fn execute_request(&self, req: Request) -> Pending {
2604 let (method, url, mut headers, body, version, extensions) = req.pieces();
2605 if url.scheme() != "http" && url.scheme() != "https" {
2606 return Pending::new_err(error::url_bad_scheme(url));
2607 }
2608
2609 if self.inner.https_only && url.scheme() != "https" {
2611 return Pending::new_err(error::url_bad_scheme(url));
2612 }
2613
2614 for (key, value) in &self.inner.headers {
2617 if let Entry::Vacant(entry) = headers.entry(key) {
2618 entry.insert(value.clone());
2619 }
2620 }
2621
2622 let uri = match try_uri(&url) {
2623 Ok(uri) => uri,
2624 _ => return Pending::new_err(error::url_invalid_uri(url)),
2625 };
2626
2627 let body = body.unwrap_or_else(Body::empty);
2628
2629 self.proxy_auth(&uri, &mut headers);
2630 self.proxy_custom_headers(&uri, &mut headers);
2631
2632 let builder = hyper::Request::builder()
2633 .method(method.clone())
2634 .uri(uri)
2635 .version(version);
2636
2637 let in_flight = match version {
2638 #[cfg(feature = "http3")]
2639 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2640 let mut req = builder.body(body).expect("valid request parts");
2641 *req.headers_mut() = headers.clone();
2642 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2643 ResponseFuture::H3(h3.call(req))
2644 }
2645 _ => {
2646 let mut req = builder.body(body).expect("valid request parts");
2647 *req.headers_mut() = headers.clone();
2648 let mut hyper = self.inner.hyper.clone();
2649 ResponseFuture::Default(hyper.call(req))
2650 }
2651 };
2652
2653 let total_timeout = self
2654 .inner
2655 .total_timeout
2656 .fetch(&extensions)
2657 .copied()
2658 .map(tokio::time::sleep)
2659 .map(Box::pin);
2660
2661 let read_timeout_fut = self
2662 .inner
2663 .read_timeout
2664 .map(tokio::time::sleep)
2665 .map(Box::pin);
2666
2667 Pending {
2668 inner: PendingInner::Request(Box::pin(PendingRequest {
2669 method,
2670 url,
2671 headers,
2672
2673 client: self.inner.clone(),
2674
2675 in_flight,
2676 total_timeout,
2677 read_timeout_fut,
2678 read_timeout: self.inner.read_timeout,
2679 })),
2680 }
2681 }
2682
2683 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2684 if !self.inner.proxies_maybe_http_auth {
2685 return;
2686 }
2687
2688 if dst.scheme() != Some(&Scheme::HTTP) {
2692 return;
2693 }
2694
2695 if headers.contains_key(PROXY_AUTHORIZATION) {
2696 return;
2697 }
2698
2699 for proxy in self.inner.proxies.iter() {
2700 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2701 headers.insert(PROXY_AUTHORIZATION, header);
2702 break;
2703 }
2704 }
2705 }
2706
2707 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2708 if !self.inner.proxies_maybe_http_custom_headers {
2709 return;
2710 }
2711
2712 if dst.scheme() != Some(&Scheme::HTTP) {
2713 return;
2714 }
2715
2716 for proxy in self.inner.proxies.iter() {
2717 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2718 iter.iter().for_each(|(key, value)| {
2719 headers.insert(key, value.clone());
2720 });
2721 break;
2722 }
2723 }
2724 }
2725}
2726
2727impl fmt::Debug for Client {
2728 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2729 let mut builder = f.debug_struct("Client");
2730 self.inner.fmt_fields(&mut builder);
2731 builder.finish()
2732 }
2733}
2734
2735impl tower_service::Service<Request> for Client {
2736 type Response = Response;
2737 type Error = crate::Error;
2738 type Future = Pending;
2739
2740 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2741 Poll::Ready(Ok(()))
2742 }
2743
2744 fn call(&mut self, req: Request) -> Self::Future {
2745 self.execute_request(req)
2746 }
2747}
2748
2749impl tower_service::Service<Request> for &'_ Client {
2750 type Response = Response;
2751 type Error = crate::Error;
2752 type Future = Pending;
2753
2754 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2755 Poll::Ready(Ok(()))
2756 }
2757
2758 fn call(&mut self, req: Request) -> Self::Future {
2759 self.execute_request(req)
2760 }
2761}
2762
2763impl fmt::Debug for ClientBuilder {
2764 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2765 let mut builder = f.debug_struct("ClientBuilder");
2766 self.config.fmt_fields(&mut builder);
2767 builder.finish()
2768 }
2769}
2770
2771impl Config {
2772 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2773 #[cfg(feature = "cookies")]
2777 {
2778 if let Some(_) = self.cookie_store {
2779 f.field("cookie_store", &true);
2780 }
2781 }
2782
2783 f.field("accepts", &self.accepts);
2784
2785 if !self.proxies.is_empty() {
2786 f.field("proxies", &self.proxies);
2787 }
2788
2789 if !self.redirect_policy.is_default() {
2790 f.field("redirect_policy", &self.redirect_policy);
2791 }
2792
2793 if self.referer {
2794 f.field("referer", &true);
2795 }
2796
2797 f.field("default_headers", &self.headers);
2798
2799 if self.http1_title_case_headers {
2800 f.field("http1_title_case_headers", &true);
2801 }
2802
2803 if self.http1_allow_obsolete_multiline_headers_in_responses {
2804 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2805 }
2806
2807 if self.http1_ignore_invalid_headers_in_responses {
2808 f.field("http1_ignore_invalid_headers_in_responses", &true);
2809 }
2810
2811 if self.http1_allow_spaces_after_header_name_in_responses {
2812 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2813 }
2814
2815 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2816 f.field("http1_only", &true);
2817 }
2818
2819 #[cfg(feature = "http2")]
2820 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2821 f.field("http2_prior_knowledge", &true);
2822 }
2823
2824 if let Some(ref d) = self.connect_timeout {
2825 f.field("connect_timeout", d);
2826 }
2827
2828 if let Some(ref d) = self.timeout {
2829 f.field("timeout", d);
2830 }
2831
2832 if let Some(ref v) = self.local_address {
2833 f.field("local_address", v);
2834 }
2835
2836 #[cfg(any(
2837 target_os = "android",
2838 target_os = "fuchsia",
2839 target_os = "illumos",
2840 target_os = "ios",
2841 target_os = "linux",
2842 target_os = "macos",
2843 target_os = "solaris",
2844 target_os = "tvos",
2845 target_os = "visionos",
2846 target_os = "watchos",
2847 ))]
2848 if let Some(ref v) = self.interface {
2849 f.field("interface", v);
2850 }
2851
2852 if self.nodelay {
2853 f.field("tcp_nodelay", &true);
2854 }
2855
2856 #[cfg(feature = "__tls")]
2857 {
2858 if !self.hostname_verification {
2859 f.field("tls_danger_accept_invalid_hostnames", &true);
2860 }
2861 }
2862
2863 #[cfg(feature = "__tls")]
2864 {
2865 if !self.certs_verification {
2866 f.field("tls_danger_accept_invalid_certs", &true);
2867 }
2868
2869 if let Some(ref min_tls_version) = self.min_tls_version {
2870 f.field("tls_version_min", min_tls_version);
2871 }
2872
2873 if let Some(ref max_tls_version) = self.max_tls_version {
2874 f.field("tls_version_max", max_tls_version);
2875 }
2876
2877 f.field("tls_sni", &self.tls_sni);
2878
2879 f.field("tls_info", &self.tls_info);
2880 }
2881
2882 #[cfg(feature = "__rustls")]
2883 {
2884 f.field("tls_sslkeylogfile", &self.tls_sslkeylogfile);
2885 }
2886
2887 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2888 {
2889 f.field("tls_backend", &self.tls);
2890 }
2891
2892 if !self.dns_overrides.is_empty() {
2893 f.field("dns_overrides", &self.dns_overrides);
2894 }
2895
2896 #[cfg(feature = "http3")]
2897 {
2898 if self.tls_enable_early_data {
2899 f.field("tls_enable_early_data", &true);
2900 }
2901 }
2902
2903 #[cfg(unix)]
2904 if let Some(ref p) = self.unix_socket {
2905 f.field("unix_socket", p);
2906 }
2907 }
2908}
2909
2910#[cfg(not(feature = "cookies"))]
2911type MaybeCookieService<T> = T;
2912
2913#[cfg(feature = "cookies")]
2914type MaybeCookieService<T> = CookieService<T>;
2915
2916#[cfg(not(any(
2917 feature = "gzip",
2918 feature = "brotli",
2919 feature = "zstd",
2920 feature = "deflate"
2921)))]
2922type MaybeDecompression<T> = T;
2923
2924#[cfg(any(
2925 feature = "gzip",
2926 feature = "brotli",
2927 feature = "zstd",
2928 feature = "deflate"
2929))]
2930type MaybeDecompression<T> = Decompression<T>;
2931
2932type LayeredService<T> = MaybeDecompression<
2933 FollowRedirect<
2934 MaybeCookieService<tower::retry::Retry<crate::retry::Policy, T>>,
2935 TowerRedirectPolicy,
2936 >,
2937>;
2938type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
2939
2940struct ClientRef {
2941 accepts: Accepts,
2942 #[cfg(feature = "cookies")]
2943 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2944 headers: HeaderMap,
2945 hyper: LayeredService<HyperService>,
2946 #[cfg(feature = "http3")]
2947 h3_client: Option<LayeredService<H3Client>>,
2948 referer: bool,
2949 total_timeout: RequestConfig<TotalTimeout>,
2950 read_timeout: Option<Duration>,
2951 proxies: Arc<Vec<ProxyMatcher>>,
2952 proxies_maybe_http_auth: bool,
2953 proxies_maybe_http_custom_headers: bool,
2954 https_only: bool,
2955 redirect_policy_desc: Option<String>,
2956}
2957
2958impl ClientRef {
2959 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2960 #[cfg(feature = "cookies")]
2964 {
2965 if let Some(_) = self.cookie_store {
2966 f.field("cookie_store", &true);
2967 }
2968 }
2969
2970 f.field("accepts", &self.accepts);
2971
2972 if !self.proxies.is_empty() {
2973 f.field("proxies", &self.proxies);
2974 }
2975
2976 if let Some(s) = &self.redirect_policy_desc {
2977 f.field("redirect_policy", s);
2978 }
2979
2980 if self.referer {
2981 f.field("referer", &true);
2982 }
2983
2984 f.field("default_headers", &self.headers);
2985
2986 self.total_timeout.fmt_as_field(f);
2987
2988 if let Some(ref d) = self.read_timeout {
2989 f.field("read_timeout", d);
2990 }
2991 }
2992}
2993
2994pin_project! {
2995 pub struct Pending {
2996 #[pin]
2997 inner: PendingInner,
2998 }
2999}
3000
3001enum PendingInner {
3002 Request(Pin<Box<PendingRequest>>),
3003 Error(Option<crate::Error>),
3004}
3005
3006pin_project! {
3007 struct PendingRequest {
3008 method: Method,
3009 url: Url,
3010 headers: HeaderMap,
3011
3012 client: Arc<ClientRef>,
3013
3014 #[pin]
3015 in_flight: ResponseFuture,
3016 #[pin]
3017 total_timeout: Option<Pin<Box<Sleep>>>,
3018 #[pin]
3019 read_timeout_fut: Option<Pin<Box<Sleep>>>,
3020 read_timeout: Option<Duration>,
3021 }
3022}
3023
3024enum ResponseFuture {
3025 Default(LayeredFuture<HyperService>),
3026 #[cfg(feature = "http3")]
3027 H3(LayeredFuture<H3Client>),
3028}
3029
3030impl PendingRequest {
3031 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
3032 self.project().in_flight
3033 }
3034
3035 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3036 self.project().total_timeout
3037 }
3038
3039 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3040 self.project().read_timeout_fut
3041 }
3042}
3043
3044impl Pending {
3045 pub(super) fn new_err(err: crate::Error) -> Pending {
3046 Pending {
3047 inner: PendingInner::Error(Some(err)),
3048 }
3049 }
3050
3051 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
3052 self.project().inner
3053 }
3054}
3055
3056impl Future for Pending {
3057 type Output = Result<Response, crate::Error>;
3058
3059 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3060 let inner = self.inner();
3061 match inner.get_mut() {
3062 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
3063 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
3064 .take()
3065 .expect("Pending error polled more than once"))),
3066 }
3067 }
3068}
3069
3070impl Future for PendingRequest {
3071 type Output = Result<Response, crate::Error>;
3072
3073 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3074 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
3075 if let Poll::Ready(()) = delay.poll(cx) {
3076 return Poll::Ready(Err(
3077 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3078 ));
3079 }
3080 }
3081
3082 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
3083 if let Poll::Ready(()) = delay.poll(cx) {
3084 return Poll::Ready(Err(
3085 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3086 ));
3087 }
3088 }
3089
3090 let res = match self.as_mut().in_flight().get_mut() {
3091 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
3092 Err(e) => {
3093 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
3094 }
3095 Ok(res) => res.map(super::body::boxed),
3096 },
3097 #[cfg(feature = "http3")]
3098 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
3099 Err(e) => {
3100 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
3101 }
3102 Ok(res) => res.map(super::body::boxed),
3103 },
3104 };
3105
3106 if let Some(url) = &res
3107 .extensions()
3108 .get::<tower_http::follow_redirect::RequestUri>()
3109 {
3110 self.url = match Url::parse(&url.0.to_string()) {
3111 Ok(url) => url,
3112 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3113 }
3114 };
3115
3116 let res = Response::new(
3117 res,
3118 self.url.clone(),
3119 self.total_timeout.take(),
3120 self.read_timeout,
3121 );
3122 Poll::Ready(Ok(res))
3123 }
3124}
3125
3126impl fmt::Debug for Pending {
3127 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3128 match self.inner {
3129 PendingInner::Request(ref req) => f
3130 .debug_struct("Pending")
3131 .field("method", &req.method)
3132 .field("url", &req.url)
3133 .finish(),
3134 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3135 }
3136 }
3137}
3138
3139#[cfg(test)]
3140mod tests {
3141 #![cfg(not(feature = "rustls-no-provider"))]
3142
3143 #[tokio::test]
3144 async fn execute_request_rejects_invalid_urls() {
3145 let url_str = "hxxps://www.rust-lang.org/";
3146 let url = url::Url::parse(url_str).unwrap();
3147 let result = crate::get(url.clone()).await;
3148
3149 assert!(result.is_err());
3150 let err = result.err().unwrap();
3151 assert!(err.is_builder());
3152 assert_eq!(url_str, err.url().unwrap().as_str());
3153 }
3154
3155 #[tokio::test]
3157 async fn execute_request_rejects_invalid_hostname() {
3158 let url_str = "https://{{hostname}}/";
3159 let url = url::Url::parse(url_str).unwrap();
3160 let result = crate::get(url.clone()).await;
3161
3162 assert!(result.is_err());
3163 let err = result.err().unwrap();
3164 assert!(err.is_builder());
3165 assert_eq!(url_str, err.url().unwrap().as_str());
3166 }
3167
3168 #[test]
3169 fn test_future_size() {
3170 let s = std::mem::size_of::<super::Pending>();
3171 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3172 }
3173}