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 connect_timeout: Option<Duration>,
171 connection_verbose: bool,
172 pool_idle_timeout: Option<Duration>,
173 pool_max_idle_per_host: usize,
174 tcp_keepalive: Option<Duration>,
175 tcp_keepalive_interval: Option<Duration>,
176 tcp_keepalive_retries: Option<u32>,
177 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
178 tcp_user_timeout: Option<Duration>,
179 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
180 identity: Option<Identity>,
181 proxies: Vec<ProxyMatcher>,
182 auto_sys_proxy: bool,
183 redirect_policy: redirect::Policy,
184 retry_policy: crate::retry::Builder,
185 referer: bool,
186 read_timeout: Option<Duration>,
187 timeout: Option<Duration>,
188 #[cfg(feature = "__tls")]
189 root_certs: Vec<Certificate>,
190 #[cfg(feature = "__tls")]
191 tls_certs_only: bool,
192 #[cfg(feature = "__rustls")]
193 crls: Vec<CertificateRevocationList>,
194 #[cfg(feature = "__tls")]
195 min_tls_version: Option<tls::Version>,
196 #[cfg(feature = "__tls")]
197 max_tls_version: Option<tls::Version>,
198 #[cfg(feature = "__tls")]
199 tls_info: bool,
200 #[cfg(feature = "__tls")]
201 tls: TlsBackend,
202 connector_layers: Vec<BoxedConnectorLayer>,
203 http_version_pref: HttpVersionPref,
204 http09_responses: bool,
205 http1_title_case_headers: bool,
206 http1_allow_obsolete_multiline_headers_in_responses: bool,
207 http1_ignore_invalid_headers_in_responses: bool,
208 http1_allow_spaces_after_header_name_in_responses: bool,
209 #[cfg(feature = "http2")]
210 http2_initial_stream_window_size: Option<u32>,
211 #[cfg(feature = "http2")]
212 http2_initial_connection_window_size: Option<u32>,
213 #[cfg(feature = "http2")]
214 http2_adaptive_window: bool,
215 #[cfg(feature = "http2")]
216 http2_max_frame_size: Option<u32>,
217 #[cfg(feature = "http2")]
218 http2_max_header_list_size: Option<u32>,
219 #[cfg(feature = "http2")]
220 http2_keep_alive_interval: Option<Duration>,
221 #[cfg(feature = "http2")]
222 http2_keep_alive_timeout: Option<Duration>,
223 #[cfg(feature = "http2")]
224 http2_keep_alive_while_idle: bool,
225 local_address: Option<IpAddr>,
226 #[cfg(any(
227 target_os = "android",
228 target_os = "fuchsia",
229 target_os = "illumos",
230 target_os = "ios",
231 target_os = "linux",
232 target_os = "macos",
233 target_os = "solaris",
234 target_os = "tvos",
235 target_os = "visionos",
236 target_os = "watchos",
237 ))]
238 interface: Option<String>,
239 nodelay: bool,
240 #[cfg(feature = "cookies")]
241 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
242 hickory_dns: bool,
243 error: Option<crate::Error>,
244 https_only: bool,
245 #[cfg(feature = "http3")]
246 tls_enable_early_data: bool,
247 #[cfg(feature = "http3")]
248 quic_max_idle_timeout: Option<Duration>,
249 #[cfg(feature = "http3")]
250 quic_stream_receive_window: Option<VarInt>,
251 #[cfg(feature = "http3")]
252 quic_receive_window: Option<VarInt>,
253 #[cfg(feature = "http3")]
254 quic_send_window: Option<u64>,
255 #[cfg(feature = "http3")]
256 quic_congestion_bbr: bool,
257 #[cfg(feature = "http3")]
258 h3_max_field_section_size: Option<u64>,
259 #[cfg(feature = "http3")]
260 h3_send_grease: Option<bool>,
261 dns_overrides: HashMap<String, Vec<SocketAddr>>,
262 dns_resolver: Option<Arc<dyn Resolve>>,
263
264 #[cfg(unix)]
265 unix_socket: Option<Arc<std::path::Path>>,
266 #[cfg(target_os = "windows")]
267 windows_named_pipe: Option<Arc<std::ffi::OsStr>>,
268}
269
270impl Default for ClientBuilder {
271 fn default() -> Self {
272 Self::new()
273 }
274}
275
276impl ClientBuilder {
277 pub fn new() -> Self {
281 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
282 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
283
284 ClientBuilder {
285 config: Config {
286 error: None,
287 accepts: Accepts::default(),
288 headers,
289 #[cfg(feature = "__tls")]
290 hostname_verification: true,
291 #[cfg(feature = "__tls")]
292 certs_verification: true,
293 #[cfg(feature = "__tls")]
294 tls_sni: true,
295 connect_timeout: None,
296 connection_verbose: false,
297 pool_idle_timeout: Some(Duration::from_secs(90)),
298 pool_max_idle_per_host: usize::MAX,
299 tcp_keepalive: Some(Duration::from_secs(15)),
300 tcp_keepalive_interval: Some(Duration::from_secs(15)),
301 tcp_keepalive_retries: Some(3),
302 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
303 tcp_user_timeout: Some(Duration::from_secs(30)),
304 proxies: Vec::new(),
305 auto_sys_proxy: true,
306 redirect_policy: redirect::Policy::default(),
307 retry_policy: crate::retry::Builder::default(),
308 referer: true,
309 read_timeout: None,
310 timeout: None,
311 #[cfg(feature = "__tls")]
312 root_certs: Vec::new(),
313 #[cfg(feature = "__tls")]
314 tls_certs_only: false,
315 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
316 identity: None,
317 #[cfg(feature = "__rustls")]
318 crls: vec![],
319 #[cfg(feature = "__tls")]
320 min_tls_version: None,
321 #[cfg(feature = "__tls")]
322 max_tls_version: None,
323 #[cfg(feature = "__tls")]
324 tls_info: false,
325 #[cfg(feature = "__tls")]
326 tls: TlsBackend::default(),
327 connector_layers: Vec::new(),
328 http_version_pref: HttpVersionPref::All,
329 http09_responses: false,
330 http1_title_case_headers: false,
331 http1_allow_obsolete_multiline_headers_in_responses: false,
332 http1_ignore_invalid_headers_in_responses: false,
333 http1_allow_spaces_after_header_name_in_responses: false,
334 #[cfg(feature = "http2")]
335 http2_initial_stream_window_size: None,
336 #[cfg(feature = "http2")]
337 http2_initial_connection_window_size: None,
338 #[cfg(feature = "http2")]
339 http2_adaptive_window: false,
340 #[cfg(feature = "http2")]
341 http2_max_frame_size: None,
342 #[cfg(feature = "http2")]
343 http2_max_header_list_size: None,
344 #[cfg(feature = "http2")]
345 http2_keep_alive_interval: None,
346 #[cfg(feature = "http2")]
347 http2_keep_alive_timeout: None,
348 #[cfg(feature = "http2")]
349 http2_keep_alive_while_idle: false,
350 local_address: None,
351 #[cfg(any(
352 target_os = "android",
353 target_os = "fuchsia",
354 target_os = "illumos",
355 target_os = "ios",
356 target_os = "linux",
357 target_os = "macos",
358 target_os = "solaris",
359 target_os = "tvos",
360 target_os = "visionos",
361 target_os = "watchos",
362 ))]
363 interface: None,
364 nodelay: true,
365 hickory_dns: cfg!(feature = "hickory-dns"),
366 #[cfg(feature = "cookies")]
367 cookie_store: None,
368 https_only: false,
369 dns_overrides: HashMap::new(),
370 #[cfg(feature = "http3")]
371 tls_enable_early_data: false,
372 #[cfg(feature = "http3")]
373 quic_max_idle_timeout: None,
374 #[cfg(feature = "http3")]
375 quic_stream_receive_window: None,
376 #[cfg(feature = "http3")]
377 quic_receive_window: None,
378 #[cfg(feature = "http3")]
379 quic_send_window: None,
380 #[cfg(feature = "http3")]
381 quic_congestion_bbr: false,
382 #[cfg(feature = "http3")]
383 h3_max_field_section_size: None,
384 #[cfg(feature = "http3")]
385 h3_send_grease: None,
386 dns_resolver: None,
387 #[cfg(unix)]
388 unix_socket: None,
389 #[cfg(target_os = "windows")]
390 windows_named_pipe: None,
391 },
392 }
393 }
394}
395
396impl ClientBuilder {
397 pub fn build(self) -> crate::Result<Client> {
404 let config = self.config;
405
406 if let Some(err) = config.error {
407 return Err(err);
408 }
409
410 let mut proxies = config.proxies;
411 if config.auto_sys_proxy {
412 proxies.push(ProxyMatcher::system());
413 }
414 let proxies = Arc::new(proxies);
415
416 #[allow(unused)]
417 #[cfg(feature = "http3")]
418 let mut h3_connector = None;
419
420 let resolver = {
421 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
422 false => Arc::new(GaiResolver::new()),
423 #[cfg(feature = "hickory-dns")]
424 true => Arc::new(HickoryDnsResolver::default()),
425 #[cfg(not(feature = "hickory-dns"))]
426 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
427 };
428 if let Some(dns_resolver) = config.dns_resolver {
429 resolver = dns_resolver;
430 }
431 if !config.dns_overrides.is_empty() {
432 resolver = Arc::new(DnsResolverWithOverrides::new(
433 resolver,
434 config.dns_overrides,
435 ));
436 }
437 DynResolver::new(resolver)
438 };
439
440 let mut connector_builder = {
441 #[cfg(feature = "__tls")]
442 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
443 headers.get(USER_AGENT).cloned()
444 }
445
446 let mut http = HttpConnector::new_with_resolver(resolver.clone());
447 http.set_connect_timeout(config.connect_timeout);
448
449 #[cfg(all(feature = "http3", feature = "__rustls"))]
450 let build_h3_connector =
451 |resolver,
452 tls,
453 quic_max_idle_timeout: Option<Duration>,
454 quic_stream_receive_window,
455 quic_receive_window,
456 quic_send_window,
457 quic_congestion_bbr,
458 h3_max_field_section_size,
459 h3_send_grease,
460 local_address,
461 http_version_pref: &HttpVersionPref| {
462 let mut transport_config = TransportConfig::default();
463
464 if let Some(max_idle_timeout) = quic_max_idle_timeout {
465 transport_config.max_idle_timeout(Some(
466 max_idle_timeout.try_into().map_err(error::builder)?,
467 ));
468 }
469
470 if let Some(stream_receive_window) = quic_stream_receive_window {
471 transport_config.stream_receive_window(stream_receive_window);
472 }
473
474 if let Some(receive_window) = quic_receive_window {
475 transport_config.receive_window(receive_window);
476 }
477
478 if let Some(send_window) = quic_send_window {
479 transport_config.send_window(send_window);
480 }
481
482 if quic_congestion_bbr {
483 let factory = Arc::new(quinn::congestion::BbrConfig::default());
484 transport_config.congestion_controller_factory(factory);
485 }
486
487 let mut h3_client_config = H3ClientConfig::default();
488
489 if let Some(max_field_section_size) = h3_max_field_section_size {
490 h3_client_config.max_field_section_size = Some(max_field_section_size);
491 }
492
493 if let Some(send_grease) = h3_send_grease {
494 h3_client_config.send_grease = Some(send_grease);
495 }
496
497 let res = H3Connector::new(
498 resolver,
499 tls,
500 local_address,
501 transport_config,
502 h3_client_config,
503 );
504
505 match res {
506 Ok(connector) => Ok(Some(connector)),
507 Err(err) => {
508 if let HttpVersionPref::Http3 = http_version_pref {
509 Err(error::builder(err))
510 } else {
511 Ok(None)
512 }
513 }
514 }
515 };
516
517 #[cfg(feature = "__tls")]
518 match config.tls {
519 #[cfg(feature = "__native-tls")]
520 TlsBackend::NativeTls => {
521 let mut tls = TlsConnector::builder();
522
523 #[cfg(all(feature = "__native-tls-alpn", not(feature = "http3")))]
524 {
525 match config.http_version_pref {
526 HttpVersionPref::Http1 => {
527 tls.request_alpns(&["http/1.1"]);
528 }
529 #[cfg(feature = "http2")]
530 HttpVersionPref::Http2 => {
531 tls.request_alpns(&["h2"]);
532 }
533 HttpVersionPref::All => {
534 tls.request_alpns(&[
535 #[cfg(feature = "http2")]
536 "h2",
537 "http/1.1",
538 ]);
539 }
540 }
541 }
542
543 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
544
545 tls.danger_accept_invalid_certs(!config.certs_verification);
546
547 tls.use_sni(config.tls_sni);
548
549 tls.disable_built_in_roots(config.tls_certs_only);
550
551 for cert in config.root_certs {
552 cert.add_to_native_tls(&mut tls);
553 }
554
555 #[cfg(feature = "__native-tls")]
556 {
557 if let Some(id) = config.identity {
558 id.add_to_native_tls(&mut tls)?;
559 }
560 }
561 #[cfg(all(feature = "__rustls", not(feature = "__native-tls")))]
562 {
563 if let Some(_id) = config.identity {
565 return Err(crate::error::builder("incompatible TLS identity type"));
566 }
567 }
568
569 if let Some(min_tls_version) = config.min_tls_version {
570 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
571 crate::error::builder("invalid minimum TLS version for backend")
575 })?;
576 tls.min_protocol_version(Some(protocol));
577 }
578
579 if let Some(max_tls_version) = config.max_tls_version {
580 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
581 crate::error::builder("invalid maximum TLS version for backend")
586 })?;
587 tls.max_protocol_version(Some(protocol));
588 }
589
590 ConnectorBuilder::new_native_tls(
591 http,
592 tls,
593 proxies.clone(),
594 user_agent(&config.headers),
595 config.local_address,
596 #[cfg(any(
597 target_os = "android",
598 target_os = "fuchsia",
599 target_os = "illumos",
600 target_os = "ios",
601 target_os = "linux",
602 target_os = "macos",
603 target_os = "solaris",
604 target_os = "tvos",
605 target_os = "visionos",
606 target_os = "watchos",
607 ))]
608 config.interface.as_deref(),
609 config.nodelay,
610 config.tls_info,
611 )?
612 }
613 #[cfg(feature = "__native-tls")]
614 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_native_tls(
615 http,
616 conn,
617 proxies.clone(),
618 user_agent(&config.headers),
619 config.local_address,
620 #[cfg(any(
621 target_os = "android",
622 target_os = "fuchsia",
623 target_os = "illumos",
624 target_os = "ios",
625 target_os = "linux",
626 target_os = "macos",
627 target_os = "solaris",
628 target_os = "tvos",
629 target_os = "visionos",
630 target_os = "watchos",
631 ))]
632 config.interface.as_deref(),
633 config.nodelay,
634 config.tls_info,
635 ),
636 #[cfg(feature = "__rustls")]
637 TlsBackend::BuiltRustls(conn) => {
638 #[cfg(feature = "http3")]
639 {
640 let mut h3_tls = conn.clone();
641 h3_tls.alpn_protocols = vec!["h3".into()];
642
643 h3_connector = build_h3_connector(
644 resolver.clone(),
645 h3_tls,
646 config.quic_max_idle_timeout,
647 config.quic_stream_receive_window,
648 config.quic_receive_window,
649 config.quic_send_window,
650 config.quic_congestion_bbr,
651 config.h3_max_field_section_size,
652 config.h3_send_grease,
653 config.local_address,
654 &config.http_version_pref,
655 )?;
656 }
657
658 ConnectorBuilder::new_rustls_tls(
659 http,
660 conn,
661 proxies.clone(),
662 user_agent(&config.headers),
663 config.local_address,
664 #[cfg(any(
665 target_os = "android",
666 target_os = "fuchsia",
667 target_os = "illumos",
668 target_os = "ios",
669 target_os = "linux",
670 target_os = "macos",
671 target_os = "solaris",
672 target_os = "tvos",
673 target_os = "visionos",
674 target_os = "watchos",
675 ))]
676 config.interface.as_deref(),
677 config.nodelay,
678 config.tls_info,
679 )
680 }
681 #[cfg(feature = "__rustls")]
682 TlsBackend::Rustls => {
683 use crate::tls::{IgnoreHostname, NoVerifier};
684
685 let mut versions = rustls::ALL_VERSIONS.to_vec();
687
688 if let Some(min_tls_version) = config.min_tls_version {
689 versions.retain(|&supported_version| {
690 match tls::Version::from_rustls(supported_version.version) {
691 Some(version) => version >= min_tls_version,
692 None => true,
695 }
696 });
697 }
698
699 if let Some(max_tls_version) = config.max_tls_version {
700 versions.retain(|&supported_version| {
701 match tls::Version::from_rustls(supported_version.version) {
702 Some(version) => version <= max_tls_version,
703 None => false,
704 }
705 });
706 }
707
708 if versions.is_empty() {
709 return Err(crate::error::builder("empty supported tls versions"));
710 }
711
712 let provider = rustls::crypto::CryptoProvider::get_default()
715 .map(|arc| arc.clone())
716 .unwrap_or_else(default_rustls_crypto_provider);
717
718 let signature_algorithms = provider.signature_verification_algorithms;
720 let config_builder =
721 rustls::ClientConfig::builder_with_provider(provider.clone())
722 .with_protocol_versions(&versions)
723 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
724
725 let config_builder = if !config.certs_verification {
726 config_builder
727 .dangerous()
728 .with_custom_certificate_verifier(Arc::new(NoVerifier))
729 } else if !config.hostname_verification {
730 if !config.tls_certs_only {
731 return Err(crate::error::builder(
733 "disabling rustls hostname verification only allowed with tls_certs_only()"
734 ));
735 }
736
737 config_builder
738 .dangerous()
739 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
740 crate::tls::rustls_store(config.root_certs)?,
741 signature_algorithms,
742 )))
743 } else if !config.tls_certs_only {
744 if !config.crls.is_empty() {
746 return Err(crate::error::builder(
747 "CRLs only allowed with tls_certs_only()",
748 ));
749 }
750
751 let verifier = if config.root_certs.is_empty() {
752 rustls_platform_verifier::Verifier::new(provider.clone())
753 .map_err(crate::error::builder)?
754 } else {
755 #[cfg(any(
756 all(unix, not(target_os = "android")),
757 target_os = "windows"
758 ))]
759 {
760 rustls_platform_verifier::Verifier::new_with_extra_roots(
761 crate::tls::rustls_der(config.root_certs)?,
762 provider.clone(),
763 )
764 .map_err(crate::error::builder)?
765 }
766
767 #[cfg(not(any(
768 all(unix, not(target_os = "android")),
769 target_os = "windows"
770 )))]
771 return Err(crate::error::builder(
772 "rustls-platform-verifier could not load extra certs",
773 ));
774 };
775
776 config_builder
777 .dangerous()
778 .with_custom_certificate_verifier(Arc::new(verifier))
779 } else {
780 if config.crls.is_empty() {
781 config_builder.with_root_certificates(crate::tls::rustls_store(
782 config.root_certs,
783 )?)
784 } else {
785 let crls = config
786 .crls
787 .iter()
788 .map(|e| e.as_rustls_crl())
789 .collect::<Vec<_>>();
790 let verifier =
791 rustls::client::WebPkiServerVerifier::builder_with_provider(
792 Arc::new(crate::tls::rustls_store(config.root_certs)?),
793 provider,
794 )
795 .with_crls(crls)
796 .build()
797 .map_err(|_| {
798 crate::error::builder("invalid TLS verification settings")
799 })?;
800 config_builder.with_webpki_verifier(verifier)
801 }
802 };
803
804 let mut tls = if let Some(id) = config.identity {
806 id.add_to_rustls(config_builder)?
807 } else {
808 config_builder.with_no_client_auth()
809 };
810
811 tls.enable_sni = config.tls_sni;
812
813 match config.http_version_pref {
815 HttpVersionPref::Http1 => {
816 tls.alpn_protocols = vec!["http/1.1".into()];
817 }
818 #[cfg(feature = "http2")]
819 HttpVersionPref::Http2 => {
820 tls.alpn_protocols = vec!["h2".into()];
821 }
822 #[cfg(feature = "http3")]
823 HttpVersionPref::Http3 => {
824 }
826 HttpVersionPref::All => {
827 tls.alpn_protocols = vec![
828 #[cfg(feature = "http2")]
829 "h2".into(),
830 "http/1.1".into(),
831 ];
832 }
833 }
834
835 #[cfg(feature = "http3")]
836 {
837 let mut h3_tls = tls.clone();
838 h3_tls.enable_early_data = config.tls_enable_early_data;
839
840 h3_tls.alpn_protocols = vec!["h3".into()];
842
843 h3_connector = build_h3_connector(
844 resolver.clone(),
845 h3_tls,
846 config.quic_max_idle_timeout,
847 config.quic_stream_receive_window,
848 config.quic_receive_window,
849 config.quic_send_window,
850 config.quic_congestion_bbr,
851 config.h3_max_field_section_size,
852 config.h3_send_grease,
853 config.local_address,
854 &config.http_version_pref,
855 )?;
856 }
857
858 ConnectorBuilder::new_rustls_tls(
859 http,
860 tls,
861 proxies.clone(),
862 user_agent(&config.headers),
863 config.local_address,
864 #[cfg(any(
865 target_os = "android",
866 target_os = "fuchsia",
867 target_os = "illumos",
868 target_os = "ios",
869 target_os = "linux",
870 target_os = "macos",
871 target_os = "solaris",
872 target_os = "tvos",
873 target_os = "visionos",
874 target_os = "watchos",
875 ))]
876 config.interface.as_deref(),
877 config.nodelay,
878 config.tls_info,
879 )
880 }
881 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
882 TlsBackend::UnknownPreconfigured => {
883 return Err(crate::error::builder(
884 "Unknown TLS backend passed to `use_preconfigured_tls`",
885 ));
886 }
887 }
888
889 #[cfg(not(feature = "__tls"))]
890 ConnectorBuilder::new(
891 http,
892 proxies.clone(),
893 config.local_address,
894 #[cfg(any(
895 target_os = "android",
896 target_os = "fuchsia",
897 target_os = "illumos",
898 target_os = "ios",
899 target_os = "linux",
900 target_os = "macos",
901 target_os = "solaris",
902 target_os = "tvos",
903 target_os = "visionos",
904 target_os = "watchos",
905 ))]
906 config.interface.as_deref(),
907 config.nodelay,
908 )
909 };
910
911 connector_builder.set_timeout(config.connect_timeout);
912 connector_builder.set_verbose(config.connection_verbose);
913 connector_builder.set_keepalive(config.tcp_keepalive);
914 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
915 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
916 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
917 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
918
919 #[cfg(feature = "socks")]
920 connector_builder.set_socks_resolver(resolver);
921
922 #[cfg(unix)]
926 connector_builder.set_unix_socket(config.unix_socket);
927 #[cfg(target_os = "windows")]
928 connector_builder.set_windows_named_pipe(config.windows_named_pipe.clone());
929
930 let mut builder =
931 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
932 #[cfg(feature = "http2")]
933 {
934 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
935 builder.http2_only(true);
936 }
937
938 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
939 {
940 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
941 }
942 if let Some(http2_initial_connection_window_size) =
943 config.http2_initial_connection_window_size
944 {
945 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
946 }
947 if config.http2_adaptive_window {
948 builder.http2_adaptive_window(true);
949 }
950 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
951 builder.http2_max_frame_size(http2_max_frame_size);
952 }
953 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
954 builder.http2_max_header_list_size(http2_max_header_list_size);
955 }
956 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
957 builder.http2_keep_alive_interval(http2_keep_alive_interval);
958 }
959 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
960 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
961 }
962 if config.http2_keep_alive_while_idle {
963 builder.http2_keep_alive_while_idle(true);
964 }
965 }
966
967 builder.timer(hyper_util::rt::TokioTimer::new());
968 builder.pool_timer(hyper_util::rt::TokioTimer::new());
969 builder.pool_idle_timeout(config.pool_idle_timeout);
970 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
971
972 if config.http09_responses {
973 builder.http09_responses(true);
974 }
975
976 if config.http1_title_case_headers {
977 builder.http1_title_case_headers(true);
978 }
979
980 if config.http1_allow_obsolete_multiline_headers_in_responses {
981 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
982 }
983
984 if config.http1_ignore_invalid_headers_in_responses {
985 builder.http1_ignore_invalid_headers_in_responses(true);
986 }
987
988 if config.http1_allow_spaces_after_header_name_in_responses {
989 builder.http1_allow_spaces_after_header_name_in_responses(true);
990 }
991
992 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
993 let proxies_maybe_http_custom_headers =
994 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
995
996 let redirect_policy_desc = if config.redirect_policy.is_default() {
997 None
998 } else {
999 Some(format!("{:?}", &config.redirect_policy))
1000 };
1001
1002 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
1003 let hyper_service = HyperService {
1004 hyper: hyper_client,
1005 };
1006
1007 let redirect_policy = {
1008 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
1009 p.with_referer(config.referer)
1010 .with_https_only(config.https_only);
1011 p
1012 };
1013
1014 let retry_policy = config.retry_policy.into_policy();
1015
1016 let svc = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
1017
1018 #[cfg(feature = "cookies")]
1019 let svc = CookieService::new(svc, config.cookie_store.clone());
1020 let hyper = FollowRedirect::with_policy(svc, redirect_policy.clone());
1021 #[cfg(any(
1022 feature = "gzip",
1023 feature = "brotli",
1024 feature = "zstd",
1025 feature = "deflate"
1026 ))]
1027 let hyper = Decompression::new(hyper)
1028 .no_gzip()
1031 .no_deflate()
1032 .no_br()
1033 .no_zstd();
1034 #[cfg(feature = "gzip")]
1035 let hyper = hyper.gzip(config.accepts.gzip);
1036 #[cfg(feature = "brotli")]
1037 let hyper = hyper.br(config.accepts.brotli);
1038 #[cfg(feature = "zstd")]
1039 let hyper = hyper.zstd(config.accepts.zstd);
1040 #[cfg(feature = "deflate")]
1041 let hyper = hyper.deflate(config.accepts.deflate);
1042
1043 Ok(Client {
1044 inner: Arc::new(ClientRef {
1045 accepts: config.accepts,
1046 #[cfg(feature = "cookies")]
1047 cookie_store: config.cookie_store.clone(),
1048 #[cfg(feature = "http3")]
1051 h3_client: match h3_connector {
1052 Some(h3_connector) => {
1053 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1054 let svc = tower::retry::Retry::new(retry_policy, h3_service);
1055 #[cfg(feature = "cookies")]
1056 let svc = CookieService::new(svc, config.cookie_store);
1057 let svc = FollowRedirect::with_policy(svc, redirect_policy);
1058 #[cfg(any(
1059 feature = "gzip",
1060 feature = "brotli",
1061 feature = "zstd",
1062 feature = "deflate"
1063 ))]
1064 let svc = Decompression::new(svc)
1065 .no_gzip()
1068 .no_deflate()
1069 .no_br()
1070 .no_zstd();
1071 #[cfg(feature = "gzip")]
1072 let svc = svc.gzip(config.accepts.gzip);
1073 #[cfg(feature = "brotli")]
1074 let svc = svc.br(config.accepts.brotli);
1075 #[cfg(feature = "zstd")]
1076 let svc = svc.zstd(config.accepts.zstd);
1077 #[cfg(feature = "deflate")]
1078 let svc = svc.deflate(config.accepts.deflate);
1079 Some(svc)
1080 }
1081 None => None,
1082 },
1083 headers: config.headers,
1084 referer: config.referer,
1085 read_timeout: config.read_timeout,
1086 total_timeout: RequestConfig::new(config.timeout),
1087 hyper,
1088 proxies,
1089 proxies_maybe_http_auth,
1090 proxies_maybe_http_custom_headers,
1091 https_only: config.https_only,
1092 redirect_policy_desc,
1093 }),
1094 })
1095 }
1096
1097 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1120 where
1121 V: TryInto<HeaderValue>,
1122 V::Error: Into<http::Error>,
1123 {
1124 match value.try_into() {
1125 Ok(value) => {
1126 self.config.headers.insert(USER_AGENT, value);
1127 }
1128 Err(e) => {
1129 self.config.error = Some(crate::error::builder(e.into()));
1130 }
1131 };
1132 self
1133 }
1134 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1158 for (key, value) in headers.iter() {
1159 self.config.headers.insert(key, value.clone());
1160 }
1161 self
1162 }
1163
1164 #[cfg(feature = "cookies")]
1179 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1180 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1181 if enable {
1182 self.cookie_provider(Arc::new(cookie::Jar::default()))
1183 } else {
1184 self.config.cookie_store = None;
1185 self
1186 }
1187 }
1188
1189 #[cfg(feature = "cookies")]
1203 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1204 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1205 mut self,
1206 cookie_store: Arc<C>,
1207 ) -> ClientBuilder {
1208 self.config.cookie_store = Some(cookie_store as _);
1209 self
1210 }
1211
1212 #[cfg(feature = "gzip")]
1229 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1230 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1231 self.config.accepts.gzip = enable;
1232 self
1233 }
1234
1235 #[cfg(feature = "brotli")]
1252 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1253 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1254 self.config.accepts.brotli = enable;
1255 self
1256 }
1257
1258 #[cfg(feature = "zstd")]
1275 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1276 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1277 self.config.accepts.zstd = enable;
1278 self
1279 }
1280
1281 #[cfg(feature = "deflate")]
1298 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1299 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1300 self.config.accepts.deflate = enable;
1301 self
1302 }
1303
1304 pub fn no_gzip(self) -> ClientBuilder {
1310 #[cfg(feature = "gzip")]
1311 {
1312 self.gzip(false)
1313 }
1314
1315 #[cfg(not(feature = "gzip"))]
1316 {
1317 self
1318 }
1319 }
1320
1321 pub fn no_brotli(self) -> ClientBuilder {
1327 #[cfg(feature = "brotli")]
1328 {
1329 self.brotli(false)
1330 }
1331
1332 #[cfg(not(feature = "brotli"))]
1333 {
1334 self
1335 }
1336 }
1337
1338 pub fn no_zstd(self) -> ClientBuilder {
1344 #[cfg(feature = "zstd")]
1345 {
1346 self.zstd(false)
1347 }
1348
1349 #[cfg(not(feature = "zstd"))]
1350 {
1351 self
1352 }
1353 }
1354
1355 pub fn no_deflate(self) -> ClientBuilder {
1361 #[cfg(feature = "deflate")]
1362 {
1363 self.deflate(false)
1364 }
1365
1366 #[cfg(not(feature = "deflate"))]
1367 {
1368 self
1369 }
1370 }
1371
1372 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1378 self.config.redirect_policy = policy;
1379 self
1380 }
1381
1382 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1386 self.config.referer = enable;
1387 self
1388 }
1389
1390 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1397 self.config.retry_policy = policy;
1398 self
1399 }
1400
1401 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1409 self.config.proxies.push(proxy.into_matcher());
1410 self.config.auto_sys_proxy = false;
1411 self
1412 }
1413
1414 pub fn no_proxy(mut self) -> ClientBuilder {
1422 self.config.proxies.clear();
1423 self.config.auto_sys_proxy = false;
1424 self
1425 }
1426
1427 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1436 self.config.timeout = Some(timeout);
1437 self
1438 }
1439
1440 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1448 self.config.read_timeout = Some(timeout);
1449 self
1450 }
1451
1452 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1461 self.config.connect_timeout = Some(timeout);
1462 self
1463 }
1464
1465 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1472 self.config.connection_verbose = verbose;
1473 self
1474 }
1475
1476 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1484 where
1485 D: Into<Option<Duration>>,
1486 {
1487 self.config.pool_idle_timeout = val.into();
1488 self
1489 }
1490
1491 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1495 self.config.pool_max_idle_per_host = max;
1496 self
1497 }
1498
1499 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1501 self.config.http1_title_case_headers = true;
1502 self
1503 }
1504
1505 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1511 mut self,
1512 value: bool,
1513 ) -> ClientBuilder {
1514 self.config
1515 .http1_allow_obsolete_multiline_headers_in_responses = value;
1516 self
1517 }
1518
1519 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1521 self.config.http1_ignore_invalid_headers_in_responses = value;
1522 self
1523 }
1524
1525 pub fn http1_allow_spaces_after_header_name_in_responses(
1531 mut self,
1532 value: bool,
1533 ) -> ClientBuilder {
1534 self.config
1535 .http1_allow_spaces_after_header_name_in_responses = value;
1536 self
1537 }
1538
1539 pub fn http1_only(mut self) -> ClientBuilder {
1541 self.config.http_version_pref = HttpVersionPref::Http1;
1542 self
1543 }
1544
1545 pub fn http09_responses(mut self) -> ClientBuilder {
1547 self.config.http09_responses = true;
1548 self
1549 }
1550
1551 #[cfg(feature = "http2")]
1553 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1554 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1555 self.config.http_version_pref = HttpVersionPref::Http2;
1556 self
1557 }
1558
1559 #[cfg(feature = "http3")]
1561 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1562 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1563 self.config.http_version_pref = HttpVersionPref::Http3;
1564 self
1565 }
1566
1567 #[cfg(feature = "http2")]
1571 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1572 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1573 self.config.http2_initial_stream_window_size = sz.into();
1574 self
1575 }
1576
1577 #[cfg(feature = "http2")]
1581 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1582 pub fn http2_initial_connection_window_size(
1583 mut self,
1584 sz: impl Into<Option<u32>>,
1585 ) -> ClientBuilder {
1586 self.config.http2_initial_connection_window_size = sz.into();
1587 self
1588 }
1589
1590 #[cfg(feature = "http2")]
1595 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1596 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1597 self.config.http2_adaptive_window = enabled;
1598 self
1599 }
1600
1601 #[cfg(feature = "http2")]
1605 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1606 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1607 self.config.http2_max_frame_size = sz.into();
1608 self
1609 }
1610
1611 #[cfg(feature = "http2")]
1615 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1616 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1617 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1618 self
1619 }
1620
1621 #[cfg(feature = "http2")]
1626 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1627 pub fn http2_keep_alive_interval(
1628 mut self,
1629 interval: impl Into<Option<Duration>>,
1630 ) -> ClientBuilder {
1631 self.config.http2_keep_alive_interval = interval.into();
1632 self
1633 }
1634
1635 #[cfg(feature = "http2")]
1641 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1642 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1643 self.config.http2_keep_alive_timeout = Some(timeout);
1644 self
1645 }
1646
1647 #[cfg(feature = "http2")]
1654 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1655 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1656 self.config.http2_keep_alive_while_idle = enabled;
1657 self
1658 }
1659
1660 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1666 self.config.nodelay = enabled;
1667 self
1668 }
1669
1670 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1685 where
1686 T: Into<Option<IpAddr>>,
1687 {
1688 self.config.local_address = addr.into();
1689 self
1690 }
1691
1692 #[cfg(any(
1725 target_os = "android",
1726 target_os = "fuchsia",
1727 target_os = "illumos",
1728 target_os = "ios",
1729 target_os = "linux",
1730 target_os = "macos",
1731 target_os = "solaris",
1732 target_os = "tvos",
1733 target_os = "visionos",
1734 target_os = "watchos",
1735 ))]
1736 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1737 self.config.interface = Some(interface.to_string());
1738 self
1739 }
1740
1741 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1745 where
1746 D: Into<Option<Duration>>,
1747 {
1748 self.config.tcp_keepalive = val.into();
1749 self
1750 }
1751
1752 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1756 where
1757 D: Into<Option<Duration>>,
1758 {
1759 self.config.tcp_keepalive_interval = val.into();
1760 self
1761 }
1762
1763 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1767 where
1768 C: Into<Option<u32>>,
1769 {
1770 self.config.tcp_keepalive_retries = retries.into();
1771 self
1772 }
1773
1774 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1781 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1782 where
1783 D: Into<Option<Duration>>,
1784 {
1785 self.config.tcp_user_timeout = val.into();
1786 self
1787 }
1788
1789 #[cfg(unix)]
1803 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1804 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1805 self
1806 }
1807
1808 #[cfg(target_os = "windows")]
1820 pub fn windows_named_pipe(mut self, pipe: impl WindowsNamedPipeProvider) -> ClientBuilder {
1821 self.config.windows_named_pipe = Some(
1822 pipe.reqwest_windows_named_pipe_path(crate::connect::windows_named_pipe::Internal)
1823 .into(),
1824 );
1825 self
1826 }
1827
1828 #[cfg(feature = "__tls")]
1847 #[cfg_attr(
1848 docsrs,
1849 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1850 )]
1851 pub fn tls_certs_merge(
1852 mut self,
1853 certs: impl IntoIterator<Item = Certificate>,
1854 ) -> ClientBuilder {
1855 self.config.root_certs.extend(certs);
1856 self
1857 }
1858
1859 #[cfg(feature = "__tls")]
1872 #[cfg_attr(
1873 docsrs,
1874 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1875 )]
1876 pub fn tls_certs_only(mut self, certs: impl IntoIterator<Item = Certificate>) -> ClientBuilder {
1877 self.config.root_certs.extend(certs);
1878 self.config.tls_certs_only = true;
1879 self
1880 }
1881
1882 #[cfg(feature = "__tls")]
1885 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1886 self.config.root_certs.push(cert);
1887 self
1888 }
1889
1890 #[cfg(feature = "__rustls")]
1904 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1905 pub fn tls_crls_only(
1906 mut self,
1907 crls: impl IntoIterator<Item = CertificateRevocationList>,
1908 ) -> ClientBuilder {
1909 self.config.crls.extend(crls);
1910 self
1911 }
1912
1913 #[cfg(feature = "__rustls")]
1915 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1916 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1917 self.config.crls.push(crl);
1918 self
1919 }
1920
1921 #[cfg(feature = "__rustls")]
1923 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1924 pub fn add_crls(
1925 mut self,
1926 crls: impl IntoIterator<Item = CertificateRevocationList>,
1927 ) -> ClientBuilder {
1928 self.config.crls.extend(crls);
1929 self
1930 }
1931
1932 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
1939 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
1940 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1941 self.config.identity = Some(identity);
1942 self
1943 }
1944
1945 #[cfg(feature = "__tls")]
1966 #[cfg_attr(
1967 docsrs,
1968 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1969 )]
1970 pub fn tls_danger_accept_invalid_hostnames(
1971 mut self,
1972 accept_invalid_hostname: bool,
1973 ) -> ClientBuilder {
1974 self.config.hostname_verification = !accept_invalid_hostname;
1975 self
1976 }
1977
1978 #[cfg(feature = "__tls")]
1980 pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder {
1981 self.tls_danger_accept_invalid_hostnames(accept_invalid_hostname)
1982 }
1983
1984 #[cfg(feature = "__tls")]
2001 #[cfg_attr(
2002 docsrs,
2003 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2004 )]
2005 pub fn tls_danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
2006 self.config.certs_verification = !accept_invalid_certs;
2007 self
2008 }
2009
2010 #[cfg(feature = "__tls")]
2012 pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder {
2013 self.tls_danger_accept_invalid_certs(accept_invalid_certs)
2014 }
2015
2016 #[cfg(feature = "__tls")]
2025 #[cfg_attr(
2026 docsrs,
2027 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2028 )]
2029 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
2030 self.config.tls_sni = tls_sni;
2031 self
2032 }
2033
2034 #[cfg(feature = "__tls")]
2050 #[cfg_attr(
2051 docsrs,
2052 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2053 )]
2054 pub fn tls_version_min(mut self, version: tls::Version) -> ClientBuilder {
2055 self.config.min_tls_version = Some(version);
2056 self
2057 }
2058
2059 #[cfg(feature = "__tls")]
2061 pub fn min_tls_version(self, version: tls::Version) -> ClientBuilder {
2062 self.tls_version_min(version)
2063 }
2064
2065 #[cfg(feature = "__tls")]
2084 #[cfg_attr(
2085 docsrs,
2086 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2087 )]
2088 pub fn tls_version_max(mut self, version: tls::Version) -> ClientBuilder {
2089 self.config.max_tls_version = Some(version);
2090 self
2091 }
2092
2093 #[cfg(feature = "__tls")]
2095 pub fn max_tls_version(self, version: tls::Version) -> ClientBuilder {
2096 self.tls_version_max(version)
2097 }
2098
2099 #[cfg(feature = "__native-tls")]
2108 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2109 pub fn tls_backend_native(mut self) -> ClientBuilder {
2110 self.config.tls = TlsBackend::NativeTls;
2111 self
2112 }
2113
2114 #[cfg(feature = "__native-tls")]
2116 pub fn use_native_tls(self) -> ClientBuilder {
2117 self.tls_backend_native()
2118 }
2119
2120 #[cfg(feature = "__rustls")]
2129 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2130 pub fn tls_backend_rustls(mut self) -> ClientBuilder {
2131 self.config.tls = TlsBackend::Rustls;
2132 self
2133 }
2134
2135 #[cfg(feature = "__rustls")]
2137 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2138 pub fn use_rustls_tls(self) -> ClientBuilder {
2139 self.tls_backend_rustls()
2140 }
2141
2142 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2168 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
2169 pub fn tls_backend_preconfigured(mut self, tls: impl Any) -> ClientBuilder {
2170 let mut tls = Some(tls);
2171 #[cfg(feature = "__native-tls")]
2172 {
2173 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2174 let tls = conn.take().expect("is definitely Some");
2175 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2176 self.config.tls = tls;
2177 return self;
2178 }
2179 }
2180 #[cfg(feature = "__rustls")]
2181 {
2182 if let Some(conn) =
2183 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2184 {
2185 let tls = conn.take().expect("is definitely Some");
2186 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2187 self.config.tls = tls;
2188 return self;
2189 }
2190 }
2191
2192 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2194 self
2195 }
2196
2197 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2199 pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder {
2200 self.tls_backend_preconfigured(tls)
2201 }
2202
2203 #[cfg(feature = "__tls")]
2210 #[cfg_attr(
2211 docsrs,
2212 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2213 )]
2214 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2215 self.config.tls_info = tls_info;
2216 self
2217 }
2218
2219 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2223 self.config.https_only = enabled;
2224 self
2225 }
2226
2227 #[cfg(feature = "hickory-dns")]
2241 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2242 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2243 self.config.hickory_dns = enable;
2244 self
2245 }
2246
2247 pub fn no_hickory_dns(self) -> ClientBuilder {
2253 #[cfg(feature = "hickory-dns")]
2254 {
2255 self.hickory_dns(false)
2256 }
2257
2258 #[cfg(not(feature = "hickory-dns"))]
2259 {
2260 self
2261 }
2262 }
2263
2264 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2269 self.resolve_to_addrs(domain, &[addr])
2270 }
2271
2272 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2277 self.config
2278 .dns_overrides
2279 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2280 self
2281 }
2282
2283 pub fn dns_resolver<R>(mut self, resolver: R) -> ClientBuilder
2288 where
2289 R: crate::dns::resolve::IntoResolve,
2290 {
2291 self.config.dns_resolver = Some(resolver.into_resolve());
2292 self
2293 }
2294
2295 #[cfg(feature = "http3")]
2300 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2301 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2302 self.config.tls_enable_early_data = enabled;
2303 self
2304 }
2305
2306 #[cfg(feature = "http3")]
2312 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2313 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2314 self.config.quic_max_idle_timeout = Some(value);
2315 self
2316 }
2317
2318 #[cfg(feature = "http3")]
2329 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2330 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2331 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2332 self
2333 }
2334
2335 #[cfg(feature = "http3")]
2346 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2347 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2348 self.config.quic_receive_window = Some(value.try_into().unwrap());
2349 self
2350 }
2351
2352 #[cfg(feature = "http3")]
2358 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2359 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2360 self.config.quic_send_window = Some(value);
2361 self
2362 }
2363
2364 #[cfg(feature = "http3")]
2372 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2373 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2374 self.config.quic_congestion_bbr = true;
2375 self
2376 }
2377
2378 #[cfg(feature = "http3")]
2388 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2389 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2390 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2391 self
2392 }
2393
2394 #[cfg(feature = "http3")]
2406 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2407 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2408 self.config.h3_send_grease = Some(enabled);
2409 self
2410 }
2411
2412 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2436 where
2437 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2438 L::Service:
2439 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2440 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2441 {
2442 let layer = BoxCloneSyncServiceLayer::new(layer);
2443
2444 self.config.connector_layers.push(layer);
2445
2446 self
2447 }
2448}
2449
2450type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2451
2452impl Default for Client {
2453 fn default() -> Self {
2454 Self::new()
2455 }
2456}
2457
2458#[cfg(feature = "__rustls")]
2459fn default_rustls_crypto_provider() -> Arc<rustls::crypto::CryptoProvider> {
2460 #[cfg(not(feature = "__rustls-aws-lc-rs"))]
2461 panic!("No provider set");
2462
2463 #[cfg(feature = "__rustls-aws-lc-rs")]
2464 Arc::new(rustls::crypto::aws_lc_rs::default_provider())
2465}
2466
2467impl Client {
2468 pub fn new() -> Client {
2478 ClientBuilder::new().build().expect("Client::new()")
2479 }
2480
2481 pub fn builder() -> ClientBuilder {
2485 ClientBuilder::new()
2486 }
2487
2488 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2494 self.request(Method::GET, url)
2495 }
2496
2497 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2503 self.request(Method::POST, url)
2504 }
2505
2506 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2512 self.request(Method::PUT, url)
2513 }
2514
2515 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2521 self.request(Method::PATCH, url)
2522 }
2523
2524 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2530 self.request(Method::DELETE, url)
2531 }
2532
2533 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2539 self.request(Method::HEAD, url)
2540 }
2541
2542 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2551 let req = url.into_url().map(move |url| Request::new(method, url));
2552 RequestBuilder::new(self.clone(), req)
2553 }
2554
2555 pub fn execute(
2568 &self,
2569 request: Request,
2570 ) -> impl Future<Output = Result<Response, crate::Error>> {
2571 self.execute_request(request)
2572 }
2573
2574 pub(super) fn execute_request(&self, req: Request) -> Pending {
2575 let (method, url, mut headers, body, version, extensions) = req.pieces();
2576 if url.scheme() != "http" && url.scheme() != "https" {
2577 return Pending::new_err(error::url_bad_scheme(url));
2578 }
2579
2580 if self.inner.https_only && url.scheme() != "https" {
2582 return Pending::new_err(error::url_bad_scheme(url));
2583 }
2584
2585 for (key, value) in &self.inner.headers {
2588 if let Entry::Vacant(entry) = headers.entry(key) {
2589 entry.insert(value.clone());
2590 }
2591 }
2592
2593 let uri = match try_uri(&url) {
2594 Ok(uri) => uri,
2595 _ => return Pending::new_err(error::url_invalid_uri(url)),
2596 };
2597
2598 let body = body.unwrap_or_else(Body::empty);
2599
2600 self.proxy_auth(&uri, &mut headers);
2601 self.proxy_custom_headers(&uri, &mut headers);
2602
2603 let builder = hyper::Request::builder()
2604 .method(method.clone())
2605 .uri(uri)
2606 .version(version);
2607
2608 let in_flight = match version {
2609 #[cfg(feature = "http3")]
2610 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2611 let mut req = builder.body(body).expect("valid request parts");
2612 *req.headers_mut() = headers.clone();
2613 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2614 ResponseFuture::H3(h3.call(req))
2615 }
2616 _ => {
2617 let mut req = builder.body(body).expect("valid request parts");
2618 *req.headers_mut() = headers.clone();
2619 let mut hyper = self.inner.hyper.clone();
2620 ResponseFuture::Default(hyper.call(req))
2621 }
2622 };
2623
2624 let total_timeout = self
2625 .inner
2626 .total_timeout
2627 .fetch(&extensions)
2628 .copied()
2629 .map(tokio::time::sleep)
2630 .map(Box::pin);
2631
2632 let read_timeout_fut = self
2633 .inner
2634 .read_timeout
2635 .map(tokio::time::sleep)
2636 .map(Box::pin);
2637
2638 Pending {
2639 inner: PendingInner::Request(Box::pin(PendingRequest {
2640 method,
2641 url,
2642 headers,
2643
2644 client: self.inner.clone(),
2645
2646 in_flight,
2647 total_timeout,
2648 read_timeout_fut,
2649 read_timeout: self.inner.read_timeout,
2650 })),
2651 }
2652 }
2653
2654 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2655 if !self.inner.proxies_maybe_http_auth {
2656 return;
2657 }
2658
2659 if dst.scheme() != Some(&Scheme::HTTP) {
2663 return;
2664 }
2665
2666 if headers.contains_key(PROXY_AUTHORIZATION) {
2667 return;
2668 }
2669
2670 for proxy in self.inner.proxies.iter() {
2671 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2672 headers.insert(PROXY_AUTHORIZATION, header);
2673 break;
2674 }
2675 }
2676 }
2677
2678 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2679 if !self.inner.proxies_maybe_http_custom_headers {
2680 return;
2681 }
2682
2683 if dst.scheme() != Some(&Scheme::HTTP) {
2684 return;
2685 }
2686
2687 for proxy in self.inner.proxies.iter() {
2688 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2689 iter.iter().for_each(|(key, value)| {
2690 headers.insert(key, value.clone());
2691 });
2692 break;
2693 }
2694 }
2695 }
2696}
2697
2698impl fmt::Debug for Client {
2699 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2700 let mut builder = f.debug_struct("Client");
2701 self.inner.fmt_fields(&mut builder);
2702 builder.finish()
2703 }
2704}
2705
2706impl tower_service::Service<Request> for Client {
2707 type Response = Response;
2708 type Error = crate::Error;
2709 type Future = Pending;
2710
2711 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2712 Poll::Ready(Ok(()))
2713 }
2714
2715 fn call(&mut self, req: Request) -> Self::Future {
2716 self.execute_request(req)
2717 }
2718}
2719
2720impl tower_service::Service<Request> for &'_ Client {
2721 type Response = Response;
2722 type Error = crate::Error;
2723 type Future = Pending;
2724
2725 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2726 Poll::Ready(Ok(()))
2727 }
2728
2729 fn call(&mut self, req: Request) -> Self::Future {
2730 self.execute_request(req)
2731 }
2732}
2733
2734impl fmt::Debug for ClientBuilder {
2735 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2736 let mut builder = f.debug_struct("ClientBuilder");
2737 self.config.fmt_fields(&mut builder);
2738 builder.finish()
2739 }
2740}
2741
2742impl Config {
2743 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2744 #[cfg(feature = "cookies")]
2748 {
2749 if let Some(_) = self.cookie_store {
2750 f.field("cookie_store", &true);
2751 }
2752 }
2753
2754 f.field("accepts", &self.accepts);
2755
2756 if !self.proxies.is_empty() {
2757 f.field("proxies", &self.proxies);
2758 }
2759
2760 if !self.redirect_policy.is_default() {
2761 f.field("redirect_policy", &self.redirect_policy);
2762 }
2763
2764 if self.referer {
2765 f.field("referer", &true);
2766 }
2767
2768 f.field("default_headers", &self.headers);
2769
2770 if self.http1_title_case_headers {
2771 f.field("http1_title_case_headers", &true);
2772 }
2773
2774 if self.http1_allow_obsolete_multiline_headers_in_responses {
2775 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2776 }
2777
2778 if self.http1_ignore_invalid_headers_in_responses {
2779 f.field("http1_ignore_invalid_headers_in_responses", &true);
2780 }
2781
2782 if self.http1_allow_spaces_after_header_name_in_responses {
2783 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2784 }
2785
2786 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2787 f.field("http1_only", &true);
2788 }
2789
2790 #[cfg(feature = "http2")]
2791 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2792 f.field("http2_prior_knowledge", &true);
2793 }
2794
2795 if let Some(ref d) = self.connect_timeout {
2796 f.field("connect_timeout", d);
2797 }
2798
2799 if let Some(ref d) = self.timeout {
2800 f.field("timeout", d);
2801 }
2802
2803 if let Some(ref v) = self.local_address {
2804 f.field("local_address", v);
2805 }
2806
2807 #[cfg(any(
2808 target_os = "android",
2809 target_os = "fuchsia",
2810 target_os = "illumos",
2811 target_os = "ios",
2812 target_os = "linux",
2813 target_os = "macos",
2814 target_os = "solaris",
2815 target_os = "tvos",
2816 target_os = "visionos",
2817 target_os = "watchos",
2818 ))]
2819 if let Some(ref v) = self.interface {
2820 f.field("interface", v);
2821 }
2822
2823 if self.nodelay {
2824 f.field("tcp_nodelay", &true);
2825 }
2826
2827 #[cfg(feature = "__tls")]
2828 {
2829 if !self.hostname_verification {
2830 f.field("tls_danger_accept_invalid_hostnames", &true);
2831 }
2832 }
2833
2834 #[cfg(feature = "__tls")]
2835 {
2836 if !self.certs_verification {
2837 f.field("tls_danger_accept_invalid_certs", &true);
2838 }
2839
2840 if let Some(ref min_tls_version) = self.min_tls_version {
2841 f.field("tls_version_min", min_tls_version);
2842 }
2843
2844 if let Some(ref max_tls_version) = self.max_tls_version {
2845 f.field("tls_version_max", max_tls_version);
2846 }
2847
2848 f.field("tls_sni", &self.tls_sni);
2849
2850 f.field("tls_info", &self.tls_info);
2851 }
2852
2853 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2854 {
2855 f.field("tls_backend", &self.tls);
2856 }
2857
2858 if !self.dns_overrides.is_empty() {
2859 f.field("dns_overrides", &self.dns_overrides);
2860 }
2861
2862 #[cfg(feature = "http3")]
2863 {
2864 if self.tls_enable_early_data {
2865 f.field("tls_enable_early_data", &true);
2866 }
2867 }
2868
2869 #[cfg(unix)]
2870 if let Some(ref p) = self.unix_socket {
2871 f.field("unix_socket", p);
2872 }
2873 }
2874}
2875
2876#[cfg(not(feature = "cookies"))]
2877type MaybeCookieService<T> = T;
2878
2879#[cfg(feature = "cookies")]
2880type MaybeCookieService<T> = CookieService<T>;
2881
2882#[cfg(not(any(
2883 feature = "gzip",
2884 feature = "brotli",
2885 feature = "zstd",
2886 feature = "deflate"
2887)))]
2888type MaybeDecompression<T> = T;
2889
2890#[cfg(any(
2891 feature = "gzip",
2892 feature = "brotli",
2893 feature = "zstd",
2894 feature = "deflate"
2895))]
2896type MaybeDecompression<T> = Decompression<T>;
2897
2898type LayeredService<T> = MaybeDecompression<
2899 FollowRedirect<
2900 MaybeCookieService<tower::retry::Retry<crate::retry::Policy, T>>,
2901 TowerRedirectPolicy,
2902 >,
2903>;
2904type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
2905
2906struct ClientRef {
2907 accepts: Accepts,
2908 #[cfg(feature = "cookies")]
2909 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2910 headers: HeaderMap,
2911 hyper: LayeredService<HyperService>,
2912 #[cfg(feature = "http3")]
2913 h3_client: Option<LayeredService<H3Client>>,
2914 referer: bool,
2915 total_timeout: RequestConfig<TotalTimeout>,
2916 read_timeout: Option<Duration>,
2917 proxies: Arc<Vec<ProxyMatcher>>,
2918 proxies_maybe_http_auth: bool,
2919 proxies_maybe_http_custom_headers: bool,
2920 https_only: bool,
2921 redirect_policy_desc: Option<String>,
2922}
2923
2924impl ClientRef {
2925 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2926 #[cfg(feature = "cookies")]
2930 {
2931 if let Some(_) = self.cookie_store {
2932 f.field("cookie_store", &true);
2933 }
2934 }
2935
2936 f.field("accepts", &self.accepts);
2937
2938 if !self.proxies.is_empty() {
2939 f.field("proxies", &self.proxies);
2940 }
2941
2942 if let Some(s) = &self.redirect_policy_desc {
2943 f.field("redirect_policy", s);
2944 }
2945
2946 if self.referer {
2947 f.field("referer", &true);
2948 }
2949
2950 f.field("default_headers", &self.headers);
2951
2952 self.total_timeout.fmt_as_field(f);
2953
2954 if let Some(ref d) = self.read_timeout {
2955 f.field("read_timeout", d);
2956 }
2957 }
2958}
2959
2960pin_project! {
2961 pub struct Pending {
2962 #[pin]
2963 inner: PendingInner,
2964 }
2965}
2966
2967enum PendingInner {
2968 Request(Pin<Box<PendingRequest>>),
2969 Error(Option<crate::Error>),
2970}
2971
2972pin_project! {
2973 struct PendingRequest {
2974 method: Method,
2975 url: Url,
2976 headers: HeaderMap,
2977
2978 client: Arc<ClientRef>,
2979
2980 #[pin]
2981 in_flight: ResponseFuture,
2982 #[pin]
2983 total_timeout: Option<Pin<Box<Sleep>>>,
2984 #[pin]
2985 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2986 read_timeout: Option<Duration>,
2987 }
2988}
2989
2990enum ResponseFuture {
2991 Default(LayeredFuture<HyperService>),
2992 #[cfg(feature = "http3")]
2993 H3(LayeredFuture<H3Client>),
2994}
2995
2996impl PendingRequest {
2997 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2998 self.project().in_flight
2999 }
3000
3001 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3002 self.project().total_timeout
3003 }
3004
3005 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3006 self.project().read_timeout_fut
3007 }
3008}
3009
3010impl Pending {
3011 pub(super) fn new_err(err: crate::Error) -> Pending {
3012 Pending {
3013 inner: PendingInner::Error(Some(err)),
3014 }
3015 }
3016
3017 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
3018 self.project().inner
3019 }
3020}
3021
3022impl Future for Pending {
3023 type Output = Result<Response, crate::Error>;
3024
3025 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3026 let inner = self.inner();
3027 match inner.get_mut() {
3028 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
3029 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
3030 .take()
3031 .expect("Pending error polled more than once"))),
3032 }
3033 }
3034}
3035
3036impl Future for PendingRequest {
3037 type Output = Result<Response, crate::Error>;
3038
3039 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3040 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
3041 if let Poll::Ready(()) = delay.poll(cx) {
3042 return Poll::Ready(Err(
3043 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3044 ));
3045 }
3046 }
3047
3048 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
3049 if let Poll::Ready(()) = delay.poll(cx) {
3050 return Poll::Ready(Err(
3051 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3052 ));
3053 }
3054 }
3055
3056 let res = match self.as_mut().in_flight().get_mut() {
3057 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
3058 Err(e) => {
3059 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
3060 }
3061 Ok(res) => res.map(super::body::boxed),
3062 },
3063 #[cfg(feature = "http3")]
3064 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
3065 Err(e) => {
3066 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
3067 }
3068 Ok(res) => res.map(super::body::boxed),
3069 },
3070 };
3071
3072 if let Some(url) = &res
3073 .extensions()
3074 .get::<tower_http::follow_redirect::RequestUri>()
3075 {
3076 self.url = match Url::parse(&url.0.to_string()) {
3077 Ok(url) => url,
3078 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3079 }
3080 };
3081
3082 let res = Response::new(
3083 res,
3084 self.url.clone(),
3085 self.total_timeout.take(),
3086 self.read_timeout,
3087 );
3088 Poll::Ready(Ok(res))
3089 }
3090}
3091
3092impl fmt::Debug for Pending {
3093 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3094 match self.inner {
3095 PendingInner::Request(ref req) => f
3096 .debug_struct("Pending")
3097 .field("method", &req.method)
3098 .field("url", &req.url)
3099 .finish(),
3100 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3101 }
3102 }
3103}
3104
3105#[cfg(test)]
3106mod tests {
3107 #![cfg(not(feature = "rustls-no-provider"))]
3108
3109 #[tokio::test]
3110 async fn execute_request_rejects_invalid_urls() {
3111 let url_str = "hxxps://www.rust-lang.org/";
3112 let url = url::Url::parse(url_str).unwrap();
3113 let result = crate::get(url.clone()).await;
3114
3115 assert!(result.is_err());
3116 let err = result.err().unwrap();
3117 assert!(err.is_builder());
3118 assert_eq!(url_str, err.url().unwrap().as_str());
3119 }
3120
3121 #[tokio::test]
3123 async fn execute_request_rejects_invalid_hostname() {
3124 let url_str = "https://{{hostname}}/";
3125 let url = url::Url::parse(url_str).unwrap();
3126 let result = crate::get(url.clone()).await;
3127
3128 assert!(result.is_err());
3129 let err = result.err().unwrap();
3130 assert!(err.is_builder());
3131 assert_eq!(url_str, err.url().unwrap().as_str());
3132 }
3133
3134 #[test]
3135 fn test_future_size() {
3136 let s = std::mem::size_of::<super::Pending>();
3137 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3138 }
3139}