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;
22use crate::connect::{
23 sealed::{Conn, Unnameable},
24 BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
25};
26#[cfg(feature = "cookies")]
27use crate::cookie;
28#[cfg(feature = "cookies")]
29use crate::cookie::service::CookieService;
30#[cfg(feature = "hickory-dns")]
31use crate::dns::hickory::HickoryDnsResolver;
32use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
33use crate::error::{self, BoxError};
34use crate::into_url::try_uri;
35use crate::proxy::Matcher as ProxyMatcher;
36use crate::redirect::{self, TowerRedirectPolicy};
37#[cfg(feature = "__rustls")]
38use crate::tls::CertificateRevocationList;
39#[cfg(feature = "__tls")]
40use crate::tls::{self, TlsBackend};
41#[cfg(feature = "__tls")]
42use crate::Certificate;
43#[cfg(any(feature = "native-tls", feature = "__rustls"))]
44use crate::Identity;
45use crate::{IntoUrl, Method, Proxy, Url};
46
47use http::header::{Entry, HeaderMap, HeaderValue, ACCEPT, PROXY_AUTHORIZATION, USER_AGENT};
48use http::uri::Scheme;
49use http::Uri;
50use hyper_util::client::legacy::connect::HttpConnector;
51#[cfg(feature = "default-tls")]
52use native_tls_crate::TlsConnector;
53use pin_project_lite::pin_project;
54#[cfg(feature = "http3")]
55use quinn::TransportConfig;
56#[cfg(feature = "http3")]
57use quinn::VarInt;
58use tokio::time::Sleep;
59use tower::util::BoxCloneSyncServiceLayer;
60use tower::{Layer, Service};
61#[cfg(any(
62 feature = "gzip",
63 feature = "brotli",
64 feature = "zstd",
65 feature = "deflate"
66))]
67use tower_http::decompression::Decompression;
68use tower_http::follow_redirect::FollowRedirect;
69
70#[derive(Clone)]
91pub struct Client {
92 inner: Arc<ClientRef>,
93}
94
95#[must_use]
97pub struct ClientBuilder {
98 config: Config,
99}
100
101enum HttpVersionPref {
102 Http1,
103 #[cfg(feature = "http2")]
104 Http2,
105 #[cfg(feature = "http3")]
106 Http3,
107 All,
108}
109
110#[derive(Clone, Copy, Debug)]
111struct Accepts {
112 #[cfg(feature = "gzip")]
113 gzip: bool,
114 #[cfg(feature = "brotli")]
115 brotli: bool,
116 #[cfg(feature = "zstd")]
117 zstd: bool,
118 #[cfg(feature = "deflate")]
119 deflate: bool,
120}
121
122impl Default for Accepts {
123 fn default() -> Accepts {
124 Accepts {
125 #[cfg(feature = "gzip")]
126 gzip: true,
127 #[cfg(feature = "brotli")]
128 brotli: true,
129 #[cfg(feature = "zstd")]
130 zstd: true,
131 #[cfg(feature = "deflate")]
132 deflate: true,
133 }
134 }
135}
136
137#[derive(Clone)]
138struct HyperService {
139 hyper: HyperClient,
140}
141
142impl Service<hyper::Request<crate::async_impl::body::Body>> for HyperService {
143 type Error = crate::Error;
144 type Response = http::Response<hyper::body::Incoming>;
145 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + Sync>>;
146
147 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
148 self.hyper.poll_ready(cx).map_err(crate::error::request)
149 }
150
151 fn call(&mut self, req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
152 let clone = self.hyper.clone();
153 let mut inner = std::mem::replace(&mut self.hyper, clone);
154 Box::pin(async move { inner.call(req).await.map_err(crate::error::request) })
155 }
156}
157
158struct Config {
159 accepts: Accepts,
161 headers: HeaderMap,
162 #[cfg(feature = "__tls")]
163 hostname_verification: bool,
164 #[cfg(feature = "__tls")]
165 certs_verification: bool,
166 #[cfg(feature = "__tls")]
167 tls_sni: bool,
168 connect_timeout: Option<Duration>,
169 connection_verbose: bool,
170 pool_idle_timeout: Option<Duration>,
171 pool_max_idle_per_host: usize,
172 tcp_keepalive: Option<Duration>,
173 tcp_keepalive_interval: Option<Duration>,
174 tcp_keepalive_retries: Option<u32>,
175 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
176 tcp_user_timeout: Option<Duration>,
177 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
178 identity: Option<Identity>,
179 proxies: Vec<ProxyMatcher>,
180 auto_sys_proxy: bool,
181 redirect_policy: redirect::Policy,
182 retry_policy: crate::retry::Builder,
183 referer: bool,
184 read_timeout: Option<Duration>,
185 timeout: Option<Duration>,
186 #[cfg(feature = "__tls")]
187 root_certs: Vec<Certificate>,
188 #[cfg(feature = "__tls")]
189 tls_built_in_root_certs: bool,
190 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
191 tls_built_in_certs_webpki: bool,
192 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
193 tls_built_in_certs_native: bool,
194 #[cfg(feature = "__rustls")]
195 crls: Vec<CertificateRevocationList>,
196 #[cfg(feature = "__tls")]
197 min_tls_version: Option<tls::Version>,
198 #[cfg(feature = "__tls")]
199 max_tls_version: Option<tls::Version>,
200 #[cfg(feature = "__tls")]
201 tls_info: bool,
202 #[cfg(feature = "__tls")]
203 tls: TlsBackend,
204 connector_layers: Vec<BoxedConnectorLayer>,
205 http_version_pref: HttpVersionPref,
206 http09_responses: bool,
207 http1_title_case_headers: bool,
208 http1_allow_obsolete_multiline_headers_in_responses: bool,
209 http1_ignore_invalid_headers_in_responses: bool,
210 http1_allow_spaces_after_header_name_in_responses: bool,
211 #[cfg(feature = "http2")]
212 http2_initial_stream_window_size: Option<u32>,
213 #[cfg(feature = "http2")]
214 http2_initial_connection_window_size: Option<u32>,
215 #[cfg(feature = "http2")]
216 http2_adaptive_window: bool,
217 #[cfg(feature = "http2")]
218 http2_max_frame_size: Option<u32>,
219 #[cfg(feature = "http2")]
220 http2_max_header_list_size: Option<u32>,
221 #[cfg(feature = "http2")]
222 http2_keep_alive_interval: Option<Duration>,
223 #[cfg(feature = "http2")]
224 http2_keep_alive_timeout: Option<Duration>,
225 #[cfg(feature = "http2")]
226 http2_keep_alive_while_idle: bool,
227 local_address: Option<IpAddr>,
228 #[cfg(any(
229 target_os = "android",
230 target_os = "fuchsia",
231 target_os = "illumos",
232 target_os = "ios",
233 target_os = "linux",
234 target_os = "macos",
235 target_os = "solaris",
236 target_os = "tvos",
237 target_os = "visionos",
238 target_os = "watchos",
239 ))]
240 interface: Option<String>,
241 nodelay: bool,
242 #[cfg(feature = "cookies")]
243 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
244 hickory_dns: bool,
245 error: Option<crate::Error>,
246 https_only: bool,
247 #[cfg(feature = "http3")]
248 tls_enable_early_data: bool,
249 #[cfg(feature = "http3")]
250 quic_max_idle_timeout: Option<Duration>,
251 #[cfg(feature = "http3")]
252 quic_stream_receive_window: Option<VarInt>,
253 #[cfg(feature = "http3")]
254 quic_receive_window: Option<VarInt>,
255 #[cfg(feature = "http3")]
256 quic_send_window: Option<u64>,
257 #[cfg(feature = "http3")]
258 quic_congestion_bbr: bool,
259 #[cfg(feature = "http3")]
260 h3_max_field_section_size: Option<u64>,
261 #[cfg(feature = "http3")]
262 h3_send_grease: Option<bool>,
263 dns_overrides: HashMap<String, Vec<SocketAddr>>,
264 dns_resolver: Option<Arc<dyn Resolve>>,
265
266 #[cfg(unix)]
267 unix_socket: Option<Arc<std::path::Path>>,
268}
269
270impl Default for ClientBuilder {
271 fn default() -> Self {
272 Self::new()
273 }
274}
275
276impl ClientBuilder {
277 pub fn new() -> Self {
281 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
282 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
283
284 ClientBuilder {
285 config: Config {
286 error: None,
287 accepts: Accepts::default(),
288 headers,
289 #[cfg(feature = "__tls")]
290 hostname_verification: true,
291 #[cfg(feature = "__tls")]
292 certs_verification: true,
293 #[cfg(feature = "__tls")]
294 tls_sni: true,
295 connect_timeout: None,
296 connection_verbose: false,
297 pool_idle_timeout: Some(Duration::from_secs(90)),
298 pool_max_idle_per_host: usize::MAX,
299 tcp_keepalive: Some(Duration::from_secs(15)),
300 tcp_keepalive_interval: Some(Duration::from_secs(15)),
301 tcp_keepalive_retries: Some(3),
302 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
303 tcp_user_timeout: Some(Duration::from_secs(30)),
304 proxies: Vec::new(),
305 auto_sys_proxy: true,
306 redirect_policy: redirect::Policy::default(),
307 retry_policy: crate::retry::Builder::default(),
308 referer: true,
309 read_timeout: None,
310 timeout: None,
311 #[cfg(feature = "__tls")]
312 root_certs: Vec::new(),
313 #[cfg(feature = "__tls")]
314 tls_built_in_root_certs: true,
315 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
316 tls_built_in_certs_webpki: true,
317 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
318 tls_built_in_certs_native: true,
319 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
320 identity: None,
321 #[cfg(feature = "__rustls")]
322 crls: vec![],
323 #[cfg(feature = "__tls")]
324 min_tls_version: None,
325 #[cfg(feature = "__tls")]
326 max_tls_version: None,
327 #[cfg(feature = "__tls")]
328 tls_info: false,
329 #[cfg(feature = "__tls")]
330 tls: TlsBackend::default(),
331 connector_layers: Vec::new(),
332 http_version_pref: HttpVersionPref::All,
333 http09_responses: false,
334 http1_title_case_headers: false,
335 http1_allow_obsolete_multiline_headers_in_responses: false,
336 http1_ignore_invalid_headers_in_responses: false,
337 http1_allow_spaces_after_header_name_in_responses: false,
338 #[cfg(feature = "http2")]
339 http2_initial_stream_window_size: None,
340 #[cfg(feature = "http2")]
341 http2_initial_connection_window_size: None,
342 #[cfg(feature = "http2")]
343 http2_adaptive_window: false,
344 #[cfg(feature = "http2")]
345 http2_max_frame_size: None,
346 #[cfg(feature = "http2")]
347 http2_max_header_list_size: None,
348 #[cfg(feature = "http2")]
349 http2_keep_alive_interval: None,
350 #[cfg(feature = "http2")]
351 http2_keep_alive_timeout: None,
352 #[cfg(feature = "http2")]
353 http2_keep_alive_while_idle: false,
354 local_address: None,
355 #[cfg(any(
356 target_os = "android",
357 target_os = "fuchsia",
358 target_os = "illumos",
359 target_os = "ios",
360 target_os = "linux",
361 target_os = "macos",
362 target_os = "solaris",
363 target_os = "tvos",
364 target_os = "visionos",
365 target_os = "watchos",
366 ))]
367 interface: None,
368 nodelay: true,
369 hickory_dns: cfg!(feature = "hickory-dns"),
370 #[cfg(feature = "cookies")]
371 cookie_store: None,
372 https_only: false,
373 dns_overrides: HashMap::new(),
374 #[cfg(feature = "http3")]
375 tls_enable_early_data: false,
376 #[cfg(feature = "http3")]
377 quic_max_idle_timeout: None,
378 #[cfg(feature = "http3")]
379 quic_stream_receive_window: None,
380 #[cfg(feature = "http3")]
381 quic_receive_window: None,
382 #[cfg(feature = "http3")]
383 quic_send_window: None,
384 #[cfg(feature = "http3")]
385 quic_congestion_bbr: false,
386 #[cfg(feature = "http3")]
387 h3_max_field_section_size: None,
388 #[cfg(feature = "http3")]
389 h3_send_grease: None,
390 dns_resolver: None,
391 #[cfg(unix)]
392 unix_socket: None,
393 },
394 }
395 }
396}
397
398impl ClientBuilder {
399 pub fn build(self) -> crate::Result<Client> {
406 let config = self.config;
407
408 if let Some(err) = config.error {
409 return Err(err);
410 }
411
412 let mut proxies = config.proxies;
413 if config.auto_sys_proxy {
414 proxies.push(ProxyMatcher::system());
415 }
416 let proxies = Arc::new(proxies);
417
418 #[allow(unused)]
419 #[cfg(feature = "http3")]
420 let mut h3_connector = None;
421
422 let resolver = {
423 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
424 false => Arc::new(GaiResolver::new()),
425 #[cfg(feature = "hickory-dns")]
426 true => Arc::new(HickoryDnsResolver::default()),
427 #[cfg(not(feature = "hickory-dns"))]
428 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
429 };
430 if let Some(dns_resolver) = config.dns_resolver {
431 resolver = dns_resolver;
432 }
433 if !config.dns_overrides.is_empty() {
434 resolver = Arc::new(DnsResolverWithOverrides::new(
435 resolver,
436 config.dns_overrides,
437 ));
438 }
439 DynResolver::new(resolver)
440 };
441
442 let mut connector_builder = {
443 #[cfg(feature = "__tls")]
444 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
445 headers.get(USER_AGENT).cloned()
446 }
447
448 let mut http = HttpConnector::new_with_resolver(resolver.clone());
449 http.set_connect_timeout(config.connect_timeout);
450
451 #[cfg(all(feature = "http3", feature = "__rustls"))]
452 let build_h3_connector =
453 |resolver,
454 tls,
455 quic_max_idle_timeout: Option<Duration>,
456 quic_stream_receive_window,
457 quic_receive_window,
458 quic_send_window,
459 quic_congestion_bbr,
460 h3_max_field_section_size,
461 h3_send_grease,
462 local_address,
463 http_version_pref: &HttpVersionPref| {
464 let mut transport_config = TransportConfig::default();
465
466 if let Some(max_idle_timeout) = quic_max_idle_timeout {
467 transport_config.max_idle_timeout(Some(
468 max_idle_timeout.try_into().map_err(error::builder)?,
469 ));
470 }
471
472 if let Some(stream_receive_window) = quic_stream_receive_window {
473 transport_config.stream_receive_window(stream_receive_window);
474 }
475
476 if let Some(receive_window) = quic_receive_window {
477 transport_config.receive_window(receive_window);
478 }
479
480 if let Some(send_window) = quic_send_window {
481 transport_config.send_window(send_window);
482 }
483
484 if quic_congestion_bbr {
485 let factory = Arc::new(quinn::congestion::BbrConfig::default());
486 transport_config.congestion_controller_factory(factory);
487 }
488
489 let mut h3_client_config = H3ClientConfig::default();
490
491 if let Some(max_field_section_size) = h3_max_field_section_size {
492 h3_client_config.max_field_section_size = Some(max_field_section_size);
493 }
494
495 if let Some(send_grease) = h3_send_grease {
496 h3_client_config.send_grease = Some(send_grease);
497 }
498
499 let res = H3Connector::new(
500 resolver,
501 tls,
502 local_address,
503 transport_config,
504 h3_client_config,
505 );
506
507 match res {
508 Ok(connector) => Ok(Some(connector)),
509 Err(err) => {
510 if let HttpVersionPref::Http3 = http_version_pref {
511 Err(error::builder(err))
512 } else {
513 Ok(None)
514 }
515 }
516 }
517 };
518
519 #[cfg(feature = "__tls")]
520 match config.tls {
521 #[cfg(feature = "default-tls")]
522 TlsBackend::Default => {
523 let mut tls = TlsConnector::builder();
524
525 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
526 {
527 match config.http_version_pref {
528 HttpVersionPref::Http1 => {
529 tls.request_alpns(&["http/1.1"]);
530 }
531 #[cfg(feature = "http2")]
532 HttpVersionPref::Http2 => {
533 tls.request_alpns(&["h2"]);
534 }
535 HttpVersionPref::All => {
536 tls.request_alpns(&["h2", "http/1.1"]);
537 }
538 }
539 }
540
541 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
542
543 tls.danger_accept_invalid_certs(!config.certs_verification);
544
545 tls.use_sni(config.tls_sni);
546
547 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
548
549 for cert in config.root_certs {
550 cert.add_to_native_tls(&mut tls);
551 }
552
553 #[cfg(feature = "native-tls")]
554 {
555 if let Some(id) = config.identity {
556 id.add_to_native_tls(&mut tls)?;
557 }
558 }
559 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
560 {
561 if let Some(_id) = config.identity {
563 return Err(crate::error::builder("incompatible TLS identity type"));
564 }
565 }
566
567 if let Some(min_tls_version) = config.min_tls_version {
568 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
569 crate::error::builder("invalid minimum TLS version for backend")
573 })?;
574 tls.min_protocol_version(Some(protocol));
575 }
576
577 if let Some(max_tls_version) = config.max_tls_version {
578 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
579 crate::error::builder("invalid maximum TLS version for backend")
584 })?;
585 tls.max_protocol_version(Some(protocol));
586 }
587
588 ConnectorBuilder::new_default_tls(
589 http,
590 tls,
591 proxies.clone(),
592 user_agent(&config.headers),
593 config.local_address,
594 #[cfg(any(
595 target_os = "android",
596 target_os = "fuchsia",
597 target_os = "illumos",
598 target_os = "ios",
599 target_os = "linux",
600 target_os = "macos",
601 target_os = "solaris",
602 target_os = "tvos",
603 target_os = "visionos",
604 target_os = "watchos",
605 ))]
606 config.interface.as_deref(),
607 config.nodelay,
608 config.tls_info,
609 )?
610 }
611 #[cfg(feature = "native-tls")]
612 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_default_tls(
613 http,
614 conn,
615 proxies.clone(),
616 user_agent(&config.headers),
617 config.local_address,
618 #[cfg(any(
619 target_os = "android",
620 target_os = "fuchsia",
621 target_os = "illumos",
622 target_os = "ios",
623 target_os = "linux",
624 target_os = "macos",
625 target_os = "solaris",
626 target_os = "tvos",
627 target_os = "visionos",
628 target_os = "watchos",
629 ))]
630 config.interface.as_deref(),
631 config.nodelay,
632 config.tls_info,
633 ),
634 #[cfg(feature = "__rustls")]
635 TlsBackend::BuiltRustls(conn) => {
636 #[cfg(feature = "http3")]
637 {
638 h3_connector = build_h3_connector(
639 resolver.clone(),
640 conn.clone(),
641 config.quic_max_idle_timeout,
642 config.quic_stream_receive_window,
643 config.quic_receive_window,
644 config.quic_send_window,
645 config.quic_congestion_bbr,
646 config.h3_max_field_section_size,
647 config.h3_send_grease,
648 config.local_address,
649 &config.http_version_pref,
650 )?;
651 }
652
653 ConnectorBuilder::new_rustls_tls(
654 http,
655 conn,
656 proxies.clone(),
657 user_agent(&config.headers),
658 config.local_address,
659 #[cfg(any(
660 target_os = "android",
661 target_os = "fuchsia",
662 target_os = "illumos",
663 target_os = "ios",
664 target_os = "linux",
665 target_os = "macos",
666 target_os = "solaris",
667 target_os = "tvos",
668 target_os = "visionos",
669 target_os = "watchos",
670 ))]
671 config.interface.as_deref(),
672 config.nodelay,
673 config.tls_info,
674 )
675 }
676 #[cfg(feature = "__rustls")]
677 TlsBackend::Rustls => {
678 use crate::tls::{IgnoreHostname, NoVerifier};
679
680 let mut root_cert_store = rustls::RootCertStore::empty();
682 for cert in config.root_certs {
683 cert.add_to_rustls(&mut root_cert_store)?;
684 }
685
686 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
687 if config.tls_built_in_certs_webpki {
688 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
689 }
690
691 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
692 if config.tls_built_in_certs_native {
693 let mut valid_count = 0;
694 let mut invalid_count = 0;
695
696 let load_results = rustls_native_certs::load_native_certs();
697 for cert in load_results.certs {
698 match root_cert_store.add(cert.into()) {
702 Ok(_) => valid_count += 1,
703 Err(err) => {
704 invalid_count += 1;
705 log::debug!("rustls failed to parse DER certificate: {err:?}");
706 }
707 }
708 }
709 if valid_count == 0 && invalid_count > 0 {
710 let err = if load_results.errors.is_empty() {
711 crate::error::builder(
712 "zero valid certificates found in native root store",
713 )
714 } else {
715 use std::fmt::Write as _;
716 let mut acc = String::new();
717 for err in load_results.errors {
718 let _ = writeln!(&mut acc, "{err}");
719 }
720
721 crate::error::builder(acc)
722 };
723
724 return Err(err);
725 }
726 }
727
728 let mut versions = rustls::ALL_VERSIONS.to_vec();
730
731 if let Some(min_tls_version) = config.min_tls_version {
732 versions.retain(|&supported_version| {
733 match tls::Version::from_rustls(supported_version.version) {
734 Some(version) => version >= min_tls_version,
735 None => true,
738 }
739 });
740 }
741
742 if let Some(max_tls_version) = config.max_tls_version {
743 versions.retain(|&supported_version| {
744 match tls::Version::from_rustls(supported_version.version) {
745 Some(version) => version <= max_tls_version,
746 None => false,
747 }
748 });
749 }
750
751 if versions.is_empty() {
752 return Err(crate::error::builder("empty supported tls versions"));
753 }
754
755 let provider = rustls::crypto::CryptoProvider::get_default()
758 .map(|arc| arc.clone())
759 .unwrap_or_else(|| {
760 #[cfg(not(feature = "__rustls-ring"))]
761 panic!("No provider set");
762
763 #[cfg(feature = "__rustls-ring")]
764 Arc::new(rustls::crypto::ring::default_provider())
765 });
766
767 let signature_algorithms = provider.signature_verification_algorithms;
769 let config_builder =
770 rustls::ClientConfig::builder_with_provider(provider.clone())
771 .with_protocol_versions(&versions)
772 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
773
774 let config_builder = if !config.certs_verification {
775 config_builder
776 .dangerous()
777 .with_custom_certificate_verifier(Arc::new(NoVerifier))
778 } else if !config.hostname_verification {
779 config_builder
780 .dangerous()
781 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
782 root_cert_store,
783 signature_algorithms,
784 )))
785 } else {
786 if config.crls.is_empty() {
787 config_builder.with_root_certificates(root_cert_store)
788 } else {
789 let crls = config
790 .crls
791 .iter()
792 .map(|e| e.as_rustls_crl())
793 .collect::<Vec<_>>();
794 let verifier =
795 rustls::client::WebPkiServerVerifier::builder_with_provider(
796 Arc::new(root_cert_store),
797 provider,
798 )
799 .with_crls(crls)
800 .build()
801 .map_err(|_| {
802 crate::error::builder("invalid TLS verification settings")
803 })?;
804 config_builder.with_webpki_verifier(verifier)
805 }
806 };
807
808 let mut tls = if let Some(id) = config.identity {
810 id.add_to_rustls(config_builder)?
811 } else {
812 config_builder.with_no_client_auth()
813 };
814
815 tls.enable_sni = config.tls_sni;
816
817 match config.http_version_pref {
819 HttpVersionPref::Http1 => {
820 tls.alpn_protocols = vec!["http/1.1".into()];
821 }
822 #[cfg(feature = "http2")]
823 HttpVersionPref::Http2 => {
824 tls.alpn_protocols = vec!["h2".into()];
825 }
826 #[cfg(feature = "http3")]
827 HttpVersionPref::Http3 => {
828 tls.alpn_protocols = vec!["h3".into()];
829 }
830 HttpVersionPref::All => {
831 tls.alpn_protocols = vec![
832 #[cfg(feature = "http2")]
833 "h2".into(),
834 "http/1.1".into(),
835 ];
836 }
837 }
838
839 #[cfg(feature = "http3")]
840 {
841 tls.enable_early_data = config.tls_enable_early_data;
842
843 h3_connector = build_h3_connector(
844 resolver.clone(),
845 tls.clone(),
846 config.quic_max_idle_timeout,
847 config.quic_stream_receive_window,
848 config.quic_receive_window,
849 config.quic_send_window,
850 config.quic_congestion_bbr,
851 config.h3_max_field_section_size,
852 config.h3_send_grease,
853 config.local_address,
854 &config.http_version_pref,
855 )?;
856 }
857
858 ConnectorBuilder::new_rustls_tls(
859 http,
860 tls,
861 proxies.clone(),
862 user_agent(&config.headers),
863 config.local_address,
864 #[cfg(any(
865 target_os = "android",
866 target_os = "fuchsia",
867 target_os = "illumos",
868 target_os = "ios",
869 target_os = "linux",
870 target_os = "macos",
871 target_os = "solaris",
872 target_os = "tvos",
873 target_os = "visionos",
874 target_os = "watchos",
875 ))]
876 config.interface.as_deref(),
877 config.nodelay,
878 config.tls_info,
879 )
880 }
881 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
882 TlsBackend::UnknownPreconfigured => {
883 return Err(crate::error::builder(
884 "Unknown TLS backend passed to `use_preconfigured_tls`",
885 ));
886 }
887 }
888
889 #[cfg(not(feature = "__tls"))]
890 ConnectorBuilder::new(
891 http,
892 proxies.clone(),
893 config.local_address,
894 #[cfg(any(
895 target_os = "android",
896 target_os = "fuchsia",
897 target_os = "illumos",
898 target_os = "ios",
899 target_os = "linux",
900 target_os = "macos",
901 target_os = "solaris",
902 target_os = "tvos",
903 target_os = "visionos",
904 target_os = "watchos",
905 ))]
906 config.interface.as_deref(),
907 config.nodelay,
908 )
909 };
910
911 connector_builder.set_timeout(config.connect_timeout);
912 connector_builder.set_verbose(config.connection_verbose);
913 connector_builder.set_keepalive(config.tcp_keepalive);
914 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
915 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
916 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
917 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
918
919 #[cfg(feature = "socks")]
920 connector_builder.set_socks_resolver(resolver);
921
922 #[cfg(unix)]
926 connector_builder.set_unix_socket(config.unix_socket);
927
928 let mut builder =
929 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
930 #[cfg(feature = "http2")]
931 {
932 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
933 builder.http2_only(true);
934 }
935
936 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
937 {
938 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
939 }
940 if let Some(http2_initial_connection_window_size) =
941 config.http2_initial_connection_window_size
942 {
943 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
944 }
945 if config.http2_adaptive_window {
946 builder.http2_adaptive_window(true);
947 }
948 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
949 builder.http2_max_frame_size(http2_max_frame_size);
950 }
951 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
952 builder.http2_max_header_list_size(http2_max_header_list_size);
953 }
954 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
955 builder.http2_keep_alive_interval(http2_keep_alive_interval);
956 }
957 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
958 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
959 }
960 if config.http2_keep_alive_while_idle {
961 builder.http2_keep_alive_while_idle(true);
962 }
963 }
964
965 builder.timer(hyper_util::rt::TokioTimer::new());
966 builder.pool_timer(hyper_util::rt::TokioTimer::new());
967 builder.pool_idle_timeout(config.pool_idle_timeout);
968 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
969
970 if config.http09_responses {
971 builder.http09_responses(true);
972 }
973
974 if config.http1_title_case_headers {
975 builder.http1_title_case_headers(true);
976 }
977
978 if config.http1_allow_obsolete_multiline_headers_in_responses {
979 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
980 }
981
982 if config.http1_ignore_invalid_headers_in_responses {
983 builder.http1_ignore_invalid_headers_in_responses(true);
984 }
985
986 if config.http1_allow_spaces_after_header_name_in_responses {
987 builder.http1_allow_spaces_after_header_name_in_responses(true);
988 }
989
990 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
991 let proxies_maybe_http_custom_headers =
992 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
993
994 let redirect_policy_desc = if config.redirect_policy.is_default() {
995 None
996 } else {
997 Some(format!("{:?}", &config.redirect_policy))
998 };
999
1000 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
1001 let hyper_service = HyperService {
1002 hyper: hyper_client,
1003 };
1004
1005 let redirect_policy = {
1006 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
1007 p.with_referer(config.referer)
1008 .with_https_only(config.https_only);
1009 p
1010 };
1011
1012 let retry_policy = config.retry_policy.into_policy();
1013
1014 let svc = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
1015
1016 #[cfg(feature = "cookies")]
1017 let svc = CookieService::new(svc, config.cookie_store.clone());
1018 let hyper = FollowRedirect::with_policy(svc, redirect_policy.clone());
1019 #[cfg(any(
1020 feature = "gzip",
1021 feature = "brotli",
1022 feature = "zstd",
1023 feature = "deflate"
1024 ))]
1025 let hyper = Decompression::new(hyper)
1026 .no_gzip()
1029 .no_deflate()
1030 .no_br()
1031 .no_zstd();
1032 #[cfg(feature = "gzip")]
1033 let hyper = hyper.gzip(config.accepts.gzip);
1034 #[cfg(feature = "brotli")]
1035 let hyper = hyper.br(config.accepts.brotli);
1036 #[cfg(feature = "zstd")]
1037 let hyper = hyper.zstd(config.accepts.zstd);
1038 #[cfg(feature = "deflate")]
1039 let hyper = hyper.deflate(config.accepts.deflate);
1040
1041 Ok(Client {
1042 inner: Arc::new(ClientRef {
1043 accepts: config.accepts,
1044 #[cfg(feature = "cookies")]
1045 cookie_store: config.cookie_store.clone(),
1046 #[cfg(feature = "http3")]
1049 h3_client: match h3_connector {
1050 Some(h3_connector) => {
1051 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1052 let svc = tower::retry::Retry::new(retry_policy, h3_service);
1053 #[cfg(feature = "cookies")]
1054 let svc = CookieService::new(svc, config.cookie_store);
1055 let svc = FollowRedirect::with_policy(svc, redirect_policy);
1056 #[cfg(any(
1057 feature = "gzip",
1058 feature = "brotli",
1059 feature = "zstd",
1060 feature = "deflate"
1061 ))]
1062 let svc = Decompression::new(svc)
1063 .no_gzip()
1066 .no_deflate()
1067 .no_br()
1068 .no_zstd();
1069 #[cfg(feature = "gzip")]
1070 let svc = svc.gzip(config.accepts.gzip);
1071 #[cfg(feature = "brotli")]
1072 let svc = svc.br(config.accepts.brotli);
1073 #[cfg(feature = "zstd")]
1074 let svc = svc.zstd(config.accepts.zstd);
1075 #[cfg(feature = "deflate")]
1076 let svc = svc.deflate(config.accepts.deflate);
1077 Some(svc)
1078 }
1079 None => None,
1080 },
1081 headers: config.headers,
1082 referer: config.referer,
1083 read_timeout: config.read_timeout,
1084 total_timeout: RequestConfig::new(config.timeout),
1085 hyper,
1086 proxies,
1087 proxies_maybe_http_auth,
1088 proxies_maybe_http_custom_headers,
1089 https_only: config.https_only,
1090 redirect_policy_desc,
1091 }),
1092 })
1093 }
1094
1095 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1118 where
1119 V: TryInto<HeaderValue>,
1120 V::Error: Into<http::Error>,
1121 {
1122 match value.try_into() {
1123 Ok(value) => {
1124 self.config.headers.insert(USER_AGENT, value);
1125 }
1126 Err(e) => {
1127 self.config.error = Some(crate::error::builder(e.into()));
1128 }
1129 };
1130 self
1131 }
1132 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1156 for (key, value) in headers.iter() {
1157 self.config.headers.insert(key, value.clone());
1158 }
1159 self
1160 }
1161
1162 #[cfg(feature = "cookies")]
1177 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1178 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1179 if enable {
1180 self.cookie_provider(Arc::new(cookie::Jar::default()))
1181 } else {
1182 self.config.cookie_store = None;
1183 self
1184 }
1185 }
1186
1187 #[cfg(feature = "cookies")]
1201 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1202 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1203 mut self,
1204 cookie_store: Arc<C>,
1205 ) -> ClientBuilder {
1206 self.config.cookie_store = Some(cookie_store as _);
1207 self
1208 }
1209
1210 #[cfg(feature = "gzip")]
1227 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1228 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1229 self.config.accepts.gzip = enable;
1230 self
1231 }
1232
1233 #[cfg(feature = "brotli")]
1250 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1251 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1252 self.config.accepts.brotli = enable;
1253 self
1254 }
1255
1256 #[cfg(feature = "zstd")]
1273 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1274 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1275 self.config.accepts.zstd = enable;
1276 self
1277 }
1278
1279 #[cfg(feature = "deflate")]
1296 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1297 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1298 self.config.accepts.deflate = enable;
1299 self
1300 }
1301
1302 pub fn no_gzip(self) -> ClientBuilder {
1308 #[cfg(feature = "gzip")]
1309 {
1310 self.gzip(false)
1311 }
1312
1313 #[cfg(not(feature = "gzip"))]
1314 {
1315 self
1316 }
1317 }
1318
1319 pub fn no_brotli(self) -> ClientBuilder {
1325 #[cfg(feature = "brotli")]
1326 {
1327 self.brotli(false)
1328 }
1329
1330 #[cfg(not(feature = "brotli"))]
1331 {
1332 self
1333 }
1334 }
1335
1336 pub fn no_zstd(self) -> ClientBuilder {
1342 #[cfg(feature = "zstd")]
1343 {
1344 self.zstd(false)
1345 }
1346
1347 #[cfg(not(feature = "zstd"))]
1348 {
1349 self
1350 }
1351 }
1352
1353 pub fn no_deflate(self) -> ClientBuilder {
1359 #[cfg(feature = "deflate")]
1360 {
1361 self.deflate(false)
1362 }
1363
1364 #[cfg(not(feature = "deflate"))]
1365 {
1366 self
1367 }
1368 }
1369
1370 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1376 self.config.redirect_policy = policy;
1377 self
1378 }
1379
1380 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1384 self.config.referer = enable;
1385 self
1386 }
1387
1388 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1395 self.config.retry_policy = policy;
1396 self
1397 }
1398
1399 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1407 self.config.proxies.push(proxy.into_matcher());
1408 self.config.auto_sys_proxy = false;
1409 self
1410 }
1411
1412 pub fn no_proxy(mut self) -> ClientBuilder {
1420 self.config.proxies.clear();
1421 self.config.auto_sys_proxy = false;
1422 self
1423 }
1424
1425 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1434 self.config.timeout = Some(timeout);
1435 self
1436 }
1437
1438 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1446 self.config.read_timeout = Some(timeout);
1447 self
1448 }
1449
1450 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1459 self.config.connect_timeout = Some(timeout);
1460 self
1461 }
1462
1463 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1470 self.config.connection_verbose = verbose;
1471 self
1472 }
1473
1474 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1482 where
1483 D: Into<Option<Duration>>,
1484 {
1485 self.config.pool_idle_timeout = val.into();
1486 self
1487 }
1488
1489 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1493 self.config.pool_max_idle_per_host = max;
1494 self
1495 }
1496
1497 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1499 self.config.http1_title_case_headers = true;
1500 self
1501 }
1502
1503 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1509 mut self,
1510 value: bool,
1511 ) -> ClientBuilder {
1512 self.config
1513 .http1_allow_obsolete_multiline_headers_in_responses = value;
1514 self
1515 }
1516
1517 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1519 self.config.http1_ignore_invalid_headers_in_responses = value;
1520 self
1521 }
1522
1523 pub fn http1_allow_spaces_after_header_name_in_responses(
1529 mut self,
1530 value: bool,
1531 ) -> ClientBuilder {
1532 self.config
1533 .http1_allow_spaces_after_header_name_in_responses = value;
1534 self
1535 }
1536
1537 pub fn http1_only(mut self) -> ClientBuilder {
1539 self.config.http_version_pref = HttpVersionPref::Http1;
1540 self
1541 }
1542
1543 pub fn http09_responses(mut self) -> ClientBuilder {
1545 self.config.http09_responses = true;
1546 self
1547 }
1548
1549 #[cfg(feature = "http2")]
1551 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1552 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1553 self.config.http_version_pref = HttpVersionPref::Http2;
1554 self
1555 }
1556
1557 #[cfg(feature = "http3")]
1559 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1560 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1561 self.config.http_version_pref = HttpVersionPref::Http3;
1562 self
1563 }
1564
1565 #[cfg(feature = "http2")]
1569 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1570 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1571 self.config.http2_initial_stream_window_size = sz.into();
1572 self
1573 }
1574
1575 #[cfg(feature = "http2")]
1579 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1580 pub fn http2_initial_connection_window_size(
1581 mut self,
1582 sz: impl Into<Option<u32>>,
1583 ) -> ClientBuilder {
1584 self.config.http2_initial_connection_window_size = sz.into();
1585 self
1586 }
1587
1588 #[cfg(feature = "http2")]
1593 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1594 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1595 self.config.http2_adaptive_window = enabled;
1596 self
1597 }
1598
1599 #[cfg(feature = "http2")]
1603 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1604 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1605 self.config.http2_max_frame_size = sz.into();
1606 self
1607 }
1608
1609 #[cfg(feature = "http2")]
1613 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1614 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1615 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1616 self
1617 }
1618
1619 #[cfg(feature = "http2")]
1624 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1625 pub fn http2_keep_alive_interval(
1626 mut self,
1627 interval: impl Into<Option<Duration>>,
1628 ) -> ClientBuilder {
1629 self.config.http2_keep_alive_interval = interval.into();
1630 self
1631 }
1632
1633 #[cfg(feature = "http2")]
1639 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1640 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1641 self.config.http2_keep_alive_timeout = Some(timeout);
1642 self
1643 }
1644
1645 #[cfg(feature = "http2")]
1652 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1653 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1654 self.config.http2_keep_alive_while_idle = enabled;
1655 self
1656 }
1657
1658 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1664 self.config.nodelay = enabled;
1665 self
1666 }
1667
1668 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1683 where
1684 T: Into<Option<IpAddr>>,
1685 {
1686 self.config.local_address = addr.into();
1687 self
1688 }
1689
1690 #[cfg(any(
1723 target_os = "android",
1724 target_os = "fuchsia",
1725 target_os = "illumos",
1726 target_os = "ios",
1727 target_os = "linux",
1728 target_os = "macos",
1729 target_os = "solaris",
1730 target_os = "tvos",
1731 target_os = "visionos",
1732 target_os = "watchos",
1733 ))]
1734 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1735 self.config.interface = Some(interface.to_string());
1736 self
1737 }
1738
1739 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1743 where
1744 D: Into<Option<Duration>>,
1745 {
1746 self.config.tcp_keepalive = val.into();
1747 self
1748 }
1749
1750 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1754 where
1755 D: Into<Option<Duration>>,
1756 {
1757 self.config.tcp_keepalive_interval = val.into();
1758 self
1759 }
1760
1761 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1765 where
1766 C: Into<Option<u32>>,
1767 {
1768 self.config.tcp_keepalive_retries = retries.into();
1769 self
1770 }
1771
1772 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1779 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1780 where
1781 D: Into<Option<Duration>>,
1782 {
1783 self.config.tcp_user_timeout = val.into();
1784 self
1785 }
1786
1787 #[cfg(unix)]
1801 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1802 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1803 self
1804 }
1805
1806 #[cfg(feature = "__tls")]
1818 #[cfg_attr(
1819 docsrs,
1820 doc(cfg(any(
1821 feature = "default-tls",
1822 feature = "native-tls",
1823 feature = "rustls-tls"
1824 )))
1825 )]
1826 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1827 self.config.root_certs.push(cert);
1828 self
1829 }
1830
1831 #[cfg(feature = "__rustls")]
1838 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1839 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1840 self.config.crls.push(crl);
1841 self
1842 }
1843
1844 #[cfg(feature = "__rustls")]
1851 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1852 pub fn add_crls(
1853 mut self,
1854 crls: impl IntoIterator<Item = CertificateRevocationList>,
1855 ) -> ClientBuilder {
1856 self.config.crls.extend(crls);
1857 self
1858 }
1859
1860 #[cfg(feature = "__tls")]
1878 #[cfg_attr(
1879 docsrs,
1880 doc(cfg(any(
1881 feature = "default-tls",
1882 feature = "native-tls",
1883 feature = "rustls-tls"
1884 )))
1885 )]
1886 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1887 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1888
1889 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1890 {
1891 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1892 }
1893
1894 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1895 {
1896 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1897 }
1898
1899 self
1900 }
1901
1902 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1906 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1907 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1908 self.config.tls_built_in_certs_webpki = enabled;
1909 self
1910 }
1911
1912 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1916 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1917 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1918 self.config.tls_built_in_certs_native = enabled;
1919 self
1920 }
1921
1922 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1929 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1930 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1931 self.config.identity = Some(identity);
1932 self
1933 }
1934
1935 #[cfg(feature = "__tls")]
1951 #[cfg_attr(
1952 docsrs,
1953 doc(cfg(any(
1954 feature = "default-tls",
1955 feature = "native-tls",
1956 feature = "rustls-tls"
1957 )))
1958 )]
1959 pub fn danger_accept_invalid_hostnames(
1960 mut self,
1961 accept_invalid_hostname: bool,
1962 ) -> ClientBuilder {
1963 self.config.hostname_verification = !accept_invalid_hostname;
1964 self
1965 }
1966
1967 #[cfg(feature = "__tls")]
1984 #[cfg_attr(
1985 docsrs,
1986 doc(cfg(any(
1987 feature = "default-tls",
1988 feature = "native-tls",
1989 feature = "rustls-tls"
1990 )))
1991 )]
1992 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1993 self.config.certs_verification = !accept_invalid_certs;
1994 self
1995 }
1996
1997 #[cfg(feature = "__tls")]
2006 #[cfg_attr(
2007 docsrs,
2008 doc(cfg(any(
2009 feature = "default-tls",
2010 feature = "native-tls",
2011 feature = "rustls-tls"
2012 )))
2013 )]
2014 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
2015 self.config.tls_sni = tls_sni;
2016 self
2017 }
2018
2019 #[cfg(feature = "__tls")]
2035 #[cfg_attr(
2036 docsrs,
2037 doc(cfg(any(
2038 feature = "default-tls",
2039 feature = "native-tls",
2040 feature = "rustls-tls"
2041 )))
2042 )]
2043 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
2044 self.config.min_tls_version = Some(version);
2045 self
2046 }
2047
2048 #[cfg(feature = "__tls")]
2067 #[cfg_attr(
2068 docsrs,
2069 doc(cfg(any(
2070 feature = "default-tls",
2071 feature = "native-tls",
2072 feature = "rustls-tls"
2073 )))
2074 )]
2075 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
2076 self.config.max_tls_version = Some(version);
2077 self
2078 }
2079
2080 #[cfg(feature = "native-tls")]
2089 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2090 pub fn use_native_tls(mut self) -> ClientBuilder {
2091 self.config.tls = TlsBackend::Default;
2092 self
2093 }
2094
2095 #[cfg(feature = "__rustls")]
2104 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
2105 pub fn use_rustls_tls(mut self) -> ClientBuilder {
2106 self.config.tls = TlsBackend::Rustls;
2107 self
2108 }
2109
2110 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
2129 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
2130 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
2131 let mut tls = Some(tls);
2132 #[cfg(feature = "native-tls")]
2133 {
2134 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2135 let tls = conn.take().expect("is definitely Some");
2136 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2137 self.config.tls = tls;
2138 return self;
2139 }
2140 }
2141 #[cfg(feature = "__rustls")]
2142 {
2143 if let Some(conn) =
2144 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2145 {
2146 let tls = conn.take().expect("is definitely Some");
2147 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2148 self.config.tls = tls;
2149 return self;
2150 }
2151 }
2152
2153 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2155 self
2156 }
2157
2158 #[cfg(feature = "__tls")]
2165 #[cfg_attr(
2166 docsrs,
2167 doc(cfg(any(
2168 feature = "default-tls",
2169 feature = "native-tls",
2170 feature = "rustls-tls"
2171 )))
2172 )]
2173 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2174 self.config.tls_info = tls_info;
2175 self
2176 }
2177
2178 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2182 self.config.https_only = enabled;
2183 self
2184 }
2185
2186 #[doc(hidden)]
2187 #[cfg(feature = "hickory-dns")]
2188 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2189 #[deprecated(note = "use `hickory_dns` instead")]
2190 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
2191 self.config.hickory_dns = enable;
2192 self
2193 }
2194
2195 #[cfg(feature = "hickory-dns")]
2209 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2210 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2211 self.config.hickory_dns = enable;
2212 self
2213 }
2214
2215 #[doc(hidden)]
2216 #[deprecated(note = "use `no_hickory_dns` instead")]
2217 pub fn no_trust_dns(self) -> ClientBuilder {
2218 self.no_hickory_dns()
2219 }
2220
2221 pub fn no_hickory_dns(self) -> ClientBuilder {
2227 #[cfg(feature = "hickory-dns")]
2228 {
2229 self.hickory_dns(false)
2230 }
2231
2232 #[cfg(not(feature = "hickory-dns"))]
2233 {
2234 self
2235 }
2236 }
2237
2238 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2243 self.resolve_to_addrs(domain, &[addr])
2244 }
2245
2246 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2251 self.config
2252 .dns_overrides
2253 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2254 self
2255 }
2256
2257 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
2263 self.config.dns_resolver = Some(resolver as _);
2264 self
2265 }
2266
2267 pub fn dns_resolver2<R>(mut self, resolver: R) -> ClientBuilder
2274 where
2275 R: crate::dns::resolve::IntoResolve,
2276 {
2277 self.config.dns_resolver = Some(resolver.into_resolve());
2278 self
2279 }
2280
2281 #[cfg(feature = "http3")]
2286 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2287 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2288 self.config.tls_enable_early_data = enabled;
2289 self
2290 }
2291
2292 #[cfg(feature = "http3")]
2298 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2299 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2300 self.config.quic_max_idle_timeout = Some(value);
2301 self
2302 }
2303
2304 #[cfg(feature = "http3")]
2315 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2316 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2317 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2318 self
2319 }
2320
2321 #[cfg(feature = "http3")]
2332 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2333 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2334 self.config.quic_receive_window = Some(value.try_into().unwrap());
2335 self
2336 }
2337
2338 #[cfg(feature = "http3")]
2344 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2345 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2346 self.config.quic_send_window = Some(value);
2347 self
2348 }
2349
2350 #[cfg(feature = "http3")]
2358 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2359 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2360 self.config.quic_congestion_bbr = true;
2361 self
2362 }
2363
2364 #[cfg(feature = "http3")]
2374 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2375 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2376 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2377 self
2378 }
2379
2380 #[cfg(feature = "http3")]
2392 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2393 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2394 self.config.h3_send_grease = Some(enabled);
2395 self
2396 }
2397
2398 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2422 where
2423 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2424 L::Service:
2425 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2426 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2427 {
2428 let layer = BoxCloneSyncServiceLayer::new(layer);
2429
2430 self.config.connector_layers.push(layer);
2431
2432 self
2433 }
2434}
2435
2436type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2437
2438impl Default for Client {
2439 fn default() -> Self {
2440 Self::new()
2441 }
2442}
2443
2444impl Client {
2445 pub fn new() -> Client {
2455 ClientBuilder::new().build().expect("Client::new()")
2456 }
2457
2458 pub fn builder() -> ClientBuilder {
2462 ClientBuilder::new()
2463 }
2464
2465 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2471 self.request(Method::GET, url)
2472 }
2473
2474 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2480 self.request(Method::POST, url)
2481 }
2482
2483 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2489 self.request(Method::PUT, url)
2490 }
2491
2492 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2498 self.request(Method::PATCH, url)
2499 }
2500
2501 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2507 self.request(Method::DELETE, url)
2508 }
2509
2510 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2516 self.request(Method::HEAD, url)
2517 }
2518
2519 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2528 let req = url.into_url().map(move |url| Request::new(method, url));
2529 RequestBuilder::new(self.clone(), req)
2530 }
2531
2532 pub fn execute(
2545 &self,
2546 request: Request,
2547 ) -> impl Future<Output = Result<Response, crate::Error>> {
2548 self.execute_request(request)
2549 }
2550
2551 pub(super) fn execute_request(&self, req: Request) -> Pending {
2552 let (method, url, mut headers, body, version, extensions) = req.pieces();
2553 if url.scheme() != "http" && url.scheme() != "https" {
2554 return Pending::new_err(error::url_bad_scheme(url));
2555 }
2556
2557 if self.inner.https_only && url.scheme() != "https" {
2559 return Pending::new_err(error::url_bad_scheme(url));
2560 }
2561
2562 for (key, value) in &self.inner.headers {
2565 if let Entry::Vacant(entry) = headers.entry(key) {
2566 entry.insert(value.clone());
2567 }
2568 }
2569
2570 let uri = match try_uri(&url) {
2571 Ok(uri) => uri,
2572 _ => return Pending::new_err(error::url_invalid_uri(url)),
2573 };
2574
2575 let body = body.unwrap_or_else(Body::empty);
2576
2577 self.proxy_auth(&uri, &mut headers);
2578 self.proxy_custom_headers(&uri, &mut headers);
2579
2580 let builder = hyper::Request::builder()
2581 .method(method.clone())
2582 .uri(uri)
2583 .version(version);
2584
2585 let in_flight = match version {
2586 #[cfg(feature = "http3")]
2587 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2588 let mut req = builder.body(body).expect("valid request parts");
2589 *req.headers_mut() = headers.clone();
2590 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2591 ResponseFuture::H3(h3.call(req))
2592 }
2593 _ => {
2594 let mut req = builder.body(body).expect("valid request parts");
2595 *req.headers_mut() = headers.clone();
2596 let mut hyper = self.inner.hyper.clone();
2597 ResponseFuture::Default(hyper.call(req))
2598 }
2599 };
2600
2601 let total_timeout = self
2602 .inner
2603 .total_timeout
2604 .fetch(&extensions)
2605 .copied()
2606 .map(tokio::time::sleep)
2607 .map(Box::pin);
2608
2609 let read_timeout_fut = self
2610 .inner
2611 .read_timeout
2612 .map(tokio::time::sleep)
2613 .map(Box::pin);
2614
2615 Pending {
2616 inner: PendingInner::Request(Box::pin(PendingRequest {
2617 method,
2618 url,
2619 headers,
2620
2621 client: self.inner.clone(),
2622
2623 in_flight,
2624 total_timeout,
2625 read_timeout_fut,
2626 read_timeout: self.inner.read_timeout,
2627 })),
2628 }
2629 }
2630
2631 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2632 if !self.inner.proxies_maybe_http_auth {
2633 return;
2634 }
2635
2636 if dst.scheme() != Some(&Scheme::HTTP) {
2640 return;
2641 }
2642
2643 if headers.contains_key(PROXY_AUTHORIZATION) {
2644 return;
2645 }
2646
2647 for proxy in self.inner.proxies.iter() {
2648 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2649 headers.insert(PROXY_AUTHORIZATION, header);
2650 break;
2651 }
2652 }
2653 }
2654
2655 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2656 if !self.inner.proxies_maybe_http_custom_headers {
2657 return;
2658 }
2659
2660 if dst.scheme() != Some(&Scheme::HTTP) {
2661 return;
2662 }
2663
2664 for proxy in self.inner.proxies.iter() {
2665 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2666 iter.iter().for_each(|(key, value)| {
2667 headers.insert(key, value.clone());
2668 });
2669 break;
2670 }
2671 }
2672 }
2673}
2674
2675impl fmt::Debug for Client {
2676 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2677 let mut builder = f.debug_struct("Client");
2678 self.inner.fmt_fields(&mut builder);
2679 builder.finish()
2680 }
2681}
2682
2683impl tower_service::Service<Request> for Client {
2684 type Response = Response;
2685 type Error = crate::Error;
2686 type Future = Pending;
2687
2688 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2689 Poll::Ready(Ok(()))
2690 }
2691
2692 fn call(&mut self, req: Request) -> Self::Future {
2693 self.execute_request(req)
2694 }
2695}
2696
2697impl tower_service::Service<Request> for &'_ Client {
2698 type Response = Response;
2699 type Error = crate::Error;
2700 type Future = Pending;
2701
2702 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2703 Poll::Ready(Ok(()))
2704 }
2705
2706 fn call(&mut self, req: Request) -> Self::Future {
2707 self.execute_request(req)
2708 }
2709}
2710
2711impl fmt::Debug for ClientBuilder {
2712 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2713 let mut builder = f.debug_struct("ClientBuilder");
2714 self.config.fmt_fields(&mut builder);
2715 builder.finish()
2716 }
2717}
2718
2719impl Config {
2720 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2721 #[cfg(feature = "cookies")]
2725 {
2726 if let Some(_) = self.cookie_store {
2727 f.field("cookie_store", &true);
2728 }
2729 }
2730
2731 f.field("accepts", &self.accepts);
2732
2733 if !self.proxies.is_empty() {
2734 f.field("proxies", &self.proxies);
2735 }
2736
2737 if !self.redirect_policy.is_default() {
2738 f.field("redirect_policy", &self.redirect_policy);
2739 }
2740
2741 if self.referer {
2742 f.field("referer", &true);
2743 }
2744
2745 f.field("default_headers", &self.headers);
2746
2747 if self.http1_title_case_headers {
2748 f.field("http1_title_case_headers", &true);
2749 }
2750
2751 if self.http1_allow_obsolete_multiline_headers_in_responses {
2752 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2753 }
2754
2755 if self.http1_ignore_invalid_headers_in_responses {
2756 f.field("http1_ignore_invalid_headers_in_responses", &true);
2757 }
2758
2759 if self.http1_allow_spaces_after_header_name_in_responses {
2760 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2761 }
2762
2763 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2764 f.field("http1_only", &true);
2765 }
2766
2767 #[cfg(feature = "http2")]
2768 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2769 f.field("http2_prior_knowledge", &true);
2770 }
2771
2772 if let Some(ref d) = self.connect_timeout {
2773 f.field("connect_timeout", d);
2774 }
2775
2776 if let Some(ref d) = self.timeout {
2777 f.field("timeout", d);
2778 }
2779
2780 if let Some(ref v) = self.local_address {
2781 f.field("local_address", v);
2782 }
2783
2784 #[cfg(any(
2785 target_os = "android",
2786 target_os = "fuchsia",
2787 target_os = "illumos",
2788 target_os = "ios",
2789 target_os = "linux",
2790 target_os = "macos",
2791 target_os = "solaris",
2792 target_os = "tvos",
2793 target_os = "visionos",
2794 target_os = "watchos",
2795 ))]
2796 if let Some(ref v) = self.interface {
2797 f.field("interface", v);
2798 }
2799
2800 if self.nodelay {
2801 f.field("tcp_nodelay", &true);
2802 }
2803
2804 #[cfg(feature = "__tls")]
2805 {
2806 if !self.hostname_verification {
2807 f.field("danger_accept_invalid_hostnames", &true);
2808 }
2809 }
2810
2811 #[cfg(feature = "__tls")]
2812 {
2813 if !self.certs_verification {
2814 f.field("danger_accept_invalid_certs", &true);
2815 }
2816
2817 if let Some(ref min_tls_version) = self.min_tls_version {
2818 f.field("min_tls_version", min_tls_version);
2819 }
2820
2821 if let Some(ref max_tls_version) = self.max_tls_version {
2822 f.field("max_tls_version", max_tls_version);
2823 }
2824
2825 f.field("tls_sni", &self.tls_sni);
2826
2827 f.field("tls_info", &self.tls_info);
2828 }
2829
2830 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2831 {
2832 f.field("tls_backend", &self.tls);
2833 }
2834
2835 if !self.dns_overrides.is_empty() {
2836 f.field("dns_overrides", &self.dns_overrides);
2837 }
2838
2839 #[cfg(feature = "http3")]
2840 {
2841 if self.tls_enable_early_data {
2842 f.field("tls_enable_early_data", &true);
2843 }
2844 }
2845
2846 #[cfg(unix)]
2847 if let Some(ref p) = self.unix_socket {
2848 f.field("unix_socket", p);
2849 }
2850 }
2851}
2852
2853#[cfg(not(feature = "cookies"))]
2854type MaybeCookieService<T> = T;
2855
2856#[cfg(feature = "cookies")]
2857type MaybeCookieService<T> = CookieService<T>;
2858
2859#[cfg(not(any(
2860 feature = "gzip",
2861 feature = "brotli",
2862 feature = "zstd",
2863 feature = "deflate"
2864)))]
2865type MaybeDecompression<T> = T;
2866
2867#[cfg(any(
2868 feature = "gzip",
2869 feature = "brotli",
2870 feature = "zstd",
2871 feature = "deflate"
2872))]
2873type MaybeDecompression<T> = Decompression<T>;
2874
2875type LayeredService<T> = MaybeDecompression<
2876 FollowRedirect<
2877 MaybeCookieService<tower::retry::Retry<crate::retry::Policy, T>>,
2878 TowerRedirectPolicy,
2879 >,
2880>;
2881type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
2882
2883struct ClientRef {
2884 accepts: Accepts,
2885 #[cfg(feature = "cookies")]
2886 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2887 headers: HeaderMap,
2888 hyper: LayeredService<HyperService>,
2889 #[cfg(feature = "http3")]
2890 h3_client: Option<LayeredService<H3Client>>,
2891 referer: bool,
2892 total_timeout: RequestConfig<TotalTimeout>,
2893 read_timeout: Option<Duration>,
2894 proxies: Arc<Vec<ProxyMatcher>>,
2895 proxies_maybe_http_auth: bool,
2896 proxies_maybe_http_custom_headers: bool,
2897 https_only: bool,
2898 redirect_policy_desc: Option<String>,
2899}
2900
2901impl ClientRef {
2902 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2903 #[cfg(feature = "cookies")]
2907 {
2908 if let Some(_) = self.cookie_store {
2909 f.field("cookie_store", &true);
2910 }
2911 }
2912
2913 f.field("accepts", &self.accepts);
2914
2915 if !self.proxies.is_empty() {
2916 f.field("proxies", &self.proxies);
2917 }
2918
2919 if let Some(s) = &self.redirect_policy_desc {
2920 f.field("redirect_policy", s);
2921 }
2922
2923 if self.referer {
2924 f.field("referer", &true);
2925 }
2926
2927 f.field("default_headers", &self.headers);
2928
2929 self.total_timeout.fmt_as_field(f);
2930
2931 if let Some(ref d) = self.read_timeout {
2932 f.field("read_timeout", d);
2933 }
2934 }
2935}
2936
2937pin_project! {
2938 pub struct Pending {
2939 #[pin]
2940 inner: PendingInner,
2941 }
2942}
2943
2944enum PendingInner {
2945 Request(Pin<Box<PendingRequest>>),
2946 Error(Option<crate::Error>),
2947}
2948
2949pin_project! {
2950 struct PendingRequest {
2951 method: Method,
2952 url: Url,
2953 headers: HeaderMap,
2954
2955 client: Arc<ClientRef>,
2956
2957 #[pin]
2958 in_flight: ResponseFuture,
2959 #[pin]
2960 total_timeout: Option<Pin<Box<Sleep>>>,
2961 #[pin]
2962 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2963 read_timeout: Option<Duration>,
2964 }
2965}
2966
2967enum ResponseFuture {
2968 Default(LayeredFuture<HyperService>),
2969 #[cfg(feature = "http3")]
2970 H3(LayeredFuture<H3Client>),
2971}
2972
2973impl PendingRequest {
2974 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2975 self.project().in_flight
2976 }
2977
2978 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2979 self.project().total_timeout
2980 }
2981
2982 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2983 self.project().read_timeout_fut
2984 }
2985}
2986
2987impl Pending {
2988 pub(super) fn new_err(err: crate::Error) -> Pending {
2989 Pending {
2990 inner: PendingInner::Error(Some(err)),
2991 }
2992 }
2993
2994 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2995 self.project().inner
2996 }
2997}
2998
2999impl Future for Pending {
3000 type Output = Result<Response, crate::Error>;
3001
3002 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3003 let inner = self.inner();
3004 match inner.get_mut() {
3005 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
3006 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
3007 .take()
3008 .expect("Pending error polled more than once"))),
3009 }
3010 }
3011}
3012
3013impl Future for PendingRequest {
3014 type Output = Result<Response, crate::Error>;
3015
3016 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3017 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
3018 if let Poll::Ready(()) = delay.poll(cx) {
3019 return Poll::Ready(Err(
3020 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3021 ));
3022 }
3023 }
3024
3025 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
3026 if let Poll::Ready(()) = delay.poll(cx) {
3027 return Poll::Ready(Err(
3028 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3029 ));
3030 }
3031 }
3032
3033 let res = match self.as_mut().in_flight().get_mut() {
3034 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
3035 Err(e) => {
3036 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
3037 }
3038 Ok(res) => res.map(super::body::boxed),
3039 },
3040 #[cfg(feature = "http3")]
3041 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
3042 Err(e) => {
3043 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
3044 }
3045 Ok(res) => res.map(super::body::boxed),
3046 },
3047 };
3048
3049 if let Some(url) = &res
3050 .extensions()
3051 .get::<tower_http::follow_redirect::RequestUri>()
3052 {
3053 self.url = match Url::parse(&url.0.to_string()) {
3054 Ok(url) => url,
3055 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3056 }
3057 };
3058
3059 let res = Response::new(
3060 res,
3061 self.url.clone(),
3062 self.total_timeout.take(),
3063 self.read_timeout,
3064 );
3065 Poll::Ready(Ok(res))
3066 }
3067}
3068
3069impl fmt::Debug for Pending {
3070 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3071 match self.inner {
3072 PendingInner::Request(ref req) => f
3073 .debug_struct("Pending")
3074 .field("method", &req.method)
3075 .field("url", &req.url)
3076 .finish(),
3077 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3078 }
3079 }
3080}
3081
3082#[cfg(test)]
3083mod tests {
3084 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
3085
3086 #[tokio::test]
3087 async fn execute_request_rejects_invalid_urls() {
3088 let url_str = "hxxps://www.rust-lang.org/";
3089 let url = url::Url::parse(url_str).unwrap();
3090 let result = crate::get(url.clone()).await;
3091
3092 assert!(result.is_err());
3093 let err = result.err().unwrap();
3094 assert!(err.is_builder());
3095 assert_eq!(url_str, err.url().unwrap().as_str());
3096 }
3097
3098 #[tokio::test]
3100 async fn execute_request_rejects_invalid_hostname() {
3101 let url_str = "https://{{hostname}}/";
3102 let url = url::Url::parse(url_str).unwrap();
3103 let result = crate::get(url.clone()).await;
3104
3105 assert!(result.is_err());
3106 let err = result.err().unwrap();
3107 assert!(err.is_builder());
3108 assert_eq!(url_str, err.url().unwrap().as_str());
3109 }
3110
3111 #[test]
3112 fn test_future_size() {
3113 let s = std::mem::size_of::<super::Pending>();
3114 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3115 }
3116}