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(feature = "native-tls", feature = "__rustls"))]
171 identity: Option<Identity>,
172 proxies: Vec<ProxyMatcher>,
173 auto_sys_proxy: bool,
174 redirect_policy: redirect::Policy,
175 referer: bool,
176 read_timeout: Option<Duration>,
177 timeout: Option<Duration>,
178 #[cfg(feature = "__tls")]
179 root_certs: Vec<Certificate>,
180 #[cfg(feature = "__tls")]
181 tls_built_in_root_certs: bool,
182 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
183 tls_built_in_certs_webpki: bool,
184 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
185 tls_built_in_certs_native: bool,
186 #[cfg(feature = "__rustls")]
187 crls: Vec<CertificateRevocationList>,
188 #[cfg(feature = "__tls")]
189 min_tls_version: Option<tls::Version>,
190 #[cfg(feature = "__tls")]
191 max_tls_version: Option<tls::Version>,
192 #[cfg(feature = "__tls")]
193 tls_info: bool,
194 #[cfg(feature = "__tls")]
195 tls: TlsBackend,
196 connector_layers: Vec<BoxedConnectorLayer>,
197 http_version_pref: HttpVersionPref,
198 http09_responses: bool,
199 http1_title_case_headers: bool,
200 http1_allow_obsolete_multiline_headers_in_responses: bool,
201 http1_ignore_invalid_headers_in_responses: bool,
202 http1_allow_spaces_after_header_name_in_responses: bool,
203 #[cfg(feature = "http2")]
204 http2_initial_stream_window_size: Option<u32>,
205 #[cfg(feature = "http2")]
206 http2_initial_connection_window_size: Option<u32>,
207 #[cfg(feature = "http2")]
208 http2_adaptive_window: bool,
209 #[cfg(feature = "http2")]
210 http2_max_frame_size: Option<u32>,
211 #[cfg(feature = "http2")]
212 http2_max_header_list_size: Option<u32>,
213 #[cfg(feature = "http2")]
214 http2_keep_alive_interval: Option<Duration>,
215 #[cfg(feature = "http2")]
216 http2_keep_alive_timeout: Option<Duration>,
217 #[cfg(feature = "http2")]
218 http2_keep_alive_while_idle: bool,
219 local_address: Option<IpAddr>,
220 #[cfg(any(
221 target_os = "android",
222 target_os = "fuchsia",
223 target_os = "illumos",
224 target_os = "ios",
225 target_os = "linux",
226 target_os = "macos",
227 target_os = "solaris",
228 target_os = "tvos",
229 target_os = "visionos",
230 target_os = "watchos",
231 ))]
232 interface: Option<String>,
233 nodelay: bool,
234 #[cfg(feature = "cookies")]
235 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
236 hickory_dns: bool,
237 error: Option<crate::Error>,
238 https_only: bool,
239 #[cfg(feature = "http3")]
240 tls_enable_early_data: bool,
241 #[cfg(feature = "http3")]
242 quic_max_idle_timeout: Option<Duration>,
243 #[cfg(feature = "http3")]
244 quic_stream_receive_window: Option<VarInt>,
245 #[cfg(feature = "http3")]
246 quic_receive_window: Option<VarInt>,
247 #[cfg(feature = "http3")]
248 quic_send_window: Option<u64>,
249 #[cfg(feature = "http3")]
250 quic_congestion_bbr: bool,
251 #[cfg(feature = "http3")]
252 h3_max_field_section_size: Option<u64>,
253 #[cfg(feature = "http3")]
254 h3_send_grease: Option<bool>,
255 dns_overrides: HashMap<String, Vec<SocketAddr>>,
256 dns_resolver: Option<Arc<dyn Resolve>>,
257}
258
259impl Default for ClientBuilder {
260 fn default() -> Self {
261 Self::new()
262 }
263}
264
265impl ClientBuilder {
266 pub fn new() -> Self {
270 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
271 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
272
273 ClientBuilder {
274 config: Config {
275 error: None,
276 accepts: Accepts::default(),
277 headers,
278 #[cfg(feature = "__tls")]
279 hostname_verification: true,
280 #[cfg(feature = "__tls")]
281 certs_verification: true,
282 #[cfg(feature = "__tls")]
283 tls_sni: true,
284 connect_timeout: None,
285 connection_verbose: false,
286 pool_idle_timeout: Some(Duration::from_secs(90)),
287 pool_max_idle_per_host: usize::MAX,
288 tcp_keepalive: None, tcp_keepalive_interval: None,
292 tcp_keepalive_retries: None,
293 proxies: Vec::new(),
294 auto_sys_proxy: true,
295 redirect_policy: redirect::Policy::default(),
296 referer: true,
297 read_timeout: None,
298 timeout: None,
299 #[cfg(feature = "__tls")]
300 root_certs: Vec::new(),
301 #[cfg(feature = "__tls")]
302 tls_built_in_root_certs: true,
303 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
304 tls_built_in_certs_webpki: true,
305 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
306 tls_built_in_certs_native: true,
307 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
308 identity: None,
309 #[cfg(feature = "__rustls")]
310 crls: vec![],
311 #[cfg(feature = "__tls")]
312 min_tls_version: None,
313 #[cfg(feature = "__tls")]
314 max_tls_version: None,
315 #[cfg(feature = "__tls")]
316 tls_info: false,
317 #[cfg(feature = "__tls")]
318 tls: TlsBackend::default(),
319 connector_layers: Vec::new(),
320 http_version_pref: HttpVersionPref::All,
321 http09_responses: false,
322 http1_title_case_headers: false,
323 http1_allow_obsolete_multiline_headers_in_responses: false,
324 http1_ignore_invalid_headers_in_responses: false,
325 http1_allow_spaces_after_header_name_in_responses: false,
326 #[cfg(feature = "http2")]
327 http2_initial_stream_window_size: None,
328 #[cfg(feature = "http2")]
329 http2_initial_connection_window_size: None,
330 #[cfg(feature = "http2")]
331 http2_adaptive_window: false,
332 #[cfg(feature = "http2")]
333 http2_max_frame_size: None,
334 #[cfg(feature = "http2")]
335 http2_max_header_list_size: None,
336 #[cfg(feature = "http2")]
337 http2_keep_alive_interval: None,
338 #[cfg(feature = "http2")]
339 http2_keep_alive_timeout: None,
340 #[cfg(feature = "http2")]
341 http2_keep_alive_while_idle: false,
342 local_address: None,
343 #[cfg(any(
344 target_os = "android",
345 target_os = "fuchsia",
346 target_os = "illumos",
347 target_os = "ios",
348 target_os = "linux",
349 target_os = "macos",
350 target_os = "solaris",
351 target_os = "tvos",
352 target_os = "visionos",
353 target_os = "watchos",
354 ))]
355 interface: None,
356 nodelay: true,
357 hickory_dns: cfg!(feature = "hickory-dns"),
358 #[cfg(feature = "cookies")]
359 cookie_store: None,
360 https_only: false,
361 dns_overrides: HashMap::new(),
362 #[cfg(feature = "http3")]
363 tls_enable_early_data: false,
364 #[cfg(feature = "http3")]
365 quic_max_idle_timeout: None,
366 #[cfg(feature = "http3")]
367 quic_stream_receive_window: None,
368 #[cfg(feature = "http3")]
369 quic_receive_window: None,
370 #[cfg(feature = "http3")]
371 quic_send_window: None,
372 #[cfg(feature = "http3")]
373 quic_congestion_bbr: false,
374 #[cfg(feature = "http3")]
375 h3_max_field_section_size: None,
376 #[cfg(feature = "http3")]
377 h3_send_grease: None,
378 dns_resolver: None,
379 },
380 }
381 }
382}
383
384impl ClientBuilder {
385 pub fn build(self) -> crate::Result<Client> {
392 let config = self.config;
393
394 if let Some(err) = config.error {
395 return Err(err);
396 }
397
398 let mut proxies = config.proxies;
399 if config.auto_sys_proxy {
400 proxies.push(ProxyMatcher::system());
401 }
402 let proxies = Arc::new(proxies);
403
404 #[allow(unused)]
405 #[cfg(feature = "http3")]
406 let mut h3_connector = None;
407
408 let resolver = {
409 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
410 false => Arc::new(GaiResolver::new()),
411 #[cfg(feature = "hickory-dns")]
412 true => Arc::new(HickoryDnsResolver::default()),
413 #[cfg(not(feature = "hickory-dns"))]
414 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
415 };
416 if let Some(dns_resolver) = config.dns_resolver {
417 resolver = dns_resolver;
418 }
419 if !config.dns_overrides.is_empty() {
420 resolver = Arc::new(DnsResolverWithOverrides::new(
421 resolver,
422 config.dns_overrides,
423 ));
424 }
425 DynResolver::new(resolver)
426 };
427
428 let mut connector_builder = {
429 #[cfg(feature = "__tls")]
430 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
431 headers.get(USER_AGENT).cloned()
432 }
433
434 let mut http = HttpConnector::new_with_resolver(resolver.clone());
435 http.set_connect_timeout(config.connect_timeout);
436
437 #[cfg(all(feature = "http3", feature = "__rustls"))]
438 let build_h3_connector =
439 |resolver,
440 tls,
441 quic_max_idle_timeout: Option<Duration>,
442 quic_stream_receive_window,
443 quic_receive_window,
444 quic_send_window,
445 quic_congestion_bbr,
446 h3_max_field_section_size,
447 h3_send_grease,
448 local_address,
449 http_version_pref: &HttpVersionPref| {
450 let mut transport_config = TransportConfig::default();
451
452 if let Some(max_idle_timeout) = quic_max_idle_timeout {
453 transport_config.max_idle_timeout(Some(
454 max_idle_timeout.try_into().map_err(error::builder)?,
455 ));
456 }
457
458 if let Some(stream_receive_window) = quic_stream_receive_window {
459 transport_config.stream_receive_window(stream_receive_window);
460 }
461
462 if let Some(receive_window) = quic_receive_window {
463 transport_config.receive_window(receive_window);
464 }
465
466 if let Some(send_window) = quic_send_window {
467 transport_config.send_window(send_window);
468 }
469
470 if quic_congestion_bbr {
471 let factory = Arc::new(quinn::congestion::BbrConfig::default());
472 transport_config.congestion_controller_factory(factory);
473 }
474
475 let mut h3_client_config = H3ClientConfig::default();
476
477 if let Some(max_field_section_size) = h3_max_field_section_size {
478 h3_client_config.max_field_section_size = Some(max_field_section_size);
479 }
480
481 if let Some(send_grease) = h3_send_grease {
482 h3_client_config.send_grease = Some(send_grease);
483 }
484
485 let res = H3Connector::new(
486 resolver,
487 tls,
488 local_address,
489 transport_config,
490 h3_client_config,
491 );
492
493 match res {
494 Ok(connector) => Ok(Some(connector)),
495 Err(err) => {
496 if let HttpVersionPref::Http3 = http_version_pref {
497 Err(error::builder(err))
498 } else {
499 Ok(None)
500 }
501 }
502 }
503 };
504
505 #[cfg(feature = "__tls")]
506 match config.tls {
507 #[cfg(feature = "default-tls")]
508 TlsBackend::Default => {
509 let mut tls = TlsConnector::builder();
510
511 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
512 {
513 match config.http_version_pref {
514 HttpVersionPref::Http1 => {
515 tls.request_alpns(&["http/1.1"]);
516 }
517 #[cfg(feature = "http2")]
518 HttpVersionPref::Http2 => {
519 tls.request_alpns(&["h2"]);
520 }
521 HttpVersionPref::All => {
522 tls.request_alpns(&["h2", "http/1.1"]);
523 }
524 }
525 }
526
527 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
528
529 tls.danger_accept_invalid_certs(!config.certs_verification);
530
531 tls.use_sni(config.tls_sni);
532
533 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
534
535 for cert in config.root_certs {
536 cert.add_to_native_tls(&mut tls);
537 }
538
539 #[cfg(feature = "native-tls")]
540 {
541 if let Some(id) = config.identity {
542 id.add_to_native_tls(&mut tls)?;
543 }
544 }
545 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
546 {
547 if let Some(_id) = config.identity {
549 return Err(crate::error::builder("incompatible TLS identity type"));
550 }
551 }
552
553 if let Some(min_tls_version) = config.min_tls_version {
554 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
555 crate::error::builder("invalid minimum TLS version for backend")
559 })?;
560 tls.min_protocol_version(Some(protocol));
561 }
562
563 if let Some(max_tls_version) = config.max_tls_version {
564 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
565 crate::error::builder("invalid maximum TLS version for backend")
570 })?;
571 tls.max_protocol_version(Some(protocol));
572 }
573
574 ConnectorBuilder::new_default_tls(
575 http,
576 tls,
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 }
597 #[cfg(feature = "native-tls")]
598 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_default_tls(
599 http,
600 conn,
601 proxies.clone(),
602 user_agent(&config.headers),
603 config.local_address,
604 #[cfg(any(
605 target_os = "android",
606 target_os = "fuchsia",
607 target_os = "illumos",
608 target_os = "ios",
609 target_os = "linux",
610 target_os = "macos",
611 target_os = "solaris",
612 target_os = "tvos",
613 target_os = "visionos",
614 target_os = "watchos",
615 ))]
616 config.interface.as_deref(),
617 config.nodelay,
618 config.tls_info,
619 ),
620 #[cfg(feature = "__rustls")]
621 TlsBackend::BuiltRustls(conn) => {
622 #[cfg(feature = "http3")]
623 {
624 h3_connector = build_h3_connector(
625 resolver.clone(),
626 conn.clone(),
627 config.quic_max_idle_timeout,
628 config.quic_stream_receive_window,
629 config.quic_receive_window,
630 config.quic_send_window,
631 config.quic_congestion_bbr,
632 config.h3_max_field_section_size,
633 config.h3_send_grease,
634 config.local_address,
635 &config.http_version_pref,
636 )?;
637 }
638
639 ConnectorBuilder::new_rustls_tls(
640 http,
641 conn,
642 proxies.clone(),
643 user_agent(&config.headers),
644 config.local_address,
645 #[cfg(any(
646 target_os = "android",
647 target_os = "fuchsia",
648 target_os = "illumos",
649 target_os = "ios",
650 target_os = "linux",
651 target_os = "macos",
652 target_os = "solaris",
653 target_os = "tvos",
654 target_os = "visionos",
655 target_os = "watchos",
656 ))]
657 config.interface.as_deref(),
658 config.nodelay,
659 config.tls_info,
660 )
661 }
662 #[cfg(feature = "__rustls")]
663 TlsBackend::Rustls => {
664 use crate::tls::{IgnoreHostname, NoVerifier};
665
666 let mut root_cert_store = rustls::RootCertStore::empty();
668 for cert in config.root_certs {
669 cert.add_to_rustls(&mut root_cert_store)?;
670 }
671
672 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
673 if config.tls_built_in_certs_webpki {
674 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
675 }
676
677 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
678 if config.tls_built_in_certs_native {
679 let mut valid_count = 0;
680 let mut invalid_count = 0;
681
682 let load_results = rustls_native_certs::load_native_certs();
683 for cert in load_results.certs {
684 match root_cert_store.add(cert.into()) {
688 Ok(_) => valid_count += 1,
689 Err(err) => {
690 invalid_count += 1;
691 log::debug!("rustls failed to parse DER certificate: {err:?}");
692 }
693 }
694 }
695 if valid_count == 0 && invalid_count > 0 {
696 let err = if load_results.errors.is_empty() {
697 crate::error::builder(
698 "zero valid certificates found in native root store",
699 )
700 } else {
701 use std::fmt::Write as _;
702 let mut acc = String::new();
703 for err in load_results.errors {
704 let _ = writeln!(&mut acc, "{err}");
705 }
706
707 crate::error::builder(acc)
708 };
709
710 return Err(err);
711 }
712 }
713
714 let mut versions = rustls::ALL_VERSIONS.to_vec();
716
717 if let Some(min_tls_version) = config.min_tls_version {
718 versions.retain(|&supported_version| {
719 match tls::Version::from_rustls(supported_version.version) {
720 Some(version) => version >= min_tls_version,
721 None => true,
724 }
725 });
726 }
727
728 if let Some(max_tls_version) = config.max_tls_version {
729 versions.retain(|&supported_version| {
730 match tls::Version::from_rustls(supported_version.version) {
731 Some(version) => version <= max_tls_version,
732 None => false,
733 }
734 });
735 }
736
737 if versions.is_empty() {
738 return Err(crate::error::builder("empty supported tls versions"));
739 }
740
741 let provider = rustls::crypto::CryptoProvider::get_default()
744 .map(|arc| arc.clone())
745 .unwrap_or_else(|| {
746 #[cfg(not(feature = "__rustls-ring"))]
747 panic!("No provider set");
748
749 #[cfg(feature = "__rustls-ring")]
750 Arc::new(rustls::crypto::ring::default_provider())
751 });
752
753 let signature_algorithms = provider.signature_verification_algorithms;
755 let config_builder =
756 rustls::ClientConfig::builder_with_provider(provider.clone())
757 .with_protocol_versions(&versions)
758 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
759
760 let config_builder = if !config.certs_verification {
761 config_builder
762 .dangerous()
763 .with_custom_certificate_verifier(Arc::new(NoVerifier))
764 } else if !config.hostname_verification {
765 config_builder
766 .dangerous()
767 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
768 root_cert_store,
769 signature_algorithms,
770 )))
771 } else {
772 if config.crls.is_empty() {
773 config_builder.with_root_certificates(root_cert_store)
774 } else {
775 let crls = config
776 .crls
777 .iter()
778 .map(|e| e.as_rustls_crl())
779 .collect::<Vec<_>>();
780 let verifier =
781 rustls::client::WebPkiServerVerifier::builder_with_provider(
782 Arc::new(root_cert_store),
783 provider,
784 )
785 .with_crls(crls)
786 .build()
787 .map_err(|_| {
788 crate::error::builder("invalid TLS verification settings")
789 })?;
790 config_builder.with_webpki_verifier(verifier)
791 }
792 };
793
794 let mut tls = if let Some(id) = config.identity {
796 id.add_to_rustls(config_builder)?
797 } else {
798 config_builder.with_no_client_auth()
799 };
800
801 tls.enable_sni = config.tls_sni;
802
803 match config.http_version_pref {
805 HttpVersionPref::Http1 => {
806 tls.alpn_protocols = vec!["http/1.1".into()];
807 }
808 #[cfg(feature = "http2")]
809 HttpVersionPref::Http2 => {
810 tls.alpn_protocols = vec!["h2".into()];
811 }
812 #[cfg(feature = "http3")]
813 HttpVersionPref::Http3 => {
814 tls.alpn_protocols = vec!["h3".into()];
815 }
816 HttpVersionPref::All => {
817 tls.alpn_protocols = vec![
818 #[cfg(feature = "http2")]
819 "h2".into(),
820 "http/1.1".into(),
821 ];
822 }
823 }
824
825 #[cfg(feature = "http3")]
826 {
827 tls.enable_early_data = config.tls_enable_early_data;
828
829 h3_connector = build_h3_connector(
830 resolver.clone(),
831 tls.clone(),
832 config.quic_max_idle_timeout,
833 config.quic_stream_receive_window,
834 config.quic_receive_window,
835 config.quic_send_window,
836 config.quic_congestion_bbr,
837 config.h3_max_field_section_size,
838 config.h3_send_grease,
839 config.local_address,
840 &config.http_version_pref,
841 )?;
842 }
843
844 ConnectorBuilder::new_rustls_tls(
845 http,
846 tls,
847 proxies.clone(),
848 user_agent(&config.headers),
849 config.local_address,
850 #[cfg(any(
851 target_os = "android",
852 target_os = "fuchsia",
853 target_os = "illumos",
854 target_os = "ios",
855 target_os = "linux",
856 target_os = "macos",
857 target_os = "solaris",
858 target_os = "tvos",
859 target_os = "visionos",
860 target_os = "watchos",
861 ))]
862 config.interface.as_deref(),
863 config.nodelay,
864 config.tls_info,
865 )
866 }
867 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
868 TlsBackend::UnknownPreconfigured => {
869 return Err(crate::error::builder(
870 "Unknown TLS backend passed to `use_preconfigured_tls`",
871 ));
872 }
873 }
874
875 #[cfg(not(feature = "__tls"))]
876 ConnectorBuilder::new(
877 http,
878 proxies.clone(),
879 config.local_address,
880 #[cfg(any(
881 target_os = "android",
882 target_os = "fuchsia",
883 target_os = "illumos",
884 target_os = "ios",
885 target_os = "linux",
886 target_os = "macos",
887 target_os = "solaris",
888 target_os = "tvos",
889 target_os = "visionos",
890 target_os = "watchos",
891 ))]
892 config.interface.as_deref(),
893 config.nodelay,
894 )
895 };
896
897 connector_builder.set_timeout(config.connect_timeout);
898 connector_builder.set_verbose(config.connection_verbose);
899 connector_builder.set_keepalive(config.tcp_keepalive);
900 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
901 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
902
903 #[cfg(feature = "socks")]
904 connector_builder.set_socks_resolver(resolver);
905
906 let mut builder =
907 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
908 #[cfg(feature = "http2")]
909 {
910 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
911 builder.http2_only(true);
912 }
913
914 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
915 {
916 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
917 }
918 if let Some(http2_initial_connection_window_size) =
919 config.http2_initial_connection_window_size
920 {
921 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
922 }
923 if config.http2_adaptive_window {
924 builder.http2_adaptive_window(true);
925 }
926 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
927 builder.http2_max_frame_size(http2_max_frame_size);
928 }
929 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
930 builder.http2_max_header_list_size(http2_max_header_list_size);
931 }
932 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
933 builder.http2_keep_alive_interval(http2_keep_alive_interval);
934 }
935 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
936 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
937 }
938 if config.http2_keep_alive_while_idle {
939 builder.http2_keep_alive_while_idle(true);
940 }
941 }
942
943 builder.timer(hyper_util::rt::TokioTimer::new());
944 builder.pool_timer(hyper_util::rt::TokioTimer::new());
945 builder.pool_idle_timeout(config.pool_idle_timeout);
946 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
947
948 if config.http09_responses {
949 builder.http09_responses(true);
950 }
951
952 if config.http1_title_case_headers {
953 builder.http1_title_case_headers(true);
954 }
955
956 if config.http1_allow_obsolete_multiline_headers_in_responses {
957 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
958 }
959
960 if config.http1_ignore_invalid_headers_in_responses {
961 builder.http1_ignore_invalid_headers_in_responses(true);
962 }
963
964 if config.http1_allow_spaces_after_header_name_in_responses {
965 builder.http1_allow_spaces_after_header_name_in_responses(true);
966 }
967
968 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
969 let proxies_maybe_http_custom_headers =
970 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
971
972 let redirect_policy_desc = if config.redirect_policy.is_default() {
973 None
974 } else {
975 Some(format!("{:?}", &config.redirect_policy))
976 };
977
978 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
979 let hyper_service = HyperService {
980 #[cfg(feature = "cookies")]
981 cookie_store: config.cookie_store.clone(),
982 hyper: hyper_client,
983 };
984
985 let policy = {
986 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
987 p.with_referer(config.referer)
988 .with_https_only(config.https_only);
989 p
990 };
991
992 let hyper = FollowRedirect::with_policy(hyper_service, policy.clone());
993
994 Ok(Client {
995 inner: Arc::new(ClientRef {
996 accepts: config.accepts,
997 #[cfg(feature = "cookies")]
998 cookie_store: config.cookie_store.clone(),
999 #[cfg(feature = "http3")]
1002 h3_client: match h3_connector {
1003 Some(h3_connector) => {
1004 #[cfg(not(feature = "cookies"))]
1005 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1006 #[cfg(feature = "cookies")]
1007 let h3_service = H3Client::new(
1008 h3_connector,
1009 config.pool_idle_timeout,
1010 config.cookie_store,
1011 );
1012 Some(FollowRedirect::with_policy(h3_service, policy))
1013 }
1014 None => None,
1015 },
1016 headers: config.headers,
1017 referer: config.referer,
1018 read_timeout: config.read_timeout,
1019 request_timeout: RequestConfig::new(config.timeout),
1020 hyper,
1021 proxies,
1022 proxies_maybe_http_auth,
1023 proxies_maybe_http_custom_headers,
1024 https_only: config.https_only,
1025 redirect_policy_desc,
1026 }),
1027 })
1028 }
1029
1030 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1053 where
1054 V: TryInto<HeaderValue>,
1055 V::Error: Into<http::Error>,
1056 {
1057 match value.try_into() {
1058 Ok(value) => {
1059 self.config.headers.insert(USER_AGENT, value);
1060 }
1061 Err(e) => {
1062 self.config.error = Some(crate::error::builder(e.into()));
1063 }
1064 };
1065 self
1066 }
1067 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1091 for (key, value) in headers.iter() {
1092 self.config.headers.insert(key, value.clone());
1093 }
1094 self
1095 }
1096
1097 #[cfg(feature = "cookies")]
1112 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1113 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1114 if enable {
1115 self.cookie_provider(Arc::new(cookie::Jar::default()))
1116 } else {
1117 self.config.cookie_store = None;
1118 self
1119 }
1120 }
1121
1122 #[cfg(feature = "cookies")]
1136 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1137 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1138 mut self,
1139 cookie_store: Arc<C>,
1140 ) -> ClientBuilder {
1141 self.config.cookie_store = Some(cookie_store as _);
1142 self
1143 }
1144
1145 #[cfg(feature = "gzip")]
1162 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1163 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1164 self.config.accepts.gzip = enable;
1165 self
1166 }
1167
1168 #[cfg(feature = "brotli")]
1185 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1186 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1187 self.config.accepts.brotli = enable;
1188 self
1189 }
1190
1191 #[cfg(feature = "zstd")]
1208 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1209 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1210 self.config.accepts.zstd = enable;
1211 self
1212 }
1213
1214 #[cfg(feature = "deflate")]
1231 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1232 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1233 self.config.accepts.deflate = enable;
1234 self
1235 }
1236
1237 pub fn no_gzip(self) -> ClientBuilder {
1243 #[cfg(feature = "gzip")]
1244 {
1245 self.gzip(false)
1246 }
1247
1248 #[cfg(not(feature = "gzip"))]
1249 {
1250 self
1251 }
1252 }
1253
1254 pub fn no_brotli(self) -> ClientBuilder {
1260 #[cfg(feature = "brotli")]
1261 {
1262 self.brotli(false)
1263 }
1264
1265 #[cfg(not(feature = "brotli"))]
1266 {
1267 self
1268 }
1269 }
1270
1271 pub fn no_zstd(self) -> ClientBuilder {
1277 #[cfg(feature = "zstd")]
1278 {
1279 self.zstd(false)
1280 }
1281
1282 #[cfg(not(feature = "zstd"))]
1283 {
1284 self
1285 }
1286 }
1287
1288 pub fn no_deflate(self) -> ClientBuilder {
1294 #[cfg(feature = "deflate")]
1295 {
1296 self.deflate(false)
1297 }
1298
1299 #[cfg(not(feature = "deflate"))]
1300 {
1301 self
1302 }
1303 }
1304
1305 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1311 self.config.redirect_policy = policy;
1312 self
1313 }
1314
1315 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1319 self.config.referer = enable;
1320 self
1321 }
1322
1323 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1331 self.config.proxies.push(proxy.into_matcher());
1332 self.config.auto_sys_proxy = false;
1333 self
1334 }
1335
1336 pub fn no_proxy(mut self) -> ClientBuilder {
1344 self.config.proxies.clear();
1345 self.config.auto_sys_proxy = false;
1346 self
1347 }
1348
1349 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1358 self.config.timeout = Some(timeout);
1359 self
1360 }
1361
1362 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1370 self.config.read_timeout = Some(timeout);
1371 self
1372 }
1373
1374 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1383 self.config.connect_timeout = Some(timeout);
1384 self
1385 }
1386
1387 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1394 self.config.connection_verbose = verbose;
1395 self
1396 }
1397
1398 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1406 where
1407 D: Into<Option<Duration>>,
1408 {
1409 self.config.pool_idle_timeout = val.into();
1410 self
1411 }
1412
1413 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1415 self.config.pool_max_idle_per_host = max;
1416 self
1417 }
1418
1419 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1421 self.config.http1_title_case_headers = true;
1422 self
1423 }
1424
1425 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1431 mut self,
1432 value: bool,
1433 ) -> ClientBuilder {
1434 self.config
1435 .http1_allow_obsolete_multiline_headers_in_responses = value;
1436 self
1437 }
1438
1439 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1441 self.config.http1_ignore_invalid_headers_in_responses = value;
1442 self
1443 }
1444
1445 pub fn http1_allow_spaces_after_header_name_in_responses(
1451 mut self,
1452 value: bool,
1453 ) -> ClientBuilder {
1454 self.config
1455 .http1_allow_spaces_after_header_name_in_responses = value;
1456 self
1457 }
1458
1459 pub fn http1_only(mut self) -> ClientBuilder {
1461 self.config.http_version_pref = HttpVersionPref::Http1;
1462 self
1463 }
1464
1465 pub fn http09_responses(mut self) -> ClientBuilder {
1467 self.config.http09_responses = true;
1468 self
1469 }
1470
1471 #[cfg(feature = "http2")]
1473 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1474 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1475 self.config.http_version_pref = HttpVersionPref::Http2;
1476 self
1477 }
1478
1479 #[cfg(feature = "http3")]
1481 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1482 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1483 self.config.http_version_pref = HttpVersionPref::Http3;
1484 self
1485 }
1486
1487 #[cfg(feature = "http2")]
1491 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1492 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1493 self.config.http2_initial_stream_window_size = sz.into();
1494 self
1495 }
1496
1497 #[cfg(feature = "http2")]
1501 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1502 pub fn http2_initial_connection_window_size(
1503 mut self,
1504 sz: impl Into<Option<u32>>,
1505 ) -> ClientBuilder {
1506 self.config.http2_initial_connection_window_size = sz.into();
1507 self
1508 }
1509
1510 #[cfg(feature = "http2")]
1515 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1516 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1517 self.config.http2_adaptive_window = enabled;
1518 self
1519 }
1520
1521 #[cfg(feature = "http2")]
1525 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1526 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1527 self.config.http2_max_frame_size = sz.into();
1528 self
1529 }
1530
1531 #[cfg(feature = "http2")]
1535 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1536 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1537 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1538 self
1539 }
1540
1541 #[cfg(feature = "http2")]
1546 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1547 pub fn http2_keep_alive_interval(
1548 mut self,
1549 interval: impl Into<Option<Duration>>,
1550 ) -> ClientBuilder {
1551 self.config.http2_keep_alive_interval = interval.into();
1552 self
1553 }
1554
1555 #[cfg(feature = "http2")]
1561 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1562 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1563 self.config.http2_keep_alive_timeout = Some(timeout);
1564 self
1565 }
1566
1567 #[cfg(feature = "http2")]
1574 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1575 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1576 self.config.http2_keep_alive_while_idle = enabled;
1577 self
1578 }
1579
1580 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1586 self.config.nodelay = enabled;
1587 self
1588 }
1589
1590 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1605 where
1606 T: Into<Option<IpAddr>>,
1607 {
1608 self.config.local_address = addr.into();
1609 self
1610 }
1611
1612 #[cfg(any(
1645 target_os = "android",
1646 target_os = "fuchsia",
1647 target_os = "illumos",
1648 target_os = "ios",
1649 target_os = "linux",
1650 target_os = "macos",
1651 target_os = "solaris",
1652 target_os = "tvos",
1653 target_os = "visionos",
1654 target_os = "watchos",
1655 ))]
1656 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1657 self.config.interface = Some(interface.to_string());
1658 self
1659 }
1660
1661 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1665 where
1666 D: Into<Option<Duration>>,
1667 {
1668 self.config.tcp_keepalive = val.into();
1669 self
1670 }
1671
1672 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1676 where
1677 D: Into<Option<Duration>>,
1678 {
1679 self.config.tcp_keepalive_interval = val.into();
1680 self
1681 }
1682
1683 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1687 where
1688 C: Into<Option<u32>>,
1689 {
1690 self.config.tcp_keepalive_retries = retries.into();
1691 self
1692 }
1693
1694 #[cfg(feature = "__tls")]
1706 #[cfg_attr(
1707 docsrs,
1708 doc(cfg(any(
1709 feature = "default-tls",
1710 feature = "native-tls",
1711 feature = "rustls-tls"
1712 )))
1713 )]
1714 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1715 self.config.root_certs.push(cert);
1716 self
1717 }
1718
1719 #[cfg(feature = "__rustls")]
1726 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1727 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1728 self.config.crls.push(crl);
1729 self
1730 }
1731
1732 #[cfg(feature = "__rustls")]
1739 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1740 pub fn add_crls(
1741 mut self,
1742 crls: impl IntoIterator<Item = CertificateRevocationList>,
1743 ) -> ClientBuilder {
1744 self.config.crls.extend(crls);
1745 self
1746 }
1747
1748 #[cfg(feature = "__tls")]
1766 #[cfg_attr(
1767 docsrs,
1768 doc(cfg(any(
1769 feature = "default-tls",
1770 feature = "native-tls",
1771 feature = "rustls-tls"
1772 )))
1773 )]
1774 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1775 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1776
1777 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1778 {
1779 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1780 }
1781
1782 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1783 {
1784 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1785 }
1786
1787 self
1788 }
1789
1790 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1794 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1795 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1796 self.config.tls_built_in_certs_webpki = enabled;
1797 self
1798 }
1799
1800 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1804 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1805 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1806 self.config.tls_built_in_certs_native = enabled;
1807 self
1808 }
1809
1810 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1817 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1818 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1819 self.config.identity = Some(identity);
1820 self
1821 }
1822
1823 #[cfg(feature = "__tls")]
1839 #[cfg_attr(
1840 docsrs,
1841 doc(cfg(any(
1842 feature = "default-tls",
1843 feature = "native-tls",
1844 feature = "rustls-tls"
1845 )))
1846 )]
1847 pub fn danger_accept_invalid_hostnames(
1848 mut self,
1849 accept_invalid_hostname: bool,
1850 ) -> ClientBuilder {
1851 self.config.hostname_verification = !accept_invalid_hostname;
1852 self
1853 }
1854
1855 #[cfg(feature = "__tls")]
1872 #[cfg_attr(
1873 docsrs,
1874 doc(cfg(any(
1875 feature = "default-tls",
1876 feature = "native-tls",
1877 feature = "rustls-tls"
1878 )))
1879 )]
1880 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1881 self.config.certs_verification = !accept_invalid_certs;
1882 self
1883 }
1884
1885 #[cfg(feature = "__tls")]
1894 #[cfg_attr(
1895 docsrs,
1896 doc(cfg(any(
1897 feature = "default-tls",
1898 feature = "native-tls",
1899 feature = "rustls-tls"
1900 )))
1901 )]
1902 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1903 self.config.tls_sni = tls_sni;
1904 self
1905 }
1906
1907 #[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 min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1932 self.config.min_tls_version = Some(version);
1933 self
1934 }
1935
1936 #[cfg(feature = "__tls")]
1955 #[cfg_attr(
1956 docsrs,
1957 doc(cfg(any(
1958 feature = "default-tls",
1959 feature = "native-tls",
1960 feature = "rustls-tls"
1961 )))
1962 )]
1963 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1964 self.config.max_tls_version = Some(version);
1965 self
1966 }
1967
1968 #[cfg(feature = "native-tls")]
1977 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1978 pub fn use_native_tls(mut self) -> ClientBuilder {
1979 self.config.tls = TlsBackend::Default;
1980 self
1981 }
1982
1983 #[cfg(feature = "__rustls")]
1992 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1993 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1994 self.config.tls = TlsBackend::Rustls;
1995 self
1996 }
1997
1998 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
2017 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
2018 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
2019 let mut tls = Some(tls);
2020 #[cfg(feature = "native-tls")]
2021 {
2022 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2023 let tls = conn.take().expect("is definitely Some");
2024 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2025 self.config.tls = tls;
2026 return self;
2027 }
2028 }
2029 #[cfg(feature = "__rustls")]
2030 {
2031 if let Some(conn) =
2032 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2033 {
2034 let tls = conn.take().expect("is definitely Some");
2035 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2036 self.config.tls = tls;
2037 return self;
2038 }
2039 }
2040
2041 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2043 self
2044 }
2045
2046 #[cfg(feature = "__tls")]
2053 #[cfg_attr(
2054 docsrs,
2055 doc(cfg(any(
2056 feature = "default-tls",
2057 feature = "native-tls",
2058 feature = "rustls-tls"
2059 )))
2060 )]
2061 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2062 self.config.tls_info = tls_info;
2063 self
2064 }
2065
2066 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2070 self.config.https_only = enabled;
2071 self
2072 }
2073
2074 #[doc(hidden)]
2075 #[cfg(feature = "hickory-dns")]
2076 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2077 #[deprecated(note = "use `hickory_dns` instead")]
2078 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
2079 self.config.hickory_dns = enable;
2080 self
2081 }
2082
2083 #[cfg(feature = "hickory-dns")]
2097 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2098 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2099 self.config.hickory_dns = enable;
2100 self
2101 }
2102
2103 #[doc(hidden)]
2104 #[deprecated(note = "use `no_hickory_dns` instead")]
2105 pub fn no_trust_dns(self) -> ClientBuilder {
2106 self.no_hickory_dns()
2107 }
2108
2109 pub fn no_hickory_dns(self) -> ClientBuilder {
2115 #[cfg(feature = "hickory-dns")]
2116 {
2117 self.hickory_dns(false)
2118 }
2119
2120 #[cfg(not(feature = "hickory-dns"))]
2121 {
2122 self
2123 }
2124 }
2125
2126 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2131 self.resolve_to_addrs(domain, &[addr])
2132 }
2133
2134 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2139 self.config
2140 .dns_overrides
2141 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2142 self
2143 }
2144
2145 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
2151 self.config.dns_resolver = Some(resolver as _);
2152 self
2153 }
2154
2155 #[cfg(feature = "http3")]
2160 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2161 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2162 self.config.tls_enable_early_data = enabled;
2163 self
2164 }
2165
2166 #[cfg(feature = "http3")]
2172 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2173 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2174 self.config.quic_max_idle_timeout = Some(value);
2175 self
2176 }
2177
2178 #[cfg(feature = "http3")]
2189 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2190 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2191 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2192 self
2193 }
2194
2195 #[cfg(feature = "http3")]
2206 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2207 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2208 self.config.quic_receive_window = Some(value.try_into().unwrap());
2209 self
2210 }
2211
2212 #[cfg(feature = "http3")]
2218 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2219 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2220 self.config.quic_send_window = Some(value);
2221 self
2222 }
2223
2224 #[cfg(feature = "http3")]
2232 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2233 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2234 self.config.quic_congestion_bbr = true;
2235 self
2236 }
2237
2238 #[cfg(feature = "http3")]
2248 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2249 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2250 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2251 self
2252 }
2253
2254 #[cfg(feature = "http3")]
2266 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2267 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2268 self.config.h3_send_grease = Some(enabled);
2269 self
2270 }
2271
2272 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2296 where
2297 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2298 L::Service:
2299 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2300 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2301 {
2302 let layer = BoxCloneSyncServiceLayer::new(layer);
2303
2304 self.config.connector_layers.push(layer);
2305
2306 self
2307 }
2308}
2309
2310type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2311
2312impl Default for Client {
2313 fn default() -> Self {
2314 Self::new()
2315 }
2316}
2317
2318impl Client {
2319 pub fn new() -> Client {
2329 ClientBuilder::new().build().expect("Client::new()")
2330 }
2331
2332 pub fn builder() -> ClientBuilder {
2336 ClientBuilder::new()
2337 }
2338
2339 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2345 self.request(Method::GET, url)
2346 }
2347
2348 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2354 self.request(Method::POST, url)
2355 }
2356
2357 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2363 self.request(Method::PUT, url)
2364 }
2365
2366 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2372 self.request(Method::PATCH, url)
2373 }
2374
2375 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2381 self.request(Method::DELETE, url)
2382 }
2383
2384 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2390 self.request(Method::HEAD, url)
2391 }
2392
2393 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2402 let req = url.into_url().map(move |url| Request::new(method, url));
2403 RequestBuilder::new(self.clone(), req)
2404 }
2405
2406 pub fn execute(
2419 &self,
2420 request: Request,
2421 ) -> impl Future<Output = Result<Response, crate::Error>> {
2422 self.execute_request(request)
2423 }
2424
2425 pub(super) fn execute_request(&self, req: Request) -> Pending {
2426 let (method, url, mut headers, body, version, extensions) = req.pieces();
2427 if url.scheme() != "http" && url.scheme() != "https" {
2428 return Pending::new_err(error::url_bad_scheme(url));
2429 }
2430
2431 if self.inner.https_only && url.scheme() != "https" {
2433 return Pending::new_err(error::url_bad_scheme(url));
2434 }
2435
2436 for (key, value) in &self.inner.headers {
2439 if let Entry::Vacant(entry) = headers.entry(key) {
2440 entry.insert(value.clone());
2441 }
2442 }
2443
2444 let accept_encoding = self.inner.accepts.as_str();
2445
2446 if let Some(accept_encoding) = accept_encoding {
2447 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2448 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2449 }
2450 }
2451
2452 let uri = match try_uri(&url) {
2453 Ok(uri) => uri,
2454 _ => return Pending::new_err(error::url_invalid_uri(url)),
2455 };
2456
2457 let (reusable, body) = match body {
2458 Some(body) => {
2459 let (reusable, body) = body.try_reuse();
2460 (Some(reusable), body)
2461 }
2462 None => (None, Body::empty()),
2463 };
2464
2465 self.proxy_auth(&uri, &mut headers);
2466 self.proxy_custom_headers(&uri, &mut headers);
2467
2468 let builder = hyper::Request::builder()
2469 .method(method.clone())
2470 .uri(uri)
2471 .version(version);
2472
2473 let in_flight = match version {
2474 #[cfg(feature = "http3")]
2475 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2476 let mut req = builder.body(body).expect("valid request parts");
2477 *req.headers_mut() = headers.clone();
2478 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2479 ResponseFuture::H3(h3.call(req))
2480 }
2481 _ => {
2482 let mut req = builder.body(body).expect("valid request parts");
2483 *req.headers_mut() = headers.clone();
2484 let mut hyper = self.inner.hyper.clone();
2485 ResponseFuture::Default(hyper.call(req))
2486 }
2487 };
2488
2489 let total_timeout = self
2490 .inner
2491 .request_timeout
2492 .fetch(&extensions)
2493 .copied()
2494 .map(tokio::time::sleep)
2495 .map(Box::pin);
2496
2497 let read_timeout_fut = self
2498 .inner
2499 .read_timeout
2500 .map(tokio::time::sleep)
2501 .map(Box::pin);
2502
2503 Pending {
2504 inner: PendingInner::Request(PendingRequest {
2505 method,
2506 url,
2507 headers,
2508 body: reusable,
2509
2510 retry_count: 0,
2511
2512 client: self.inner.clone(),
2513
2514 in_flight,
2515 total_timeout,
2516 read_timeout_fut,
2517 read_timeout: self.inner.read_timeout,
2518 }),
2519 }
2520 }
2521
2522 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2523 if !self.inner.proxies_maybe_http_auth {
2524 return;
2525 }
2526
2527 if dst.scheme() != Some(&Scheme::HTTP) {
2531 return;
2532 }
2533
2534 if headers.contains_key(PROXY_AUTHORIZATION) {
2535 return;
2536 }
2537
2538 for proxy in self.inner.proxies.iter() {
2539 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2540 headers.insert(PROXY_AUTHORIZATION, header);
2541 }
2542
2543 break;
2544 }
2545 }
2546
2547 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2548 if !self.inner.proxies_maybe_http_custom_headers {
2549 return;
2550 }
2551
2552 if dst.scheme() != Some(&Scheme::HTTP) {
2553 return;
2554 }
2555
2556 for proxy in self.inner.proxies.iter() {
2557 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2558 iter.iter().for_each(|(key, value)| {
2559 headers.insert(key, value.clone());
2560 });
2561 }
2562
2563 break;
2564 }
2565 }
2566}
2567
2568impl fmt::Debug for Client {
2569 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2570 let mut builder = f.debug_struct("Client");
2571 self.inner.fmt_fields(&mut builder);
2572 builder.finish()
2573 }
2574}
2575
2576impl tower_service::Service<Request> for Client {
2577 type Response = Response;
2578 type Error = crate::Error;
2579 type Future = Pending;
2580
2581 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2582 Poll::Ready(Ok(()))
2583 }
2584
2585 fn call(&mut self, req: Request) -> Self::Future {
2586 self.execute_request(req)
2587 }
2588}
2589
2590impl tower_service::Service<Request> for &'_ Client {
2591 type Response = Response;
2592 type Error = crate::Error;
2593 type Future = Pending;
2594
2595 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2596 Poll::Ready(Ok(()))
2597 }
2598
2599 fn call(&mut self, req: Request) -> Self::Future {
2600 self.execute_request(req)
2601 }
2602}
2603
2604impl fmt::Debug for ClientBuilder {
2605 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2606 let mut builder = f.debug_struct("ClientBuilder");
2607 self.config.fmt_fields(&mut builder);
2608 builder.finish()
2609 }
2610}
2611
2612impl Config {
2613 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2614 #[cfg(feature = "cookies")]
2618 {
2619 if let Some(_) = self.cookie_store {
2620 f.field("cookie_store", &true);
2621 }
2622 }
2623
2624 f.field("accepts", &self.accepts);
2625
2626 if !self.proxies.is_empty() {
2627 f.field("proxies", &self.proxies);
2628 }
2629
2630 if !self.redirect_policy.is_default() {
2631 f.field("redirect_policy", &self.redirect_policy);
2632 }
2633
2634 if self.referer {
2635 f.field("referer", &true);
2636 }
2637
2638 f.field("default_headers", &self.headers);
2639
2640 if self.http1_title_case_headers {
2641 f.field("http1_title_case_headers", &true);
2642 }
2643
2644 if self.http1_allow_obsolete_multiline_headers_in_responses {
2645 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2646 }
2647
2648 if self.http1_ignore_invalid_headers_in_responses {
2649 f.field("http1_ignore_invalid_headers_in_responses", &true);
2650 }
2651
2652 if self.http1_allow_spaces_after_header_name_in_responses {
2653 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2654 }
2655
2656 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2657 f.field("http1_only", &true);
2658 }
2659
2660 #[cfg(feature = "http2")]
2661 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2662 f.field("http2_prior_knowledge", &true);
2663 }
2664
2665 if let Some(ref d) = self.connect_timeout {
2666 f.field("connect_timeout", d);
2667 }
2668
2669 if let Some(ref d) = self.timeout {
2670 f.field("timeout", d);
2671 }
2672
2673 if let Some(ref v) = self.local_address {
2674 f.field("local_address", v);
2675 }
2676
2677 #[cfg(any(
2678 target_os = "android",
2679 target_os = "fuchsia",
2680 target_os = "illumos",
2681 target_os = "ios",
2682 target_os = "linux",
2683 target_os = "macos",
2684 target_os = "solaris",
2685 target_os = "tvos",
2686 target_os = "visionos",
2687 target_os = "watchos",
2688 ))]
2689 if let Some(ref v) = self.interface {
2690 f.field("interface", v);
2691 }
2692
2693 if self.nodelay {
2694 f.field("tcp_nodelay", &true);
2695 }
2696
2697 #[cfg(feature = "__tls")]
2698 {
2699 if !self.hostname_verification {
2700 f.field("danger_accept_invalid_hostnames", &true);
2701 }
2702 }
2703
2704 #[cfg(feature = "__tls")]
2705 {
2706 if !self.certs_verification {
2707 f.field("danger_accept_invalid_certs", &true);
2708 }
2709
2710 if let Some(ref min_tls_version) = self.min_tls_version {
2711 f.field("min_tls_version", min_tls_version);
2712 }
2713
2714 if let Some(ref max_tls_version) = self.max_tls_version {
2715 f.field("max_tls_version", max_tls_version);
2716 }
2717
2718 f.field("tls_sni", &self.tls_sni);
2719
2720 f.field("tls_info", &self.tls_info);
2721 }
2722
2723 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2724 {
2725 f.field("tls_backend", &self.tls);
2726 }
2727
2728 if !self.dns_overrides.is_empty() {
2729 f.field("dns_overrides", &self.dns_overrides);
2730 }
2731
2732 #[cfg(feature = "http3")]
2733 {
2734 if self.tls_enable_early_data {
2735 f.field("tls_enable_early_data", &true);
2736 }
2737 }
2738 }
2739}
2740
2741struct ClientRef {
2742 accepts: Accepts,
2743 #[cfg(feature = "cookies")]
2744 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2745 headers: HeaderMap,
2746 hyper: FollowRedirect<HyperService, TowerRedirectPolicy>,
2747 #[cfg(feature = "http3")]
2748 h3_client: Option<FollowRedirect<H3Client, TowerRedirectPolicy>>,
2749 referer: bool,
2750 request_timeout: RequestConfig<RequestTimeout>,
2751 read_timeout: Option<Duration>,
2752 proxies: Arc<Vec<ProxyMatcher>>,
2753 proxies_maybe_http_auth: bool,
2754 proxies_maybe_http_custom_headers: bool,
2755 https_only: bool,
2756 redirect_policy_desc: Option<String>,
2757}
2758
2759impl ClientRef {
2760 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2761 #[cfg(feature = "cookies")]
2765 {
2766 if let Some(_) = self.cookie_store {
2767 f.field("cookie_store", &true);
2768 }
2769 }
2770
2771 f.field("accepts", &self.accepts);
2772
2773 if !self.proxies.is_empty() {
2774 f.field("proxies", &self.proxies);
2775 }
2776
2777 if let Some(s) = &self.redirect_policy_desc {
2778 f.field("redirect_policy", s);
2779 }
2780
2781 if self.referer {
2782 f.field("referer", &true);
2783 }
2784
2785 f.field("default_headers", &self.headers);
2786
2787 self.request_timeout.fmt_as_field(f);
2788
2789 if let Some(ref d) = self.read_timeout {
2790 f.field("read_timeout", d);
2791 }
2792 }
2793}
2794
2795pin_project! {
2796 pub struct Pending {
2797 #[pin]
2798 inner: PendingInner,
2799 }
2800}
2801
2802enum PendingInner {
2803 Request(PendingRequest),
2804 Error(Option<crate::Error>),
2805}
2806
2807pin_project! {
2808 struct PendingRequest {
2809 method: Method,
2810 url: Url,
2811 headers: HeaderMap,
2812 body: Option<Option<Bytes>>,
2813
2814 retry_count: usize,
2815
2816 client: Arc<ClientRef>,
2817
2818 #[pin]
2819 in_flight: ResponseFuture,
2820 #[pin]
2821 total_timeout: Option<Pin<Box<Sleep>>>,
2822 #[pin]
2823 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2824 read_timeout: Option<Duration>,
2825 }
2826}
2827
2828enum ResponseFuture {
2829 Default(tower_http::follow_redirect::ResponseFuture<HyperService, Body, TowerRedirectPolicy>),
2830 #[cfg(feature = "http3")]
2831 H3(tower_http::follow_redirect::ResponseFuture<H3Client, Body, TowerRedirectPolicy>),
2832}
2833
2834impl PendingRequest {
2835 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2836 self.project().in_flight
2837 }
2838
2839 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2840 self.project().total_timeout
2841 }
2842
2843 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2844 self.project().read_timeout_fut
2845 }
2846
2847 #[cfg(any(feature = "http2", feature = "http3"))]
2848 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2849 use log::trace;
2850
2851 if !is_retryable_error(err) {
2852 return false;
2853 }
2854
2855 trace!("can retry {err:?}");
2856
2857 let body = match self.body {
2858 Some(Some(ref body)) => Body::reusable(body.clone()),
2859 Some(None) => {
2860 log::debug!("error was retryable, but body not reusable");
2861 return false;
2862 }
2863 None => Body::empty(),
2864 };
2865
2866 if self.retry_count >= 2 {
2867 trace!("retry count too high");
2868 return false;
2869 }
2870 self.retry_count += 1;
2871
2872 let uri = try_uri(&self.url).expect("URL was already validated as URI");
2874
2875 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
2876 #[cfg(feature = "http3")]
2877 ResponseFuture::H3(_) => {
2878 let mut req = hyper::Request::builder()
2879 .method(self.method.clone())
2880 .uri(uri)
2881 .body(body)
2882 .expect("valid request parts");
2883 *req.headers_mut() = self.headers.clone();
2884 let mut h3 = self
2885 .client
2886 .h3_client
2887 .as_ref()
2888 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2889 .clone();
2890 ResponseFuture::H3(h3.call(req))
2891 }
2892 _ => {
2893 let mut req = hyper::Request::builder()
2894 .method(self.method.clone())
2895 .uri(uri)
2896 .body(body)
2897 .expect("valid request parts");
2898 *req.headers_mut() = self.headers.clone();
2899 let mut hyper = self.client.hyper.clone();
2900 ResponseFuture::Default(hyper.call(req))
2901 }
2902 };
2903
2904 true
2905 }
2906}
2907
2908#[cfg(any(feature = "http2", feature = "http3"))]
2909fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
2910 let err = if let Some(err) = err.source() {
2912 err
2913 } else {
2914 return false;
2915 };
2916
2917 #[cfg(feature = "http3")]
2918 if let Some(cause) = err.source() {
2919 if let Some(err) = cause.downcast_ref::<h3::error::ConnectionError>() {
2920 log::debug!("determining if HTTP/3 error {err} can be retried");
2921 return err.to_string().as_str() == "timeout";
2923 }
2924 }
2925
2926 #[cfg(feature = "http2")]
2927 if let Some(cause) = err.source() {
2928 if let Some(err) = cause.downcast_ref::<h2::Error>() {
2929 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
2931 return true;
2932 }
2933
2934 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
2937 {
2938 return true;
2939 }
2940 }
2941 }
2942 false
2943}
2944
2945impl Pending {
2946 pub(super) fn new_err(err: crate::Error) -> Pending {
2947 Pending {
2948 inner: PendingInner::Error(Some(err)),
2949 }
2950 }
2951
2952 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2953 self.project().inner
2954 }
2955}
2956
2957impl Future for Pending {
2958 type Output = Result<Response, crate::Error>;
2959
2960 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2961 let inner = self.inner();
2962 match inner.get_mut() {
2963 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2964 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2965 .take()
2966 .expect("Pending error polled more than once"))),
2967 }
2968 }
2969}
2970
2971impl Future for PendingRequest {
2972 type Output = Result<Response, crate::Error>;
2973
2974 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2975 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
2976 if let Poll::Ready(()) = delay.poll(cx) {
2977 return Poll::Ready(Err(
2978 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2979 ));
2980 }
2981 }
2982
2983 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
2984 if let Poll::Ready(()) = delay.poll(cx) {
2985 return Poll::Ready(Err(
2986 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2987 ));
2988 }
2989 }
2990
2991 loop {
2992 let res = match self.as_mut().in_flight().get_mut() {
2993 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
2994 Poll::Ready(Err(e)) => {
2995 #[cfg(feature = "http2")]
2996 if e.is_request() {
2997 if let Some(e) = e.source() {
2998 if self.as_mut().retry_error(e) {
2999 continue;
3000 }
3001 }
3002 }
3003
3004 return Poll::Ready(Err(e));
3005 }
3006 Poll::Ready(Ok(res)) => res.map(super::body::boxed),
3007 Poll::Pending => return Poll::Pending,
3008 },
3009 #[cfg(feature = "http3")]
3010 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
3011 Poll::Ready(Err(e)) => {
3012 if self.as_mut().retry_error(&e) {
3013 continue;
3014 }
3015 return Poll::Ready(Err(
3016 crate::error::request(e).with_url(self.url.clone())
3017 ));
3018 }
3019 Poll::Ready(Ok(res)) => res,
3020 Poll::Pending => return Poll::Pending,
3021 },
3022 };
3023
3024 #[cfg(feature = "cookies")]
3025 {
3026 if let Some(ref cookie_store) = self.client.cookie_store {
3027 let mut cookies =
3028 cookie::extract_response_cookie_headers(res.headers()).peekable();
3029 if cookies.peek().is_some() {
3030 cookie_store.set_cookies(&mut cookies, &self.url);
3031 }
3032 }
3033 }
3034 if let Some(url) = &res
3035 .extensions()
3036 .get::<tower_http::follow_redirect::RequestUri>()
3037 {
3038 self.url = match Url::parse(&url.0.to_string()) {
3039 Ok(url) => url,
3040 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3041 }
3042 };
3043
3044 let res = Response::new(
3045 res,
3046 self.url.clone(),
3047 self.client.accepts,
3048 self.total_timeout.take(),
3049 self.read_timeout,
3050 );
3051 return Poll::Ready(Ok(res));
3052 }
3053 }
3054}
3055
3056impl fmt::Debug for Pending {
3057 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3058 match self.inner {
3059 PendingInner::Request(ref req) => f
3060 .debug_struct("Pending")
3061 .field("method", &req.method)
3062 .field("url", &req.url)
3063 .finish(),
3064 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3065 }
3066 }
3067}
3068
3069#[cfg(test)]
3070mod tests {
3071 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
3072
3073 #[tokio::test]
3074 async fn execute_request_rejects_invalid_urls() {
3075 let url_str = "hxxps://www.rust-lang.org/";
3076 let url = url::Url::parse(url_str).unwrap();
3077 let result = crate::get(url.clone()).await;
3078
3079 assert!(result.is_err());
3080 let err = result.err().unwrap();
3081 assert!(err.is_builder());
3082 assert_eq!(url_str, err.url().unwrap().as_str());
3083 }
3084
3085 #[tokio::test]
3087 async fn execute_request_rejects_invalid_hostname() {
3088 let url_str = "https://{{hostname}}/";
3089 let url = url::Url::parse(url_str).unwrap();
3090 let result = crate::get(url.clone()).await;
3091
3092 assert!(result.is_err());
3093 let err = result.err().unwrap();
3094 assert!(err.is_builder());
3095 assert_eq!(url_str, err.url().unwrap().as_str());
3096 }
3097}