1#[cfg(any(feature = "native-tls", feature = "__rustls",))]
2use std::any::Any;
3use std::future::Future;
4use std::net::IpAddr;
5use std::pin::Pin;
6use std::sync::Arc;
7use std::task::{ready, Context, Poll};
8use std::time::Duration;
9use std::{collections::HashMap, convert::TryInto, net::SocketAddr};
10use std::{fmt, str};
11
12use super::request::{Request, RequestBuilder};
13use super::response::Response;
14use super::Body;
15#[cfg(feature = "http3")]
16use crate::async_impl::h3_client::connect::{H3ClientConfig, H3Connector};
17#[cfg(feature = "http3")]
18use crate::async_impl::h3_client::H3Client;
19use crate::config::{RequestConfig, TotalTimeout};
20#[cfg(unix)]
21use crate::connect::uds::UnixSocketProvider;
22#[cfg(target_os = "windows")]
23use crate::connect::windows_named_pipe::WindowsNamedPipeProvider;
24use crate::connect::{
25 sealed::{Conn, Unnameable},
26 BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
27};
28#[cfg(feature = "cookies")]
29use crate::cookie;
30#[cfg(feature = "cookies")]
31use crate::cookie::service::CookieService;
32#[cfg(feature = "hickory-dns")]
33use crate::dns::hickory::HickoryDnsResolver;
34use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
35use crate::error::{self, BoxError};
36use crate::into_url::try_uri;
37use crate::proxy::Matcher as ProxyMatcher;
38use crate::redirect::{self, TowerRedirectPolicy};
39#[cfg(feature = "__rustls")]
40use crate::tls::CertificateRevocationList;
41#[cfg(feature = "__tls")]
42use crate::tls::{self, TlsBackend};
43#[cfg(feature = "__tls")]
44use crate::Certificate;
45#[cfg(any(feature = "native-tls", feature = "__rustls"))]
46use crate::Identity;
47use crate::{IntoUrl, Method, Proxy, Url};
48
49use http::header::{Entry, HeaderMap, HeaderValue, ACCEPT, PROXY_AUTHORIZATION, USER_AGENT};
50use http::uri::Scheme;
51use http::Uri;
52use hyper_util::client::legacy::connect::HttpConnector;
53#[cfg(feature = "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};
63#[cfg(any(
64 feature = "gzip",
65 feature = "brotli",
66 feature = "zstd",
67 feature = "deflate"
68))]
69use tower_http::decompression::Decompression;
70use tower_http::follow_redirect::FollowRedirect;
71
72#[derive(Clone)]
93pub struct Client {
94 inner: Arc<ClientRef>,
95}
96
97#[must_use]
99pub struct ClientBuilder {
100 config: Config,
101}
102
103enum HttpVersionPref {
104 Http1,
105 #[cfg(feature = "http2")]
106 Http2,
107 #[cfg(feature = "http3")]
108 Http3,
109 All,
110}
111
112#[derive(Clone, Copy, Debug)]
113struct Accepts {
114 #[cfg(feature = "gzip")]
115 gzip: bool,
116 #[cfg(feature = "brotli")]
117 brotli: bool,
118 #[cfg(feature = "zstd")]
119 zstd: bool,
120 #[cfg(feature = "deflate")]
121 deflate: bool,
122}
123
124impl Default for Accepts {
125 fn default() -> Accepts {
126 Accepts {
127 #[cfg(feature = "gzip")]
128 gzip: true,
129 #[cfg(feature = "brotli")]
130 brotli: true,
131 #[cfg(feature = "zstd")]
132 zstd: true,
133 #[cfg(feature = "deflate")]
134 deflate: true,
135 }
136 }
137}
138
139#[derive(Clone)]
140struct HyperService {
141 hyper: HyperClient,
142}
143
144impl Service<hyper::Request<crate::async_impl::body::Body>> for HyperService {
145 type Error = crate::Error;
146 type Response = http::Response<hyper::body::Incoming>;
147 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + Sync>>;
148
149 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
150 self.hyper.poll_ready(cx).map_err(crate::error::request)
151 }
152
153 fn call(&mut self, req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
154 let clone = self.hyper.clone();
155 let mut inner = std::mem::replace(&mut self.hyper, clone);
156 Box::pin(async move { inner.call(req).await.map_err(crate::error::request) })
157 }
158}
159
160struct Config {
161 accepts: Accepts,
163 headers: HeaderMap,
164 #[cfg(feature = "__tls")]
165 hostname_verification: bool,
166 #[cfg(feature = "__tls")]
167 certs_verification: bool,
168 #[cfg(feature = "__tls")]
169 tls_sni: bool,
170 connect_timeout: Option<Duration>,
171 connection_verbose: bool,
172 pool_idle_timeout: Option<Duration>,
173 pool_max_idle_per_host: usize,
174 tcp_keepalive: Option<Duration>,
175 tcp_keepalive_interval: Option<Duration>,
176 tcp_keepalive_retries: Option<u32>,
177 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
178 tcp_user_timeout: Option<Duration>,
179 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
180 identity: Option<Identity>,
181 proxies: Vec<ProxyMatcher>,
182 auto_sys_proxy: bool,
183 redirect_policy: redirect::Policy,
184 retry_policy: crate::retry::Builder,
185 referer: bool,
186 read_timeout: Option<Duration>,
187 timeout: Option<Duration>,
188 #[cfg(feature = "__tls")]
189 root_certs: Vec<Certificate>,
190 #[cfg(feature = "__tls")]
191 tls_built_in_root_certs: bool,
192 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
193 tls_built_in_certs_webpki: bool,
194 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
195 tls_built_in_certs_native: bool,
196 #[cfg(feature = "__rustls")]
197 crls: Vec<CertificateRevocationList>,
198 #[cfg(feature = "__tls")]
199 min_tls_version: Option<tls::Version>,
200 #[cfg(feature = "__tls")]
201 max_tls_version: Option<tls::Version>,
202 #[cfg(feature = "__tls")]
203 tls_info: bool,
204 #[cfg(feature = "__tls")]
205 tls: TlsBackend,
206 connector_layers: Vec<BoxedConnectorLayer>,
207 http_version_pref: HttpVersionPref,
208 http09_responses: bool,
209 http1_title_case_headers: bool,
210 http1_allow_obsolete_multiline_headers_in_responses: bool,
211 http1_ignore_invalid_headers_in_responses: bool,
212 http1_allow_spaces_after_header_name_in_responses: bool,
213 #[cfg(feature = "http2")]
214 http2_initial_stream_window_size: Option<u32>,
215 #[cfg(feature = "http2")]
216 http2_initial_connection_window_size: Option<u32>,
217 #[cfg(feature = "http2")]
218 http2_adaptive_window: bool,
219 #[cfg(feature = "http2")]
220 http2_max_frame_size: Option<u32>,
221 #[cfg(feature = "http2")]
222 http2_max_header_list_size: Option<u32>,
223 #[cfg(feature = "http2")]
224 http2_keep_alive_interval: Option<Duration>,
225 #[cfg(feature = "http2")]
226 http2_keep_alive_timeout: Option<Duration>,
227 #[cfg(feature = "http2")]
228 http2_keep_alive_while_idle: bool,
229 local_address: Option<IpAddr>,
230 #[cfg(any(
231 target_os = "android",
232 target_os = "fuchsia",
233 target_os = "illumos",
234 target_os = "ios",
235 target_os = "linux",
236 target_os = "macos",
237 target_os = "solaris",
238 target_os = "tvos",
239 target_os = "visionos",
240 target_os = "watchos",
241 ))]
242 interface: Option<String>,
243 nodelay: bool,
244 #[cfg(feature = "cookies")]
245 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
246 hickory_dns: bool,
247 error: Option<crate::Error>,
248 https_only: bool,
249 #[cfg(feature = "http3")]
250 tls_enable_early_data: bool,
251 #[cfg(feature = "http3")]
252 quic_max_idle_timeout: Option<Duration>,
253 #[cfg(feature = "http3")]
254 quic_stream_receive_window: Option<VarInt>,
255 #[cfg(feature = "http3")]
256 quic_receive_window: Option<VarInt>,
257 #[cfg(feature = "http3")]
258 quic_send_window: Option<u64>,
259 #[cfg(feature = "http3")]
260 quic_congestion_bbr: bool,
261 #[cfg(feature = "http3")]
262 h3_max_field_section_size: Option<u64>,
263 #[cfg(feature = "http3")]
264 h3_send_grease: Option<bool>,
265 dns_overrides: HashMap<String, Vec<SocketAddr>>,
266 dns_resolver: Option<Arc<dyn Resolve>>,
267
268 #[cfg(unix)]
269 unix_socket: Option<Arc<std::path::Path>>,
270 #[cfg(target_os = "windows")]
271 windows_named_pipe: Option<Arc<std::ffi::OsStr>>,
272}
273
274impl Default for ClientBuilder {
275 fn default() -> Self {
276 Self::new()
277 }
278}
279
280impl ClientBuilder {
281 pub fn new() -> Self {
285 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
286 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
287
288 ClientBuilder {
289 config: Config {
290 error: None,
291 accepts: Accepts::default(),
292 headers,
293 #[cfg(feature = "__tls")]
294 hostname_verification: true,
295 #[cfg(feature = "__tls")]
296 certs_verification: true,
297 #[cfg(feature = "__tls")]
298 tls_sni: true,
299 connect_timeout: None,
300 connection_verbose: false,
301 pool_idle_timeout: Some(Duration::from_secs(90)),
302 pool_max_idle_per_host: usize::MAX,
303 tcp_keepalive: Some(Duration::from_secs(15)),
304 tcp_keepalive_interval: Some(Duration::from_secs(15)),
305 tcp_keepalive_retries: Some(3),
306 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
307 tcp_user_timeout: Some(Duration::from_secs(30)),
308 proxies: Vec::new(),
309 auto_sys_proxy: true,
310 redirect_policy: redirect::Policy::default(),
311 retry_policy: crate::retry::Builder::default(),
312 referer: true,
313 read_timeout: None,
314 timeout: None,
315 #[cfg(feature = "__tls")]
316 root_certs: Vec::new(),
317 #[cfg(feature = "__tls")]
318 tls_built_in_root_certs: true,
319 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
320 tls_built_in_certs_webpki: true,
321 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
322 tls_built_in_certs_native: true,
323 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
324 identity: None,
325 #[cfg(feature = "__rustls")]
326 crls: vec![],
327 #[cfg(feature = "__tls")]
328 min_tls_version: None,
329 #[cfg(feature = "__tls")]
330 max_tls_version: None,
331 #[cfg(feature = "__tls")]
332 tls_info: false,
333 #[cfg(feature = "__tls")]
334 tls: TlsBackend::default(),
335 connector_layers: Vec::new(),
336 http_version_pref: HttpVersionPref::All,
337 http09_responses: false,
338 http1_title_case_headers: false,
339 http1_allow_obsolete_multiline_headers_in_responses: false,
340 http1_ignore_invalid_headers_in_responses: false,
341 http1_allow_spaces_after_header_name_in_responses: false,
342 #[cfg(feature = "http2")]
343 http2_initial_stream_window_size: None,
344 #[cfg(feature = "http2")]
345 http2_initial_connection_window_size: None,
346 #[cfg(feature = "http2")]
347 http2_adaptive_window: false,
348 #[cfg(feature = "http2")]
349 http2_max_frame_size: None,
350 #[cfg(feature = "http2")]
351 http2_max_header_list_size: None,
352 #[cfg(feature = "http2")]
353 http2_keep_alive_interval: None,
354 #[cfg(feature = "http2")]
355 http2_keep_alive_timeout: None,
356 #[cfg(feature = "http2")]
357 http2_keep_alive_while_idle: false,
358 local_address: None,
359 #[cfg(any(
360 target_os = "android",
361 target_os = "fuchsia",
362 target_os = "illumos",
363 target_os = "ios",
364 target_os = "linux",
365 target_os = "macos",
366 target_os = "solaris",
367 target_os = "tvos",
368 target_os = "visionos",
369 target_os = "watchos",
370 ))]
371 interface: None,
372 nodelay: true,
373 hickory_dns: cfg!(feature = "hickory-dns"),
374 #[cfg(feature = "cookies")]
375 cookie_store: None,
376 https_only: false,
377 dns_overrides: HashMap::new(),
378 #[cfg(feature = "http3")]
379 tls_enable_early_data: false,
380 #[cfg(feature = "http3")]
381 quic_max_idle_timeout: None,
382 #[cfg(feature = "http3")]
383 quic_stream_receive_window: None,
384 #[cfg(feature = "http3")]
385 quic_receive_window: None,
386 #[cfg(feature = "http3")]
387 quic_send_window: None,
388 #[cfg(feature = "http3")]
389 quic_congestion_bbr: false,
390 #[cfg(feature = "http3")]
391 h3_max_field_section_size: None,
392 #[cfg(feature = "http3")]
393 h3_send_grease: None,
394 dns_resolver: None,
395 #[cfg(unix)]
396 unix_socket: None,
397 #[cfg(target_os = "windows")]
398 windows_named_pipe: None,
399 },
400 }
401 }
402}
403
404impl ClientBuilder {
405 pub fn build(self) -> crate::Result<Client> {
412 let config = self.config;
413
414 if let Some(err) = config.error {
415 return Err(err);
416 }
417
418 let mut proxies = config.proxies;
419 if config.auto_sys_proxy {
420 proxies.push(ProxyMatcher::system());
421 }
422 let proxies = Arc::new(proxies);
423
424 #[allow(unused)]
425 #[cfg(feature = "http3")]
426 let mut h3_connector = None;
427
428 let resolver = {
429 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
430 false => Arc::new(GaiResolver::new()),
431 #[cfg(feature = "hickory-dns")]
432 true => Arc::new(HickoryDnsResolver::default()),
433 #[cfg(not(feature = "hickory-dns"))]
434 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
435 };
436 if let Some(dns_resolver) = config.dns_resolver {
437 resolver = dns_resolver;
438 }
439 if !config.dns_overrides.is_empty() {
440 resolver = Arc::new(DnsResolverWithOverrides::new(
441 resolver,
442 config.dns_overrides,
443 ));
444 }
445 DynResolver::new(resolver)
446 };
447
448 let mut connector_builder = {
449 #[cfg(feature = "__tls")]
450 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
451 headers.get(USER_AGENT).cloned()
452 }
453
454 let mut http = HttpConnector::new_with_resolver(resolver.clone());
455 http.set_connect_timeout(config.connect_timeout);
456
457 #[cfg(all(feature = "http3", feature = "__rustls"))]
458 let build_h3_connector =
459 |resolver,
460 tls,
461 quic_max_idle_timeout: Option<Duration>,
462 quic_stream_receive_window,
463 quic_receive_window,
464 quic_send_window,
465 quic_congestion_bbr,
466 h3_max_field_section_size,
467 h3_send_grease,
468 local_address,
469 http_version_pref: &HttpVersionPref| {
470 let mut transport_config = TransportConfig::default();
471
472 if let Some(max_idle_timeout) = quic_max_idle_timeout {
473 transport_config.max_idle_timeout(Some(
474 max_idle_timeout.try_into().map_err(error::builder)?,
475 ));
476 }
477
478 if let Some(stream_receive_window) = quic_stream_receive_window {
479 transport_config.stream_receive_window(stream_receive_window);
480 }
481
482 if let Some(receive_window) = quic_receive_window {
483 transport_config.receive_window(receive_window);
484 }
485
486 if let Some(send_window) = quic_send_window {
487 transport_config.send_window(send_window);
488 }
489
490 if quic_congestion_bbr {
491 let factory = Arc::new(quinn::congestion::BbrConfig::default());
492 transport_config.congestion_controller_factory(factory);
493 }
494
495 let mut h3_client_config = H3ClientConfig::default();
496
497 if let Some(max_field_section_size) = h3_max_field_section_size {
498 h3_client_config.max_field_section_size = Some(max_field_section_size);
499 }
500
501 if let Some(send_grease) = h3_send_grease {
502 h3_client_config.send_grease = Some(send_grease);
503 }
504
505 let res = H3Connector::new(
506 resolver,
507 tls,
508 local_address,
509 transport_config,
510 h3_client_config,
511 );
512
513 match res {
514 Ok(connector) => Ok(Some(connector)),
515 Err(err) => {
516 if let HttpVersionPref::Http3 = http_version_pref {
517 Err(error::builder(err))
518 } else {
519 Ok(None)
520 }
521 }
522 }
523 };
524
525 #[cfg(feature = "__tls")]
526 match config.tls {
527 #[cfg(feature = "default-tls")]
528 TlsBackend::Default => {
529 let mut tls = TlsConnector::builder();
530
531 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
532 {
533 match config.http_version_pref {
534 HttpVersionPref::Http1 => {
535 tls.request_alpns(&["http/1.1"]);
536 }
537 #[cfg(feature = "http2")]
538 HttpVersionPref::Http2 => {
539 tls.request_alpns(&["h2"]);
540 }
541 HttpVersionPref::All => {
542 tls.request_alpns(&["h2", "http/1.1"]);
543 }
544 }
545 }
546
547 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
548
549 tls.danger_accept_invalid_certs(!config.certs_verification);
550
551 tls.use_sni(config.tls_sni);
552
553 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
554
555 for cert in config.root_certs {
556 cert.add_to_native_tls(&mut tls);
557 }
558
559 #[cfg(feature = "native-tls")]
560 {
561 if let Some(id) = config.identity {
562 id.add_to_native_tls(&mut tls)?;
563 }
564 }
565 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
566 {
567 if let Some(_id) = config.identity {
569 return Err(crate::error::builder("incompatible TLS identity type"));
570 }
571 }
572
573 if let Some(min_tls_version) = config.min_tls_version {
574 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
575 crate::error::builder("invalid minimum TLS version for backend")
579 })?;
580 tls.min_protocol_version(Some(protocol));
581 }
582
583 if let Some(max_tls_version) = config.max_tls_version {
584 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
585 crate::error::builder("invalid maximum TLS version for backend")
590 })?;
591 tls.max_protocol_version(Some(protocol));
592 }
593
594 ConnectorBuilder::new_default_tls(
595 http,
596 tls,
597 proxies.clone(),
598 user_agent(&config.headers),
599 config.local_address,
600 #[cfg(any(
601 target_os = "android",
602 target_os = "fuchsia",
603 target_os = "illumos",
604 target_os = "ios",
605 target_os = "linux",
606 target_os = "macos",
607 target_os = "solaris",
608 target_os = "tvos",
609 target_os = "visionos",
610 target_os = "watchos",
611 ))]
612 config.interface.as_deref(),
613 config.nodelay,
614 config.tls_info,
615 )?
616 }
617 #[cfg(feature = "native-tls")]
618 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_default_tls(
619 http,
620 conn,
621 proxies.clone(),
622 user_agent(&config.headers),
623 config.local_address,
624 #[cfg(any(
625 target_os = "android",
626 target_os = "fuchsia",
627 target_os = "illumos",
628 target_os = "ios",
629 target_os = "linux",
630 target_os = "macos",
631 target_os = "solaris",
632 target_os = "tvos",
633 target_os = "visionos",
634 target_os = "watchos",
635 ))]
636 config.interface.as_deref(),
637 config.nodelay,
638 config.tls_info,
639 ),
640 #[cfg(feature = "__rustls")]
641 TlsBackend::BuiltRustls(conn) => {
642 #[cfg(feature = "http3")]
643 {
644 h3_connector = build_h3_connector(
645 resolver.clone(),
646 conn.clone(),
647 config.quic_max_idle_timeout,
648 config.quic_stream_receive_window,
649 config.quic_receive_window,
650 config.quic_send_window,
651 config.quic_congestion_bbr,
652 config.h3_max_field_section_size,
653 config.h3_send_grease,
654 config.local_address,
655 &config.http_version_pref,
656 )?;
657 }
658
659 ConnectorBuilder::new_rustls_tls(
660 http,
661 conn,
662 proxies.clone(),
663 user_agent(&config.headers),
664 config.local_address,
665 #[cfg(any(
666 target_os = "android",
667 target_os = "fuchsia",
668 target_os = "illumos",
669 target_os = "ios",
670 target_os = "linux",
671 target_os = "macos",
672 target_os = "solaris",
673 target_os = "tvos",
674 target_os = "visionos",
675 target_os = "watchos",
676 ))]
677 config.interface.as_deref(),
678 config.nodelay,
679 config.tls_info,
680 )
681 }
682 #[cfg(feature = "__rustls")]
683 TlsBackend::Rustls => {
684 use crate::tls::{IgnoreHostname, NoVerifier};
685
686 let mut root_cert_store = rustls::RootCertStore::empty();
688 for cert in config.root_certs {
689 cert.add_to_rustls(&mut root_cert_store)?;
690 }
691
692 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
693 if config.tls_built_in_certs_webpki {
694 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
695 }
696
697 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
698 if config.tls_built_in_certs_native {
699 let mut valid_count = 0;
700 let mut invalid_count = 0;
701
702 let load_results = rustls_native_certs::load_native_certs();
703 for cert in load_results.certs {
704 match root_cert_store.add(cert.into()) {
708 Ok(_) => valid_count += 1,
709 Err(err) => {
710 invalid_count += 1;
711 log::debug!("rustls failed to parse DER certificate: {err:?}");
712 }
713 }
714 }
715 if valid_count == 0 && invalid_count > 0 {
716 let err = if load_results.errors.is_empty() {
717 crate::error::builder(
718 "zero valid certificates found in native root store",
719 )
720 } else {
721 use std::fmt::Write as _;
722 let mut acc = String::new();
723 for err in load_results.errors {
724 let _ = writeln!(&mut acc, "{err}");
725 }
726
727 crate::error::builder(acc)
728 };
729
730 return Err(err);
731 }
732 }
733
734 let mut versions = rustls::ALL_VERSIONS.to_vec();
736
737 if let Some(min_tls_version) = config.min_tls_version {
738 versions.retain(|&supported_version| {
739 match tls::Version::from_rustls(supported_version.version) {
740 Some(version) => version >= min_tls_version,
741 None => true,
744 }
745 });
746 }
747
748 if let Some(max_tls_version) = config.max_tls_version {
749 versions.retain(|&supported_version| {
750 match tls::Version::from_rustls(supported_version.version) {
751 Some(version) => version <= max_tls_version,
752 None => false,
753 }
754 });
755 }
756
757 if versions.is_empty() {
758 return Err(crate::error::builder("empty supported tls versions"));
759 }
760
761 let provider = rustls::crypto::CryptoProvider::get_default()
764 .map(|arc| arc.clone())
765 .unwrap_or_else(|| {
766 #[cfg(not(feature = "__rustls-ring"))]
767 panic!("No provider set");
768
769 #[cfg(feature = "__rustls-ring")]
770 Arc::new(rustls::crypto::ring::default_provider())
771 });
772
773 let signature_algorithms = provider.signature_verification_algorithms;
775 let config_builder =
776 rustls::ClientConfig::builder_with_provider(provider.clone())
777 .with_protocol_versions(&versions)
778 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
779
780 let config_builder = if !config.certs_verification {
781 config_builder
782 .dangerous()
783 .with_custom_certificate_verifier(Arc::new(NoVerifier))
784 } else if !config.hostname_verification {
785 config_builder
786 .dangerous()
787 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
788 root_cert_store,
789 signature_algorithms,
790 )))
791 } else {
792 if config.crls.is_empty() {
793 config_builder.with_root_certificates(root_cert_store)
794 } else {
795 let crls = config
796 .crls
797 .iter()
798 .map(|e| e.as_rustls_crl())
799 .collect::<Vec<_>>();
800 let verifier =
801 rustls::client::WebPkiServerVerifier::builder_with_provider(
802 Arc::new(root_cert_store),
803 provider,
804 )
805 .with_crls(crls)
806 .build()
807 .map_err(|_| {
808 crate::error::builder("invalid TLS verification settings")
809 })?;
810 config_builder.with_webpki_verifier(verifier)
811 }
812 };
813
814 let mut tls = if let Some(id) = config.identity {
816 id.add_to_rustls(config_builder)?
817 } else {
818 config_builder.with_no_client_auth()
819 };
820
821 tls.enable_sni = config.tls_sni;
822
823 match config.http_version_pref {
825 HttpVersionPref::Http1 => {
826 tls.alpn_protocols = vec!["http/1.1".into()];
827 }
828 #[cfg(feature = "http2")]
829 HttpVersionPref::Http2 => {
830 tls.alpn_protocols = vec!["h2".into()];
831 }
832 #[cfg(feature = "http3")]
833 HttpVersionPref::Http3 => {
834 tls.alpn_protocols = vec!["h3".into()];
835 }
836 HttpVersionPref::All => {
837 tls.alpn_protocols = vec![
838 #[cfg(feature = "http2")]
839 "h2".into(),
840 "http/1.1".into(),
841 ];
842 }
843 }
844
845 #[cfg(feature = "http3")]
846 {
847 tls.enable_early_data = config.tls_enable_early_data;
848
849 h3_connector = build_h3_connector(
850 resolver.clone(),
851 tls.clone(),
852 config.quic_max_idle_timeout,
853 config.quic_stream_receive_window,
854 config.quic_receive_window,
855 config.quic_send_window,
856 config.quic_congestion_bbr,
857 config.h3_max_field_section_size,
858 config.h3_send_grease,
859 config.local_address,
860 &config.http_version_pref,
861 )?;
862 }
863
864 ConnectorBuilder::new_rustls_tls(
865 http,
866 tls,
867 proxies.clone(),
868 user_agent(&config.headers),
869 config.local_address,
870 #[cfg(any(
871 target_os = "android",
872 target_os = "fuchsia",
873 target_os = "illumos",
874 target_os = "ios",
875 target_os = "linux",
876 target_os = "macos",
877 target_os = "solaris",
878 target_os = "tvos",
879 target_os = "visionos",
880 target_os = "watchos",
881 ))]
882 config.interface.as_deref(),
883 config.nodelay,
884 config.tls_info,
885 )
886 }
887 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
888 TlsBackend::UnknownPreconfigured => {
889 return Err(crate::error::builder(
890 "Unknown TLS backend passed to `use_preconfigured_tls`",
891 ));
892 }
893 }
894
895 #[cfg(not(feature = "__tls"))]
896 ConnectorBuilder::new(
897 http,
898 proxies.clone(),
899 config.local_address,
900 #[cfg(any(
901 target_os = "android",
902 target_os = "fuchsia",
903 target_os = "illumos",
904 target_os = "ios",
905 target_os = "linux",
906 target_os = "macos",
907 target_os = "solaris",
908 target_os = "tvos",
909 target_os = "visionos",
910 target_os = "watchos",
911 ))]
912 config.interface.as_deref(),
913 config.nodelay,
914 )
915 };
916
917 connector_builder.set_timeout(config.connect_timeout);
918 connector_builder.set_verbose(config.connection_verbose);
919 connector_builder.set_keepalive(config.tcp_keepalive);
920 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
921 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
922 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
923 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
924
925 #[cfg(feature = "socks")]
926 connector_builder.set_socks_resolver(resolver);
927
928 #[cfg(unix)]
932 connector_builder.set_unix_socket(config.unix_socket);
933 #[cfg(target_os = "windows")]
934 connector_builder.set_windows_named_pipe(config.windows_named_pipe.clone());
935
936 let mut builder =
937 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
938 #[cfg(feature = "http2")]
939 {
940 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
941 builder.http2_only(true);
942 }
943
944 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
945 {
946 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
947 }
948 if let Some(http2_initial_connection_window_size) =
949 config.http2_initial_connection_window_size
950 {
951 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
952 }
953 if config.http2_adaptive_window {
954 builder.http2_adaptive_window(true);
955 }
956 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
957 builder.http2_max_frame_size(http2_max_frame_size);
958 }
959 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
960 builder.http2_max_header_list_size(http2_max_header_list_size);
961 }
962 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
963 builder.http2_keep_alive_interval(http2_keep_alive_interval);
964 }
965 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
966 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
967 }
968 if config.http2_keep_alive_while_idle {
969 builder.http2_keep_alive_while_idle(true);
970 }
971 }
972
973 builder.timer(hyper_util::rt::TokioTimer::new());
974 builder.pool_timer(hyper_util::rt::TokioTimer::new());
975 builder.pool_idle_timeout(config.pool_idle_timeout);
976 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
977
978 if config.http09_responses {
979 builder.http09_responses(true);
980 }
981
982 if config.http1_title_case_headers {
983 builder.http1_title_case_headers(true);
984 }
985
986 if config.http1_allow_obsolete_multiline_headers_in_responses {
987 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
988 }
989
990 if config.http1_ignore_invalid_headers_in_responses {
991 builder.http1_ignore_invalid_headers_in_responses(true);
992 }
993
994 if config.http1_allow_spaces_after_header_name_in_responses {
995 builder.http1_allow_spaces_after_header_name_in_responses(true);
996 }
997
998 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
999 let proxies_maybe_http_custom_headers =
1000 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
1001
1002 let redirect_policy_desc = if config.redirect_policy.is_default() {
1003 None
1004 } else {
1005 Some(format!("{:?}", &config.redirect_policy))
1006 };
1007
1008 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
1009 let hyper_service = HyperService {
1010 hyper: hyper_client,
1011 };
1012
1013 let redirect_policy = {
1014 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
1015 p.with_referer(config.referer)
1016 .with_https_only(config.https_only);
1017 p
1018 };
1019
1020 let retry_policy = config.retry_policy.into_policy();
1021
1022 let svc = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
1023
1024 #[cfg(feature = "cookies")]
1025 let svc = CookieService::new(svc, config.cookie_store.clone());
1026 let hyper = FollowRedirect::with_policy(svc, redirect_policy.clone());
1027 #[cfg(any(
1028 feature = "gzip",
1029 feature = "brotli",
1030 feature = "zstd",
1031 feature = "deflate"
1032 ))]
1033 let hyper = Decompression::new(hyper)
1034 .no_gzip()
1037 .no_deflate()
1038 .no_br()
1039 .no_zstd();
1040 #[cfg(feature = "gzip")]
1041 let hyper = hyper.gzip(config.accepts.gzip);
1042 #[cfg(feature = "brotli")]
1043 let hyper = hyper.br(config.accepts.brotli);
1044 #[cfg(feature = "zstd")]
1045 let hyper = hyper.zstd(config.accepts.zstd);
1046 #[cfg(feature = "deflate")]
1047 let hyper = hyper.deflate(config.accepts.deflate);
1048
1049 Ok(Client {
1050 inner: Arc::new(ClientRef {
1051 accepts: config.accepts,
1052 #[cfg(feature = "cookies")]
1053 cookie_store: config.cookie_store.clone(),
1054 #[cfg(feature = "http3")]
1057 h3_client: match h3_connector {
1058 Some(h3_connector) => {
1059 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1060 let svc = tower::retry::Retry::new(retry_policy, h3_service);
1061 #[cfg(feature = "cookies")]
1062 let svc = CookieService::new(svc, config.cookie_store);
1063 let svc = FollowRedirect::with_policy(svc, redirect_policy);
1064 #[cfg(any(
1065 feature = "gzip",
1066 feature = "brotli",
1067 feature = "zstd",
1068 feature = "deflate"
1069 ))]
1070 let svc = Decompression::new(svc)
1071 .no_gzip()
1074 .no_deflate()
1075 .no_br()
1076 .no_zstd();
1077 #[cfg(feature = "gzip")]
1078 let svc = svc.gzip(config.accepts.gzip);
1079 #[cfg(feature = "brotli")]
1080 let svc = svc.br(config.accepts.brotli);
1081 #[cfg(feature = "zstd")]
1082 let svc = svc.zstd(config.accepts.zstd);
1083 #[cfg(feature = "deflate")]
1084 let svc = svc.deflate(config.accepts.deflate);
1085 Some(svc)
1086 }
1087 None => None,
1088 },
1089 headers: config.headers,
1090 referer: config.referer,
1091 read_timeout: config.read_timeout,
1092 total_timeout: RequestConfig::new(config.timeout),
1093 hyper,
1094 proxies,
1095 proxies_maybe_http_auth,
1096 proxies_maybe_http_custom_headers,
1097 https_only: config.https_only,
1098 redirect_policy_desc,
1099 }),
1100 })
1101 }
1102
1103 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1126 where
1127 V: TryInto<HeaderValue>,
1128 V::Error: Into<http::Error>,
1129 {
1130 match value.try_into() {
1131 Ok(value) => {
1132 self.config.headers.insert(USER_AGENT, value);
1133 }
1134 Err(e) => {
1135 self.config.error = Some(crate::error::builder(e.into()));
1136 }
1137 };
1138 self
1139 }
1140 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1164 for (key, value) in headers.iter() {
1165 self.config.headers.insert(key, value.clone());
1166 }
1167 self
1168 }
1169
1170 #[cfg(feature = "cookies")]
1185 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1186 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1187 if enable {
1188 self.cookie_provider(Arc::new(cookie::Jar::default()))
1189 } else {
1190 self.config.cookie_store = None;
1191 self
1192 }
1193 }
1194
1195 #[cfg(feature = "cookies")]
1209 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1210 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1211 mut self,
1212 cookie_store: Arc<C>,
1213 ) -> ClientBuilder {
1214 self.config.cookie_store = Some(cookie_store as _);
1215 self
1216 }
1217
1218 #[cfg(feature = "gzip")]
1235 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1236 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1237 self.config.accepts.gzip = enable;
1238 self
1239 }
1240
1241 #[cfg(feature = "brotli")]
1258 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1259 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1260 self.config.accepts.brotli = enable;
1261 self
1262 }
1263
1264 #[cfg(feature = "zstd")]
1281 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1282 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1283 self.config.accepts.zstd = enable;
1284 self
1285 }
1286
1287 #[cfg(feature = "deflate")]
1304 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1305 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1306 self.config.accepts.deflate = enable;
1307 self
1308 }
1309
1310 pub fn no_gzip(self) -> ClientBuilder {
1316 #[cfg(feature = "gzip")]
1317 {
1318 self.gzip(false)
1319 }
1320
1321 #[cfg(not(feature = "gzip"))]
1322 {
1323 self
1324 }
1325 }
1326
1327 pub fn no_brotli(self) -> ClientBuilder {
1333 #[cfg(feature = "brotli")]
1334 {
1335 self.brotli(false)
1336 }
1337
1338 #[cfg(not(feature = "brotli"))]
1339 {
1340 self
1341 }
1342 }
1343
1344 pub fn no_zstd(self) -> ClientBuilder {
1350 #[cfg(feature = "zstd")]
1351 {
1352 self.zstd(false)
1353 }
1354
1355 #[cfg(not(feature = "zstd"))]
1356 {
1357 self
1358 }
1359 }
1360
1361 pub fn no_deflate(self) -> ClientBuilder {
1367 #[cfg(feature = "deflate")]
1368 {
1369 self.deflate(false)
1370 }
1371
1372 #[cfg(not(feature = "deflate"))]
1373 {
1374 self
1375 }
1376 }
1377
1378 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1384 self.config.redirect_policy = policy;
1385 self
1386 }
1387
1388 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1392 self.config.referer = enable;
1393 self
1394 }
1395
1396 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1403 self.config.retry_policy = policy;
1404 self
1405 }
1406
1407 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1415 self.config.proxies.push(proxy.into_matcher());
1416 self.config.auto_sys_proxy = false;
1417 self
1418 }
1419
1420 pub fn no_proxy(mut self) -> ClientBuilder {
1428 self.config.proxies.clear();
1429 self.config.auto_sys_proxy = false;
1430 self
1431 }
1432
1433 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1442 self.config.timeout = Some(timeout);
1443 self
1444 }
1445
1446 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1454 self.config.read_timeout = Some(timeout);
1455 self
1456 }
1457
1458 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1467 self.config.connect_timeout = Some(timeout);
1468 self
1469 }
1470
1471 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1478 self.config.connection_verbose = verbose;
1479 self
1480 }
1481
1482 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1490 where
1491 D: Into<Option<Duration>>,
1492 {
1493 self.config.pool_idle_timeout = val.into();
1494 self
1495 }
1496
1497 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1501 self.config.pool_max_idle_per_host = max;
1502 self
1503 }
1504
1505 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1507 self.config.http1_title_case_headers = true;
1508 self
1509 }
1510
1511 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1517 mut self,
1518 value: bool,
1519 ) -> ClientBuilder {
1520 self.config
1521 .http1_allow_obsolete_multiline_headers_in_responses = value;
1522 self
1523 }
1524
1525 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1527 self.config.http1_ignore_invalid_headers_in_responses = value;
1528 self
1529 }
1530
1531 pub fn http1_allow_spaces_after_header_name_in_responses(
1537 mut self,
1538 value: bool,
1539 ) -> ClientBuilder {
1540 self.config
1541 .http1_allow_spaces_after_header_name_in_responses = value;
1542 self
1543 }
1544
1545 pub fn http1_only(mut self) -> ClientBuilder {
1547 self.config.http_version_pref = HttpVersionPref::Http1;
1548 self
1549 }
1550
1551 pub fn http09_responses(mut self) -> ClientBuilder {
1553 self.config.http09_responses = true;
1554 self
1555 }
1556
1557 #[cfg(feature = "http2")]
1559 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1560 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1561 self.config.http_version_pref = HttpVersionPref::Http2;
1562 self
1563 }
1564
1565 #[cfg(feature = "http3")]
1567 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1568 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1569 self.config.http_version_pref = HttpVersionPref::Http3;
1570 self
1571 }
1572
1573 #[cfg(feature = "http2")]
1577 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1578 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1579 self.config.http2_initial_stream_window_size = sz.into();
1580 self
1581 }
1582
1583 #[cfg(feature = "http2")]
1587 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1588 pub fn http2_initial_connection_window_size(
1589 mut self,
1590 sz: impl Into<Option<u32>>,
1591 ) -> ClientBuilder {
1592 self.config.http2_initial_connection_window_size = sz.into();
1593 self
1594 }
1595
1596 #[cfg(feature = "http2")]
1601 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1602 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1603 self.config.http2_adaptive_window = enabled;
1604 self
1605 }
1606
1607 #[cfg(feature = "http2")]
1611 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1612 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1613 self.config.http2_max_frame_size = sz.into();
1614 self
1615 }
1616
1617 #[cfg(feature = "http2")]
1621 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1622 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1623 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1624 self
1625 }
1626
1627 #[cfg(feature = "http2")]
1632 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1633 pub fn http2_keep_alive_interval(
1634 mut self,
1635 interval: impl Into<Option<Duration>>,
1636 ) -> ClientBuilder {
1637 self.config.http2_keep_alive_interval = interval.into();
1638 self
1639 }
1640
1641 #[cfg(feature = "http2")]
1647 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1648 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1649 self.config.http2_keep_alive_timeout = Some(timeout);
1650 self
1651 }
1652
1653 #[cfg(feature = "http2")]
1660 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1661 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1662 self.config.http2_keep_alive_while_idle = enabled;
1663 self
1664 }
1665
1666 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1672 self.config.nodelay = enabled;
1673 self
1674 }
1675
1676 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1691 where
1692 T: Into<Option<IpAddr>>,
1693 {
1694 self.config.local_address = addr.into();
1695 self
1696 }
1697
1698 #[cfg(any(
1731 target_os = "android",
1732 target_os = "fuchsia",
1733 target_os = "illumos",
1734 target_os = "ios",
1735 target_os = "linux",
1736 target_os = "macos",
1737 target_os = "solaris",
1738 target_os = "tvos",
1739 target_os = "visionos",
1740 target_os = "watchos",
1741 ))]
1742 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1743 self.config.interface = Some(interface.to_string());
1744 self
1745 }
1746
1747 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1751 where
1752 D: Into<Option<Duration>>,
1753 {
1754 self.config.tcp_keepalive = val.into();
1755 self
1756 }
1757
1758 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1762 where
1763 D: Into<Option<Duration>>,
1764 {
1765 self.config.tcp_keepalive_interval = val.into();
1766 self
1767 }
1768
1769 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1773 where
1774 C: Into<Option<u32>>,
1775 {
1776 self.config.tcp_keepalive_retries = retries.into();
1777 self
1778 }
1779
1780 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1787 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1788 where
1789 D: Into<Option<Duration>>,
1790 {
1791 self.config.tcp_user_timeout = val.into();
1792 self
1793 }
1794
1795 #[cfg(unix)]
1809 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1810 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1811 self
1812 }
1813
1814 #[cfg(target_os = "windows")]
1826 pub fn windows_named_pipe(mut self, pipe: impl WindowsNamedPipeProvider) -> ClientBuilder {
1827 self.config.windows_named_pipe = Some(
1828 pipe.reqwest_windows_named_pipe_path(crate::connect::windows_named_pipe::Internal)
1829 .into(),
1830 );
1831 self
1832 }
1833
1834 #[cfg(feature = "__tls")]
1846 #[cfg_attr(
1847 docsrs,
1848 doc(cfg(any(
1849 feature = "default-tls",
1850 feature = "native-tls",
1851 feature = "rustls-tls"
1852 )))
1853 )]
1854 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1855 self.config.root_certs.push(cert);
1856 self
1857 }
1858
1859 #[cfg(feature = "__rustls")]
1866 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1867 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1868 self.config.crls.push(crl);
1869 self
1870 }
1871
1872 #[cfg(feature = "__rustls")]
1879 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1880 pub fn add_crls(
1881 mut self,
1882 crls: impl IntoIterator<Item = CertificateRevocationList>,
1883 ) -> ClientBuilder {
1884 self.config.crls.extend(crls);
1885 self
1886 }
1887
1888 #[cfg(feature = "__tls")]
1906 #[cfg_attr(
1907 docsrs,
1908 doc(cfg(any(
1909 feature = "default-tls",
1910 feature = "native-tls",
1911 feature = "rustls-tls"
1912 )))
1913 )]
1914 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1915 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1916
1917 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1918 {
1919 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1920 }
1921
1922 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1923 {
1924 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1925 }
1926
1927 self
1928 }
1929
1930 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1934 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1935 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1936 self.config.tls_built_in_certs_webpki = enabled;
1937 self
1938 }
1939
1940 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1944 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1945 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1946 self.config.tls_built_in_certs_native = enabled;
1947 self
1948 }
1949
1950 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1957 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1958 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1959 self.config.identity = Some(identity);
1960 self
1961 }
1962
1963 #[cfg(feature = "__tls")]
1979 #[cfg_attr(
1980 docsrs,
1981 doc(cfg(any(
1982 feature = "default-tls",
1983 feature = "native-tls",
1984 feature = "rustls-tls"
1985 )))
1986 )]
1987 pub fn danger_accept_invalid_hostnames(
1988 mut self,
1989 accept_invalid_hostname: bool,
1990 ) -> ClientBuilder {
1991 self.config.hostname_verification = !accept_invalid_hostname;
1992 self
1993 }
1994
1995 #[cfg(feature = "__tls")]
2012 #[cfg_attr(
2013 docsrs,
2014 doc(cfg(any(
2015 feature = "default-tls",
2016 feature = "native-tls",
2017 feature = "rustls-tls"
2018 )))
2019 )]
2020 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
2021 self.config.certs_verification = !accept_invalid_certs;
2022 self
2023 }
2024
2025 #[cfg(feature = "__tls")]
2034 #[cfg_attr(
2035 docsrs,
2036 doc(cfg(any(
2037 feature = "default-tls",
2038 feature = "native-tls",
2039 feature = "rustls-tls"
2040 )))
2041 )]
2042 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
2043 self.config.tls_sni = tls_sni;
2044 self
2045 }
2046
2047 #[cfg(feature = "__tls")]
2063 #[cfg_attr(
2064 docsrs,
2065 doc(cfg(any(
2066 feature = "default-tls",
2067 feature = "native-tls",
2068 feature = "rustls-tls"
2069 )))
2070 )]
2071 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
2072 self.config.min_tls_version = Some(version);
2073 self
2074 }
2075
2076 #[cfg(feature = "__tls")]
2095 #[cfg_attr(
2096 docsrs,
2097 doc(cfg(any(
2098 feature = "default-tls",
2099 feature = "native-tls",
2100 feature = "rustls-tls"
2101 )))
2102 )]
2103 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
2104 self.config.max_tls_version = Some(version);
2105 self
2106 }
2107
2108 #[cfg(feature = "native-tls")]
2117 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2118 pub fn use_native_tls(mut self) -> ClientBuilder {
2119 self.config.tls = TlsBackend::Default;
2120 self
2121 }
2122
2123 #[cfg(feature = "__rustls")]
2132 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
2133 pub fn use_rustls_tls(mut self) -> ClientBuilder {
2134 self.config.tls = TlsBackend::Rustls;
2135 self
2136 }
2137
2138 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
2157 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
2158 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
2159 let mut tls = Some(tls);
2160 #[cfg(feature = "native-tls")]
2161 {
2162 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2163 let tls = conn.take().expect("is definitely Some");
2164 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2165 self.config.tls = tls;
2166 return self;
2167 }
2168 }
2169 #[cfg(feature = "__rustls")]
2170 {
2171 if let Some(conn) =
2172 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2173 {
2174 let tls = conn.take().expect("is definitely Some");
2175 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2176 self.config.tls = tls;
2177 return self;
2178 }
2179 }
2180
2181 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2183 self
2184 }
2185
2186 #[cfg(feature = "__tls")]
2193 #[cfg_attr(
2194 docsrs,
2195 doc(cfg(any(
2196 feature = "default-tls",
2197 feature = "native-tls",
2198 feature = "rustls-tls"
2199 )))
2200 )]
2201 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2202 self.config.tls_info = tls_info;
2203 self
2204 }
2205
2206 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2210 self.config.https_only = enabled;
2211 self
2212 }
2213
2214 #[doc(hidden)]
2215 #[cfg(feature = "hickory-dns")]
2216 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2217 #[deprecated(note = "use `hickory_dns` instead")]
2218 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
2219 self.config.hickory_dns = enable;
2220 self
2221 }
2222
2223 #[cfg(feature = "hickory-dns")]
2237 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2238 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2239 self.config.hickory_dns = enable;
2240 self
2241 }
2242
2243 #[doc(hidden)]
2244 #[deprecated(note = "use `no_hickory_dns` instead")]
2245 pub fn no_trust_dns(self) -> ClientBuilder {
2246 self.no_hickory_dns()
2247 }
2248
2249 pub fn no_hickory_dns(self) -> ClientBuilder {
2255 #[cfg(feature = "hickory-dns")]
2256 {
2257 self.hickory_dns(false)
2258 }
2259
2260 #[cfg(not(feature = "hickory-dns"))]
2261 {
2262 self
2263 }
2264 }
2265
2266 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2271 self.resolve_to_addrs(domain, &[addr])
2272 }
2273
2274 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2279 self.config
2280 .dns_overrides
2281 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2282 self
2283 }
2284
2285 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
2291 self.config.dns_resolver = Some(resolver as _);
2292 self
2293 }
2294
2295 pub fn dns_resolver2<R>(mut self, resolver: R) -> ClientBuilder
2302 where
2303 R: crate::dns::resolve::IntoResolve,
2304 {
2305 self.config.dns_resolver = Some(resolver.into_resolve());
2306 self
2307 }
2308
2309 #[cfg(feature = "http3")]
2314 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2315 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2316 self.config.tls_enable_early_data = enabled;
2317 self
2318 }
2319
2320 #[cfg(feature = "http3")]
2326 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2327 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2328 self.config.quic_max_idle_timeout = Some(value);
2329 self
2330 }
2331
2332 #[cfg(feature = "http3")]
2343 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2344 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2345 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2346 self
2347 }
2348
2349 #[cfg(feature = "http3")]
2360 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2361 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2362 self.config.quic_receive_window = Some(value.try_into().unwrap());
2363 self
2364 }
2365
2366 #[cfg(feature = "http3")]
2372 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2373 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2374 self.config.quic_send_window = Some(value);
2375 self
2376 }
2377
2378 #[cfg(feature = "http3")]
2386 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2387 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2388 self.config.quic_congestion_bbr = true;
2389 self
2390 }
2391
2392 #[cfg(feature = "http3")]
2402 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2403 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2404 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2405 self
2406 }
2407
2408 #[cfg(feature = "http3")]
2420 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2421 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2422 self.config.h3_send_grease = Some(enabled);
2423 self
2424 }
2425
2426 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2450 where
2451 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2452 L::Service:
2453 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2454 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2455 {
2456 let layer = BoxCloneSyncServiceLayer::new(layer);
2457
2458 self.config.connector_layers.push(layer);
2459
2460 self
2461 }
2462}
2463
2464type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2465
2466impl Default for Client {
2467 fn default() -> Self {
2468 Self::new()
2469 }
2470}
2471
2472impl Client {
2473 pub fn new() -> Client {
2483 ClientBuilder::new().build().expect("Client::new()")
2484 }
2485
2486 pub fn builder() -> ClientBuilder {
2490 ClientBuilder::new()
2491 }
2492
2493 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2499 self.request(Method::GET, url)
2500 }
2501
2502 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2508 self.request(Method::POST, url)
2509 }
2510
2511 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2517 self.request(Method::PUT, url)
2518 }
2519
2520 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2526 self.request(Method::PATCH, url)
2527 }
2528
2529 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2535 self.request(Method::DELETE, url)
2536 }
2537
2538 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2544 self.request(Method::HEAD, url)
2545 }
2546
2547 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2556 let req = url.into_url().map(move |url| Request::new(method, url));
2557 RequestBuilder::new(self.clone(), req)
2558 }
2559
2560 pub fn execute(
2573 &self,
2574 request: Request,
2575 ) -> impl Future<Output = Result<Response, crate::Error>> {
2576 self.execute_request(request)
2577 }
2578
2579 pub(super) fn execute_request(&self, req: Request) -> Pending {
2580 let (method, url, mut headers, body, version, extensions) = req.pieces();
2581 if url.scheme() != "http" && url.scheme() != "https" {
2582 return Pending::new_err(error::url_bad_scheme(url));
2583 }
2584
2585 if self.inner.https_only && url.scheme() != "https" {
2587 return Pending::new_err(error::url_bad_scheme(url));
2588 }
2589
2590 for (key, value) in &self.inner.headers {
2593 if let Entry::Vacant(entry) = headers.entry(key) {
2594 entry.insert(value.clone());
2595 }
2596 }
2597
2598 let uri = match try_uri(&url) {
2599 Ok(uri) => uri,
2600 _ => return Pending::new_err(error::url_invalid_uri(url)),
2601 };
2602
2603 let body = body.unwrap_or_else(Body::empty);
2604
2605 self.proxy_auth(&uri, &mut headers);
2606 self.proxy_custom_headers(&uri, &mut headers);
2607
2608 let builder = hyper::Request::builder()
2609 .method(method.clone())
2610 .uri(uri)
2611 .version(version);
2612
2613 let in_flight = match version {
2614 #[cfg(feature = "http3")]
2615 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2616 let mut req = builder.body(body).expect("valid request parts");
2617 *req.headers_mut() = headers.clone();
2618 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2619 ResponseFuture::H3(h3.call(req))
2620 }
2621 _ => {
2622 let mut req = builder.body(body).expect("valid request parts");
2623 *req.headers_mut() = headers.clone();
2624 let mut hyper = self.inner.hyper.clone();
2625 ResponseFuture::Default(hyper.call(req))
2626 }
2627 };
2628
2629 let total_timeout = self
2630 .inner
2631 .total_timeout
2632 .fetch(&extensions)
2633 .copied()
2634 .map(tokio::time::sleep)
2635 .map(Box::pin);
2636
2637 let read_timeout_fut = self
2638 .inner
2639 .read_timeout
2640 .map(tokio::time::sleep)
2641 .map(Box::pin);
2642
2643 Pending {
2644 inner: PendingInner::Request(Box::pin(PendingRequest {
2645 method,
2646 url,
2647 headers,
2648
2649 client: self.inner.clone(),
2650
2651 in_flight,
2652 total_timeout,
2653 read_timeout_fut,
2654 read_timeout: self.inner.read_timeout,
2655 })),
2656 }
2657 }
2658
2659 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2660 if !self.inner.proxies_maybe_http_auth {
2661 return;
2662 }
2663
2664 if dst.scheme() != Some(&Scheme::HTTP) {
2668 return;
2669 }
2670
2671 if headers.contains_key(PROXY_AUTHORIZATION) {
2672 return;
2673 }
2674
2675 for proxy in self.inner.proxies.iter() {
2676 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2677 headers.insert(PROXY_AUTHORIZATION, header);
2678 break;
2679 }
2680 }
2681 }
2682
2683 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2684 if !self.inner.proxies_maybe_http_custom_headers {
2685 return;
2686 }
2687
2688 if dst.scheme() != Some(&Scheme::HTTP) {
2689 return;
2690 }
2691
2692 for proxy in self.inner.proxies.iter() {
2693 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2694 iter.iter().for_each(|(key, value)| {
2695 headers.insert(key, value.clone());
2696 });
2697 break;
2698 }
2699 }
2700 }
2701}
2702
2703impl fmt::Debug for Client {
2704 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2705 let mut builder = f.debug_struct("Client");
2706 self.inner.fmt_fields(&mut builder);
2707 builder.finish()
2708 }
2709}
2710
2711impl tower_service::Service<Request> for Client {
2712 type Response = Response;
2713 type Error = crate::Error;
2714 type Future = Pending;
2715
2716 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2717 Poll::Ready(Ok(()))
2718 }
2719
2720 fn call(&mut self, req: Request) -> Self::Future {
2721 self.execute_request(req)
2722 }
2723}
2724
2725impl tower_service::Service<Request> for &'_ Client {
2726 type Response = Response;
2727 type Error = crate::Error;
2728 type Future = Pending;
2729
2730 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2731 Poll::Ready(Ok(()))
2732 }
2733
2734 fn call(&mut self, req: Request) -> Self::Future {
2735 self.execute_request(req)
2736 }
2737}
2738
2739impl fmt::Debug for ClientBuilder {
2740 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2741 let mut builder = f.debug_struct("ClientBuilder");
2742 self.config.fmt_fields(&mut builder);
2743 builder.finish()
2744 }
2745}
2746
2747impl Config {
2748 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2749 #[cfg(feature = "cookies")]
2753 {
2754 if let Some(_) = self.cookie_store {
2755 f.field("cookie_store", &true);
2756 }
2757 }
2758
2759 f.field("accepts", &self.accepts);
2760
2761 if !self.proxies.is_empty() {
2762 f.field("proxies", &self.proxies);
2763 }
2764
2765 if !self.redirect_policy.is_default() {
2766 f.field("redirect_policy", &self.redirect_policy);
2767 }
2768
2769 if self.referer {
2770 f.field("referer", &true);
2771 }
2772
2773 f.field("default_headers", &self.headers);
2774
2775 if self.http1_title_case_headers {
2776 f.field("http1_title_case_headers", &true);
2777 }
2778
2779 if self.http1_allow_obsolete_multiline_headers_in_responses {
2780 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2781 }
2782
2783 if self.http1_ignore_invalid_headers_in_responses {
2784 f.field("http1_ignore_invalid_headers_in_responses", &true);
2785 }
2786
2787 if self.http1_allow_spaces_after_header_name_in_responses {
2788 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2789 }
2790
2791 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2792 f.field("http1_only", &true);
2793 }
2794
2795 #[cfg(feature = "http2")]
2796 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2797 f.field("http2_prior_knowledge", &true);
2798 }
2799
2800 if let Some(ref d) = self.connect_timeout {
2801 f.field("connect_timeout", d);
2802 }
2803
2804 if let Some(ref d) = self.timeout {
2805 f.field("timeout", d);
2806 }
2807
2808 if let Some(ref v) = self.local_address {
2809 f.field("local_address", v);
2810 }
2811
2812 #[cfg(any(
2813 target_os = "android",
2814 target_os = "fuchsia",
2815 target_os = "illumos",
2816 target_os = "ios",
2817 target_os = "linux",
2818 target_os = "macos",
2819 target_os = "solaris",
2820 target_os = "tvos",
2821 target_os = "visionos",
2822 target_os = "watchos",
2823 ))]
2824 if let Some(ref v) = self.interface {
2825 f.field("interface", v);
2826 }
2827
2828 if self.nodelay {
2829 f.field("tcp_nodelay", &true);
2830 }
2831
2832 #[cfg(feature = "__tls")]
2833 {
2834 if !self.hostname_verification {
2835 f.field("danger_accept_invalid_hostnames", &true);
2836 }
2837 }
2838
2839 #[cfg(feature = "__tls")]
2840 {
2841 if !self.certs_verification {
2842 f.field("danger_accept_invalid_certs", &true);
2843 }
2844
2845 if let Some(ref min_tls_version) = self.min_tls_version {
2846 f.field("min_tls_version", min_tls_version);
2847 }
2848
2849 if let Some(ref max_tls_version) = self.max_tls_version {
2850 f.field("max_tls_version", max_tls_version);
2851 }
2852
2853 f.field("tls_sni", &self.tls_sni);
2854
2855 f.field("tls_info", &self.tls_info);
2856 }
2857
2858 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2859 {
2860 f.field("tls_backend", &self.tls);
2861 }
2862
2863 if !self.dns_overrides.is_empty() {
2864 f.field("dns_overrides", &self.dns_overrides);
2865 }
2866
2867 #[cfg(feature = "http3")]
2868 {
2869 if self.tls_enable_early_data {
2870 f.field("tls_enable_early_data", &true);
2871 }
2872 }
2873
2874 #[cfg(unix)]
2875 if let Some(ref p) = self.unix_socket {
2876 f.field("unix_socket", p);
2877 }
2878 }
2879}
2880
2881#[cfg(not(feature = "cookies"))]
2882type MaybeCookieService<T> = T;
2883
2884#[cfg(feature = "cookies")]
2885type MaybeCookieService<T> = CookieService<T>;
2886
2887#[cfg(not(any(
2888 feature = "gzip",
2889 feature = "brotli",
2890 feature = "zstd",
2891 feature = "deflate"
2892)))]
2893type MaybeDecompression<T> = T;
2894
2895#[cfg(any(
2896 feature = "gzip",
2897 feature = "brotli",
2898 feature = "zstd",
2899 feature = "deflate"
2900))]
2901type MaybeDecompression<T> = Decompression<T>;
2902
2903type LayeredService<T> = MaybeDecompression<
2904 FollowRedirect<
2905 MaybeCookieService<tower::retry::Retry<crate::retry::Policy, T>>,
2906 TowerRedirectPolicy,
2907 >,
2908>;
2909type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
2910
2911struct ClientRef {
2912 accepts: Accepts,
2913 #[cfg(feature = "cookies")]
2914 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2915 headers: HeaderMap,
2916 hyper: LayeredService<HyperService>,
2917 #[cfg(feature = "http3")]
2918 h3_client: Option<LayeredService<H3Client>>,
2919 referer: bool,
2920 total_timeout: RequestConfig<TotalTimeout>,
2921 read_timeout: Option<Duration>,
2922 proxies: Arc<Vec<ProxyMatcher>>,
2923 proxies_maybe_http_auth: bool,
2924 proxies_maybe_http_custom_headers: bool,
2925 https_only: bool,
2926 redirect_policy_desc: Option<String>,
2927}
2928
2929impl ClientRef {
2930 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2931 #[cfg(feature = "cookies")]
2935 {
2936 if let Some(_) = self.cookie_store {
2937 f.field("cookie_store", &true);
2938 }
2939 }
2940
2941 f.field("accepts", &self.accepts);
2942
2943 if !self.proxies.is_empty() {
2944 f.field("proxies", &self.proxies);
2945 }
2946
2947 if let Some(s) = &self.redirect_policy_desc {
2948 f.field("redirect_policy", s);
2949 }
2950
2951 if self.referer {
2952 f.field("referer", &true);
2953 }
2954
2955 f.field("default_headers", &self.headers);
2956
2957 self.total_timeout.fmt_as_field(f);
2958
2959 if let Some(ref d) = self.read_timeout {
2960 f.field("read_timeout", d);
2961 }
2962 }
2963}
2964
2965pin_project! {
2966 pub struct Pending {
2967 #[pin]
2968 inner: PendingInner,
2969 }
2970}
2971
2972enum PendingInner {
2973 Request(Pin<Box<PendingRequest>>),
2974 Error(Option<crate::Error>),
2975}
2976
2977pin_project! {
2978 struct PendingRequest {
2979 method: Method,
2980 url: Url,
2981 headers: HeaderMap,
2982
2983 client: Arc<ClientRef>,
2984
2985 #[pin]
2986 in_flight: ResponseFuture,
2987 #[pin]
2988 total_timeout: Option<Pin<Box<Sleep>>>,
2989 #[pin]
2990 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2991 read_timeout: Option<Duration>,
2992 }
2993}
2994
2995enum ResponseFuture {
2996 Default(LayeredFuture<HyperService>),
2997 #[cfg(feature = "http3")]
2998 H3(LayeredFuture<H3Client>),
2999}
3000
3001impl PendingRequest {
3002 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
3003 self.project().in_flight
3004 }
3005
3006 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3007 self.project().total_timeout
3008 }
3009
3010 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3011 self.project().read_timeout_fut
3012 }
3013}
3014
3015impl Pending {
3016 pub(super) fn new_err(err: crate::Error) -> Pending {
3017 Pending {
3018 inner: PendingInner::Error(Some(err)),
3019 }
3020 }
3021
3022 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
3023 self.project().inner
3024 }
3025}
3026
3027impl Future for Pending {
3028 type Output = Result<Response, crate::Error>;
3029
3030 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3031 let inner = self.inner();
3032 match inner.get_mut() {
3033 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
3034 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
3035 .take()
3036 .expect("Pending error polled more than once"))),
3037 }
3038 }
3039}
3040
3041impl Future for PendingRequest {
3042 type Output = Result<Response, crate::Error>;
3043
3044 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3045 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
3046 if let Poll::Ready(()) = delay.poll(cx) {
3047 return Poll::Ready(Err(
3048 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3049 ));
3050 }
3051 }
3052
3053 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
3054 if let Poll::Ready(()) = delay.poll(cx) {
3055 return Poll::Ready(Err(
3056 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3057 ));
3058 }
3059 }
3060
3061 let res = match self.as_mut().in_flight().get_mut() {
3062 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
3063 Err(e) => {
3064 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
3065 }
3066 Ok(res) => res.map(super::body::boxed),
3067 },
3068 #[cfg(feature = "http3")]
3069 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
3070 Err(e) => {
3071 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
3072 }
3073 Ok(res) => res.map(super::body::boxed),
3074 },
3075 };
3076
3077 if let Some(url) = &res
3078 .extensions()
3079 .get::<tower_http::follow_redirect::RequestUri>()
3080 {
3081 self.url = match Url::parse(&url.0.to_string()) {
3082 Ok(url) => url,
3083 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3084 }
3085 };
3086
3087 let res = Response::new(
3088 res,
3089 self.url.clone(),
3090 self.total_timeout.take(),
3091 self.read_timeout,
3092 );
3093 Poll::Ready(Ok(res))
3094 }
3095}
3096
3097impl fmt::Debug for Pending {
3098 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3099 match self.inner {
3100 PendingInner::Request(ref req) => f
3101 .debug_struct("Pending")
3102 .field("method", &req.method)
3103 .field("url", &req.url)
3104 .finish(),
3105 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3106 }
3107 }
3108}
3109
3110#[cfg(test)]
3111mod tests {
3112 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
3113
3114 #[tokio::test]
3115 async fn execute_request_rejects_invalid_urls() {
3116 let url_str = "hxxps://www.rust-lang.org/";
3117 let url = url::Url::parse(url_str).unwrap();
3118 let result = crate::get(url.clone()).await;
3119
3120 assert!(result.is_err());
3121 let err = result.err().unwrap();
3122 assert!(err.is_builder());
3123 assert_eq!(url_str, err.url().unwrap().as_str());
3124 }
3125
3126 #[tokio::test]
3128 async fn execute_request_rejects_invalid_hostname() {
3129 let url_str = "https://{{hostname}}/";
3130 let url = url::Url::parse(url_str).unwrap();
3131 let result = crate::get(url.clone()).await;
3132
3133 assert!(result.is_err());
3134 let err = result.err().unwrap();
3135 assert!(err.is_builder());
3136 assert_eq!(url_str, err.url().unwrap().as_str());
3137 }
3138
3139 #[test]
3140 fn test_future_size() {
3141 let s = std::mem::size_of::<super::Pending>();
3142 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3143 }
3144}