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::decoder::Accepts;
13use super::request::{Request, RequestBuilder};
14use super::response::Response;
15use super::Body;
16#[cfg(feature = "http3")]
17use crate::async_impl::h3_client::connect::{H3ClientConfig, H3Connector};
18#[cfg(feature = "http3")]
19use crate::async_impl::h3_client::H3Client;
20use crate::config::{RequestConfig, TotalTimeout};
21#[cfg(unix)]
22use crate::connect::uds::UnixSocketProvider;
23use crate::connect::{
24 sealed::{Conn, Unnameable},
25 BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
26};
27#[cfg(feature = "cookies")]
28use crate::cookie;
29#[cfg(feature = "cookies")]
30use crate::cookie::service::CookieService;
31#[cfg(feature = "hickory-dns")]
32use crate::dns::hickory::HickoryDnsResolver;
33use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
34use crate::error::{self, BoxError};
35use crate::into_url::try_uri;
36use crate::proxy::Matcher as ProxyMatcher;
37use crate::redirect::{self, TowerRedirectPolicy};
38#[cfg(feature = "__rustls")]
39use crate::tls::CertificateRevocationList;
40#[cfg(feature = "__tls")]
41use crate::tls::{self, TlsBackend};
42#[cfg(feature = "__tls")]
43use crate::Certificate;
44#[cfg(any(feature = "native-tls", feature = "__rustls"))]
45use crate::Identity;
46use crate::{IntoUrl, Method, Proxy, Url};
47
48use http::header::{
49 Entry, HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, PROXY_AUTHORIZATION, RANGE, USER_AGENT,
50};
51use http::uri::Scheme;
52use http::Uri;
53use hyper_util::client::legacy::connect::HttpConnector;
54#[cfg(feature = "default-tls")]
55use native_tls_crate::TlsConnector;
56use pin_project_lite::pin_project;
57#[cfg(feature = "http3")]
58use quinn::TransportConfig;
59#[cfg(feature = "http3")]
60use quinn::VarInt;
61use tokio::time::Sleep;
62use tower::util::BoxCloneSyncServiceLayer;
63use tower::{Layer, Service};
64use tower_http::follow_redirect::FollowRedirect;
65
66#[derive(Clone)]
80pub struct Client {
81 inner: Arc<ClientRef>,
82}
83
84#[must_use]
86pub struct ClientBuilder {
87 config: Config,
88}
89
90enum HttpVersionPref {
91 Http1,
92 #[cfg(feature = "http2")]
93 Http2,
94 #[cfg(feature = "http3")]
95 Http3,
96 All,
97}
98
99#[derive(Clone)]
100struct HyperService {
101 hyper: HyperClient,
102}
103
104impl Service<hyper::Request<crate::async_impl::body::Body>> for HyperService {
105 type Error = crate::Error;
106 type Response = http::Response<hyper::body::Incoming>;
107 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + Sync>>;
108
109 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
110 self.hyper.poll_ready(cx).map_err(crate::error::request)
111 }
112
113 fn call(&mut self, req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
114 let clone = self.hyper.clone();
115 let mut inner = std::mem::replace(&mut self.hyper, clone);
116 Box::pin(async move { inner.call(req).await.map_err(crate::error::request) })
117 }
118}
119
120struct Config {
121 accepts: Accepts,
123 headers: HeaderMap,
124 #[cfg(feature = "__tls")]
125 hostname_verification: bool,
126 #[cfg(feature = "__tls")]
127 certs_verification: bool,
128 #[cfg(feature = "__tls")]
129 tls_sni: bool,
130 connect_timeout: Option<Duration>,
131 connection_verbose: bool,
132 pool_idle_timeout: Option<Duration>,
133 pool_max_idle_per_host: usize,
134 tcp_keepalive: Option<Duration>,
135 tcp_keepalive_interval: Option<Duration>,
136 tcp_keepalive_retries: Option<u32>,
137 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
138 tcp_user_timeout: Option<Duration>,
139 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
140 identity: Option<Identity>,
141 proxies: Vec<ProxyMatcher>,
142 auto_sys_proxy: bool,
143 redirect_policy: redirect::Policy,
144 retry_policy: crate::retry::Builder,
145 referer: bool,
146 read_timeout: Option<Duration>,
147 timeout: Option<Duration>,
148 #[cfg(feature = "__tls")]
149 root_certs: Vec<Certificate>,
150 #[cfg(feature = "__tls")]
151 tls_built_in_root_certs: bool,
152 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
153 tls_built_in_certs_webpki: bool,
154 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
155 tls_built_in_certs_native: bool,
156 #[cfg(feature = "__rustls")]
157 crls: Vec<CertificateRevocationList>,
158 #[cfg(feature = "__tls")]
159 min_tls_version: Option<tls::Version>,
160 #[cfg(feature = "__tls")]
161 max_tls_version: Option<tls::Version>,
162 #[cfg(feature = "__tls")]
163 tls_info: bool,
164 #[cfg(feature = "__tls")]
165 tls: TlsBackend,
166 connector_layers: Vec<BoxedConnectorLayer>,
167 http_version_pref: HttpVersionPref,
168 http09_responses: bool,
169 http1_title_case_headers: bool,
170 http1_allow_obsolete_multiline_headers_in_responses: bool,
171 http1_ignore_invalid_headers_in_responses: bool,
172 http1_allow_spaces_after_header_name_in_responses: bool,
173 #[cfg(feature = "http2")]
174 http2_initial_stream_window_size: Option<u32>,
175 #[cfg(feature = "http2")]
176 http2_initial_connection_window_size: Option<u32>,
177 #[cfg(feature = "http2")]
178 http2_adaptive_window: bool,
179 #[cfg(feature = "http2")]
180 http2_max_frame_size: Option<u32>,
181 #[cfg(feature = "http2")]
182 http2_max_header_list_size: Option<u32>,
183 #[cfg(feature = "http2")]
184 http2_keep_alive_interval: Option<Duration>,
185 #[cfg(feature = "http2")]
186 http2_keep_alive_timeout: Option<Duration>,
187 #[cfg(feature = "http2")]
188 http2_keep_alive_while_idle: bool,
189 local_address: Option<IpAddr>,
190 #[cfg(any(
191 target_os = "android",
192 target_os = "fuchsia",
193 target_os = "illumos",
194 target_os = "ios",
195 target_os = "linux",
196 target_os = "macos",
197 target_os = "solaris",
198 target_os = "tvos",
199 target_os = "visionos",
200 target_os = "watchos",
201 ))]
202 interface: Option<String>,
203 nodelay: bool,
204 #[cfg(feature = "cookies")]
205 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
206 hickory_dns: bool,
207 error: Option<crate::Error>,
208 https_only: bool,
209 #[cfg(feature = "http3")]
210 tls_enable_early_data: bool,
211 #[cfg(feature = "http3")]
212 quic_max_idle_timeout: Option<Duration>,
213 #[cfg(feature = "http3")]
214 quic_stream_receive_window: Option<VarInt>,
215 #[cfg(feature = "http3")]
216 quic_receive_window: Option<VarInt>,
217 #[cfg(feature = "http3")]
218 quic_send_window: Option<u64>,
219 #[cfg(feature = "http3")]
220 quic_congestion_bbr: bool,
221 #[cfg(feature = "http3")]
222 h3_max_field_section_size: Option<u64>,
223 #[cfg(feature = "http3")]
224 h3_send_grease: Option<bool>,
225 dns_overrides: HashMap<String, Vec<SocketAddr>>,
226 dns_resolver: Option<Arc<dyn Resolve>>,
227
228 #[cfg(unix)]
229 unix_socket: Option<Arc<std::path::Path>>,
230}
231
232impl Default for ClientBuilder {
233 fn default() -> Self {
234 Self::new()
235 }
236}
237
238impl ClientBuilder {
239 pub fn new() -> Self {
243 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
244 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
245
246 ClientBuilder {
247 config: Config {
248 error: None,
249 accepts: Accepts::default(),
250 headers,
251 #[cfg(feature = "__tls")]
252 hostname_verification: true,
253 #[cfg(feature = "__tls")]
254 certs_verification: true,
255 #[cfg(feature = "__tls")]
256 tls_sni: true,
257 connect_timeout: None,
258 connection_verbose: false,
259 pool_idle_timeout: Some(Duration::from_secs(90)),
260 pool_max_idle_per_host: usize::MAX,
261 tcp_keepalive: Some(Duration::from_secs(15)),
262 tcp_keepalive_interval: Some(Duration::from_secs(15)),
263 tcp_keepalive_retries: Some(3),
264 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
265 tcp_user_timeout: Some(Duration::from_secs(30)),
266 proxies: Vec::new(),
267 auto_sys_proxy: true,
268 redirect_policy: redirect::Policy::default(),
269 retry_policy: crate::retry::Builder::default(),
270 referer: true,
271 read_timeout: None,
272 timeout: None,
273 #[cfg(feature = "__tls")]
274 root_certs: Vec::new(),
275 #[cfg(feature = "__tls")]
276 tls_built_in_root_certs: true,
277 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
278 tls_built_in_certs_webpki: true,
279 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
280 tls_built_in_certs_native: true,
281 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
282 identity: None,
283 #[cfg(feature = "__rustls")]
284 crls: vec![],
285 #[cfg(feature = "__tls")]
286 min_tls_version: None,
287 #[cfg(feature = "__tls")]
288 max_tls_version: None,
289 #[cfg(feature = "__tls")]
290 tls_info: false,
291 #[cfg(feature = "__tls")]
292 tls: TlsBackend::default(),
293 connector_layers: Vec::new(),
294 http_version_pref: HttpVersionPref::All,
295 http09_responses: false,
296 http1_title_case_headers: false,
297 http1_allow_obsolete_multiline_headers_in_responses: false,
298 http1_ignore_invalid_headers_in_responses: false,
299 http1_allow_spaces_after_header_name_in_responses: false,
300 #[cfg(feature = "http2")]
301 http2_initial_stream_window_size: None,
302 #[cfg(feature = "http2")]
303 http2_initial_connection_window_size: None,
304 #[cfg(feature = "http2")]
305 http2_adaptive_window: false,
306 #[cfg(feature = "http2")]
307 http2_max_frame_size: None,
308 #[cfg(feature = "http2")]
309 http2_max_header_list_size: None,
310 #[cfg(feature = "http2")]
311 http2_keep_alive_interval: None,
312 #[cfg(feature = "http2")]
313 http2_keep_alive_timeout: None,
314 #[cfg(feature = "http2")]
315 http2_keep_alive_while_idle: false,
316 local_address: None,
317 #[cfg(any(
318 target_os = "android",
319 target_os = "fuchsia",
320 target_os = "illumos",
321 target_os = "ios",
322 target_os = "linux",
323 target_os = "macos",
324 target_os = "solaris",
325 target_os = "tvos",
326 target_os = "visionos",
327 target_os = "watchos",
328 ))]
329 interface: None,
330 nodelay: true,
331 hickory_dns: cfg!(feature = "hickory-dns"),
332 #[cfg(feature = "cookies")]
333 cookie_store: None,
334 https_only: false,
335 dns_overrides: HashMap::new(),
336 #[cfg(feature = "http3")]
337 tls_enable_early_data: false,
338 #[cfg(feature = "http3")]
339 quic_max_idle_timeout: None,
340 #[cfg(feature = "http3")]
341 quic_stream_receive_window: None,
342 #[cfg(feature = "http3")]
343 quic_receive_window: None,
344 #[cfg(feature = "http3")]
345 quic_send_window: None,
346 #[cfg(feature = "http3")]
347 quic_congestion_bbr: false,
348 #[cfg(feature = "http3")]
349 h3_max_field_section_size: None,
350 #[cfg(feature = "http3")]
351 h3_send_grease: None,
352 dns_resolver: None,
353 #[cfg(unix)]
354 unix_socket: None,
355 },
356 }
357 }
358}
359
360impl ClientBuilder {
361 pub fn build(self) -> crate::Result<Client> {
368 let config = self.config;
369
370 if let Some(err) = config.error {
371 return Err(err);
372 }
373
374 let mut proxies = config.proxies;
375 if config.auto_sys_proxy {
376 proxies.push(ProxyMatcher::system());
377 }
378 let proxies = Arc::new(proxies);
379
380 #[allow(unused)]
381 #[cfg(feature = "http3")]
382 let mut h3_connector = None;
383
384 let resolver = {
385 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
386 false => Arc::new(GaiResolver::new()),
387 #[cfg(feature = "hickory-dns")]
388 true => Arc::new(HickoryDnsResolver::default()),
389 #[cfg(not(feature = "hickory-dns"))]
390 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
391 };
392 if let Some(dns_resolver) = config.dns_resolver {
393 resolver = dns_resolver;
394 }
395 if !config.dns_overrides.is_empty() {
396 resolver = Arc::new(DnsResolverWithOverrides::new(
397 resolver,
398 config.dns_overrides,
399 ));
400 }
401 DynResolver::new(resolver)
402 };
403
404 let mut connector_builder = {
405 #[cfg(feature = "__tls")]
406 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
407 headers.get(USER_AGENT).cloned()
408 }
409
410 let mut http = HttpConnector::new_with_resolver(resolver.clone());
411 http.set_connect_timeout(config.connect_timeout);
412
413 #[cfg(all(feature = "http3", feature = "__rustls"))]
414 let build_h3_connector =
415 |resolver,
416 tls,
417 quic_max_idle_timeout: Option<Duration>,
418 quic_stream_receive_window,
419 quic_receive_window,
420 quic_send_window,
421 quic_congestion_bbr,
422 h3_max_field_section_size,
423 h3_send_grease,
424 local_address,
425 http_version_pref: &HttpVersionPref| {
426 let mut transport_config = TransportConfig::default();
427
428 if let Some(max_idle_timeout) = quic_max_idle_timeout {
429 transport_config.max_idle_timeout(Some(
430 max_idle_timeout.try_into().map_err(error::builder)?,
431 ));
432 }
433
434 if let Some(stream_receive_window) = quic_stream_receive_window {
435 transport_config.stream_receive_window(stream_receive_window);
436 }
437
438 if let Some(receive_window) = quic_receive_window {
439 transport_config.receive_window(receive_window);
440 }
441
442 if let Some(send_window) = quic_send_window {
443 transport_config.send_window(send_window);
444 }
445
446 if quic_congestion_bbr {
447 let factory = Arc::new(quinn::congestion::BbrConfig::default());
448 transport_config.congestion_controller_factory(factory);
449 }
450
451 let mut h3_client_config = H3ClientConfig::default();
452
453 if let Some(max_field_section_size) = h3_max_field_section_size {
454 h3_client_config.max_field_section_size = Some(max_field_section_size);
455 }
456
457 if let Some(send_grease) = h3_send_grease {
458 h3_client_config.send_grease = Some(send_grease);
459 }
460
461 let res = H3Connector::new(
462 resolver,
463 tls,
464 local_address,
465 transport_config,
466 h3_client_config,
467 );
468
469 match res {
470 Ok(connector) => Ok(Some(connector)),
471 Err(err) => {
472 if let HttpVersionPref::Http3 = http_version_pref {
473 Err(error::builder(err))
474 } else {
475 Ok(None)
476 }
477 }
478 }
479 };
480
481 #[cfg(feature = "__tls")]
482 match config.tls {
483 #[cfg(feature = "default-tls")]
484 TlsBackend::Default => {
485 let mut tls = TlsConnector::builder();
486
487 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
488 {
489 match config.http_version_pref {
490 HttpVersionPref::Http1 => {
491 tls.request_alpns(&["http/1.1"]);
492 }
493 #[cfg(feature = "http2")]
494 HttpVersionPref::Http2 => {
495 tls.request_alpns(&["h2"]);
496 }
497 HttpVersionPref::All => {
498 tls.request_alpns(&["h2", "http/1.1"]);
499 }
500 }
501 }
502
503 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
504
505 tls.danger_accept_invalid_certs(!config.certs_verification);
506
507 tls.use_sni(config.tls_sni);
508
509 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
510
511 for cert in config.root_certs {
512 cert.add_to_native_tls(&mut tls);
513 }
514
515 #[cfg(feature = "native-tls")]
516 {
517 if let Some(id) = config.identity {
518 id.add_to_native_tls(&mut tls)?;
519 }
520 }
521 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
522 {
523 if let Some(_id) = config.identity {
525 return Err(crate::error::builder("incompatible TLS identity type"));
526 }
527 }
528
529 if let Some(min_tls_version) = config.min_tls_version {
530 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
531 crate::error::builder("invalid minimum TLS version for backend")
535 })?;
536 tls.min_protocol_version(Some(protocol));
537 }
538
539 if let Some(max_tls_version) = config.max_tls_version {
540 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
541 crate::error::builder("invalid maximum TLS version for backend")
546 })?;
547 tls.max_protocol_version(Some(protocol));
548 }
549
550 ConnectorBuilder::new_default_tls(
551 http,
552 tls,
553 proxies.clone(),
554 user_agent(&config.headers),
555 config.local_address,
556 #[cfg(any(
557 target_os = "android",
558 target_os = "fuchsia",
559 target_os = "illumos",
560 target_os = "ios",
561 target_os = "linux",
562 target_os = "macos",
563 target_os = "solaris",
564 target_os = "tvos",
565 target_os = "visionos",
566 target_os = "watchos",
567 ))]
568 config.interface.as_deref(),
569 config.nodelay,
570 config.tls_info,
571 )?
572 }
573 #[cfg(feature = "native-tls")]
574 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_default_tls(
575 http,
576 conn,
577 proxies.clone(),
578 user_agent(&config.headers),
579 config.local_address,
580 #[cfg(any(
581 target_os = "android",
582 target_os = "fuchsia",
583 target_os = "illumos",
584 target_os = "ios",
585 target_os = "linux",
586 target_os = "macos",
587 target_os = "solaris",
588 target_os = "tvos",
589 target_os = "visionos",
590 target_os = "watchos",
591 ))]
592 config.interface.as_deref(),
593 config.nodelay,
594 config.tls_info,
595 ),
596 #[cfg(feature = "__rustls")]
597 TlsBackend::BuiltRustls(conn) => {
598 #[cfg(feature = "http3")]
599 {
600 h3_connector = build_h3_connector(
601 resolver.clone(),
602 conn.clone(),
603 config.quic_max_idle_timeout,
604 config.quic_stream_receive_window,
605 config.quic_receive_window,
606 config.quic_send_window,
607 config.quic_congestion_bbr,
608 config.h3_max_field_section_size,
609 config.h3_send_grease,
610 config.local_address,
611 &config.http_version_pref,
612 )?;
613 }
614
615 ConnectorBuilder::new_rustls_tls(
616 http,
617 conn,
618 proxies.clone(),
619 user_agent(&config.headers),
620 config.local_address,
621 #[cfg(any(
622 target_os = "android",
623 target_os = "fuchsia",
624 target_os = "illumos",
625 target_os = "ios",
626 target_os = "linux",
627 target_os = "macos",
628 target_os = "solaris",
629 target_os = "tvos",
630 target_os = "visionos",
631 target_os = "watchos",
632 ))]
633 config.interface.as_deref(),
634 config.nodelay,
635 config.tls_info,
636 )
637 }
638 #[cfg(feature = "__rustls")]
639 TlsBackend::Rustls => {
640 use crate::tls::{IgnoreHostname, NoVerifier};
641
642 let mut root_cert_store = rustls::RootCertStore::empty();
644 for cert in config.root_certs {
645 cert.add_to_rustls(&mut root_cert_store)?;
646 }
647
648 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
649 if config.tls_built_in_certs_webpki {
650 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
651 }
652
653 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
654 if config.tls_built_in_certs_native {
655 let mut valid_count = 0;
656 let mut invalid_count = 0;
657
658 let load_results = rustls_native_certs::load_native_certs();
659 for cert in load_results.certs {
660 match root_cert_store.add(cert.into()) {
664 Ok(_) => valid_count += 1,
665 Err(err) => {
666 invalid_count += 1;
667 log::debug!("rustls failed to parse DER certificate: {err:?}");
668 }
669 }
670 }
671 if valid_count == 0 && invalid_count > 0 {
672 let err = if load_results.errors.is_empty() {
673 crate::error::builder(
674 "zero valid certificates found in native root store",
675 )
676 } else {
677 use std::fmt::Write as _;
678 let mut acc = String::new();
679 for err in load_results.errors {
680 let _ = writeln!(&mut acc, "{err}");
681 }
682
683 crate::error::builder(acc)
684 };
685
686 return Err(err);
687 }
688 }
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(|| {
722 #[cfg(not(feature = "__rustls-ring"))]
723 panic!("No provider set");
724
725 #[cfg(feature = "__rustls-ring")]
726 Arc::new(rustls::crypto::ring::default_provider())
727 });
728
729 let signature_algorithms = provider.signature_verification_algorithms;
731 let config_builder =
732 rustls::ClientConfig::builder_with_provider(provider.clone())
733 .with_protocol_versions(&versions)
734 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
735
736 let config_builder = if !config.certs_verification {
737 config_builder
738 .dangerous()
739 .with_custom_certificate_verifier(Arc::new(NoVerifier))
740 } else if !config.hostname_verification {
741 config_builder
742 .dangerous()
743 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
744 root_cert_store,
745 signature_algorithms,
746 )))
747 } else {
748 if config.crls.is_empty() {
749 config_builder.with_root_certificates(root_cert_store)
750 } else {
751 let crls = config
752 .crls
753 .iter()
754 .map(|e| e.as_rustls_crl())
755 .collect::<Vec<_>>();
756 let verifier =
757 rustls::client::WebPkiServerVerifier::builder_with_provider(
758 Arc::new(root_cert_store),
759 provider,
760 )
761 .with_crls(crls)
762 .build()
763 .map_err(|_| {
764 crate::error::builder("invalid TLS verification settings")
765 })?;
766 config_builder.with_webpki_verifier(verifier)
767 }
768 };
769
770 let mut tls = if let Some(id) = config.identity {
772 id.add_to_rustls(config_builder)?
773 } else {
774 config_builder.with_no_client_auth()
775 };
776
777 tls.enable_sni = config.tls_sni;
778
779 match config.http_version_pref {
781 HttpVersionPref::Http1 => {
782 tls.alpn_protocols = vec!["http/1.1".into()];
783 }
784 #[cfg(feature = "http2")]
785 HttpVersionPref::Http2 => {
786 tls.alpn_protocols = vec!["h2".into()];
787 }
788 #[cfg(feature = "http3")]
789 HttpVersionPref::Http3 => {
790 tls.alpn_protocols = vec!["h3".into()];
791 }
792 HttpVersionPref::All => {
793 tls.alpn_protocols = vec![
794 #[cfg(feature = "http2")]
795 "h2".into(),
796 "http/1.1".into(),
797 ];
798 }
799 }
800
801 #[cfg(feature = "http3")]
802 {
803 tls.enable_early_data = config.tls_enable_early_data;
804
805 h3_connector = build_h3_connector(
806 resolver.clone(),
807 tls.clone(),
808 config.quic_max_idle_timeout,
809 config.quic_stream_receive_window,
810 config.quic_receive_window,
811 config.quic_send_window,
812 config.quic_congestion_bbr,
813 config.h3_max_field_section_size,
814 config.h3_send_grease,
815 config.local_address,
816 &config.http_version_pref,
817 )?;
818 }
819
820 ConnectorBuilder::new_rustls_tls(
821 http,
822 tls,
823 proxies.clone(),
824 user_agent(&config.headers),
825 config.local_address,
826 #[cfg(any(
827 target_os = "android",
828 target_os = "fuchsia",
829 target_os = "illumos",
830 target_os = "ios",
831 target_os = "linux",
832 target_os = "macos",
833 target_os = "solaris",
834 target_os = "tvos",
835 target_os = "visionos",
836 target_os = "watchos",
837 ))]
838 config.interface.as_deref(),
839 config.nodelay,
840 config.tls_info,
841 )
842 }
843 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
844 TlsBackend::UnknownPreconfigured => {
845 return Err(crate::error::builder(
846 "Unknown TLS backend passed to `use_preconfigured_tls`",
847 ));
848 }
849 }
850
851 #[cfg(not(feature = "__tls"))]
852 ConnectorBuilder::new(
853 http,
854 proxies.clone(),
855 config.local_address,
856 #[cfg(any(
857 target_os = "android",
858 target_os = "fuchsia",
859 target_os = "illumos",
860 target_os = "ios",
861 target_os = "linux",
862 target_os = "macos",
863 target_os = "solaris",
864 target_os = "tvos",
865 target_os = "visionos",
866 target_os = "watchos",
867 ))]
868 config.interface.as_deref(),
869 config.nodelay,
870 )
871 };
872
873 connector_builder.set_timeout(config.connect_timeout);
874 connector_builder.set_verbose(config.connection_verbose);
875 connector_builder.set_keepalive(config.tcp_keepalive);
876 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
877 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
878 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
879 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
880
881 #[cfg(feature = "socks")]
882 connector_builder.set_socks_resolver(resolver);
883
884 #[cfg(unix)]
888 connector_builder.set_unix_socket(config.unix_socket);
889
890 let mut builder =
891 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
892 #[cfg(feature = "http2")]
893 {
894 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
895 builder.http2_only(true);
896 }
897
898 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
899 {
900 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
901 }
902 if let Some(http2_initial_connection_window_size) =
903 config.http2_initial_connection_window_size
904 {
905 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
906 }
907 if config.http2_adaptive_window {
908 builder.http2_adaptive_window(true);
909 }
910 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
911 builder.http2_max_frame_size(http2_max_frame_size);
912 }
913 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
914 builder.http2_max_header_list_size(http2_max_header_list_size);
915 }
916 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
917 builder.http2_keep_alive_interval(http2_keep_alive_interval);
918 }
919 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
920 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
921 }
922 if config.http2_keep_alive_while_idle {
923 builder.http2_keep_alive_while_idle(true);
924 }
925 }
926
927 builder.timer(hyper_util::rt::TokioTimer::new());
928 builder.pool_timer(hyper_util::rt::TokioTimer::new());
929 builder.pool_idle_timeout(config.pool_idle_timeout);
930 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
931
932 if config.http09_responses {
933 builder.http09_responses(true);
934 }
935
936 if config.http1_title_case_headers {
937 builder.http1_title_case_headers(true);
938 }
939
940 if config.http1_allow_obsolete_multiline_headers_in_responses {
941 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
942 }
943
944 if config.http1_ignore_invalid_headers_in_responses {
945 builder.http1_ignore_invalid_headers_in_responses(true);
946 }
947
948 if config.http1_allow_spaces_after_header_name_in_responses {
949 builder.http1_allow_spaces_after_header_name_in_responses(true);
950 }
951
952 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
953 let proxies_maybe_http_custom_headers =
954 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
955
956 let redirect_policy_desc = if config.redirect_policy.is_default() {
957 None
958 } else {
959 Some(format!("{:?}", &config.redirect_policy))
960 };
961
962 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
963 let hyper_service = HyperService {
964 hyper: hyper_client,
965 };
966
967 let redirect_policy = {
968 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
969 p.with_referer(config.referer)
970 .with_https_only(config.https_only);
971 p
972 };
973
974 let retry_policy = config.retry_policy.into_policy();
975
976 let svc = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
977
978 #[cfg(feature = "cookies")]
979 let svc = CookieService::new(svc, config.cookie_store.clone());
980 let hyper = FollowRedirect::with_policy(svc, redirect_policy.clone());
981
982 Ok(Client {
983 inner: Arc::new(ClientRef {
984 accepts: config.accepts,
985 #[cfg(feature = "cookies")]
986 cookie_store: config.cookie_store.clone(),
987 #[cfg(feature = "http3")]
990 h3_client: match h3_connector {
991 Some(h3_connector) => {
992 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
993 let svc = tower::retry::Retry::new(retry_policy, h3_service);
994 #[cfg(feature = "cookies")]
995 let svc = CookieService::new(svc, config.cookie_store);
996 Some(FollowRedirect::with_policy(svc, redirect_policy))
997 }
998 None => None,
999 },
1000 headers: config.headers,
1001 referer: config.referer,
1002 read_timeout: config.read_timeout,
1003 total_timeout: RequestConfig::new(config.timeout),
1004 hyper,
1005 proxies,
1006 proxies_maybe_http_auth,
1007 proxies_maybe_http_custom_headers,
1008 https_only: config.https_only,
1009 redirect_policy_desc,
1010 }),
1011 })
1012 }
1013
1014 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1037 where
1038 V: TryInto<HeaderValue>,
1039 V::Error: Into<http::Error>,
1040 {
1041 match value.try_into() {
1042 Ok(value) => {
1043 self.config.headers.insert(USER_AGENT, value);
1044 }
1045 Err(e) => {
1046 self.config.error = Some(crate::error::builder(e.into()));
1047 }
1048 };
1049 self
1050 }
1051 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1075 for (key, value) in headers.iter() {
1076 self.config.headers.insert(key, value.clone());
1077 }
1078 self
1079 }
1080
1081 #[cfg(feature = "cookies")]
1096 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1097 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1098 if enable {
1099 self.cookie_provider(Arc::new(cookie::Jar::default()))
1100 } else {
1101 self.config.cookie_store = None;
1102 self
1103 }
1104 }
1105
1106 #[cfg(feature = "cookies")]
1120 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1121 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1122 mut self,
1123 cookie_store: Arc<C>,
1124 ) -> ClientBuilder {
1125 self.config.cookie_store = Some(cookie_store as _);
1126 self
1127 }
1128
1129 #[cfg(feature = "gzip")]
1146 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1147 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1148 self.config.accepts.gzip = enable;
1149 self
1150 }
1151
1152 #[cfg(feature = "brotli")]
1169 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1170 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1171 self.config.accepts.brotli = enable;
1172 self
1173 }
1174
1175 #[cfg(feature = "zstd")]
1192 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1193 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1194 self.config.accepts.zstd = enable;
1195 self
1196 }
1197
1198 #[cfg(feature = "deflate")]
1215 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1216 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1217 self.config.accepts.deflate = enable;
1218 self
1219 }
1220
1221 pub fn no_gzip(self) -> ClientBuilder {
1227 #[cfg(feature = "gzip")]
1228 {
1229 self.gzip(false)
1230 }
1231
1232 #[cfg(not(feature = "gzip"))]
1233 {
1234 self
1235 }
1236 }
1237
1238 pub fn no_brotli(self) -> ClientBuilder {
1244 #[cfg(feature = "brotli")]
1245 {
1246 self.brotli(false)
1247 }
1248
1249 #[cfg(not(feature = "brotli"))]
1250 {
1251 self
1252 }
1253 }
1254
1255 pub fn no_zstd(self) -> ClientBuilder {
1261 #[cfg(feature = "zstd")]
1262 {
1263 self.zstd(false)
1264 }
1265
1266 #[cfg(not(feature = "zstd"))]
1267 {
1268 self
1269 }
1270 }
1271
1272 pub fn no_deflate(self) -> ClientBuilder {
1278 #[cfg(feature = "deflate")]
1279 {
1280 self.deflate(false)
1281 }
1282
1283 #[cfg(not(feature = "deflate"))]
1284 {
1285 self
1286 }
1287 }
1288
1289 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1295 self.config.redirect_policy = policy;
1296 self
1297 }
1298
1299 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1303 self.config.referer = enable;
1304 self
1305 }
1306
1307 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1314 self.config.retry_policy = policy;
1315 self
1316 }
1317
1318 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1326 self.config.proxies.push(proxy.into_matcher());
1327 self.config.auto_sys_proxy = false;
1328 self
1329 }
1330
1331 pub fn no_proxy(mut self) -> ClientBuilder {
1339 self.config.proxies.clear();
1340 self.config.auto_sys_proxy = false;
1341 self
1342 }
1343
1344 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1353 self.config.timeout = Some(timeout);
1354 self
1355 }
1356
1357 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1365 self.config.read_timeout = Some(timeout);
1366 self
1367 }
1368
1369 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1378 self.config.connect_timeout = Some(timeout);
1379 self
1380 }
1381
1382 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1389 self.config.connection_verbose = verbose;
1390 self
1391 }
1392
1393 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1401 where
1402 D: Into<Option<Duration>>,
1403 {
1404 self.config.pool_idle_timeout = val.into();
1405 self
1406 }
1407
1408 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1410 self.config.pool_max_idle_per_host = max;
1411 self
1412 }
1413
1414 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1416 self.config.http1_title_case_headers = true;
1417 self
1418 }
1419
1420 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1426 mut self,
1427 value: bool,
1428 ) -> ClientBuilder {
1429 self.config
1430 .http1_allow_obsolete_multiline_headers_in_responses = value;
1431 self
1432 }
1433
1434 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1436 self.config.http1_ignore_invalid_headers_in_responses = value;
1437 self
1438 }
1439
1440 pub fn http1_allow_spaces_after_header_name_in_responses(
1446 mut self,
1447 value: bool,
1448 ) -> ClientBuilder {
1449 self.config
1450 .http1_allow_spaces_after_header_name_in_responses = value;
1451 self
1452 }
1453
1454 pub fn http1_only(mut self) -> ClientBuilder {
1456 self.config.http_version_pref = HttpVersionPref::Http1;
1457 self
1458 }
1459
1460 pub fn http09_responses(mut self) -> ClientBuilder {
1462 self.config.http09_responses = true;
1463 self
1464 }
1465
1466 #[cfg(feature = "http2")]
1468 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1469 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1470 self.config.http_version_pref = HttpVersionPref::Http2;
1471 self
1472 }
1473
1474 #[cfg(feature = "http3")]
1476 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1477 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1478 self.config.http_version_pref = HttpVersionPref::Http3;
1479 self
1480 }
1481
1482 #[cfg(feature = "http2")]
1486 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1487 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1488 self.config.http2_initial_stream_window_size = sz.into();
1489 self
1490 }
1491
1492 #[cfg(feature = "http2")]
1496 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1497 pub fn http2_initial_connection_window_size(
1498 mut self,
1499 sz: impl Into<Option<u32>>,
1500 ) -> ClientBuilder {
1501 self.config.http2_initial_connection_window_size = sz.into();
1502 self
1503 }
1504
1505 #[cfg(feature = "http2")]
1510 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1511 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1512 self.config.http2_adaptive_window = enabled;
1513 self
1514 }
1515
1516 #[cfg(feature = "http2")]
1520 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1521 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1522 self.config.http2_max_frame_size = sz.into();
1523 self
1524 }
1525
1526 #[cfg(feature = "http2")]
1530 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1531 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1532 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1533 self
1534 }
1535
1536 #[cfg(feature = "http2")]
1541 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1542 pub fn http2_keep_alive_interval(
1543 mut self,
1544 interval: impl Into<Option<Duration>>,
1545 ) -> ClientBuilder {
1546 self.config.http2_keep_alive_interval = interval.into();
1547 self
1548 }
1549
1550 #[cfg(feature = "http2")]
1556 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1557 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1558 self.config.http2_keep_alive_timeout = Some(timeout);
1559 self
1560 }
1561
1562 #[cfg(feature = "http2")]
1569 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1570 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1571 self.config.http2_keep_alive_while_idle = enabled;
1572 self
1573 }
1574
1575 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1581 self.config.nodelay = enabled;
1582 self
1583 }
1584
1585 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1600 where
1601 T: Into<Option<IpAddr>>,
1602 {
1603 self.config.local_address = addr.into();
1604 self
1605 }
1606
1607 #[cfg(any(
1640 target_os = "android",
1641 target_os = "fuchsia",
1642 target_os = "illumos",
1643 target_os = "ios",
1644 target_os = "linux",
1645 target_os = "macos",
1646 target_os = "solaris",
1647 target_os = "tvos",
1648 target_os = "visionos",
1649 target_os = "watchos",
1650 ))]
1651 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1652 self.config.interface = Some(interface.to_string());
1653 self
1654 }
1655
1656 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1660 where
1661 D: Into<Option<Duration>>,
1662 {
1663 self.config.tcp_keepalive = val.into();
1664 self
1665 }
1666
1667 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1671 where
1672 D: Into<Option<Duration>>,
1673 {
1674 self.config.tcp_keepalive_interval = val.into();
1675 self
1676 }
1677
1678 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1682 where
1683 C: Into<Option<u32>>,
1684 {
1685 self.config.tcp_keepalive_retries = retries.into();
1686 self
1687 }
1688
1689 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1696 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1697 where
1698 D: Into<Option<Duration>>,
1699 {
1700 self.config.tcp_user_timeout = val.into();
1701 self
1702 }
1703
1704 #[cfg(unix)]
1718 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1719 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1720 self
1721 }
1722
1723 #[cfg(feature = "__tls")]
1735 #[cfg_attr(
1736 docsrs,
1737 doc(cfg(any(
1738 feature = "default-tls",
1739 feature = "native-tls",
1740 feature = "rustls-tls"
1741 )))
1742 )]
1743 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1744 self.config.root_certs.push(cert);
1745 self
1746 }
1747
1748 #[cfg(feature = "__rustls")]
1755 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1756 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1757 self.config.crls.push(crl);
1758 self
1759 }
1760
1761 #[cfg(feature = "__rustls")]
1768 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1769 pub fn add_crls(
1770 mut self,
1771 crls: impl IntoIterator<Item = CertificateRevocationList>,
1772 ) -> ClientBuilder {
1773 self.config.crls.extend(crls);
1774 self
1775 }
1776
1777 #[cfg(feature = "__tls")]
1795 #[cfg_attr(
1796 docsrs,
1797 doc(cfg(any(
1798 feature = "default-tls",
1799 feature = "native-tls",
1800 feature = "rustls-tls"
1801 )))
1802 )]
1803 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1804 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1805
1806 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1807 {
1808 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1809 }
1810
1811 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1812 {
1813 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1814 }
1815
1816 self
1817 }
1818
1819 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1823 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1824 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1825 self.config.tls_built_in_certs_webpki = enabled;
1826 self
1827 }
1828
1829 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1833 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1834 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1835 self.config.tls_built_in_certs_native = enabled;
1836 self
1837 }
1838
1839 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1846 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1847 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1848 self.config.identity = Some(identity);
1849 self
1850 }
1851
1852 #[cfg(feature = "__tls")]
1868 #[cfg_attr(
1869 docsrs,
1870 doc(cfg(any(
1871 feature = "default-tls",
1872 feature = "native-tls",
1873 feature = "rustls-tls"
1874 )))
1875 )]
1876 pub fn danger_accept_invalid_hostnames(
1877 mut self,
1878 accept_invalid_hostname: bool,
1879 ) -> ClientBuilder {
1880 self.config.hostname_verification = !accept_invalid_hostname;
1881 self
1882 }
1883
1884 #[cfg(feature = "__tls")]
1901 #[cfg_attr(
1902 docsrs,
1903 doc(cfg(any(
1904 feature = "default-tls",
1905 feature = "native-tls",
1906 feature = "rustls-tls"
1907 )))
1908 )]
1909 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1910 self.config.certs_verification = !accept_invalid_certs;
1911 self
1912 }
1913
1914 #[cfg(feature = "__tls")]
1923 #[cfg_attr(
1924 docsrs,
1925 doc(cfg(any(
1926 feature = "default-tls",
1927 feature = "native-tls",
1928 feature = "rustls-tls"
1929 )))
1930 )]
1931 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1932 self.config.tls_sni = tls_sni;
1933 self
1934 }
1935
1936 #[cfg(feature = "__tls")]
1952 #[cfg_attr(
1953 docsrs,
1954 doc(cfg(any(
1955 feature = "default-tls",
1956 feature = "native-tls",
1957 feature = "rustls-tls"
1958 )))
1959 )]
1960 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1961 self.config.min_tls_version = Some(version);
1962 self
1963 }
1964
1965 #[cfg(feature = "__tls")]
1984 #[cfg_attr(
1985 docsrs,
1986 doc(cfg(any(
1987 feature = "default-tls",
1988 feature = "native-tls",
1989 feature = "rustls-tls"
1990 )))
1991 )]
1992 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1993 self.config.max_tls_version = Some(version);
1994 self
1995 }
1996
1997 #[cfg(feature = "native-tls")]
2006 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2007 pub fn use_native_tls(mut self) -> ClientBuilder {
2008 self.config.tls = TlsBackend::Default;
2009 self
2010 }
2011
2012 #[cfg(feature = "__rustls")]
2021 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
2022 pub fn use_rustls_tls(mut self) -> ClientBuilder {
2023 self.config.tls = TlsBackend::Rustls;
2024 self
2025 }
2026
2027 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
2046 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
2047 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
2048 let mut tls = Some(tls);
2049 #[cfg(feature = "native-tls")]
2050 {
2051 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2052 let tls = conn.take().expect("is definitely Some");
2053 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2054 self.config.tls = tls;
2055 return self;
2056 }
2057 }
2058 #[cfg(feature = "__rustls")]
2059 {
2060 if let Some(conn) =
2061 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2062 {
2063 let tls = conn.take().expect("is definitely Some");
2064 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2065 self.config.tls = tls;
2066 return self;
2067 }
2068 }
2069
2070 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2072 self
2073 }
2074
2075 #[cfg(feature = "__tls")]
2082 #[cfg_attr(
2083 docsrs,
2084 doc(cfg(any(
2085 feature = "default-tls",
2086 feature = "native-tls",
2087 feature = "rustls-tls"
2088 )))
2089 )]
2090 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2091 self.config.tls_info = tls_info;
2092 self
2093 }
2094
2095 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2099 self.config.https_only = enabled;
2100 self
2101 }
2102
2103 #[doc(hidden)]
2104 #[cfg(feature = "hickory-dns")]
2105 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2106 #[deprecated(note = "use `hickory_dns` instead")]
2107 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
2108 self.config.hickory_dns = enable;
2109 self
2110 }
2111
2112 #[cfg(feature = "hickory-dns")]
2126 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2127 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2128 self.config.hickory_dns = enable;
2129 self
2130 }
2131
2132 #[doc(hidden)]
2133 #[deprecated(note = "use `no_hickory_dns` instead")]
2134 pub fn no_trust_dns(self) -> ClientBuilder {
2135 self.no_hickory_dns()
2136 }
2137
2138 pub fn no_hickory_dns(self) -> ClientBuilder {
2144 #[cfg(feature = "hickory-dns")]
2145 {
2146 self.hickory_dns(false)
2147 }
2148
2149 #[cfg(not(feature = "hickory-dns"))]
2150 {
2151 self
2152 }
2153 }
2154
2155 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2160 self.resolve_to_addrs(domain, &[addr])
2161 }
2162
2163 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2168 self.config
2169 .dns_overrides
2170 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2171 self
2172 }
2173
2174 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
2180 self.config.dns_resolver = Some(resolver as _);
2181 self
2182 }
2183
2184 pub fn dns_resolver2<R>(mut self, resolver: R) -> ClientBuilder
2191 where
2192 R: crate::dns::resolve::IntoResolve,
2193 {
2194 self.config.dns_resolver = Some(resolver.into_resolve());
2195 self
2196 }
2197
2198 #[cfg(feature = "http3")]
2203 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2204 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2205 self.config.tls_enable_early_data = enabled;
2206 self
2207 }
2208
2209 #[cfg(feature = "http3")]
2215 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2216 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2217 self.config.quic_max_idle_timeout = Some(value);
2218 self
2219 }
2220
2221 #[cfg(feature = "http3")]
2232 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2233 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2234 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2235 self
2236 }
2237
2238 #[cfg(feature = "http3")]
2249 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2250 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2251 self.config.quic_receive_window = Some(value.try_into().unwrap());
2252 self
2253 }
2254
2255 #[cfg(feature = "http3")]
2261 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2262 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2263 self.config.quic_send_window = Some(value);
2264 self
2265 }
2266
2267 #[cfg(feature = "http3")]
2275 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2276 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2277 self.config.quic_congestion_bbr = true;
2278 self
2279 }
2280
2281 #[cfg(feature = "http3")]
2291 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2292 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2293 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2294 self
2295 }
2296
2297 #[cfg(feature = "http3")]
2309 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2310 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2311 self.config.h3_send_grease = Some(enabled);
2312 self
2313 }
2314
2315 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2339 where
2340 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2341 L::Service:
2342 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2343 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2344 {
2345 let layer = BoxCloneSyncServiceLayer::new(layer);
2346
2347 self.config.connector_layers.push(layer);
2348
2349 self
2350 }
2351}
2352
2353type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2354
2355impl Default for Client {
2356 fn default() -> Self {
2357 Self::new()
2358 }
2359}
2360
2361impl Client {
2362 pub fn new() -> Client {
2372 ClientBuilder::new().build().expect("Client::new()")
2373 }
2374
2375 pub fn builder() -> ClientBuilder {
2379 ClientBuilder::new()
2380 }
2381
2382 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2388 self.request(Method::GET, url)
2389 }
2390
2391 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2397 self.request(Method::POST, url)
2398 }
2399
2400 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2406 self.request(Method::PUT, url)
2407 }
2408
2409 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2415 self.request(Method::PATCH, url)
2416 }
2417
2418 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2424 self.request(Method::DELETE, url)
2425 }
2426
2427 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2433 self.request(Method::HEAD, url)
2434 }
2435
2436 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2445 let req = url.into_url().map(move |url| Request::new(method, url));
2446 RequestBuilder::new(self.clone(), req)
2447 }
2448
2449 pub fn execute(
2462 &self,
2463 request: Request,
2464 ) -> impl Future<Output = Result<Response, crate::Error>> {
2465 self.execute_request(request)
2466 }
2467
2468 pub(super) fn execute_request(&self, req: Request) -> Pending {
2469 let (method, url, mut headers, body, version, extensions) = req.pieces();
2470 if url.scheme() != "http" && url.scheme() != "https" {
2471 return Pending::new_err(error::url_bad_scheme(url));
2472 }
2473
2474 if self.inner.https_only && url.scheme() != "https" {
2476 return Pending::new_err(error::url_bad_scheme(url));
2477 }
2478
2479 for (key, value) in &self.inner.headers {
2482 if let Entry::Vacant(entry) = headers.entry(key) {
2483 entry.insert(value.clone());
2484 }
2485 }
2486
2487 let accept_encoding = self.inner.accepts.as_str();
2488
2489 if let Some(accept_encoding) = accept_encoding {
2490 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2491 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2492 }
2493 }
2494
2495 let uri = match try_uri(&url) {
2496 Ok(uri) => uri,
2497 _ => return Pending::new_err(error::url_invalid_uri(url)),
2498 };
2499
2500 let body = body.unwrap_or_else(Body::empty);
2501
2502 self.proxy_auth(&uri, &mut headers);
2503 self.proxy_custom_headers(&uri, &mut headers);
2504
2505 let builder = hyper::Request::builder()
2506 .method(method.clone())
2507 .uri(uri)
2508 .version(version);
2509
2510 let in_flight = match version {
2511 #[cfg(feature = "http3")]
2512 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2513 let mut req = builder.body(body).expect("valid request parts");
2514 *req.headers_mut() = headers.clone();
2515 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2516 ResponseFuture::H3(h3.call(req))
2517 }
2518 _ => {
2519 let mut req = builder.body(body).expect("valid request parts");
2520 *req.headers_mut() = headers.clone();
2521 let mut hyper = self.inner.hyper.clone();
2522 ResponseFuture::Default(hyper.call(req))
2523 }
2524 };
2525
2526 let total_timeout = self
2527 .inner
2528 .total_timeout
2529 .fetch(&extensions)
2530 .copied()
2531 .map(tokio::time::sleep)
2532 .map(Box::pin);
2533
2534 let read_timeout_fut = self
2535 .inner
2536 .read_timeout
2537 .map(tokio::time::sleep)
2538 .map(Box::pin);
2539
2540 Pending {
2541 inner: PendingInner::Request(Box::pin(PendingRequest {
2542 method,
2543 url,
2544 headers,
2545
2546 client: self.inner.clone(),
2547
2548 in_flight,
2549 total_timeout,
2550 read_timeout_fut,
2551 read_timeout: self.inner.read_timeout,
2552 })),
2553 }
2554 }
2555
2556 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2557 if !self.inner.proxies_maybe_http_auth {
2558 return;
2559 }
2560
2561 if dst.scheme() != Some(&Scheme::HTTP) {
2565 return;
2566 }
2567
2568 if headers.contains_key(PROXY_AUTHORIZATION) {
2569 return;
2570 }
2571
2572 for proxy in self.inner.proxies.iter() {
2573 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2574 headers.insert(PROXY_AUTHORIZATION, header);
2575 break;
2576 }
2577 }
2578 }
2579
2580 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2581 if !self.inner.proxies_maybe_http_custom_headers {
2582 return;
2583 }
2584
2585 if dst.scheme() != Some(&Scheme::HTTP) {
2586 return;
2587 }
2588
2589 for proxy in self.inner.proxies.iter() {
2590 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2591 iter.iter().for_each(|(key, value)| {
2592 headers.insert(key, value.clone());
2593 });
2594 break;
2595 }
2596 }
2597 }
2598}
2599
2600impl fmt::Debug for Client {
2601 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2602 let mut builder = f.debug_struct("Client");
2603 self.inner.fmt_fields(&mut builder);
2604 builder.finish()
2605 }
2606}
2607
2608impl tower_service::Service<Request> for Client {
2609 type Response = Response;
2610 type Error = crate::Error;
2611 type Future = Pending;
2612
2613 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2614 Poll::Ready(Ok(()))
2615 }
2616
2617 fn call(&mut self, req: Request) -> Self::Future {
2618 self.execute_request(req)
2619 }
2620}
2621
2622impl tower_service::Service<Request> for &'_ Client {
2623 type Response = Response;
2624 type Error = crate::Error;
2625 type Future = Pending;
2626
2627 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2628 Poll::Ready(Ok(()))
2629 }
2630
2631 fn call(&mut self, req: Request) -> Self::Future {
2632 self.execute_request(req)
2633 }
2634}
2635
2636impl fmt::Debug for ClientBuilder {
2637 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2638 let mut builder = f.debug_struct("ClientBuilder");
2639 self.config.fmt_fields(&mut builder);
2640 builder.finish()
2641 }
2642}
2643
2644impl Config {
2645 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2646 #[cfg(feature = "cookies")]
2650 {
2651 if let Some(_) = self.cookie_store {
2652 f.field("cookie_store", &true);
2653 }
2654 }
2655
2656 f.field("accepts", &self.accepts);
2657
2658 if !self.proxies.is_empty() {
2659 f.field("proxies", &self.proxies);
2660 }
2661
2662 if !self.redirect_policy.is_default() {
2663 f.field("redirect_policy", &self.redirect_policy);
2664 }
2665
2666 if self.referer {
2667 f.field("referer", &true);
2668 }
2669
2670 f.field("default_headers", &self.headers);
2671
2672 if self.http1_title_case_headers {
2673 f.field("http1_title_case_headers", &true);
2674 }
2675
2676 if self.http1_allow_obsolete_multiline_headers_in_responses {
2677 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2678 }
2679
2680 if self.http1_ignore_invalid_headers_in_responses {
2681 f.field("http1_ignore_invalid_headers_in_responses", &true);
2682 }
2683
2684 if self.http1_allow_spaces_after_header_name_in_responses {
2685 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2686 }
2687
2688 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2689 f.field("http1_only", &true);
2690 }
2691
2692 #[cfg(feature = "http2")]
2693 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2694 f.field("http2_prior_knowledge", &true);
2695 }
2696
2697 if let Some(ref d) = self.connect_timeout {
2698 f.field("connect_timeout", d);
2699 }
2700
2701 if let Some(ref d) = self.timeout {
2702 f.field("timeout", d);
2703 }
2704
2705 if let Some(ref v) = self.local_address {
2706 f.field("local_address", v);
2707 }
2708
2709 #[cfg(any(
2710 target_os = "android",
2711 target_os = "fuchsia",
2712 target_os = "illumos",
2713 target_os = "ios",
2714 target_os = "linux",
2715 target_os = "macos",
2716 target_os = "solaris",
2717 target_os = "tvos",
2718 target_os = "visionos",
2719 target_os = "watchos",
2720 ))]
2721 if let Some(ref v) = self.interface {
2722 f.field("interface", v);
2723 }
2724
2725 if self.nodelay {
2726 f.field("tcp_nodelay", &true);
2727 }
2728
2729 #[cfg(feature = "__tls")]
2730 {
2731 if !self.hostname_verification {
2732 f.field("danger_accept_invalid_hostnames", &true);
2733 }
2734 }
2735
2736 #[cfg(feature = "__tls")]
2737 {
2738 if !self.certs_verification {
2739 f.field("danger_accept_invalid_certs", &true);
2740 }
2741
2742 if let Some(ref min_tls_version) = self.min_tls_version {
2743 f.field("min_tls_version", min_tls_version);
2744 }
2745
2746 if let Some(ref max_tls_version) = self.max_tls_version {
2747 f.field("max_tls_version", max_tls_version);
2748 }
2749
2750 f.field("tls_sni", &self.tls_sni);
2751
2752 f.field("tls_info", &self.tls_info);
2753 }
2754
2755 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2756 {
2757 f.field("tls_backend", &self.tls);
2758 }
2759
2760 if !self.dns_overrides.is_empty() {
2761 f.field("dns_overrides", &self.dns_overrides);
2762 }
2763
2764 #[cfg(feature = "http3")]
2765 {
2766 if self.tls_enable_early_data {
2767 f.field("tls_enable_early_data", &true);
2768 }
2769 }
2770
2771 #[cfg(unix)]
2772 if let Some(ref p) = self.unix_socket {
2773 f.field("unix_socket", p);
2774 }
2775 }
2776}
2777
2778#[cfg(not(feature = "cookies"))]
2779type LayeredService<T> =
2780 FollowRedirect<tower::retry::Retry<crate::retry::Policy, T>, TowerRedirectPolicy>;
2781#[cfg(feature = "cookies")]
2782type LayeredService<T> = FollowRedirect<
2783 CookieService<tower::retry::Retry<crate::retry::Policy, T>>,
2784 TowerRedirectPolicy,
2785>;
2786type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
2787
2788struct ClientRef {
2789 accepts: Accepts,
2790 #[cfg(feature = "cookies")]
2791 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2792 headers: HeaderMap,
2793 hyper: LayeredService<HyperService>,
2794 #[cfg(feature = "http3")]
2795 h3_client: Option<LayeredService<H3Client>>,
2796 referer: bool,
2797 total_timeout: RequestConfig<TotalTimeout>,
2798 read_timeout: Option<Duration>,
2799 proxies: Arc<Vec<ProxyMatcher>>,
2800 proxies_maybe_http_auth: bool,
2801 proxies_maybe_http_custom_headers: bool,
2802 https_only: bool,
2803 redirect_policy_desc: Option<String>,
2804}
2805
2806impl ClientRef {
2807 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2808 #[cfg(feature = "cookies")]
2812 {
2813 if let Some(_) = self.cookie_store {
2814 f.field("cookie_store", &true);
2815 }
2816 }
2817
2818 f.field("accepts", &self.accepts);
2819
2820 if !self.proxies.is_empty() {
2821 f.field("proxies", &self.proxies);
2822 }
2823
2824 if let Some(s) = &self.redirect_policy_desc {
2825 f.field("redirect_policy", s);
2826 }
2827
2828 if self.referer {
2829 f.field("referer", &true);
2830 }
2831
2832 f.field("default_headers", &self.headers);
2833
2834 self.total_timeout.fmt_as_field(f);
2835
2836 if let Some(ref d) = self.read_timeout {
2837 f.field("read_timeout", d);
2838 }
2839 }
2840}
2841
2842pin_project! {
2843 pub struct Pending {
2844 #[pin]
2845 inner: PendingInner,
2846 }
2847}
2848
2849enum PendingInner {
2850 Request(Pin<Box<PendingRequest>>),
2851 Error(Option<crate::Error>),
2852}
2853
2854pin_project! {
2855 struct PendingRequest {
2856 method: Method,
2857 url: Url,
2858 headers: HeaderMap,
2859
2860 client: Arc<ClientRef>,
2861
2862 #[pin]
2863 in_flight: ResponseFuture,
2864 #[pin]
2865 total_timeout: Option<Pin<Box<Sleep>>>,
2866 #[pin]
2867 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2868 read_timeout: Option<Duration>,
2869 }
2870}
2871
2872enum ResponseFuture {
2873 Default(LayeredFuture<HyperService>),
2874 #[cfg(feature = "http3")]
2875 H3(LayeredFuture<H3Client>),
2876}
2877
2878impl PendingRequest {
2879 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2880 self.project().in_flight
2881 }
2882
2883 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2884 self.project().total_timeout
2885 }
2886
2887 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2888 self.project().read_timeout_fut
2889 }
2890}
2891
2892impl Pending {
2893 pub(super) fn new_err(err: crate::Error) -> Pending {
2894 Pending {
2895 inner: PendingInner::Error(Some(err)),
2896 }
2897 }
2898
2899 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2900 self.project().inner
2901 }
2902}
2903
2904impl Future for Pending {
2905 type Output = Result<Response, crate::Error>;
2906
2907 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2908 let inner = self.inner();
2909 match inner.get_mut() {
2910 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2911 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2912 .take()
2913 .expect("Pending error polled more than once"))),
2914 }
2915 }
2916}
2917
2918impl Future for PendingRequest {
2919 type Output = Result<Response, crate::Error>;
2920
2921 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2922 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
2923 if let Poll::Ready(()) = delay.poll(cx) {
2924 return Poll::Ready(Err(
2925 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2926 ));
2927 }
2928 }
2929
2930 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
2931 if let Poll::Ready(()) = delay.poll(cx) {
2932 return Poll::Ready(Err(
2933 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2934 ));
2935 }
2936 }
2937
2938 let res = match self.as_mut().in_flight().get_mut() {
2939 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
2940 Err(e) => {
2941 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
2942 }
2943 Ok(res) => res.map(super::body::boxed),
2944 },
2945 #[cfg(feature = "http3")]
2946 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
2947 Err(e) => {
2948 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
2949 }
2950 Ok(res) => res,
2951 },
2952 };
2953
2954 if let Some(url) = &res
2955 .extensions()
2956 .get::<tower_http::follow_redirect::RequestUri>()
2957 {
2958 self.url = match Url::parse(&url.0.to_string()) {
2959 Ok(url) => url,
2960 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
2961 }
2962 };
2963
2964 let res = Response::new(
2965 res,
2966 self.url.clone(),
2967 self.client.accepts,
2968 self.total_timeout.take(),
2969 self.read_timeout,
2970 );
2971 Poll::Ready(Ok(res))
2972 }
2973}
2974
2975impl fmt::Debug for Pending {
2976 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2977 match self.inner {
2978 PendingInner::Request(ref req) => f
2979 .debug_struct("Pending")
2980 .field("method", &req.method)
2981 .field("url", &req.url)
2982 .finish(),
2983 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
2984 }
2985 }
2986}
2987
2988#[cfg(test)]
2989mod tests {
2990 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
2991
2992 #[tokio::test]
2993 async fn execute_request_rejects_invalid_urls() {
2994 let url_str = "hxxps://www.rust-lang.org/";
2995 let url = url::Url::parse(url_str).unwrap();
2996 let result = crate::get(url.clone()).await;
2997
2998 assert!(result.is_err());
2999 let err = result.err().unwrap();
3000 assert!(err.is_builder());
3001 assert_eq!(url_str, err.url().unwrap().as_str());
3002 }
3003
3004 #[tokio::test]
3006 async fn execute_request_rejects_invalid_hostname() {
3007 let url_str = "https://{{hostname}}/";
3008 let url = url::Url::parse(url_str).unwrap();
3009 let result = crate::get(url.clone()).await;
3010
3011 assert!(result.is_err());
3012 let err = result.err().unwrap();
3013 assert!(err.is_builder());
3014 assert_eq!(url_str, err.url().unwrap().as_str());
3015 }
3016
3017 #[test]
3018 fn test_future_size() {
3019 let s = std::mem::size_of::<super::Pending>();
3020 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3021 }
3022}