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 = "tor")]
19use crate::arti::ArtiHttpConnector;
20
21#[cfg(feature = "http3")]
22use crate::async_impl::h3_client::connect::{H3ClientConfig, H3Connector};
23#[cfg(feature = "http3")]
24use crate::async_impl::h3_client::H3Client;
25use crate::config::{RequestConfig, RequestTimeout};
26use crate::connect::{
27 sealed::{Conn, Unnameable},
28 BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
29};
30#[cfg(feature = "cookies")]
31use crate::cookie;
32#[cfg(feature = "hickory-dns")]
33use crate::dns::hickory::HickoryDnsResolver;
34use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
35use crate::error::{self, BoxError};
36use crate::into_url::try_uri;
37use crate::proxy::Matcher as ProxyMatcher;
38use crate::redirect::{self, TowerRedirectPolicy};
39#[cfg(feature = "__rustls")]
40use crate::tls::CertificateRevocationList;
41#[cfg(feature = "__tls")]
42use crate::tls::{self, TlsBackend};
43#[cfg(feature = "__tls")]
44use crate::Certificate;
45#[cfg(any(feature = "native-tls", feature = "__rustls"))]
46use crate::Identity;
47use crate::{IntoUrl, Method, Proxy, Url};
48
49#[cfg(feature = "tor")]
50use arti_client::TorClient;
51use bytes::Bytes;
52use http::header::{
53 Entry, HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, PROXY_AUTHORIZATION, RANGE, USER_AGENT,
54};
55use http::uri::Scheme;
56use http::Uri;
57use hyper_util::client::legacy::connect::HttpConnector;
58#[cfg(feature = "default-tls")]
59use native_tls_crate::TlsConnector;
60use pin_project_lite::pin_project;
61#[cfg(feature = "http3")]
62use quinn::TransportConfig;
63#[cfg(feature = "http3")]
64use quinn::VarInt;
65use tokio::time::Sleep;
66#[cfg(feature = "tor")]
67use tor_rtcompat::PreferredRuntime;
68use tower::util::BoxCloneSyncServiceLayer;
69use tower::{Layer, Service};
70use tower_http::follow_redirect::FollowRedirect;
71
72#[derive(Clone)]
86pub struct Client {
87 inner: Arc<ClientRef>,
88}
89
90#[must_use]
92pub struct ClientBuilder {
93 config: Config,
94 #[cfg(feature = "tor")]
95 tor: Option<TorClient<PreferredRuntime>>,
96}
97
98enum HttpVersionPref {
99 Http1,
100 #[cfg(feature = "http2")]
101 Http2,
102 #[cfg(feature = "http3")]
103 Http3,
104 All,
105}
106
107#[derive(Clone)]
108struct HyperService {
109 #[cfg(feature = "cookies")]
110 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
111 hyper: Box<dyn HyperClient>,
112}
113
114pub struct NonTorHyperClient(hyper_util::client::legacy::Client<Connector, Body>);
115
116impl HyperClient for NonTorHyperClient {
117 fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll<Result<(), crate::Error>> {
118 self.0.poll_ready(cx).map_err(error::request)
119 }
120
121 fn call(
122 &mut self,
123 req: hyper::Request<crate::async_impl::body::Body>,
124 ) -> Pin<
125 Box<
126 dyn Future<Output = Result<http::Response<hyper::body::Incoming>, crate::Error>>
127 + Send
128 + Sync,
129 >,
130 > {
131 let fut = self.0.call(req);
132 let fut = async move { fut.await.map_err(crate::error::request) };
133 Box::pin(fut)
134 }
135
136 fn clone_box(&self) -> Box<dyn HyperClient> {
137 Box::new(Self(self.0.clone()))
138 }
139}
140
141impl Clone for Box<dyn HyperClient> {
142 fn clone(&self) -> Box<dyn HyperClient> {
143 self.clone_box()
144 }
145}
146
147pub(crate) trait HyperClient: Send + Sync {
148 fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll<Result<(), crate::Error>>;
149 fn call(
150 &mut self,
151 req: hyper::Request<crate::async_impl::body::Body>,
152 ) -> Pin<
153 Box<
154 dyn Future<Output = Result<http::Response<hyper::body::Incoming>, crate::Error>>
155 + Send
156 + Sync,
157 >,
158 >;
159 fn clone_box(&self) -> Box<dyn HyperClient>;
160}
161
162impl Service<hyper::Request<crate::async_impl::body::Body>> for HyperService {
163 type Error = crate::Error;
164 type Response = http::Response<hyper::body::Incoming>;
165 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + Sync>>;
166
167 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
168 self.hyper.poll_ready(cx)
169 }
170
171 #[cfg(not(feature = "cookies"))]
172 fn call(&mut self, req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
173 let clone = self.hyper.clone();
174 let mut inner = std::mem::replace(&mut self.hyper, clone);
175 Box::pin(async move { inner.call(req).await })
176 }
177
178 #[cfg(feature = "cookies")]
179 fn call(&mut self, mut req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
180 let clone = self.hyper.clone();
181 let mut inner = std::mem::replace(&mut self.hyper, clone);
182 let url = Url::parse(req.uri().to_string().as_str()).expect("invalid URL");
183
184 if let Some(cookie_store) = self.cookie_store.as_ref() {
185 if req.headers().get(crate::header::COOKIE).is_none() {
186 let headers = req.headers_mut();
187 crate::util::add_cookie_header(headers, &**cookie_store, &url);
188 }
189 }
190
191 let cookie_store = self.cookie_store.clone();
192 Box::pin(async move {
193 let res = inner.call(req).await.map_err(crate::error::request);
194
195 if let Some(ref cookie_store) = cookie_store {
196 if let Ok(res) = &res {
197 let mut cookies =
198 cookie::extract_response_cookie_headers(res.headers()).peekable();
199 if cookies.peek().is_some() {
200 cookie_store.set_cookies(&mut cookies, &url);
201 }
202 }
203 }
204
205 res
206 })
207 }
208}
209
210struct Config {
211 accepts: Accepts,
213 headers: HeaderMap,
214 #[cfg(feature = "__tls")]
215 hostname_verification: bool,
216 #[cfg(feature = "__tls")]
217 certs_verification: bool,
218 #[cfg(feature = "__tls")]
219 tls_sni: bool,
220 connect_timeout: Option<Duration>,
221 connection_verbose: bool,
222 pool_idle_timeout: Option<Duration>,
223 pool_max_idle_per_host: usize,
224 tcp_keepalive: Option<Duration>,
225 tcp_keepalive_interval: Option<Duration>,
226 tcp_keepalive_retries: Option<u32>,
227 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
228 tcp_user_timeout: Option<Duration>,
229 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
230 identity: Option<Identity>,
231 proxies: Vec<ProxyMatcher>,
232 auto_sys_proxy: bool,
233 redirect_policy: redirect::Policy,
234 referer: bool,
235 read_timeout: Option<Duration>,
236 timeout: Option<Duration>,
237 #[cfg(feature = "__tls")]
238 root_certs: Vec<Certificate>,
239 #[cfg(feature = "__tls")]
240 tls_built_in_root_certs: bool,
241 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
242 tls_built_in_certs_webpki: bool,
243 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
244 tls_built_in_certs_native: bool,
245 #[cfg(feature = "__rustls")]
246 crls: Vec<CertificateRevocationList>,
247 #[cfg(feature = "__tls")]
248 min_tls_version: Option<tls::Version>,
249 #[cfg(feature = "__tls")]
250 max_tls_version: Option<tls::Version>,
251 #[cfg(feature = "__tls")]
252 tls_info: bool,
253 #[cfg(feature = "__tls")]
254 tls: TlsBackend,
255 connector_layers: Vec<BoxedConnectorLayer>,
256 http_version_pref: HttpVersionPref,
257 http09_responses: bool,
258 http1_title_case_headers: bool,
259 http1_allow_obsolete_multiline_headers_in_responses: bool,
260 http1_ignore_invalid_headers_in_responses: bool,
261 http1_allow_spaces_after_header_name_in_responses: bool,
262 #[cfg(feature = "http2")]
263 http2_initial_stream_window_size: Option<u32>,
264 #[cfg(feature = "http2")]
265 http2_initial_connection_window_size: Option<u32>,
266 #[cfg(feature = "http2")]
267 http2_adaptive_window: bool,
268 #[cfg(feature = "http2")]
269 http2_max_frame_size: Option<u32>,
270 #[cfg(feature = "http2")]
271 http2_max_header_list_size: Option<u32>,
272 #[cfg(feature = "http2")]
273 http2_keep_alive_interval: Option<Duration>,
274 #[cfg(feature = "http2")]
275 http2_keep_alive_timeout: Option<Duration>,
276 #[cfg(feature = "http2")]
277 http2_keep_alive_while_idle: bool,
278 local_address: Option<IpAddr>,
279 #[cfg(any(
280 target_os = "android",
281 target_os = "fuchsia",
282 target_os = "illumos",
283 target_os = "ios",
284 target_os = "linux",
285 target_os = "macos",
286 target_os = "solaris",
287 target_os = "tvos",
288 target_os = "visionos",
289 target_os = "watchos",
290 ))]
291 interface: Option<String>,
292 nodelay: bool,
293 #[cfg(feature = "cookies")]
294 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
295 hickory_dns: bool,
296 error: Option<crate::Error>,
297 https_only: bool,
298 #[cfg(feature = "http3")]
299 tls_enable_early_data: bool,
300 #[cfg(feature = "http3")]
301 quic_max_idle_timeout: Option<Duration>,
302 #[cfg(feature = "http3")]
303 quic_stream_receive_window: Option<VarInt>,
304 #[cfg(feature = "http3")]
305 quic_receive_window: Option<VarInt>,
306 #[cfg(feature = "http3")]
307 quic_send_window: Option<u64>,
308 #[cfg(feature = "http3")]
309 quic_congestion_bbr: bool,
310 #[cfg(feature = "http3")]
311 h3_max_field_section_size: Option<u64>,
312 #[cfg(feature = "http3")]
313 h3_send_grease: Option<bool>,
314 dns_overrides: HashMap<String, Vec<SocketAddr>>,
315 dns_resolver: Option<Arc<dyn Resolve>>,
316}
317
318impl Default for ClientBuilder {
319 fn default() -> Self {
320 Self::new()
321 }
322}
323
324impl ClientBuilder {
325 pub fn new() -> Self {
329 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
330 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
331
332 ClientBuilder {
333 #[cfg(feature = "tor")]
334 tor: None,
335 config: Config {
336 error: None,
337 accepts: Accepts::default(),
338 headers,
339 #[cfg(feature = "__tls")]
340 hostname_verification: true,
341 #[cfg(feature = "__tls")]
342 certs_verification: true,
343 #[cfg(feature = "__tls")]
344 tls_sni: true,
345 connect_timeout: None,
346 connection_verbose: false,
347 pool_idle_timeout: Some(Duration::from_secs(90)),
348 pool_max_idle_per_host: usize::MAX,
349 tcp_keepalive: None, tcp_keepalive_interval: None,
353 tcp_keepalive_retries: None,
354 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
355 tcp_user_timeout: None,
356 proxies: Vec::new(),
357 auto_sys_proxy: true,
358 redirect_policy: redirect::Policy::default(),
359 referer: true,
360 read_timeout: None,
361 timeout: None,
362 #[cfg(feature = "__tls")]
363 root_certs: Vec::new(),
364 #[cfg(feature = "__tls")]
365 tls_built_in_root_certs: true,
366 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
367 tls_built_in_certs_webpki: true,
368 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
369 tls_built_in_certs_native: true,
370 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
371 identity: None,
372 #[cfg(feature = "__rustls")]
373 crls: vec![],
374 #[cfg(feature = "__tls")]
375 min_tls_version: None,
376 #[cfg(feature = "__tls")]
377 max_tls_version: None,
378 #[cfg(feature = "__tls")]
379 tls_info: false,
380 #[cfg(feature = "__tls")]
381 tls: TlsBackend::default(),
382 connector_layers: Vec::new(),
383 http_version_pref: HttpVersionPref::All,
384 http09_responses: false,
385 http1_title_case_headers: false,
386 http1_allow_obsolete_multiline_headers_in_responses: false,
387 http1_ignore_invalid_headers_in_responses: false,
388 http1_allow_spaces_after_header_name_in_responses: false,
389 #[cfg(feature = "http2")]
390 http2_initial_stream_window_size: None,
391 #[cfg(feature = "http2")]
392 http2_initial_connection_window_size: None,
393 #[cfg(feature = "http2")]
394 http2_adaptive_window: false,
395 #[cfg(feature = "http2")]
396 http2_max_frame_size: None,
397 #[cfg(feature = "http2")]
398 http2_max_header_list_size: None,
399 #[cfg(feature = "http2")]
400 http2_keep_alive_interval: None,
401 #[cfg(feature = "http2")]
402 http2_keep_alive_timeout: None,
403 #[cfg(feature = "http2")]
404 http2_keep_alive_while_idle: false,
405 local_address: None,
406 #[cfg(any(
407 target_os = "android",
408 target_os = "fuchsia",
409 target_os = "illumos",
410 target_os = "ios",
411 target_os = "linux",
412 target_os = "macos",
413 target_os = "solaris",
414 target_os = "tvos",
415 target_os = "visionos",
416 target_os = "watchos",
417 ))]
418 interface: None,
419 nodelay: true,
420 hickory_dns: cfg!(feature = "hickory-dns"),
421 #[cfg(feature = "cookies")]
422 cookie_store: None,
423 https_only: false,
424 dns_overrides: HashMap::new(),
425 #[cfg(feature = "http3")]
426 tls_enable_early_data: false,
427 #[cfg(feature = "http3")]
428 quic_max_idle_timeout: None,
429 #[cfg(feature = "http3")]
430 quic_stream_receive_window: None,
431 #[cfg(feature = "http3")]
432 quic_receive_window: None,
433 #[cfg(feature = "http3")]
434 quic_send_window: None,
435 #[cfg(feature = "http3")]
436 quic_congestion_bbr: false,
437 #[cfg(feature = "http3")]
438 h3_max_field_section_size: None,
439 #[cfg(feature = "http3")]
440 h3_send_grease: None,
441 dns_resolver: None,
442 },
443 }
444 }
445
446 #[cfg(feature = "tor")]
447 pub async fn tor(config: arti_client::TorClientConfig) -> Result<Self, arti_client::Error> {
450 Ok(Self::new().set_tor(TorClient::create_bootstrapped(config).await?))
451 }
452}
453
454impl ClientBuilder {
455 pub fn build(self) -> crate::Result<Client> {
462 let config = self.config;
463
464 if let Some(err) = config.error {
465 return Err(err);
466 }
467
468 let mut proxies = config.proxies;
469 if config.auto_sys_proxy {
470 proxies.push(ProxyMatcher::system());
471 }
472 let proxies = Arc::new(proxies);
473
474 #[allow(unused)]
475 #[cfg(feature = "http3")]
476 let mut h3_connector = None;
477
478 let resolver = {
479 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
480 false => Arc::new(GaiResolver::new()),
481 #[cfg(feature = "hickory-dns")]
482 true => Arc::new(HickoryDnsResolver::default()),
483 #[cfg(not(feature = "hickory-dns"))]
484 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
485 };
486 if let Some(dns_resolver) = config.dns_resolver {
487 resolver = dns_resolver;
488 }
489 if !config.dns_overrides.is_empty() {
490 resolver = Arc::new(DnsResolverWithOverrides::new(
491 resolver,
492 config.dns_overrides,
493 ));
494 }
495 DynResolver::new(resolver)
496 };
497
498 let mut connector_builder = {
499 #[cfg(feature = "__tls")]
500 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
501 headers.get(USER_AGENT).cloned()
502 }
503
504 let mut http = HttpConnector::new_with_resolver(resolver.clone());
505 http.set_connect_timeout(config.connect_timeout);
506
507 #[cfg(all(feature = "http3", feature = "__rustls"))]
508 let build_h3_connector =
509 |resolver,
510 tls,
511 quic_max_idle_timeout: Option<Duration>,
512 quic_stream_receive_window,
513 quic_receive_window,
514 quic_send_window,
515 quic_congestion_bbr,
516 h3_max_field_section_size,
517 h3_send_grease,
518 local_address,
519 http_version_pref: &HttpVersionPref| {
520 let mut transport_config = TransportConfig::default();
521
522 if let Some(max_idle_timeout) = quic_max_idle_timeout {
523 transport_config.max_idle_timeout(Some(
524 max_idle_timeout.try_into().map_err(error::builder)?,
525 ));
526 }
527
528 if let Some(stream_receive_window) = quic_stream_receive_window {
529 transport_config.stream_receive_window(stream_receive_window);
530 }
531
532 if let Some(receive_window) = quic_receive_window {
533 transport_config.receive_window(receive_window);
534 }
535
536 if let Some(send_window) = quic_send_window {
537 transport_config.send_window(send_window);
538 }
539
540 if quic_congestion_bbr {
541 let factory = Arc::new(quinn::congestion::BbrConfig::default());
542 transport_config.congestion_controller_factory(factory);
543 }
544
545 let mut h3_client_config = H3ClientConfig::default();
546
547 if let Some(max_field_section_size) = h3_max_field_section_size {
548 h3_client_config.max_field_section_size = Some(max_field_section_size);
549 }
550
551 if let Some(send_grease) = h3_send_grease {
552 h3_client_config.send_grease = Some(send_grease);
553 }
554
555 let res = H3Connector::new(
556 resolver,
557 tls,
558 local_address,
559 transport_config,
560 h3_client_config,
561 );
562
563 match res {
564 Ok(connector) => Ok(Some(connector)),
565 Err(err) => {
566 if let HttpVersionPref::Http3 = http_version_pref {
567 Err(error::builder(err))
568 } else {
569 Ok(None)
570 }
571 }
572 }
573 };
574
575 #[cfg(feature = "__tls")]
576 match config.tls {
577 #[cfg(feature = "default-tls")]
578 TlsBackend::Default => {
579 let mut tls = TlsConnector::builder();
580
581 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
582 {
583 match config.http_version_pref {
584 HttpVersionPref::Http1 => {
585 tls.request_alpns(&["http/1.1"]);
586 }
587 #[cfg(feature = "http2")]
588 HttpVersionPref::Http2 => {
589 tls.request_alpns(&["h2"]);
590 }
591 HttpVersionPref::All => {
592 tls.request_alpns(&["h2", "http/1.1"]);
593 }
594 }
595 }
596
597 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
598
599 tls.danger_accept_invalid_certs(!config.certs_verification);
600
601 tls.use_sni(config.tls_sni);
602
603 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
604
605 for cert in config.root_certs {
606 cert.add_to_native_tls(&mut tls);
607 }
608
609 #[cfg(feature = "native-tls")]
610 {
611 if let Some(id) = config.identity {
612 id.add_to_native_tls(&mut tls)?;
613 }
614 }
615 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
616 {
617 if let Some(_id) = config.identity {
619 return Err(crate::error::builder("incompatible TLS identity type"));
620 }
621 }
622
623 if let Some(min_tls_version) = config.min_tls_version {
624 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
625 crate::error::builder("invalid minimum TLS version for backend")
629 })?;
630 tls.min_protocol_version(Some(protocol));
631 }
632
633 if let Some(max_tls_version) = config.max_tls_version {
634 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
635 crate::error::builder("invalid maximum TLS version for backend")
640 })?;
641 tls.max_protocol_version(Some(protocol));
642 }
643
644 ConnectorBuilder::new_default_tls(
645 http,
646 tls,
647 proxies.clone(),
648 user_agent(&config.headers),
649 config.local_address,
650 #[cfg(any(
651 target_os = "android",
652 target_os = "fuchsia",
653 target_os = "illumos",
654 target_os = "ios",
655 target_os = "linux",
656 target_os = "macos",
657 target_os = "solaris",
658 target_os = "tvos",
659 target_os = "visionos",
660 target_os = "watchos",
661 ))]
662 config.interface.as_deref(),
663 config.nodelay,
664 config.tls_info,
665 )?
666 }
667 #[cfg(feature = "native-tls")]
668 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_default_tls(
669 http,
670 conn,
671 proxies.clone(),
672 user_agent(&config.headers),
673 config.local_address,
674 #[cfg(any(
675 target_os = "android",
676 target_os = "fuchsia",
677 target_os = "illumos",
678 target_os = "ios",
679 target_os = "linux",
680 target_os = "macos",
681 target_os = "solaris",
682 target_os = "tvos",
683 target_os = "visionos",
684 target_os = "watchos",
685 ))]
686 config.interface.as_deref(),
687 config.nodelay,
688 config.tls_info,
689 ),
690 #[cfg(feature = "__rustls")]
691 TlsBackend::BuiltRustls(conn) => {
692 #[cfg(feature = "http3")]
693 {
694 h3_connector = build_h3_connector(
695 resolver.clone(),
696 conn.clone(),
697 config.quic_max_idle_timeout,
698 config.quic_stream_receive_window,
699 config.quic_receive_window,
700 config.quic_send_window,
701 config.quic_congestion_bbr,
702 config.h3_max_field_section_size,
703 config.h3_send_grease,
704 config.local_address,
705 &config.http_version_pref,
706 )?;
707 }
708
709 ConnectorBuilder::new_rustls_tls(
710 http,
711 conn,
712 proxies.clone(),
713 user_agent(&config.headers),
714 config.local_address,
715 #[cfg(any(
716 target_os = "android",
717 target_os = "fuchsia",
718 target_os = "illumos",
719 target_os = "ios",
720 target_os = "linux",
721 target_os = "macos",
722 target_os = "solaris",
723 target_os = "tvos",
724 target_os = "visionos",
725 target_os = "watchos",
726 ))]
727 config.interface.as_deref(),
728 config.nodelay,
729 config.tls_info,
730 )
731 }
732 #[cfg(feature = "__rustls")]
733 TlsBackend::Rustls => {
734 use crate::tls::{IgnoreHostname, NoVerifier};
735
736 let mut root_cert_store = rustls::RootCertStore::empty();
738 for cert in config.root_certs {
739 cert.add_to_rustls(&mut root_cert_store)?;
740 }
741
742 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
743 if config.tls_built_in_certs_webpki {
744 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
745 }
746
747 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
748 if config.tls_built_in_certs_native {
749 let mut valid_count = 0;
750 let mut invalid_count = 0;
751
752 let load_results = rustls_native_certs::load_native_certs();
753 for cert in load_results.certs {
754 match root_cert_store.add(cert.into()) {
758 Ok(_) => valid_count += 1,
759 Err(err) => {
760 invalid_count += 1;
761 log::debug!("rustls failed to parse DER certificate: {err:?}");
762 }
763 }
764 }
765 if valid_count == 0 && invalid_count > 0 {
766 let err = if load_results.errors.is_empty() {
767 crate::error::builder(
768 "zero valid certificates found in native root store",
769 )
770 } else {
771 use std::fmt::Write as _;
772 let mut acc = String::new();
773 for err in load_results.errors {
774 let _ = writeln!(&mut acc, "{err}");
775 }
776
777 crate::error::builder(acc)
778 };
779
780 return Err(err);
781 }
782 }
783
784 let mut versions = rustls::ALL_VERSIONS.to_vec();
786
787 if let Some(min_tls_version) = config.min_tls_version {
788 versions.retain(|&supported_version| {
789 match tls::Version::from_rustls(supported_version.version) {
790 Some(version) => version >= min_tls_version,
791 None => true,
794 }
795 });
796 }
797
798 if let Some(max_tls_version) = config.max_tls_version {
799 versions.retain(|&supported_version| {
800 match tls::Version::from_rustls(supported_version.version) {
801 Some(version) => version <= max_tls_version,
802 None => false,
803 }
804 });
805 }
806
807 if versions.is_empty() {
808 return Err(crate::error::builder("empty supported tls versions"));
809 }
810
811 let provider = rustls::crypto::CryptoProvider::get_default()
814 .map(|arc| arc.clone())
815 .unwrap_or_else(|| {
816 #[cfg(not(feature = "__rustls-ring"))]
817 panic!("No provider set");
818
819 #[cfg(feature = "__rustls-ring")]
820 Arc::new(rustls::crypto::ring::default_provider())
821 });
822
823 let signature_algorithms = provider.signature_verification_algorithms;
825 let config_builder =
826 rustls::ClientConfig::builder_with_provider(provider.clone())
827 .with_protocol_versions(&versions)
828 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
829
830 let config_builder = if !config.certs_verification {
831 config_builder
832 .dangerous()
833 .with_custom_certificate_verifier(Arc::new(NoVerifier))
834 } else if !config.hostname_verification {
835 config_builder
836 .dangerous()
837 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
838 root_cert_store,
839 signature_algorithms,
840 )))
841 } else {
842 if config.crls.is_empty() {
843 config_builder.with_root_certificates(root_cert_store)
844 } else {
845 let crls = config
846 .crls
847 .iter()
848 .map(|e| e.as_rustls_crl())
849 .collect::<Vec<_>>();
850 let verifier =
851 rustls::client::WebPkiServerVerifier::builder_with_provider(
852 Arc::new(root_cert_store),
853 provider,
854 )
855 .with_crls(crls)
856 .build()
857 .map_err(|_| {
858 crate::error::builder("invalid TLS verification settings")
859 })?;
860 config_builder.with_webpki_verifier(verifier)
861 }
862 };
863
864 let mut tls = if let Some(id) = config.identity {
866 id.add_to_rustls(config_builder)?
867 } else {
868 config_builder.with_no_client_auth()
869 };
870
871 tls.enable_sni = config.tls_sni;
872
873 match config.http_version_pref {
875 HttpVersionPref::Http1 => {
876 tls.alpn_protocols = vec!["http/1.1".into()];
877 }
878 #[cfg(feature = "http2")]
879 HttpVersionPref::Http2 => {
880 tls.alpn_protocols = vec!["h2".into()];
881 }
882 #[cfg(feature = "http3")]
883 HttpVersionPref::Http3 => {
884 tls.alpn_protocols = vec!["h3".into()];
885 }
886 HttpVersionPref::All => {
887 tls.alpn_protocols = vec![
888 #[cfg(feature = "http2")]
889 "h2".into(),
890 "http/1.1".into(),
891 ];
892 }
893 }
894
895 #[cfg(feature = "http3")]
896 {
897 tls.enable_early_data = config.tls_enable_early_data;
898
899 h3_connector = build_h3_connector(
900 resolver.clone(),
901 tls.clone(),
902 config.quic_max_idle_timeout,
903 config.quic_stream_receive_window,
904 config.quic_receive_window,
905 config.quic_send_window,
906 config.quic_congestion_bbr,
907 config.h3_max_field_section_size,
908 config.h3_send_grease,
909 config.local_address,
910 &config.http_version_pref,
911 )?;
912 }
913
914 ConnectorBuilder::new_rustls_tls(
915 http,
916 tls,
917 proxies.clone(),
918 user_agent(&config.headers),
919 config.local_address,
920 #[cfg(any(
921 target_os = "android",
922 target_os = "fuchsia",
923 target_os = "illumos",
924 target_os = "ios",
925 target_os = "linux",
926 target_os = "macos",
927 target_os = "solaris",
928 target_os = "tvos",
929 target_os = "visionos",
930 target_os = "watchos",
931 ))]
932 config.interface.as_deref(),
933 config.nodelay,
934 config.tls_info,
935 )
936 }
937 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
938 TlsBackend::UnknownPreconfigured => {
939 return Err(crate::error::builder(
940 "Unknown TLS backend passed to `use_preconfigured_tls`",
941 ));
942 }
943 }
944
945 #[cfg(not(feature = "__tls"))]
946 ConnectorBuilder::new(
947 http,
948 proxies.clone(),
949 config.local_address,
950 #[cfg(any(
951 target_os = "android",
952 target_os = "fuchsia",
953 target_os = "illumos",
954 target_os = "ios",
955 target_os = "linux",
956 target_os = "macos",
957 target_os = "solaris",
958 target_os = "tvos",
959 target_os = "visionos",
960 target_os = "watchos",
961 ))]
962 config.interface.as_deref(),
963 config.nodelay,
964 )
965 };
966
967 connector_builder.set_timeout(config.connect_timeout);
968 connector_builder.set_verbose(config.connection_verbose);
969 connector_builder.set_keepalive(config.tcp_keepalive);
970 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
971 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
972 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
973 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
974
975 #[cfg(feature = "socks")]
976 connector_builder.set_socks_resolver(resolver);
977
978 let mut builder =
979 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
980 #[cfg(feature = "http2")]
981 {
982 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
983 builder.http2_only(true);
984 }
985
986 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
987 {
988 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
989 }
990 if let Some(http2_initial_connection_window_size) =
991 config.http2_initial_connection_window_size
992 {
993 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
994 }
995 if config.http2_adaptive_window {
996 builder.http2_adaptive_window(true);
997 }
998 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
999 builder.http2_max_frame_size(http2_max_frame_size);
1000 }
1001 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
1002 builder.http2_max_header_list_size(http2_max_header_list_size);
1003 }
1004 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
1005 builder.http2_keep_alive_interval(http2_keep_alive_interval);
1006 }
1007 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
1008 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
1009 }
1010 if config.http2_keep_alive_while_idle {
1011 builder.http2_keep_alive_while_idle(true);
1012 }
1013 }
1014
1015 builder.timer(hyper_util::rt::TokioTimer::new());
1016 builder.pool_timer(hyper_util::rt::TokioTimer::new());
1017 builder.pool_idle_timeout(config.pool_idle_timeout);
1018 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
1019
1020 if config.http09_responses {
1021 builder.http09_responses(true);
1022 }
1023
1024 if config.http1_title_case_headers {
1025 builder.http1_title_case_headers(true);
1026 }
1027
1028 if config.http1_allow_obsolete_multiline_headers_in_responses {
1029 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
1030 }
1031
1032 if config.http1_ignore_invalid_headers_in_responses {
1033 builder.http1_ignore_invalid_headers_in_responses(true);
1034 }
1035
1036 if config.http1_allow_spaces_after_header_name_in_responses {
1037 builder.http1_allow_spaces_after_header_name_in_responses(true);
1038 }
1039
1040 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
1041 let proxies_maybe_http_custom_headers =
1042 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
1043
1044 let redirect_policy_desc = if config.redirect_policy.is_default() {
1045 None
1046 } else {
1047 Some(format!("{:?}", &config.redirect_policy))
1048 };
1049
1050 #[cfg(feature = "tor")]
1051 let hyper_client = match self.tor {
1052 Some(tor) => {
1053 let v = builder.build(ArtiHttpConnector::new(
1054 tor,
1055 match connector_builder.inner {
1056 #[cfg(not(feature = "__tls"))]
1057 crate::connect::Inner::Http(_) => crate::arti::Tls::Http,
1058 #[cfg(feature = "__rustls")]
1059 crate::connect::Inner::RustlsTls { tls, .. } => {
1060 crate::arti::Tls::RustlsTls { tls }
1061 }
1062 #[cfg(feature = "default-tls")]
1063 crate::connect::Inner::DefaultTls(_, tls_connector) => {
1064 crate::arti::Tls::DefaultTls(tls_connector)
1065 }
1066 },
1067 ));
1068
1069 Box::new(crate::arti::TorHyperClient(v)) as Box<dyn HyperClient>
1070 }
1071 None => {
1072 let v: hyper_util::client::legacy::Client<Connector, Body> =
1073 builder.build(connector_builder.build(config.connector_layers));
1074 Box::new(NonTorHyperClient(v)) as Box<dyn HyperClient>
1075 }
1076 };
1077 #[cfg(not(feature = "tor"))]
1078 let hyper_client = {
1079 let v: hyper_util::client::legacy::Client<Connector, Body> =
1080 builder.build(connector_builder.build(config.connector_layers));
1081 Box::new(NonTorHyperClient(v)) as Box<dyn HyperClient>
1082 };
1083
1084 let hyper_service = HyperService {
1085 #[cfg(feature = "cookies")]
1086 cookie_store: config.cookie_store.clone(),
1087 hyper: hyper_client,
1088 };
1089
1090 let policy = {
1091 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
1092 p.with_referer(config.referer)
1093 .with_https_only(config.https_only);
1094 p
1095 };
1096
1097 let hyper = FollowRedirect::with_policy(hyper_service, policy.clone());
1098
1099 Ok(Client {
1100 inner: Arc::new(ClientRef {
1101 accepts: config.accepts,
1102 #[cfg(feature = "cookies")]
1103 cookie_store: config.cookie_store.clone(),
1104 #[cfg(feature = "http3")]
1107 h3_client: match h3_connector {
1108 Some(h3_connector) => {
1109 #[cfg(not(feature = "cookies"))]
1110 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1111 #[cfg(feature = "cookies")]
1112 let h3_service = H3Client::new(
1113 h3_connector,
1114 config.pool_idle_timeout,
1115 config.cookie_store,
1116 );
1117 Some(FollowRedirect::with_policy(h3_service, policy))
1118 }
1119 None => None,
1120 },
1121 headers: config.headers,
1122 referer: config.referer,
1123 read_timeout: config.read_timeout,
1124 request_timeout: RequestConfig::new(config.timeout),
1125 hyper,
1126 proxies,
1127 proxies_maybe_http_auth,
1128 proxies_maybe_http_custom_headers,
1129 https_only: config.https_only,
1130 redirect_policy_desc,
1131 }),
1132 })
1133 }
1134
1135 #[cfg(feature = "tor")]
1136 pub fn set_tor(mut self, client: arti_client::TorClient<PreferredRuntime>) -> Self {
1138 self.tor = Some(client);
1139 self
1140 }
1141
1142 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1165 where
1166 V: TryInto<HeaderValue>,
1167 V::Error: Into<http::Error>,
1168 {
1169 match value.try_into() {
1170 Ok(value) => {
1171 self.config.headers.insert(USER_AGENT, value);
1172 }
1173 Err(e) => {
1174 self.config.error = Some(crate::error::builder(e.into()));
1175 }
1176 };
1177 self
1178 }
1179 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1203 for (key, value) in headers.iter() {
1204 self.config.headers.insert(key, value.clone());
1205 }
1206 self
1207 }
1208
1209 #[cfg(feature = "cookies")]
1224 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1225 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1226 if enable {
1227 self.cookie_provider(Arc::new(cookie::Jar::default()))
1228 } else {
1229 self.config.cookie_store = None;
1230 self
1231 }
1232 }
1233
1234 #[cfg(feature = "cookies")]
1248 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1249 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1250 mut self,
1251 cookie_store: Arc<C>,
1252 ) -> ClientBuilder {
1253 self.config.cookie_store = Some(cookie_store as _);
1254 self
1255 }
1256
1257 #[cfg(feature = "gzip")]
1274 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1275 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1276 self.config.accepts.gzip = enable;
1277 self
1278 }
1279
1280 #[cfg(feature = "brotli")]
1297 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1298 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1299 self.config.accepts.brotli = enable;
1300 self
1301 }
1302
1303 #[cfg(feature = "zstd")]
1320 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1321 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1322 self.config.accepts.zstd = enable;
1323 self
1324 }
1325
1326 #[cfg(feature = "deflate")]
1343 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1344 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1345 self.config.accepts.deflate = enable;
1346 self
1347 }
1348
1349 pub fn no_gzip(self) -> ClientBuilder {
1355 #[cfg(feature = "gzip")]
1356 {
1357 self.gzip(false)
1358 }
1359
1360 #[cfg(not(feature = "gzip"))]
1361 {
1362 self
1363 }
1364 }
1365
1366 pub fn no_brotli(self) -> ClientBuilder {
1372 #[cfg(feature = "brotli")]
1373 {
1374 self.brotli(false)
1375 }
1376
1377 #[cfg(not(feature = "brotli"))]
1378 {
1379 self
1380 }
1381 }
1382
1383 pub fn no_zstd(self) -> ClientBuilder {
1389 #[cfg(feature = "zstd")]
1390 {
1391 self.zstd(false)
1392 }
1393
1394 #[cfg(not(feature = "zstd"))]
1395 {
1396 self
1397 }
1398 }
1399
1400 pub fn no_deflate(self) -> ClientBuilder {
1406 #[cfg(feature = "deflate")]
1407 {
1408 self.deflate(false)
1409 }
1410
1411 #[cfg(not(feature = "deflate"))]
1412 {
1413 self
1414 }
1415 }
1416
1417 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1423 self.config.redirect_policy = policy;
1424 self
1425 }
1426
1427 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1431 self.config.referer = enable;
1432 self
1433 }
1434
1435 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1443 self.config.proxies.push(proxy.into_matcher());
1444 self.config.auto_sys_proxy = false;
1445 self
1446 }
1447
1448 pub fn no_proxy(mut self) -> ClientBuilder {
1456 self.config.proxies.clear();
1457 self.config.auto_sys_proxy = false;
1458 self
1459 }
1460
1461 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1470 self.config.timeout = Some(timeout);
1471 self
1472 }
1473
1474 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1482 self.config.read_timeout = Some(timeout);
1483 self
1484 }
1485
1486 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1495 self.config.connect_timeout = Some(timeout);
1496 self
1497 }
1498
1499 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1506 self.config.connection_verbose = verbose;
1507 self
1508 }
1509
1510 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1518 where
1519 D: Into<Option<Duration>>,
1520 {
1521 self.config.pool_idle_timeout = val.into();
1522 self
1523 }
1524
1525 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1527 self.config.pool_max_idle_per_host = max;
1528 self
1529 }
1530
1531 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1533 self.config.http1_title_case_headers = true;
1534 self
1535 }
1536
1537 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1543 mut self,
1544 value: bool,
1545 ) -> ClientBuilder {
1546 self.config
1547 .http1_allow_obsolete_multiline_headers_in_responses = value;
1548 self
1549 }
1550
1551 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1553 self.config.http1_ignore_invalid_headers_in_responses = value;
1554 self
1555 }
1556
1557 pub fn http1_allow_spaces_after_header_name_in_responses(
1563 mut self,
1564 value: bool,
1565 ) -> ClientBuilder {
1566 self.config
1567 .http1_allow_spaces_after_header_name_in_responses = value;
1568 self
1569 }
1570
1571 pub fn http1_only(mut self) -> ClientBuilder {
1573 self.config.http_version_pref = HttpVersionPref::Http1;
1574 self
1575 }
1576
1577 pub fn http09_responses(mut self) -> ClientBuilder {
1579 self.config.http09_responses = true;
1580 self
1581 }
1582
1583 #[cfg(feature = "http2")]
1585 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1586 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1587 self.config.http_version_pref = HttpVersionPref::Http2;
1588 self
1589 }
1590
1591 #[cfg(feature = "http3")]
1593 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1594 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1595 self.config.http_version_pref = HttpVersionPref::Http3;
1596 self
1597 }
1598
1599 #[cfg(feature = "http2")]
1603 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1604 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1605 self.config.http2_initial_stream_window_size = sz.into();
1606 self
1607 }
1608
1609 #[cfg(feature = "http2")]
1613 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1614 pub fn http2_initial_connection_window_size(
1615 mut self,
1616 sz: impl Into<Option<u32>>,
1617 ) -> ClientBuilder {
1618 self.config.http2_initial_connection_window_size = sz.into();
1619 self
1620 }
1621
1622 #[cfg(feature = "http2")]
1627 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1628 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1629 self.config.http2_adaptive_window = enabled;
1630 self
1631 }
1632
1633 #[cfg(feature = "http2")]
1637 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1638 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1639 self.config.http2_max_frame_size = sz.into();
1640 self
1641 }
1642
1643 #[cfg(feature = "http2")]
1647 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1648 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1649 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1650 self
1651 }
1652
1653 #[cfg(feature = "http2")]
1658 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1659 pub fn http2_keep_alive_interval(
1660 mut self,
1661 interval: impl Into<Option<Duration>>,
1662 ) -> ClientBuilder {
1663 self.config.http2_keep_alive_interval = interval.into();
1664 self
1665 }
1666
1667 #[cfg(feature = "http2")]
1673 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1674 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1675 self.config.http2_keep_alive_timeout = Some(timeout);
1676 self
1677 }
1678
1679 #[cfg(feature = "http2")]
1686 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1687 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1688 self.config.http2_keep_alive_while_idle = enabled;
1689 self
1690 }
1691
1692 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1698 self.config.nodelay = enabled;
1699 self
1700 }
1701
1702 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1717 where
1718 T: Into<Option<IpAddr>>,
1719 {
1720 self.config.local_address = addr.into();
1721 self
1722 }
1723
1724 #[cfg(any(
1757 target_os = "android",
1758 target_os = "fuchsia",
1759 target_os = "illumos",
1760 target_os = "ios",
1761 target_os = "linux",
1762 target_os = "macos",
1763 target_os = "solaris",
1764 target_os = "tvos",
1765 target_os = "visionos",
1766 target_os = "watchos",
1767 ))]
1768 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1769 self.config.interface = Some(interface.to_string());
1770 self
1771 }
1772
1773 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1777 where
1778 D: Into<Option<Duration>>,
1779 {
1780 self.config.tcp_keepalive = val.into();
1781 self
1782 }
1783
1784 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1788 where
1789 D: Into<Option<Duration>>,
1790 {
1791 self.config.tcp_keepalive_interval = val.into();
1792 self
1793 }
1794
1795 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1799 where
1800 C: Into<Option<u32>>,
1801 {
1802 self.config.tcp_keepalive_retries = retries.into();
1803 self
1804 }
1805
1806 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1813 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1814 where
1815 D: Into<Option<Duration>>,
1816 {
1817 self.config.tcp_user_timeout = val.into();
1818 self
1819 }
1820
1821 #[cfg(feature = "__tls")]
1833 #[cfg_attr(
1834 docsrs,
1835 doc(cfg(any(
1836 feature = "default-tls",
1837 feature = "native-tls",
1838 feature = "rustls-tls"
1839 )))
1840 )]
1841 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1842 self.config.root_certs.push(cert);
1843 self
1844 }
1845
1846 #[cfg(feature = "__rustls")]
1853 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1854 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1855 self.config.crls.push(crl);
1856 self
1857 }
1858
1859 #[cfg(feature = "__rustls")]
1866 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1867 pub fn add_crls(
1868 mut self,
1869 crls: impl IntoIterator<Item = CertificateRevocationList>,
1870 ) -> ClientBuilder {
1871 self.config.crls.extend(crls);
1872 self
1873 }
1874
1875 #[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 tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1902 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1903
1904 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1905 {
1906 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1907 }
1908
1909 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1910 {
1911 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1912 }
1913
1914 self
1915 }
1916
1917 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1921 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1922 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1923 self.config.tls_built_in_certs_webpki = enabled;
1924 self
1925 }
1926
1927 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1931 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1932 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1933 self.config.tls_built_in_certs_native = enabled;
1934 self
1935 }
1936
1937 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1944 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1945 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1946 self.config.identity = Some(identity);
1947 self
1948 }
1949
1950 #[cfg(feature = "__tls")]
1966 #[cfg_attr(
1967 docsrs,
1968 doc(cfg(any(
1969 feature = "default-tls",
1970 feature = "native-tls",
1971 feature = "rustls-tls"
1972 )))
1973 )]
1974 pub fn danger_accept_invalid_hostnames(
1975 mut self,
1976 accept_invalid_hostname: bool,
1977 ) -> ClientBuilder {
1978 self.config.hostname_verification = !accept_invalid_hostname;
1979 self
1980 }
1981
1982 #[cfg(feature = "__tls")]
1999 #[cfg_attr(
2000 docsrs,
2001 doc(cfg(any(
2002 feature = "default-tls",
2003 feature = "native-tls",
2004 feature = "rustls-tls"
2005 )))
2006 )]
2007 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
2008 self.config.certs_verification = !accept_invalid_certs;
2009 self
2010 }
2011
2012 #[cfg(feature = "__tls")]
2021 #[cfg_attr(
2022 docsrs,
2023 doc(cfg(any(
2024 feature = "default-tls",
2025 feature = "native-tls",
2026 feature = "rustls-tls"
2027 )))
2028 )]
2029 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
2030 self.config.tls_sni = tls_sni;
2031 self
2032 }
2033
2034 #[cfg(feature = "__tls")]
2050 #[cfg_attr(
2051 docsrs,
2052 doc(cfg(any(
2053 feature = "default-tls",
2054 feature = "native-tls",
2055 feature = "rustls-tls"
2056 )))
2057 )]
2058 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
2059 self.config.min_tls_version = Some(version);
2060 self
2061 }
2062
2063 #[cfg(feature = "__tls")]
2082 #[cfg_attr(
2083 docsrs,
2084 doc(cfg(any(
2085 feature = "default-tls",
2086 feature = "native-tls",
2087 feature = "rustls-tls"
2088 )))
2089 )]
2090 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
2091 self.config.max_tls_version = Some(version);
2092 self
2093 }
2094
2095 #[cfg(feature = "native-tls")]
2104 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2105 pub fn use_native_tls(mut self) -> ClientBuilder {
2106 self.config.tls = TlsBackend::Default;
2107 self
2108 }
2109
2110 #[cfg(feature = "__rustls")]
2119 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
2120 pub fn use_rustls_tls(mut self) -> ClientBuilder {
2121 self.config.tls = TlsBackend::Rustls;
2122 self
2123 }
2124
2125 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
2144 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
2145 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
2146 let mut tls = Some(tls);
2147 #[cfg(feature = "native-tls")]
2148 {
2149 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2150 let tls = conn.take().expect("is definitely Some");
2151 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2152 self.config.tls = tls;
2153 return self;
2154 }
2155 }
2156 #[cfg(feature = "__rustls")]
2157 {
2158 if let Some(conn) =
2159 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2160 {
2161 let tls = conn.take().expect("is definitely Some");
2162 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2163 self.config.tls = tls;
2164 return self;
2165 }
2166 }
2167
2168 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2170 self
2171 }
2172
2173 #[cfg(feature = "__tls")]
2180 #[cfg_attr(
2181 docsrs,
2182 doc(cfg(any(
2183 feature = "default-tls",
2184 feature = "native-tls",
2185 feature = "rustls-tls"
2186 )))
2187 )]
2188 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2189 self.config.tls_info = tls_info;
2190 self
2191 }
2192
2193 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2197 self.config.https_only = enabled;
2198 self
2199 }
2200
2201 #[doc(hidden)]
2202 #[cfg(feature = "hickory-dns")]
2203 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2204 #[deprecated(note = "use `hickory_dns` instead")]
2205 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
2206 self.config.hickory_dns = enable;
2207 self
2208 }
2209
2210 #[cfg(feature = "hickory-dns")]
2224 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2225 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2226 self.config.hickory_dns = enable;
2227 self
2228 }
2229
2230 #[doc(hidden)]
2231 #[deprecated(note = "use `no_hickory_dns` instead")]
2232 pub fn no_trust_dns(self) -> ClientBuilder {
2233 self.no_hickory_dns()
2234 }
2235
2236 pub fn no_hickory_dns(self) -> ClientBuilder {
2242 #[cfg(feature = "hickory-dns")]
2243 {
2244 self.hickory_dns(false)
2245 }
2246
2247 #[cfg(not(feature = "hickory-dns"))]
2248 {
2249 self
2250 }
2251 }
2252
2253 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2258 self.resolve_to_addrs(domain, &[addr])
2259 }
2260
2261 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2266 self.config
2267 .dns_overrides
2268 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2269 self
2270 }
2271
2272 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
2278 self.config.dns_resolver = Some(resolver as _);
2279 self
2280 }
2281
2282 #[cfg(feature = "http3")]
2287 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2288 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2289 self.config.tls_enable_early_data = enabled;
2290 self
2291 }
2292
2293 #[cfg(feature = "http3")]
2299 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2300 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2301 self.config.quic_max_idle_timeout = Some(value);
2302 self
2303 }
2304
2305 #[cfg(feature = "http3")]
2316 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2317 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2318 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2319 self
2320 }
2321
2322 #[cfg(feature = "http3")]
2333 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2334 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2335 self.config.quic_receive_window = Some(value.try_into().unwrap());
2336 self
2337 }
2338
2339 #[cfg(feature = "http3")]
2345 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2346 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2347 self.config.quic_send_window = Some(value);
2348 self
2349 }
2350
2351 #[cfg(feature = "http3")]
2359 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2360 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2361 self.config.quic_congestion_bbr = true;
2362 self
2363 }
2364
2365 #[cfg(feature = "http3")]
2375 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2376 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2377 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2378 self
2379 }
2380
2381 #[cfg(feature = "http3")]
2393 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2394 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2395 self.config.h3_send_grease = Some(enabled);
2396 self
2397 }
2398
2399 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2423 where
2424 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2425 L::Service:
2426 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2427 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2428 {
2429 let layer = BoxCloneSyncServiceLayer::new(layer);
2430
2431 self.config.connector_layers.push(layer);
2432
2433 self
2434 }
2435}
2436
2437impl Default for Client {
2438 fn default() -> Self {
2439 Self::new()
2440 }
2441}
2442
2443impl Client {
2444 pub fn new() -> Client {
2454 ClientBuilder::new().build().expect("Client::new()")
2455 }
2456
2457 #[cfg(feature = "tor")]
2458 pub async fn tor() -> Client {
2470 ClientBuilder::tor(Default::default())
2471 .await
2472 .expect("TorClient::new()")
2473 .build()
2474 .expect("Client::new()")
2475 }
2476
2477 pub fn builder() -> ClientBuilder {
2481 ClientBuilder::new()
2482 }
2483
2484 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2490 self.request(Method::GET, url)
2491 }
2492
2493 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2499 self.request(Method::POST, url)
2500 }
2501
2502 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2508 self.request(Method::PUT, url)
2509 }
2510
2511 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2517 self.request(Method::PATCH, url)
2518 }
2519
2520 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2526 self.request(Method::DELETE, url)
2527 }
2528
2529 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2535 self.request(Method::HEAD, url)
2536 }
2537
2538 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2547 let req = url.into_url().map(move |url| Request::new(method, url));
2548 RequestBuilder::new(self.clone(), req)
2549 }
2550
2551 pub fn execute(
2564 &self,
2565 request: Request,
2566 ) -> impl Future<Output = Result<Response, crate::Error>> {
2567 self.execute_request(request)
2568 }
2569
2570 pub(super) fn execute_request(&self, req: Request) -> Pending {
2571 let (method, url, mut headers, body, version, extensions) = req.pieces();
2572 if url.scheme() != "http" && url.scheme() != "https" {
2573 return Pending::new_err(error::url_bad_scheme(url));
2574 }
2575
2576 if self.inner.https_only && url.scheme() != "https" {
2578 return Pending::new_err(error::url_bad_scheme(url));
2579 }
2580
2581 for (key, value) in &self.inner.headers {
2584 if let Entry::Vacant(entry) = headers.entry(key) {
2585 entry.insert(value.clone());
2586 }
2587 }
2588
2589 let accept_encoding = self.inner.accepts.as_str();
2590
2591 if let Some(accept_encoding) = accept_encoding {
2592 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2593 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2594 }
2595 }
2596
2597 let uri = match try_uri(&url) {
2598 Ok(uri) => uri,
2599 _ => return Pending::new_err(error::url_invalid_uri(url)),
2600 };
2601
2602 let (reusable, body) = match body {
2603 Some(body) => {
2604 let (reusable, body) = body.try_reuse();
2605 (Some(reusable), body)
2606 }
2607 None => (None, Body::empty()),
2608 };
2609
2610 self.proxy_auth(&uri, &mut headers);
2611 self.proxy_custom_headers(&uri, &mut headers);
2612
2613 let builder = hyper::Request::builder()
2614 .method(method.clone())
2615 .uri(uri)
2616 .version(version);
2617
2618 let in_flight = match version {
2619 #[cfg(feature = "http3")]
2620 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2621 let mut req = builder.body(body).expect("valid request parts");
2622 *req.headers_mut() = headers.clone();
2623 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2624 ResponseFuture::H3(h3.call(req))
2625 }
2626 _ => {
2627 let mut req = builder.body(body).expect("valid request parts");
2628 *req.headers_mut() = headers.clone();
2629 let mut hyper = self.inner.hyper.clone();
2630 ResponseFuture::Default(hyper.call(req))
2631 }
2632 };
2633
2634 let total_timeout = self
2635 .inner
2636 .request_timeout
2637 .fetch(&extensions)
2638 .copied()
2639 .map(tokio::time::sleep)
2640 .map(Box::pin);
2641
2642 let read_timeout_fut = self
2643 .inner
2644 .read_timeout
2645 .map(tokio::time::sleep)
2646 .map(Box::pin);
2647
2648 Pending {
2649 inner: PendingInner::Request(Box::pin(PendingRequest {
2650 method,
2651 url,
2652 headers,
2653 body: reusable,
2654
2655 retry_count: 0,
2656
2657 client: self.inner.clone(),
2658
2659 in_flight,
2660 total_timeout,
2661 read_timeout_fut,
2662 read_timeout: self.inner.read_timeout,
2663 })),
2664 }
2665 }
2666
2667 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2668 if !self.inner.proxies_maybe_http_auth {
2669 return;
2670 }
2671
2672 if dst.scheme() != Some(&Scheme::HTTP) {
2676 return;
2677 }
2678
2679 if headers.contains_key(PROXY_AUTHORIZATION) {
2680 return;
2681 }
2682
2683 for proxy in self.inner.proxies.iter() {
2684 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2685 headers.insert(PROXY_AUTHORIZATION, header);
2686 break;
2687 }
2688 }
2689 }
2690
2691 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2692 if !self.inner.proxies_maybe_http_custom_headers {
2693 return;
2694 }
2695
2696 if dst.scheme() != Some(&Scheme::HTTP) {
2697 return;
2698 }
2699
2700 for proxy in self.inner.proxies.iter() {
2701 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2702 iter.iter().for_each(|(key, value)| {
2703 headers.insert(key, value.clone());
2704 });
2705 break;
2706 }
2707 }
2708 }
2709}
2710
2711impl fmt::Debug for Client {
2712 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2713 let mut builder = f.debug_struct("Client");
2714 self.inner.fmt_fields(&mut builder);
2715 builder.finish()
2716 }
2717}
2718
2719impl tower_service::Service<Request> for Client {
2720 type Response = Response;
2721 type Error = crate::Error;
2722 type Future = Pending;
2723
2724 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2725 Poll::Ready(Ok(()))
2726 }
2727
2728 fn call(&mut self, req: Request) -> Self::Future {
2729 self.execute_request(req)
2730 }
2731}
2732
2733impl tower_service::Service<Request> for &'_ Client {
2734 type Response = Response;
2735 type Error = crate::Error;
2736 type Future = Pending;
2737
2738 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2739 Poll::Ready(Ok(()))
2740 }
2741
2742 fn call(&mut self, req: Request) -> Self::Future {
2743 self.execute_request(req)
2744 }
2745}
2746
2747impl fmt::Debug for ClientBuilder {
2748 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2749 let mut builder = f.debug_struct("ClientBuilder");
2750 self.config.fmt_fields(&mut builder);
2751 builder.finish()
2752 }
2753}
2754
2755impl Config {
2756 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2757 #[cfg(feature = "cookies")]
2761 {
2762 if let Some(_) = self.cookie_store {
2763 f.field("cookie_store", &true);
2764 }
2765 }
2766
2767 f.field("accepts", &self.accepts);
2768
2769 if !self.proxies.is_empty() {
2770 f.field("proxies", &self.proxies);
2771 }
2772
2773 if !self.redirect_policy.is_default() {
2774 f.field("redirect_policy", &self.redirect_policy);
2775 }
2776
2777 if self.referer {
2778 f.field("referer", &true);
2779 }
2780
2781 f.field("default_headers", &self.headers);
2782
2783 if self.http1_title_case_headers {
2784 f.field("http1_title_case_headers", &true);
2785 }
2786
2787 if self.http1_allow_obsolete_multiline_headers_in_responses {
2788 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2789 }
2790
2791 if self.http1_ignore_invalid_headers_in_responses {
2792 f.field("http1_ignore_invalid_headers_in_responses", &true);
2793 }
2794
2795 if self.http1_allow_spaces_after_header_name_in_responses {
2796 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2797 }
2798
2799 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2800 f.field("http1_only", &true);
2801 }
2802
2803 #[cfg(feature = "http2")]
2804 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2805 f.field("http2_prior_knowledge", &true);
2806 }
2807
2808 if let Some(ref d) = self.connect_timeout {
2809 f.field("connect_timeout", d);
2810 }
2811
2812 if let Some(ref d) = self.timeout {
2813 f.field("timeout", d);
2814 }
2815
2816 if let Some(ref v) = self.local_address {
2817 f.field("local_address", v);
2818 }
2819
2820 #[cfg(any(
2821 target_os = "android",
2822 target_os = "fuchsia",
2823 target_os = "illumos",
2824 target_os = "ios",
2825 target_os = "linux",
2826 target_os = "macos",
2827 target_os = "solaris",
2828 target_os = "tvos",
2829 target_os = "visionos",
2830 target_os = "watchos",
2831 ))]
2832 if let Some(ref v) = self.interface {
2833 f.field("interface", v);
2834 }
2835
2836 if self.nodelay {
2837 f.field("tcp_nodelay", &true);
2838 }
2839
2840 #[cfg(feature = "__tls")]
2841 {
2842 if !self.hostname_verification {
2843 f.field("danger_accept_invalid_hostnames", &true);
2844 }
2845 }
2846
2847 #[cfg(feature = "__tls")]
2848 {
2849 if !self.certs_verification {
2850 f.field("danger_accept_invalid_certs", &true);
2851 }
2852
2853 if let Some(ref min_tls_version) = self.min_tls_version {
2854 f.field("min_tls_version", min_tls_version);
2855 }
2856
2857 if let Some(ref max_tls_version) = self.max_tls_version {
2858 f.field("max_tls_version", max_tls_version);
2859 }
2860
2861 f.field("tls_sni", &self.tls_sni);
2862
2863 f.field("tls_info", &self.tls_info);
2864 }
2865
2866 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2867 {
2868 f.field("tls_backend", &self.tls);
2869 }
2870
2871 if !self.dns_overrides.is_empty() {
2872 f.field("dns_overrides", &self.dns_overrides);
2873 }
2874
2875 #[cfg(feature = "http3")]
2876 {
2877 if self.tls_enable_early_data {
2878 f.field("tls_enable_early_data", &true);
2879 }
2880 }
2881 }
2882}
2883
2884struct ClientRef {
2885 accepts: Accepts,
2886 #[cfg(feature = "cookies")]
2887 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2888 headers: HeaderMap,
2889 hyper: FollowRedirect<HyperService, TowerRedirectPolicy>,
2890 #[cfg(feature = "http3")]
2891 h3_client: Option<FollowRedirect<H3Client, TowerRedirectPolicy>>,
2892 referer: bool,
2893 request_timeout: RequestConfig<RequestTimeout>,
2894 read_timeout: Option<Duration>,
2895 proxies: Arc<Vec<ProxyMatcher>>,
2896 proxies_maybe_http_auth: bool,
2897 proxies_maybe_http_custom_headers: bool,
2898 https_only: bool,
2899 redirect_policy_desc: Option<String>,
2900}
2901
2902impl ClientRef {
2903 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2904 #[cfg(feature = "cookies")]
2908 {
2909 if let Some(_) = self.cookie_store {
2910 f.field("cookie_store", &true);
2911 }
2912 }
2913
2914 f.field("accepts", &self.accepts);
2915
2916 if !self.proxies.is_empty() {
2917 f.field("proxies", &self.proxies);
2918 }
2919
2920 if let Some(s) = &self.redirect_policy_desc {
2921 f.field("redirect_policy", s);
2922 }
2923
2924 if self.referer {
2925 f.field("referer", &true);
2926 }
2927
2928 f.field("default_headers", &self.headers);
2929
2930 self.request_timeout.fmt_as_field(f);
2931
2932 if let Some(ref d) = self.read_timeout {
2933 f.field("read_timeout", d);
2934 }
2935 }
2936}
2937
2938pin_project! {
2939 pub struct Pending {
2940 #[pin]
2941 inner: PendingInner,
2942 }
2943}
2944
2945enum PendingInner {
2946 Request(Pin<Box<PendingRequest>>),
2947 Error(Option<crate::Error>),
2948}
2949
2950pin_project! {
2951 struct PendingRequest {
2952 method: Method,
2953 url: Url,
2954 headers: HeaderMap,
2955 body: Option<Option<Bytes>>,
2956
2957 retry_count: usize,
2958
2959 client: Arc<ClientRef>,
2960
2961 #[pin]
2962 in_flight: ResponseFuture,
2963 #[pin]
2964 total_timeout: Option<Pin<Box<Sleep>>>,
2965 #[pin]
2966 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2967 read_timeout: Option<Duration>,
2968 }
2969}
2970
2971enum ResponseFuture {
2972 Default(tower_http::follow_redirect::ResponseFuture<HyperService, Body, TowerRedirectPolicy>),
2973 #[cfg(feature = "http3")]
2974 H3(tower_http::follow_redirect::ResponseFuture<H3Client, Body, TowerRedirectPolicy>),
2975}
2976
2977impl PendingRequest {
2978 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2979 self.project().in_flight
2980 }
2981
2982 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2983 self.project().total_timeout
2984 }
2985
2986 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2987 self.project().read_timeout_fut
2988 }
2989
2990 #[cfg(any(feature = "http2", feature = "http3"))]
2991 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2992 use log::trace;
2993
2994 if !is_retryable_error(err) {
2995 return false;
2996 }
2997
2998 trace!("can retry {err:?}");
2999
3000 let body = match self.body {
3001 Some(Some(ref body)) => Body::reusable(body.clone()),
3002 Some(None) => {
3003 log::debug!("error was retryable, but body not reusable");
3004 return false;
3005 }
3006 None => Body::empty(),
3007 };
3008
3009 if self.retry_count >= 2 {
3010 trace!("retry count too high");
3011 return false;
3012 }
3013 self.retry_count += 1;
3014
3015 let uri = try_uri(&self.url).expect("URL was already validated as URI");
3017
3018 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
3019 #[cfg(feature = "http3")]
3020 ResponseFuture::H3(_) => {
3021 let mut req = hyper::Request::builder()
3022 .method(self.method.clone())
3023 .uri(uri)
3024 .body(body)
3025 .expect("valid request parts");
3026 *req.headers_mut() = self.headers.clone();
3027 let mut h3 = self
3028 .client
3029 .h3_client
3030 .as_ref()
3031 .expect("H3 client must exists, otherwise we can't have a h3 request here")
3032 .clone();
3033 ResponseFuture::H3(h3.call(req))
3034 }
3035 _ => {
3036 let mut req = hyper::Request::builder()
3037 .method(self.method.clone())
3038 .uri(uri)
3039 .body(body)
3040 .expect("valid request parts");
3041 *req.headers_mut() = self.headers.clone();
3042 let mut hyper = self.client.hyper.clone();
3043 ResponseFuture::Default(hyper.call(req))
3044 }
3045 };
3046
3047 true
3048 }
3049}
3050
3051#[cfg(any(feature = "http2", feature = "http3"))]
3052fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
3053 let err = if let Some(err) = err.source() {
3055 err
3056 } else {
3057 return false;
3058 };
3059
3060 #[cfg(feature = "http3")]
3061 if let Some(cause) = err.source() {
3062 if let Some(err) = cause.downcast_ref::<h3::error::ConnectionError>() {
3063 log::debug!("determining if HTTP/3 error {err} can be retried");
3064 return err.to_string().as_str() == "timeout";
3066 }
3067 }
3068
3069 #[cfg(feature = "http2")]
3070 if let Some(cause) = err.source() {
3071 if let Some(err) = cause.downcast_ref::<h2::Error>() {
3072 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
3074 return true;
3075 }
3076
3077 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
3080 {
3081 return true;
3082 }
3083 }
3084 }
3085 false
3086}
3087
3088impl Pending {
3089 pub(super) fn new_err(err: crate::Error) -> Pending {
3090 Pending {
3091 inner: PendingInner::Error(Some(err)),
3092 }
3093 }
3094
3095 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
3096 self.project().inner
3097 }
3098}
3099
3100impl Future for Pending {
3101 type Output = Result<Response, crate::Error>;
3102
3103 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3104 let inner = self.inner();
3105 match inner.get_mut() {
3106 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
3107 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
3108 .take()
3109 .expect("Pending error polled more than once"))),
3110 }
3111 }
3112}
3113
3114impl Future for PendingRequest {
3115 type Output = Result<Response, crate::Error>;
3116
3117 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3118 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
3119 if let Poll::Ready(()) = delay.poll(cx) {
3120 return Poll::Ready(Err(
3121 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3122 ));
3123 }
3124 }
3125
3126 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
3127 if let Poll::Ready(()) = delay.poll(cx) {
3128 return Poll::Ready(Err(
3129 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3130 ));
3131 }
3132 }
3133
3134 loop {
3135 let res = match self.as_mut().in_flight().get_mut() {
3136 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
3137 Poll::Ready(Err(e)) => {
3138 #[cfg(feature = "http2")]
3139 if e.is_request() {
3140 if let Some(e) = e.source() {
3141 if self.as_mut().retry_error(e) {
3142 continue;
3143 }
3144 }
3145 }
3146
3147 return Poll::Ready(Err(e));
3148 }
3149 Poll::Ready(Ok(res)) => res.map(super::body::boxed),
3150 Poll::Pending => return Poll::Pending,
3151 },
3152 #[cfg(feature = "http3")]
3153 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
3154 Poll::Ready(Err(e)) => {
3155 if self.as_mut().retry_error(&e) {
3156 continue;
3157 }
3158 return Poll::Ready(Err(
3159 crate::error::request(e).with_url(self.url.clone())
3160 ));
3161 }
3162 Poll::Ready(Ok(res)) => res,
3163 Poll::Pending => return Poll::Pending,
3164 },
3165 };
3166
3167 #[cfg(feature = "cookies")]
3168 {
3169 if let Some(ref cookie_store) = self.client.cookie_store {
3170 let mut cookies =
3171 cookie::extract_response_cookie_headers(res.headers()).peekable();
3172 if cookies.peek().is_some() {
3173 cookie_store.set_cookies(&mut cookies, &self.url);
3174 }
3175 }
3176 }
3177 if let Some(url) = &res
3178 .extensions()
3179 .get::<tower_http::follow_redirect::RequestUri>()
3180 {
3181 self.url = match Url::parse(&url.0.to_string()) {
3182 Ok(url) => url,
3183 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3184 }
3185 };
3186
3187 let res = Response::new(
3188 res,
3189 self.url.clone(),
3190 self.client.accepts,
3191 self.total_timeout.take(),
3192 self.read_timeout,
3193 );
3194 return Poll::Ready(Ok(res));
3195 }
3196 }
3197}
3198
3199impl fmt::Debug for Pending {
3200 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3201 match self.inner {
3202 PendingInner::Request(ref req) => f
3203 .debug_struct("Pending")
3204 .field("method", &req.method)
3205 .field("url", &req.url)
3206 .finish(),
3207 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3208 }
3209 }
3210}
3211
3212#[cfg(test)]
3213mod tests {
3214 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
3215
3216 #[tokio::test]
3217 async fn execute_request_rejects_invalid_urls() {
3218 let url_str = "hxxps://www.rust-lang.org/";
3219 let url = url::Url::parse(url_str).unwrap();
3220 let result = crate::get(url.clone()).await;
3221
3222 assert!(result.is_err());
3223 let err = result.err().unwrap();
3224 assert!(err.is_builder());
3225 assert_eq!(url_str, err.url().unwrap().as_str());
3226 }
3227
3228 #[tokio::test]
3230 async fn execute_request_rejects_invalid_hostname() {
3231 let url_str = "https://{{hostname}}/";
3232 let url = url::Url::parse(url_str).unwrap();
3233 let result = crate::get(url.clone()).await;
3234
3235 assert!(result.is_err());
3236 let err = result.err().unwrap();
3237 assert!(err.is_builder());
3238 assert_eq!(url_str, err.url().unwrap().as_str());
3239 }
3240
3241 #[test]
3242 fn test_future_size() {
3243 let s = std::mem::size_of::<super::Pending>();
3244 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3245 }
3246}