1#[cfg(any(feature = "native-tls", feature = "__rustls",))]
2use std::any::Any;
3#[cfg(feature = "http2")]
4use std::error::Error;
5use std::future::Future;
6use std::net::IpAddr;
7use std::pin::Pin;
8use std::sync::Arc;
9use std::task::{Context, Poll};
10use std::time::Duration;
11use std::{collections::HashMap, convert::TryInto, net::SocketAddr};
12use std::{fmt, str};
13
14use super::decoder::Accepts;
15use super::request::{Request, RequestBuilder};
16use super::response::Response;
17use super::Body;
18#[cfg(feature = "http3")]
19use crate::async_impl::h3_client::connect::{H3ClientConfig, H3Connector};
20#[cfg(feature = "http3")]
21use crate::async_impl::h3_client::H3Client;
22use crate::config::{RequestConfig, RequestTimeout};
23use crate::connect::{
24 sealed::{Conn, Unnameable},
25 BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
26};
27#[cfg(feature = "cookies")]
28use crate::cookie;
29#[cfg(feature = "hickory-dns")]
30use crate::dns::hickory::HickoryDnsResolver;
31use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
32use crate::error::{self, BoxError};
33use crate::into_url::try_uri;
34use crate::proxy::Matcher as ProxyMatcher;
35use crate::redirect::{self, TowerRedirectPolicy};
36#[cfg(feature = "__rustls")]
37use crate::tls::CertificateRevocationList;
38#[cfg(feature = "__tls")]
39use crate::tls::{self, TlsBackend};
40#[cfg(feature = "__tls")]
41use crate::Certificate;
42#[cfg(any(feature = "native-tls", feature = "__rustls"))]
43use crate::Identity;
44use crate::{IntoUrl, Method, Proxy, Url};
45
46use bytes::Bytes;
47use http::header::{
48 Entry, HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, PROXY_AUTHORIZATION, RANGE, USER_AGENT,
49};
50use http::uri::Scheme;
51use http::Uri;
52use hyper_util::client::legacy::connect::HttpConnector;
53#[cfg(feature = "default-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};
63use tower_http::follow_redirect::FollowRedirect;
64
65#[derive(Clone)]
79pub struct Client {
80 inner: Arc<ClientRef>,
81}
82
83#[must_use]
85pub struct ClientBuilder {
86 config: Config,
87}
88
89enum HttpVersionPref {
90 Http1,
91 #[cfg(feature = "http2")]
92 Http2,
93 #[cfg(feature = "http3")]
94 Http3,
95 All,
96}
97
98#[derive(Clone)]
99struct HyperService {
100 #[cfg(feature = "cookies")]
101 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
102 hyper: HyperClient,
103}
104
105impl Service<hyper::Request<crate::async_impl::body::Body>> for HyperService {
106 type Error = crate::Error;
107 type Response = http::Response<hyper::body::Incoming>;
108 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + Sync>>;
109
110 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
111 self.hyper.poll_ready(cx).map_err(crate::error::request)
112 }
113
114 #[cfg(not(feature = "cookies"))]
115 fn call(&mut self, req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
116 let clone = self.hyper.clone();
117 let mut inner = std::mem::replace(&mut self.hyper, clone);
118 Box::pin(async move { inner.call(req).await.map_err(crate::error::request) })
119 }
120
121 #[cfg(feature = "cookies")]
122 fn call(&mut self, mut req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
123 let clone = self.hyper.clone();
124 let mut inner = std::mem::replace(&mut self.hyper, clone);
125 let url = Url::parse(req.uri().to_string().as_str()).expect("invalid URL");
126
127 if let Some(cookie_store) = self.cookie_store.as_ref() {
128 if req.headers().get(crate::header::COOKIE).is_none() {
129 let headers = req.headers_mut();
130 crate::util::add_cookie_header(headers, &**cookie_store, &url);
131 }
132 }
133
134 let cookie_store = self.cookie_store.clone();
135 Box::pin(async move {
136 let res = inner.call(req).await.map_err(crate::error::request);
137
138 if let Some(ref cookie_store) = cookie_store {
139 if let Ok(res) = &res {
140 let mut cookies =
141 cookie::extract_response_cookie_headers(res.headers()).peekable();
142 if cookies.peek().is_some() {
143 cookie_store.set_cookies(&mut cookies, &url);
144 }
145 }
146 }
147
148 res
149 })
150 }
151}
152
153struct Config {
154 accepts: Accepts,
156 headers: HeaderMap,
157 #[cfg(feature = "__tls")]
158 hostname_verification: bool,
159 #[cfg(feature = "__tls")]
160 certs_verification: bool,
161 #[cfg(feature = "__tls")]
162 tls_sni: bool,
163 connect_timeout: Option<Duration>,
164 connection_verbose: bool,
165 pool_idle_timeout: Option<Duration>,
166 pool_max_idle_per_host: usize,
167 tcp_keepalive: Option<Duration>,
168 tcp_keepalive_interval: Option<Duration>,
169 tcp_keepalive_retries: Option<u32>,
170 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
171 tcp_user_timeout: Option<Duration>,
172 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
173 identity: Option<Identity>,
174 proxies: Vec<ProxyMatcher>,
175 auto_sys_proxy: bool,
176 redirect_policy: redirect::Policy,
177 referer: bool,
178 read_timeout: Option<Duration>,
179 timeout: Option<Duration>,
180 #[cfg(feature = "__tls")]
181 root_certs: Vec<Certificate>,
182 #[cfg(feature = "__tls")]
183 tls_built_in_root_certs: bool,
184 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
185 tls_built_in_certs_webpki: bool,
186 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
187 tls_built_in_certs_native: bool,
188 #[cfg(feature = "__rustls")]
189 crls: Vec<CertificateRevocationList>,
190 #[cfg(feature = "__tls")]
191 min_tls_version: Option<tls::Version>,
192 #[cfg(feature = "__tls")]
193 max_tls_version: Option<tls::Version>,
194 #[cfg(feature = "__tls")]
195 tls_info: bool,
196 #[cfg(feature = "__tls")]
197 tls: TlsBackend,
198 connector_layers: Vec<BoxedConnectorLayer>,
199 http_version_pref: HttpVersionPref,
200 http09_responses: bool,
201 http1_title_case_headers: bool,
202 http1_allow_obsolete_multiline_headers_in_responses: bool,
203 http1_ignore_invalid_headers_in_responses: bool,
204 http1_allow_spaces_after_header_name_in_responses: bool,
205 #[cfg(feature = "http2")]
206 http2_initial_stream_window_size: Option<u32>,
207 #[cfg(feature = "http2")]
208 http2_initial_connection_window_size: Option<u32>,
209 #[cfg(feature = "http2")]
210 http2_adaptive_window: bool,
211 #[cfg(feature = "http2")]
212 http2_max_frame_size: Option<u32>,
213 #[cfg(feature = "http2")]
214 http2_max_header_list_size: Option<u32>,
215 #[cfg(feature = "http2")]
216 http2_keep_alive_interval: Option<Duration>,
217 #[cfg(feature = "http2")]
218 http2_keep_alive_timeout: Option<Duration>,
219 #[cfg(feature = "http2")]
220 http2_keep_alive_while_idle: bool,
221 local_address: Option<IpAddr>,
222 #[cfg(any(
223 target_os = "android",
224 target_os = "fuchsia",
225 target_os = "illumos",
226 target_os = "ios",
227 target_os = "linux",
228 target_os = "macos",
229 target_os = "solaris",
230 target_os = "tvos",
231 target_os = "visionos",
232 target_os = "watchos",
233 ))]
234 interface: Option<String>,
235 nodelay: bool,
236 #[cfg(feature = "cookies")]
237 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
238 hickory_dns: bool,
239 error: Option<crate::Error>,
240 https_only: bool,
241 #[cfg(feature = "http3")]
242 tls_enable_early_data: bool,
243 #[cfg(feature = "http3")]
244 quic_max_idle_timeout: Option<Duration>,
245 #[cfg(feature = "http3")]
246 quic_stream_receive_window: Option<VarInt>,
247 #[cfg(feature = "http3")]
248 quic_receive_window: Option<VarInt>,
249 #[cfg(feature = "http3")]
250 quic_send_window: Option<u64>,
251 #[cfg(feature = "http3")]
252 quic_congestion_bbr: bool,
253 #[cfg(feature = "http3")]
254 h3_max_field_section_size: Option<u64>,
255 #[cfg(feature = "http3")]
256 h3_send_grease: Option<bool>,
257 dns_overrides: HashMap<String, Vec<SocketAddr>>,
258 dns_resolver: Option<Arc<dyn Resolve>>,
259}
260
261impl Default for ClientBuilder {
262 fn default() -> Self {
263 Self::new()
264 }
265}
266
267impl ClientBuilder {
268 pub fn new() -> Self {
272 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
273 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
274
275 ClientBuilder {
276 config: Config {
277 error: None,
278 accepts: Accepts::default(),
279 headers,
280 #[cfg(feature = "__tls")]
281 hostname_verification: true,
282 #[cfg(feature = "__tls")]
283 certs_verification: true,
284 #[cfg(feature = "__tls")]
285 tls_sni: true,
286 connect_timeout: None,
287 connection_verbose: false,
288 pool_idle_timeout: Some(Duration::from_secs(90)),
289 pool_max_idle_per_host: usize::MAX,
290 tcp_keepalive: None, tcp_keepalive_interval: None,
294 tcp_keepalive_retries: None,
295 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
296 tcp_user_timeout: None,
297 proxies: Vec::new(),
298 auto_sys_proxy: true,
299 redirect_policy: redirect::Policy::default(),
300 referer: true,
301 read_timeout: None,
302 timeout: None,
303 #[cfg(feature = "__tls")]
304 root_certs: Vec::new(),
305 #[cfg(feature = "__tls")]
306 tls_built_in_root_certs: true,
307 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
308 tls_built_in_certs_webpki: true,
309 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
310 tls_built_in_certs_native: true,
311 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
312 identity: None,
313 #[cfg(feature = "__rustls")]
314 crls: vec![],
315 #[cfg(feature = "__tls")]
316 min_tls_version: None,
317 #[cfg(feature = "__tls")]
318 max_tls_version: None,
319 #[cfg(feature = "__tls")]
320 tls_info: false,
321 #[cfg(feature = "__tls")]
322 tls: TlsBackend::default(),
323 connector_layers: Vec::new(),
324 http_version_pref: HttpVersionPref::All,
325 http09_responses: false,
326 http1_title_case_headers: false,
327 http1_allow_obsolete_multiline_headers_in_responses: false,
328 http1_ignore_invalid_headers_in_responses: false,
329 http1_allow_spaces_after_header_name_in_responses: false,
330 #[cfg(feature = "http2")]
331 http2_initial_stream_window_size: None,
332 #[cfg(feature = "http2")]
333 http2_initial_connection_window_size: None,
334 #[cfg(feature = "http2")]
335 http2_adaptive_window: false,
336 #[cfg(feature = "http2")]
337 http2_max_frame_size: None,
338 #[cfg(feature = "http2")]
339 http2_max_header_list_size: None,
340 #[cfg(feature = "http2")]
341 http2_keep_alive_interval: None,
342 #[cfg(feature = "http2")]
343 http2_keep_alive_timeout: None,
344 #[cfg(feature = "http2")]
345 http2_keep_alive_while_idle: false,
346 local_address: None,
347 #[cfg(any(
348 target_os = "android",
349 target_os = "fuchsia",
350 target_os = "illumos",
351 target_os = "ios",
352 target_os = "linux",
353 target_os = "macos",
354 target_os = "solaris",
355 target_os = "tvos",
356 target_os = "visionos",
357 target_os = "watchos",
358 ))]
359 interface: None,
360 nodelay: true,
361 hickory_dns: cfg!(feature = "hickory-dns"),
362 #[cfg(feature = "cookies")]
363 cookie_store: None,
364 https_only: false,
365 dns_overrides: HashMap::new(),
366 #[cfg(feature = "http3")]
367 tls_enable_early_data: false,
368 #[cfg(feature = "http3")]
369 quic_max_idle_timeout: None,
370 #[cfg(feature = "http3")]
371 quic_stream_receive_window: None,
372 #[cfg(feature = "http3")]
373 quic_receive_window: None,
374 #[cfg(feature = "http3")]
375 quic_send_window: None,
376 #[cfg(feature = "http3")]
377 quic_congestion_bbr: false,
378 #[cfg(feature = "http3")]
379 h3_max_field_section_size: None,
380 #[cfg(feature = "http3")]
381 h3_send_grease: None,
382 dns_resolver: None,
383 },
384 }
385 }
386}
387
388impl ClientBuilder {
389 pub fn build(self) -> crate::Result<Client> {
396 let config = self.config;
397
398 if let Some(err) = config.error {
399 return Err(err);
400 }
401
402 let mut proxies = config.proxies;
403 if config.auto_sys_proxy {
404 proxies.push(ProxyMatcher::system());
405 }
406 let proxies = Arc::new(proxies);
407
408 #[allow(unused)]
409 #[cfg(feature = "http3")]
410 let mut h3_connector = None;
411
412 let resolver = {
413 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
414 false => Arc::new(GaiResolver::new()),
415 #[cfg(feature = "hickory-dns")]
416 true => Arc::new(HickoryDnsResolver::default()),
417 #[cfg(not(feature = "hickory-dns"))]
418 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
419 };
420 if let Some(dns_resolver) = config.dns_resolver {
421 resolver = dns_resolver;
422 }
423 if !config.dns_overrides.is_empty() {
424 resolver = Arc::new(DnsResolverWithOverrides::new(
425 resolver,
426 config.dns_overrides,
427 ));
428 }
429 DynResolver::new(resolver)
430 };
431
432 let mut connector_builder = {
433 #[cfg(feature = "__tls")]
434 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
435 headers.get(USER_AGENT).cloned()
436 }
437
438 let mut http = HttpConnector::new_with_resolver(resolver.clone());
439 http.set_connect_timeout(config.connect_timeout);
440
441 #[cfg(all(feature = "http3", feature = "__rustls"))]
442 let build_h3_connector =
443 |resolver,
444 tls,
445 quic_max_idle_timeout: Option<Duration>,
446 quic_stream_receive_window,
447 quic_receive_window,
448 quic_send_window,
449 quic_congestion_bbr,
450 h3_max_field_section_size,
451 h3_send_grease,
452 local_address,
453 http_version_pref: &HttpVersionPref| {
454 let mut transport_config = TransportConfig::default();
455
456 if let Some(max_idle_timeout) = quic_max_idle_timeout {
457 transport_config.max_idle_timeout(Some(
458 max_idle_timeout.try_into().map_err(error::builder)?,
459 ));
460 }
461
462 if let Some(stream_receive_window) = quic_stream_receive_window {
463 transport_config.stream_receive_window(stream_receive_window);
464 }
465
466 if let Some(receive_window) = quic_receive_window {
467 transport_config.receive_window(receive_window);
468 }
469
470 if let Some(send_window) = quic_send_window {
471 transport_config.send_window(send_window);
472 }
473
474 if quic_congestion_bbr {
475 let factory = Arc::new(quinn::congestion::BbrConfig::default());
476 transport_config.congestion_controller_factory(factory);
477 }
478
479 let mut h3_client_config = H3ClientConfig::default();
480
481 if let Some(max_field_section_size) = h3_max_field_section_size {
482 h3_client_config.max_field_section_size = Some(max_field_section_size);
483 }
484
485 if let Some(send_grease) = h3_send_grease {
486 h3_client_config.send_grease = Some(send_grease);
487 }
488
489 let res = H3Connector::new(
490 resolver,
491 tls,
492 local_address,
493 transport_config,
494 h3_client_config,
495 );
496
497 match res {
498 Ok(connector) => Ok(Some(connector)),
499 Err(err) => {
500 if let HttpVersionPref::Http3 = http_version_pref {
501 Err(error::builder(err))
502 } else {
503 Ok(None)
504 }
505 }
506 }
507 };
508
509 #[cfg(feature = "__tls")]
510 match config.tls {
511 #[cfg(feature = "default-tls")]
512 TlsBackend::Default => {
513 let mut tls = TlsConnector::builder();
514
515 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
516 {
517 match config.http_version_pref {
518 HttpVersionPref::Http1 => {
519 tls.request_alpns(&["http/1.1"]);
520 }
521 #[cfg(feature = "http2")]
522 HttpVersionPref::Http2 => {
523 tls.request_alpns(&["h2"]);
524 }
525 HttpVersionPref::All => {
526 tls.request_alpns(&["h2", "http/1.1"]);
527 }
528 }
529 }
530
531 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
532
533 tls.danger_accept_invalid_certs(!config.certs_verification);
534
535 tls.use_sni(config.tls_sni);
536
537 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
538
539 for cert in config.root_certs {
540 cert.add_to_native_tls(&mut tls);
541 }
542
543 #[cfg(feature = "native-tls")]
544 {
545 if let Some(id) = config.identity {
546 id.add_to_native_tls(&mut tls)?;
547 }
548 }
549 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
550 {
551 if let Some(_id) = config.identity {
553 return Err(crate::error::builder("incompatible TLS identity type"));
554 }
555 }
556
557 if let Some(min_tls_version) = config.min_tls_version {
558 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
559 crate::error::builder("invalid minimum TLS version for backend")
563 })?;
564 tls.min_protocol_version(Some(protocol));
565 }
566
567 if let Some(max_tls_version) = config.max_tls_version {
568 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
569 crate::error::builder("invalid maximum TLS version for backend")
574 })?;
575 tls.max_protocol_version(Some(protocol));
576 }
577
578 ConnectorBuilder::new_default_tls(
579 http,
580 tls,
581 proxies.clone(),
582 user_agent(&config.headers),
583 config.local_address,
584 #[cfg(any(
585 target_os = "android",
586 target_os = "fuchsia",
587 target_os = "illumos",
588 target_os = "ios",
589 target_os = "linux",
590 target_os = "macos",
591 target_os = "solaris",
592 target_os = "tvos",
593 target_os = "visionos",
594 target_os = "watchos",
595 ))]
596 config.interface.as_deref(),
597 config.nodelay,
598 config.tls_info,
599 )?
600 }
601 #[cfg(feature = "native-tls")]
602 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_default_tls(
603 http,
604 conn,
605 proxies.clone(),
606 user_agent(&config.headers),
607 config.local_address,
608 #[cfg(any(
609 target_os = "android",
610 target_os = "fuchsia",
611 target_os = "illumos",
612 target_os = "ios",
613 target_os = "linux",
614 target_os = "macos",
615 target_os = "solaris",
616 target_os = "tvos",
617 target_os = "visionos",
618 target_os = "watchos",
619 ))]
620 config.interface.as_deref(),
621 config.nodelay,
622 config.tls_info,
623 ),
624 #[cfg(feature = "__rustls")]
625 TlsBackend::BuiltRustls(conn) => {
626 #[cfg(feature = "http3")]
627 {
628 h3_connector = build_h3_connector(
629 resolver.clone(),
630 conn.clone(),
631 config.quic_max_idle_timeout,
632 config.quic_stream_receive_window,
633 config.quic_receive_window,
634 config.quic_send_window,
635 config.quic_congestion_bbr,
636 config.h3_max_field_section_size,
637 config.h3_send_grease,
638 config.local_address,
639 &config.http_version_pref,
640 )?;
641 }
642
643 ConnectorBuilder::new_rustls_tls(
644 http,
645 conn,
646 proxies.clone(),
647 user_agent(&config.headers),
648 config.local_address,
649 #[cfg(any(
650 target_os = "android",
651 target_os = "fuchsia",
652 target_os = "illumos",
653 target_os = "ios",
654 target_os = "linux",
655 target_os = "macos",
656 target_os = "solaris",
657 target_os = "tvos",
658 target_os = "visionos",
659 target_os = "watchos",
660 ))]
661 config.interface.as_deref(),
662 config.nodelay,
663 config.tls_info,
664 )
665 }
666 #[cfg(feature = "__rustls")]
667 TlsBackend::Rustls => {
668 use crate::tls::{IgnoreHostname, NoVerifier};
669
670 let mut root_cert_store = rustls::RootCertStore::empty();
672 for cert in config.root_certs {
673 cert.add_to_rustls(&mut root_cert_store)?;
674 }
675
676 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
677 if config.tls_built_in_certs_webpki {
678 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
679 }
680
681 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
682 if config.tls_built_in_certs_native {
683 let mut valid_count = 0;
684 let mut invalid_count = 0;
685
686 let load_results = rustls_native_certs::load_native_certs();
687 for cert in load_results.certs {
688 match root_cert_store.add(cert.into()) {
692 Ok(_) => valid_count += 1,
693 Err(err) => {
694 invalid_count += 1;
695 log::debug!("rustls failed to parse DER certificate: {err:?}");
696 }
697 }
698 }
699 if valid_count == 0 && invalid_count > 0 {
700 let err = if load_results.errors.is_empty() {
701 crate::error::builder(
702 "zero valid certificates found in native root store",
703 )
704 } else {
705 use std::fmt::Write as _;
706 let mut acc = String::new();
707 for err in load_results.errors {
708 let _ = writeln!(&mut acc, "{err}");
709 }
710
711 crate::error::builder(acc)
712 };
713
714 return Err(err);
715 }
716 }
717
718 let mut versions = rustls::ALL_VERSIONS.to_vec();
720
721 if let Some(min_tls_version) = config.min_tls_version {
722 versions.retain(|&supported_version| {
723 match tls::Version::from_rustls(supported_version.version) {
724 Some(version) => version >= min_tls_version,
725 None => true,
728 }
729 });
730 }
731
732 if let Some(max_tls_version) = config.max_tls_version {
733 versions.retain(|&supported_version| {
734 match tls::Version::from_rustls(supported_version.version) {
735 Some(version) => version <= max_tls_version,
736 None => false,
737 }
738 });
739 }
740
741 if versions.is_empty() {
742 return Err(crate::error::builder("empty supported tls versions"));
743 }
744
745 let provider = rustls::crypto::CryptoProvider::get_default()
748 .map(|arc| arc.clone())
749 .unwrap_or_else(|| {
750 #[cfg(not(feature = "__rustls-ring"))]
751 panic!("No provider set");
752
753 #[cfg(feature = "__rustls-ring")]
754 Arc::new(rustls::crypto::ring::default_provider())
755 });
756
757 let signature_algorithms = provider.signature_verification_algorithms;
759 let config_builder =
760 rustls::ClientConfig::builder_with_provider(provider.clone())
761 .with_protocol_versions(&versions)
762 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
763
764 let config_builder = if !config.certs_verification {
765 config_builder
766 .dangerous()
767 .with_custom_certificate_verifier(Arc::new(NoVerifier))
768 } else if !config.hostname_verification {
769 config_builder
770 .dangerous()
771 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
772 root_cert_store,
773 signature_algorithms,
774 )))
775 } else {
776 if config.crls.is_empty() {
777 config_builder.with_root_certificates(root_cert_store)
778 } else {
779 let crls = config
780 .crls
781 .iter()
782 .map(|e| e.as_rustls_crl())
783 .collect::<Vec<_>>();
784 let verifier =
785 rustls::client::WebPkiServerVerifier::builder_with_provider(
786 Arc::new(root_cert_store),
787 provider,
788 )
789 .with_crls(crls)
790 .build()
791 .map_err(|_| {
792 crate::error::builder("invalid TLS verification settings")
793 })?;
794 config_builder.with_webpki_verifier(verifier)
795 }
796 };
797
798 let mut tls = if let Some(id) = config.identity {
800 id.add_to_rustls(config_builder)?
801 } else {
802 config_builder.with_no_client_auth()
803 };
804
805 tls.enable_sni = config.tls_sni;
806
807 match config.http_version_pref {
809 HttpVersionPref::Http1 => {
810 tls.alpn_protocols = vec!["http/1.1".into()];
811 }
812 #[cfg(feature = "http2")]
813 HttpVersionPref::Http2 => {
814 tls.alpn_protocols = vec!["h2".into()];
815 }
816 #[cfg(feature = "http3")]
817 HttpVersionPref::Http3 => {
818 tls.alpn_protocols = vec!["h3".into()];
819 }
820 HttpVersionPref::All => {
821 tls.alpn_protocols = vec![
822 #[cfg(feature = "http2")]
823 "h2".into(),
824 "http/1.1".into(),
825 ];
826 }
827 }
828
829 #[cfg(feature = "http3")]
830 {
831 tls.enable_early_data = config.tls_enable_early_data;
832
833 h3_connector = build_h3_connector(
834 resolver.clone(),
835 tls.clone(),
836 config.quic_max_idle_timeout,
837 config.quic_stream_receive_window,
838 config.quic_receive_window,
839 config.quic_send_window,
840 config.quic_congestion_bbr,
841 config.h3_max_field_section_size,
842 config.h3_send_grease,
843 config.local_address,
844 &config.http_version_pref,
845 )?;
846 }
847
848 ConnectorBuilder::new_rustls_tls(
849 http,
850 tls,
851 proxies.clone(),
852 user_agent(&config.headers),
853 config.local_address,
854 #[cfg(any(
855 target_os = "android",
856 target_os = "fuchsia",
857 target_os = "illumos",
858 target_os = "ios",
859 target_os = "linux",
860 target_os = "macos",
861 target_os = "solaris",
862 target_os = "tvos",
863 target_os = "visionos",
864 target_os = "watchos",
865 ))]
866 config.interface.as_deref(),
867 config.nodelay,
868 config.tls_info,
869 )
870 }
871 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
872 TlsBackend::UnknownPreconfigured => {
873 return Err(crate::error::builder(
874 "Unknown TLS backend passed to `use_preconfigured_tls`",
875 ));
876 }
877 }
878
879 #[cfg(not(feature = "__tls"))]
880 ConnectorBuilder::new(
881 http,
882 proxies.clone(),
883 config.local_address,
884 #[cfg(any(
885 target_os = "android",
886 target_os = "fuchsia",
887 target_os = "illumos",
888 target_os = "ios",
889 target_os = "linux",
890 target_os = "macos",
891 target_os = "solaris",
892 target_os = "tvos",
893 target_os = "visionos",
894 target_os = "watchos",
895 ))]
896 config.interface.as_deref(),
897 config.nodelay,
898 )
899 };
900
901 connector_builder.set_timeout(config.connect_timeout);
902 connector_builder.set_verbose(config.connection_verbose);
903 connector_builder.set_keepalive(config.tcp_keepalive);
904 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
905 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
906 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
907 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
908
909 #[cfg(feature = "socks")]
910 connector_builder.set_socks_resolver(resolver);
911
912 let mut builder =
913 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
914 #[cfg(feature = "http2")]
915 {
916 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
917 builder.http2_only(true);
918 }
919
920 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
921 {
922 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
923 }
924 if let Some(http2_initial_connection_window_size) =
925 config.http2_initial_connection_window_size
926 {
927 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
928 }
929 if config.http2_adaptive_window {
930 builder.http2_adaptive_window(true);
931 }
932 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
933 builder.http2_max_frame_size(http2_max_frame_size);
934 }
935 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
936 builder.http2_max_header_list_size(http2_max_header_list_size);
937 }
938 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
939 builder.http2_keep_alive_interval(http2_keep_alive_interval);
940 }
941 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
942 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
943 }
944 if config.http2_keep_alive_while_idle {
945 builder.http2_keep_alive_while_idle(true);
946 }
947 }
948
949 builder.timer(hyper_util::rt::TokioTimer::new());
950 builder.pool_timer(hyper_util::rt::TokioTimer::new());
951 builder.pool_idle_timeout(config.pool_idle_timeout);
952 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
953
954 if config.http09_responses {
955 builder.http09_responses(true);
956 }
957
958 if config.http1_title_case_headers {
959 builder.http1_title_case_headers(true);
960 }
961
962 if config.http1_allow_obsolete_multiline_headers_in_responses {
963 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
964 }
965
966 if config.http1_ignore_invalid_headers_in_responses {
967 builder.http1_ignore_invalid_headers_in_responses(true);
968 }
969
970 if config.http1_allow_spaces_after_header_name_in_responses {
971 builder.http1_allow_spaces_after_header_name_in_responses(true);
972 }
973
974 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
975 let proxies_maybe_http_custom_headers =
976 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
977
978 let redirect_policy_desc = if config.redirect_policy.is_default() {
979 None
980 } else {
981 Some(format!("{:?}", &config.redirect_policy))
982 };
983
984 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
985 let hyper_service = HyperService {
986 #[cfg(feature = "cookies")]
987 cookie_store: config.cookie_store.clone(),
988 hyper: hyper_client,
989 };
990
991 let policy = {
992 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
993 p.with_referer(config.referer)
994 .with_https_only(config.https_only);
995 p
996 };
997
998 let hyper = FollowRedirect::with_policy(hyper_service, policy.clone());
999
1000 Ok(Client {
1001 inner: Arc::new(ClientRef {
1002 accepts: config.accepts,
1003 #[cfg(feature = "cookies")]
1004 cookie_store: config.cookie_store.clone(),
1005 #[cfg(feature = "http3")]
1008 h3_client: match h3_connector {
1009 Some(h3_connector) => {
1010 #[cfg(not(feature = "cookies"))]
1011 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1012 #[cfg(feature = "cookies")]
1013 let h3_service = H3Client::new(
1014 h3_connector,
1015 config.pool_idle_timeout,
1016 config.cookie_store,
1017 );
1018 Some(FollowRedirect::with_policy(h3_service, policy))
1019 }
1020 None => None,
1021 },
1022 headers: config.headers,
1023 referer: config.referer,
1024 read_timeout: config.read_timeout,
1025 request_timeout: RequestConfig::new(config.timeout),
1026 hyper,
1027 proxies,
1028 proxies_maybe_http_auth,
1029 proxies_maybe_http_custom_headers,
1030 https_only: config.https_only,
1031 redirect_policy_desc,
1032 }),
1033 })
1034 }
1035
1036 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1059 where
1060 V: TryInto<HeaderValue>,
1061 V::Error: Into<http::Error>,
1062 {
1063 match value.try_into() {
1064 Ok(value) => {
1065 self.config.headers.insert(USER_AGENT, value);
1066 }
1067 Err(e) => {
1068 self.config.error = Some(crate::error::builder(e.into()));
1069 }
1070 };
1071 self
1072 }
1073 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1097 for (key, value) in headers.iter() {
1098 self.config.headers.insert(key, value.clone());
1099 }
1100 self
1101 }
1102
1103 #[cfg(feature = "cookies")]
1118 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1119 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1120 if enable {
1121 self.cookie_provider(Arc::new(cookie::Jar::default()))
1122 } else {
1123 self.config.cookie_store = None;
1124 self
1125 }
1126 }
1127
1128 #[cfg(feature = "cookies")]
1142 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1143 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1144 mut self,
1145 cookie_store: Arc<C>,
1146 ) -> ClientBuilder {
1147 self.config.cookie_store = Some(cookie_store as _);
1148 self
1149 }
1150
1151 #[cfg(feature = "gzip")]
1168 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1169 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1170 self.config.accepts.gzip = enable;
1171 self
1172 }
1173
1174 #[cfg(feature = "brotli")]
1191 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1192 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1193 self.config.accepts.brotli = enable;
1194 self
1195 }
1196
1197 #[cfg(feature = "zstd")]
1214 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1215 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1216 self.config.accepts.zstd = enable;
1217 self
1218 }
1219
1220 #[cfg(feature = "deflate")]
1237 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1238 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1239 self.config.accepts.deflate = enable;
1240 self
1241 }
1242
1243 pub fn no_gzip(self) -> ClientBuilder {
1249 #[cfg(feature = "gzip")]
1250 {
1251 self.gzip(false)
1252 }
1253
1254 #[cfg(not(feature = "gzip"))]
1255 {
1256 self
1257 }
1258 }
1259
1260 pub fn no_brotli(self) -> ClientBuilder {
1266 #[cfg(feature = "brotli")]
1267 {
1268 self.brotli(false)
1269 }
1270
1271 #[cfg(not(feature = "brotli"))]
1272 {
1273 self
1274 }
1275 }
1276
1277 pub fn no_zstd(self) -> ClientBuilder {
1283 #[cfg(feature = "zstd")]
1284 {
1285 self.zstd(false)
1286 }
1287
1288 #[cfg(not(feature = "zstd"))]
1289 {
1290 self
1291 }
1292 }
1293
1294 pub fn no_deflate(self) -> ClientBuilder {
1300 #[cfg(feature = "deflate")]
1301 {
1302 self.deflate(false)
1303 }
1304
1305 #[cfg(not(feature = "deflate"))]
1306 {
1307 self
1308 }
1309 }
1310
1311 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1317 self.config.redirect_policy = policy;
1318 self
1319 }
1320
1321 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1325 self.config.referer = enable;
1326 self
1327 }
1328
1329 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1337 self.config.proxies.push(proxy.into_matcher());
1338 self.config.auto_sys_proxy = false;
1339 self
1340 }
1341
1342 pub fn no_proxy(mut self) -> ClientBuilder {
1350 self.config.proxies.clear();
1351 self.config.auto_sys_proxy = false;
1352 self
1353 }
1354
1355 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1364 self.config.timeout = Some(timeout);
1365 self
1366 }
1367
1368 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1376 self.config.read_timeout = Some(timeout);
1377 self
1378 }
1379
1380 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1389 self.config.connect_timeout = Some(timeout);
1390 self
1391 }
1392
1393 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1400 self.config.connection_verbose = verbose;
1401 self
1402 }
1403
1404 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1412 where
1413 D: Into<Option<Duration>>,
1414 {
1415 self.config.pool_idle_timeout = val.into();
1416 self
1417 }
1418
1419 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1421 self.config.pool_max_idle_per_host = max;
1422 self
1423 }
1424
1425 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1427 self.config.http1_title_case_headers = true;
1428 self
1429 }
1430
1431 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1437 mut self,
1438 value: bool,
1439 ) -> ClientBuilder {
1440 self.config
1441 .http1_allow_obsolete_multiline_headers_in_responses = value;
1442 self
1443 }
1444
1445 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1447 self.config.http1_ignore_invalid_headers_in_responses = value;
1448 self
1449 }
1450
1451 pub fn http1_allow_spaces_after_header_name_in_responses(
1457 mut self,
1458 value: bool,
1459 ) -> ClientBuilder {
1460 self.config
1461 .http1_allow_spaces_after_header_name_in_responses = value;
1462 self
1463 }
1464
1465 pub fn http1_only(mut self) -> ClientBuilder {
1467 self.config.http_version_pref = HttpVersionPref::Http1;
1468 self
1469 }
1470
1471 pub fn http09_responses(mut self) -> ClientBuilder {
1473 self.config.http09_responses = true;
1474 self
1475 }
1476
1477 #[cfg(feature = "http2")]
1479 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1480 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1481 self.config.http_version_pref = HttpVersionPref::Http2;
1482 self
1483 }
1484
1485 #[cfg(feature = "http3")]
1487 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1488 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1489 self.config.http_version_pref = HttpVersionPref::Http3;
1490 self
1491 }
1492
1493 #[cfg(feature = "http2")]
1497 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1498 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1499 self.config.http2_initial_stream_window_size = sz.into();
1500 self
1501 }
1502
1503 #[cfg(feature = "http2")]
1507 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1508 pub fn http2_initial_connection_window_size(
1509 mut self,
1510 sz: impl Into<Option<u32>>,
1511 ) -> ClientBuilder {
1512 self.config.http2_initial_connection_window_size = sz.into();
1513 self
1514 }
1515
1516 #[cfg(feature = "http2")]
1521 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1522 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1523 self.config.http2_adaptive_window = enabled;
1524 self
1525 }
1526
1527 #[cfg(feature = "http2")]
1531 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1532 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1533 self.config.http2_max_frame_size = sz.into();
1534 self
1535 }
1536
1537 #[cfg(feature = "http2")]
1541 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1542 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1543 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1544 self
1545 }
1546
1547 #[cfg(feature = "http2")]
1552 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1553 pub fn http2_keep_alive_interval(
1554 mut self,
1555 interval: impl Into<Option<Duration>>,
1556 ) -> ClientBuilder {
1557 self.config.http2_keep_alive_interval = interval.into();
1558 self
1559 }
1560
1561 #[cfg(feature = "http2")]
1567 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1568 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1569 self.config.http2_keep_alive_timeout = Some(timeout);
1570 self
1571 }
1572
1573 #[cfg(feature = "http2")]
1580 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1581 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1582 self.config.http2_keep_alive_while_idle = enabled;
1583 self
1584 }
1585
1586 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1592 self.config.nodelay = enabled;
1593 self
1594 }
1595
1596 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1611 where
1612 T: Into<Option<IpAddr>>,
1613 {
1614 self.config.local_address = addr.into();
1615 self
1616 }
1617
1618 #[cfg(any(
1651 target_os = "android",
1652 target_os = "fuchsia",
1653 target_os = "illumos",
1654 target_os = "ios",
1655 target_os = "linux",
1656 target_os = "macos",
1657 target_os = "solaris",
1658 target_os = "tvos",
1659 target_os = "visionos",
1660 target_os = "watchos",
1661 ))]
1662 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1663 self.config.interface = Some(interface.to_string());
1664 self
1665 }
1666
1667 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1671 where
1672 D: Into<Option<Duration>>,
1673 {
1674 self.config.tcp_keepalive = val.into();
1675 self
1676 }
1677
1678 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1682 where
1683 D: Into<Option<Duration>>,
1684 {
1685 self.config.tcp_keepalive_interval = val.into();
1686 self
1687 }
1688
1689 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1693 where
1694 C: Into<Option<u32>>,
1695 {
1696 self.config.tcp_keepalive_retries = retries.into();
1697 self
1698 }
1699
1700 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1707 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1708 where
1709 D: Into<Option<Duration>>,
1710 {
1711 self.config.tcp_user_timeout = val.into();
1712 self
1713 }
1714
1715 #[cfg(feature = "__tls")]
1727 #[cfg_attr(
1728 docsrs,
1729 doc(cfg(any(
1730 feature = "default-tls",
1731 feature = "native-tls",
1732 feature = "rustls-tls"
1733 )))
1734 )]
1735 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1736 self.config.root_certs.push(cert);
1737 self
1738 }
1739
1740 #[cfg(feature = "__rustls")]
1747 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1748 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1749 self.config.crls.push(crl);
1750 self
1751 }
1752
1753 #[cfg(feature = "__rustls")]
1760 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1761 pub fn add_crls(
1762 mut self,
1763 crls: impl IntoIterator<Item = CertificateRevocationList>,
1764 ) -> ClientBuilder {
1765 self.config.crls.extend(crls);
1766 self
1767 }
1768
1769 #[cfg(feature = "__tls")]
1787 #[cfg_attr(
1788 docsrs,
1789 doc(cfg(any(
1790 feature = "default-tls",
1791 feature = "native-tls",
1792 feature = "rustls-tls"
1793 )))
1794 )]
1795 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1796 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1797
1798 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1799 {
1800 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1801 }
1802
1803 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1804 {
1805 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1806 }
1807
1808 self
1809 }
1810
1811 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1815 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1816 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1817 self.config.tls_built_in_certs_webpki = enabled;
1818 self
1819 }
1820
1821 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1825 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1826 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1827 self.config.tls_built_in_certs_native = enabled;
1828 self
1829 }
1830
1831 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1838 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1839 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1840 self.config.identity = Some(identity);
1841 self
1842 }
1843
1844 #[cfg(feature = "__tls")]
1860 #[cfg_attr(
1861 docsrs,
1862 doc(cfg(any(
1863 feature = "default-tls",
1864 feature = "native-tls",
1865 feature = "rustls-tls"
1866 )))
1867 )]
1868 pub fn danger_accept_invalid_hostnames(
1869 mut self,
1870 accept_invalid_hostname: bool,
1871 ) -> ClientBuilder {
1872 self.config.hostname_verification = !accept_invalid_hostname;
1873 self
1874 }
1875
1876 #[cfg(feature = "__tls")]
1893 #[cfg_attr(
1894 docsrs,
1895 doc(cfg(any(
1896 feature = "default-tls",
1897 feature = "native-tls",
1898 feature = "rustls-tls"
1899 )))
1900 )]
1901 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1902 self.config.certs_verification = !accept_invalid_certs;
1903 self
1904 }
1905
1906 #[cfg(feature = "__tls")]
1915 #[cfg_attr(
1916 docsrs,
1917 doc(cfg(any(
1918 feature = "default-tls",
1919 feature = "native-tls",
1920 feature = "rustls-tls"
1921 )))
1922 )]
1923 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1924 self.config.tls_sni = tls_sni;
1925 self
1926 }
1927
1928 #[cfg(feature = "__tls")]
1944 #[cfg_attr(
1945 docsrs,
1946 doc(cfg(any(
1947 feature = "default-tls",
1948 feature = "native-tls",
1949 feature = "rustls-tls"
1950 )))
1951 )]
1952 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1953 self.config.min_tls_version = Some(version);
1954 self
1955 }
1956
1957 #[cfg(feature = "__tls")]
1976 #[cfg_attr(
1977 docsrs,
1978 doc(cfg(any(
1979 feature = "default-tls",
1980 feature = "native-tls",
1981 feature = "rustls-tls"
1982 )))
1983 )]
1984 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1985 self.config.max_tls_version = Some(version);
1986 self
1987 }
1988
1989 #[cfg(feature = "native-tls")]
1998 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1999 pub fn use_native_tls(mut self) -> ClientBuilder {
2000 self.config.tls = TlsBackend::Default;
2001 self
2002 }
2003
2004 #[cfg(feature = "__rustls")]
2013 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
2014 pub fn use_rustls_tls(mut self) -> ClientBuilder {
2015 self.config.tls = TlsBackend::Rustls;
2016 self
2017 }
2018
2019 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
2038 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
2039 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
2040 let mut tls = Some(tls);
2041 #[cfg(feature = "native-tls")]
2042 {
2043 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2044 let tls = conn.take().expect("is definitely Some");
2045 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2046 self.config.tls = tls;
2047 return self;
2048 }
2049 }
2050 #[cfg(feature = "__rustls")]
2051 {
2052 if let Some(conn) =
2053 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2054 {
2055 let tls = conn.take().expect("is definitely Some");
2056 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2057 self.config.tls = tls;
2058 return self;
2059 }
2060 }
2061
2062 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2064 self
2065 }
2066
2067 #[cfg(feature = "__tls")]
2074 #[cfg_attr(
2075 docsrs,
2076 doc(cfg(any(
2077 feature = "default-tls",
2078 feature = "native-tls",
2079 feature = "rustls-tls"
2080 )))
2081 )]
2082 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2083 self.config.tls_info = tls_info;
2084 self
2085 }
2086
2087 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2091 self.config.https_only = enabled;
2092 self
2093 }
2094
2095 #[doc(hidden)]
2096 #[cfg(feature = "hickory-dns")]
2097 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2098 #[deprecated(note = "use `hickory_dns` instead")]
2099 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
2100 self.config.hickory_dns = enable;
2101 self
2102 }
2103
2104 #[cfg(feature = "hickory-dns")]
2118 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2119 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2120 self.config.hickory_dns = enable;
2121 self
2122 }
2123
2124 #[doc(hidden)]
2125 #[deprecated(note = "use `no_hickory_dns` instead")]
2126 pub fn no_trust_dns(self) -> ClientBuilder {
2127 self.no_hickory_dns()
2128 }
2129
2130 pub fn no_hickory_dns(self) -> ClientBuilder {
2136 #[cfg(feature = "hickory-dns")]
2137 {
2138 self.hickory_dns(false)
2139 }
2140
2141 #[cfg(not(feature = "hickory-dns"))]
2142 {
2143 self
2144 }
2145 }
2146
2147 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2152 self.resolve_to_addrs(domain, &[addr])
2153 }
2154
2155 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2160 self.config
2161 .dns_overrides
2162 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2163 self
2164 }
2165
2166 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
2172 self.config.dns_resolver = Some(resolver as _);
2173 self
2174 }
2175
2176 #[cfg(feature = "http3")]
2181 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2182 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2183 self.config.tls_enable_early_data = enabled;
2184 self
2185 }
2186
2187 #[cfg(feature = "http3")]
2193 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2194 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2195 self.config.quic_max_idle_timeout = Some(value);
2196 self
2197 }
2198
2199 #[cfg(feature = "http3")]
2210 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2211 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2212 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2213 self
2214 }
2215
2216 #[cfg(feature = "http3")]
2227 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2228 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2229 self.config.quic_receive_window = Some(value.try_into().unwrap());
2230 self
2231 }
2232
2233 #[cfg(feature = "http3")]
2239 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2240 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2241 self.config.quic_send_window = Some(value);
2242 self
2243 }
2244
2245 #[cfg(feature = "http3")]
2253 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2254 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2255 self.config.quic_congestion_bbr = true;
2256 self
2257 }
2258
2259 #[cfg(feature = "http3")]
2269 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2270 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2271 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2272 self
2273 }
2274
2275 #[cfg(feature = "http3")]
2287 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2288 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2289 self.config.h3_send_grease = Some(enabled);
2290 self
2291 }
2292
2293 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2317 where
2318 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2319 L::Service:
2320 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2321 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2322 {
2323 let layer = BoxCloneSyncServiceLayer::new(layer);
2324
2325 self.config.connector_layers.push(layer);
2326
2327 self
2328 }
2329}
2330
2331type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2332
2333impl Default for Client {
2334 fn default() -> Self {
2335 Self::new()
2336 }
2337}
2338
2339impl Client {
2340 pub fn new() -> Client {
2350 ClientBuilder::new().build().expect("Client::new()")
2351 }
2352
2353 pub fn builder() -> ClientBuilder {
2357 ClientBuilder::new()
2358 }
2359
2360 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2366 self.request(Method::GET, url)
2367 }
2368
2369 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2375 self.request(Method::POST, url)
2376 }
2377
2378 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2384 self.request(Method::PUT, url)
2385 }
2386
2387 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2393 self.request(Method::PATCH, url)
2394 }
2395
2396 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2402 self.request(Method::DELETE, url)
2403 }
2404
2405 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2411 self.request(Method::HEAD, url)
2412 }
2413
2414 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2423 let req = url.into_url().map(move |url| Request::new(method, url));
2424 RequestBuilder::new(self.clone(), req)
2425 }
2426
2427 pub fn execute(
2440 &self,
2441 request: Request,
2442 ) -> impl Future<Output = Result<Response, crate::Error>> {
2443 self.execute_request(request)
2444 }
2445
2446 pub(super) fn execute_request(&self, req: Request) -> Pending {
2447 let (method, url, mut headers, body, version, extensions) = req.pieces();
2448 if url.scheme() != "http" && url.scheme() != "https" {
2449 return Pending::new_err(error::url_bad_scheme(url));
2450 }
2451
2452 if self.inner.https_only && url.scheme() != "https" {
2454 return Pending::new_err(error::url_bad_scheme(url));
2455 }
2456
2457 for (key, value) in &self.inner.headers {
2460 if let Entry::Vacant(entry) = headers.entry(key) {
2461 entry.insert(value.clone());
2462 }
2463 }
2464
2465 let accept_encoding = self.inner.accepts.as_str();
2466
2467 if let Some(accept_encoding) = accept_encoding {
2468 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2469 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2470 }
2471 }
2472
2473 let uri = match try_uri(&url) {
2474 Ok(uri) => uri,
2475 _ => return Pending::new_err(error::url_invalid_uri(url)),
2476 };
2477
2478 let (reusable, body) = match body {
2479 Some(body) => {
2480 let (reusable, body) = body.try_reuse();
2481 (Some(reusable), body)
2482 }
2483 None => (None, Body::empty()),
2484 };
2485
2486 self.proxy_auth(&uri, &mut headers);
2487 self.proxy_custom_headers(&uri, &mut headers);
2488
2489 let builder = hyper::Request::builder()
2490 .method(method.clone())
2491 .uri(uri)
2492 .version(version);
2493
2494 let in_flight = match version {
2495 #[cfg(feature = "http3")]
2496 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2497 let mut req = builder.body(body).expect("valid request parts");
2498 *req.headers_mut() = headers.clone();
2499 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2500 ResponseFuture::H3(h3.call(req))
2501 }
2502 _ => {
2503 let mut req = builder.body(body).expect("valid request parts");
2504 *req.headers_mut() = headers.clone();
2505 let mut hyper = self.inner.hyper.clone();
2506 ResponseFuture::Default(hyper.call(req))
2507 }
2508 };
2509
2510 let total_timeout = self
2511 .inner
2512 .request_timeout
2513 .fetch(&extensions)
2514 .copied()
2515 .map(tokio::time::sleep)
2516 .map(Box::pin);
2517
2518 let read_timeout_fut = self
2519 .inner
2520 .read_timeout
2521 .map(tokio::time::sleep)
2522 .map(Box::pin);
2523
2524 Pending {
2525 inner: PendingInner::Request(Box::pin(PendingRequest {
2526 method,
2527 url,
2528 headers,
2529 body: reusable,
2530
2531 retry_count: 0,
2532
2533 client: self.inner.clone(),
2534
2535 in_flight,
2536 total_timeout,
2537 read_timeout_fut,
2538 read_timeout: self.inner.read_timeout,
2539 })),
2540 }
2541 }
2542
2543 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2544 if !self.inner.proxies_maybe_http_auth {
2545 return;
2546 }
2547
2548 if dst.scheme() != Some(&Scheme::HTTP) {
2552 return;
2553 }
2554
2555 if headers.contains_key(PROXY_AUTHORIZATION) {
2556 return;
2557 }
2558
2559 for proxy in self.inner.proxies.iter() {
2560 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2561 headers.insert(PROXY_AUTHORIZATION, header);
2562 break;
2563 }
2564 }
2565 }
2566
2567 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2568 if !self.inner.proxies_maybe_http_custom_headers {
2569 return;
2570 }
2571
2572 if dst.scheme() != Some(&Scheme::HTTP) {
2573 return;
2574 }
2575
2576 for proxy in self.inner.proxies.iter() {
2577 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2578 iter.iter().for_each(|(key, value)| {
2579 headers.insert(key, value.clone());
2580 });
2581 break;
2582 }
2583 }
2584 }
2585}
2586
2587impl fmt::Debug for Client {
2588 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2589 let mut builder = f.debug_struct("Client");
2590 self.inner.fmt_fields(&mut builder);
2591 builder.finish()
2592 }
2593}
2594
2595impl tower_service::Service<Request> for Client {
2596 type Response = Response;
2597 type Error = crate::Error;
2598 type Future = Pending;
2599
2600 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2601 Poll::Ready(Ok(()))
2602 }
2603
2604 fn call(&mut self, req: Request) -> Self::Future {
2605 self.execute_request(req)
2606 }
2607}
2608
2609impl tower_service::Service<Request> for &'_ Client {
2610 type Response = Response;
2611 type Error = crate::Error;
2612 type Future = Pending;
2613
2614 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2615 Poll::Ready(Ok(()))
2616 }
2617
2618 fn call(&mut self, req: Request) -> Self::Future {
2619 self.execute_request(req)
2620 }
2621}
2622
2623impl fmt::Debug for ClientBuilder {
2624 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2625 let mut builder = f.debug_struct("ClientBuilder");
2626 self.config.fmt_fields(&mut builder);
2627 builder.finish()
2628 }
2629}
2630
2631impl Config {
2632 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2633 #[cfg(feature = "cookies")]
2637 {
2638 if let Some(_) = self.cookie_store {
2639 f.field("cookie_store", &true);
2640 }
2641 }
2642
2643 f.field("accepts", &self.accepts);
2644
2645 if !self.proxies.is_empty() {
2646 f.field("proxies", &self.proxies);
2647 }
2648
2649 if !self.redirect_policy.is_default() {
2650 f.field("redirect_policy", &self.redirect_policy);
2651 }
2652
2653 if self.referer {
2654 f.field("referer", &true);
2655 }
2656
2657 f.field("default_headers", &self.headers);
2658
2659 if self.http1_title_case_headers {
2660 f.field("http1_title_case_headers", &true);
2661 }
2662
2663 if self.http1_allow_obsolete_multiline_headers_in_responses {
2664 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2665 }
2666
2667 if self.http1_ignore_invalid_headers_in_responses {
2668 f.field("http1_ignore_invalid_headers_in_responses", &true);
2669 }
2670
2671 if self.http1_allow_spaces_after_header_name_in_responses {
2672 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2673 }
2674
2675 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2676 f.field("http1_only", &true);
2677 }
2678
2679 #[cfg(feature = "http2")]
2680 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2681 f.field("http2_prior_knowledge", &true);
2682 }
2683
2684 if let Some(ref d) = self.connect_timeout {
2685 f.field("connect_timeout", d);
2686 }
2687
2688 if let Some(ref d) = self.timeout {
2689 f.field("timeout", d);
2690 }
2691
2692 if let Some(ref v) = self.local_address {
2693 f.field("local_address", v);
2694 }
2695
2696 #[cfg(any(
2697 target_os = "android",
2698 target_os = "fuchsia",
2699 target_os = "illumos",
2700 target_os = "ios",
2701 target_os = "linux",
2702 target_os = "macos",
2703 target_os = "solaris",
2704 target_os = "tvos",
2705 target_os = "visionos",
2706 target_os = "watchos",
2707 ))]
2708 if let Some(ref v) = self.interface {
2709 f.field("interface", v);
2710 }
2711
2712 if self.nodelay {
2713 f.field("tcp_nodelay", &true);
2714 }
2715
2716 #[cfg(feature = "__tls")]
2717 {
2718 if !self.hostname_verification {
2719 f.field("danger_accept_invalid_hostnames", &true);
2720 }
2721 }
2722
2723 #[cfg(feature = "__tls")]
2724 {
2725 if !self.certs_verification {
2726 f.field("danger_accept_invalid_certs", &true);
2727 }
2728
2729 if let Some(ref min_tls_version) = self.min_tls_version {
2730 f.field("min_tls_version", min_tls_version);
2731 }
2732
2733 if let Some(ref max_tls_version) = self.max_tls_version {
2734 f.field("max_tls_version", max_tls_version);
2735 }
2736
2737 f.field("tls_sni", &self.tls_sni);
2738
2739 f.field("tls_info", &self.tls_info);
2740 }
2741
2742 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2743 {
2744 f.field("tls_backend", &self.tls);
2745 }
2746
2747 if !self.dns_overrides.is_empty() {
2748 f.field("dns_overrides", &self.dns_overrides);
2749 }
2750
2751 #[cfg(feature = "http3")]
2752 {
2753 if self.tls_enable_early_data {
2754 f.field("tls_enable_early_data", &true);
2755 }
2756 }
2757 }
2758}
2759
2760struct ClientRef {
2761 accepts: Accepts,
2762 #[cfg(feature = "cookies")]
2763 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2764 headers: HeaderMap,
2765 hyper: FollowRedirect<HyperService, TowerRedirectPolicy>,
2766 #[cfg(feature = "http3")]
2767 h3_client: Option<FollowRedirect<H3Client, TowerRedirectPolicy>>,
2768 referer: bool,
2769 request_timeout: RequestConfig<RequestTimeout>,
2770 read_timeout: Option<Duration>,
2771 proxies: Arc<Vec<ProxyMatcher>>,
2772 proxies_maybe_http_auth: bool,
2773 proxies_maybe_http_custom_headers: bool,
2774 https_only: bool,
2775 redirect_policy_desc: Option<String>,
2776}
2777
2778impl ClientRef {
2779 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2780 #[cfg(feature = "cookies")]
2784 {
2785 if let Some(_) = self.cookie_store {
2786 f.field("cookie_store", &true);
2787 }
2788 }
2789
2790 f.field("accepts", &self.accepts);
2791
2792 if !self.proxies.is_empty() {
2793 f.field("proxies", &self.proxies);
2794 }
2795
2796 if let Some(s) = &self.redirect_policy_desc {
2797 f.field("redirect_policy", s);
2798 }
2799
2800 if self.referer {
2801 f.field("referer", &true);
2802 }
2803
2804 f.field("default_headers", &self.headers);
2805
2806 self.request_timeout.fmt_as_field(f);
2807
2808 if let Some(ref d) = self.read_timeout {
2809 f.field("read_timeout", d);
2810 }
2811 }
2812}
2813
2814pin_project! {
2815 pub struct Pending {
2816 #[pin]
2817 inner: PendingInner,
2818 }
2819}
2820
2821enum PendingInner {
2822 Request(Pin<Box<PendingRequest>>),
2823 Error(Option<crate::Error>),
2824}
2825
2826pin_project! {
2827 struct PendingRequest {
2828 method: Method,
2829 url: Url,
2830 headers: HeaderMap,
2831 body: Option<Option<Bytes>>,
2832
2833 retry_count: usize,
2834
2835 client: Arc<ClientRef>,
2836
2837 #[pin]
2838 in_flight: ResponseFuture,
2839 #[pin]
2840 total_timeout: Option<Pin<Box<Sleep>>>,
2841 #[pin]
2842 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2843 read_timeout: Option<Duration>,
2844 }
2845}
2846
2847enum ResponseFuture {
2848 Default(tower_http::follow_redirect::ResponseFuture<HyperService, Body, TowerRedirectPolicy>),
2849 #[cfg(feature = "http3")]
2850 H3(tower_http::follow_redirect::ResponseFuture<H3Client, Body, TowerRedirectPolicy>),
2851}
2852
2853impl PendingRequest {
2854 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2855 self.project().in_flight
2856 }
2857
2858 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2859 self.project().total_timeout
2860 }
2861
2862 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2863 self.project().read_timeout_fut
2864 }
2865
2866 #[cfg(any(feature = "http2", feature = "http3"))]
2867 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2868 use log::trace;
2869
2870 if !is_retryable_error(err) {
2871 return false;
2872 }
2873
2874 trace!("can retry {err:?}");
2875
2876 let body = match self.body {
2877 Some(Some(ref body)) => Body::reusable(body.clone()),
2878 Some(None) => {
2879 log::debug!("error was retryable, but body not reusable");
2880 return false;
2881 }
2882 None => Body::empty(),
2883 };
2884
2885 if self.retry_count >= 2 {
2886 trace!("retry count too high");
2887 return false;
2888 }
2889 self.retry_count += 1;
2890
2891 let uri = try_uri(&self.url).expect("URL was already validated as URI");
2893
2894 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
2895 #[cfg(feature = "http3")]
2896 ResponseFuture::H3(_) => {
2897 let mut req = hyper::Request::builder()
2898 .method(self.method.clone())
2899 .uri(uri)
2900 .body(body)
2901 .expect("valid request parts");
2902 *req.headers_mut() = self.headers.clone();
2903 let mut h3 = self
2904 .client
2905 .h3_client
2906 .as_ref()
2907 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2908 .clone();
2909 ResponseFuture::H3(h3.call(req))
2910 }
2911 _ => {
2912 let mut req = hyper::Request::builder()
2913 .method(self.method.clone())
2914 .uri(uri)
2915 .body(body)
2916 .expect("valid request parts");
2917 *req.headers_mut() = self.headers.clone();
2918 let mut hyper = self.client.hyper.clone();
2919 ResponseFuture::Default(hyper.call(req))
2920 }
2921 };
2922
2923 true
2924 }
2925}
2926
2927#[cfg(any(feature = "http2", feature = "http3"))]
2928fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
2929 let err = if let Some(err) = err.source() {
2931 err
2932 } else {
2933 return false;
2934 };
2935
2936 #[cfg(feature = "http3")]
2937 if let Some(cause) = err.source() {
2938 if let Some(err) = cause.downcast_ref::<h3::error::ConnectionError>() {
2939 log::debug!("determining if HTTP/3 error {err} can be retried");
2940 return err.to_string().as_str() == "timeout";
2942 }
2943 }
2944
2945 #[cfg(feature = "http2")]
2946 if let Some(cause) = err.source() {
2947 if let Some(err) = cause.downcast_ref::<h2::Error>() {
2948 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
2950 return true;
2951 }
2952
2953 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
2956 {
2957 return true;
2958 }
2959 }
2960 }
2961 false
2962}
2963
2964impl Pending {
2965 pub(super) fn new_err(err: crate::Error) -> Pending {
2966 Pending {
2967 inner: PendingInner::Error(Some(err)),
2968 }
2969 }
2970
2971 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2972 self.project().inner
2973 }
2974}
2975
2976impl Future for Pending {
2977 type Output = Result<Response, crate::Error>;
2978
2979 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2980 let inner = self.inner();
2981 match inner.get_mut() {
2982 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2983 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2984 .take()
2985 .expect("Pending error polled more than once"))),
2986 }
2987 }
2988}
2989
2990impl Future for PendingRequest {
2991 type Output = Result<Response, crate::Error>;
2992
2993 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2994 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
2995 if let Poll::Ready(()) = delay.poll(cx) {
2996 return Poll::Ready(Err(
2997 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2998 ));
2999 }
3000 }
3001
3002 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
3003 if let Poll::Ready(()) = delay.poll(cx) {
3004 return Poll::Ready(Err(
3005 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3006 ));
3007 }
3008 }
3009
3010 loop {
3011 let res = match self.as_mut().in_flight().get_mut() {
3012 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
3013 Poll::Ready(Err(e)) => {
3014 #[cfg(feature = "http2")]
3015 if e.is_request() {
3016 if let Some(e) = e.source() {
3017 if self.as_mut().retry_error(e) {
3018 continue;
3019 }
3020 }
3021 }
3022
3023 return Poll::Ready(Err(e));
3024 }
3025 Poll::Ready(Ok(res)) => res.map(super::body::boxed),
3026 Poll::Pending => return Poll::Pending,
3027 },
3028 #[cfg(feature = "http3")]
3029 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
3030 Poll::Ready(Err(e)) => {
3031 if self.as_mut().retry_error(&e) {
3032 continue;
3033 }
3034 return Poll::Ready(Err(
3035 crate::error::request(e).with_url(self.url.clone())
3036 ));
3037 }
3038 Poll::Ready(Ok(res)) => res,
3039 Poll::Pending => return Poll::Pending,
3040 },
3041 };
3042
3043 #[cfg(feature = "cookies")]
3044 {
3045 if let Some(ref cookie_store) = self.client.cookie_store {
3046 let mut cookies =
3047 cookie::extract_response_cookie_headers(res.headers()).peekable();
3048 if cookies.peek().is_some() {
3049 cookie_store.set_cookies(&mut cookies, &self.url);
3050 }
3051 }
3052 }
3053 if let Some(url) = &res
3054 .extensions()
3055 .get::<tower_http::follow_redirect::RequestUri>()
3056 {
3057 self.url = match Url::parse(&url.0.to_string()) {
3058 Ok(url) => url,
3059 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3060 }
3061 };
3062
3063 let res = Response::new(
3064 res,
3065 self.url.clone(),
3066 self.client.accepts,
3067 self.total_timeout.take(),
3068 self.read_timeout,
3069 );
3070 return Poll::Ready(Ok(res));
3071 }
3072 }
3073}
3074
3075impl fmt::Debug for Pending {
3076 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3077 match self.inner {
3078 PendingInner::Request(ref req) => f
3079 .debug_struct("Pending")
3080 .field("method", &req.method)
3081 .field("url", &req.url)
3082 .finish(),
3083 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3084 }
3085 }
3086}
3087
3088#[cfg(test)]
3089mod tests {
3090 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
3091
3092 #[tokio::test]
3093 async fn execute_request_rejects_invalid_urls() {
3094 let url_str = "hxxps://www.rust-lang.org/";
3095 let url = url::Url::parse(url_str).unwrap();
3096 let result = crate::get(url.clone()).await;
3097
3098 assert!(result.is_err());
3099 let err = result.err().unwrap();
3100 assert!(err.is_builder());
3101 assert_eq!(url_str, err.url().unwrap().as_str());
3102 }
3103
3104 #[tokio::test]
3106 async fn execute_request_rejects_invalid_hostname() {
3107 let url_str = "https://{{hostname}}/";
3108 let url = url::Url::parse(url_str).unwrap();
3109 let result = crate::get(url.clone()).await;
3110
3111 assert!(result.is_err());
3112 let err = result.err().unwrap();
3113 assert!(err.is_builder());
3114 assert_eq!(url_str, err.url().unwrap().as_str());
3115 }
3116
3117 #[test]
3118 fn test_future_size() {
3119 let s = std::mem::size_of::<super::Pending>();
3120 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3121 }
3122}