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::decoder::Accepts;
13use super::request::{Request, RequestBuilder};
14use super::response::Response;
15use super::Body;
16#[cfg(feature = "http3")]
17use crate::async_impl::h3_client::connect::{H3ClientConfig, H3Connector};
18#[cfg(feature = "http3")]
19use crate::async_impl::h3_client::H3Client;
20use crate::config::{RequestConfig, RequestTimeout};
21#[cfg(unix)]
22use crate::connect::uds::UnixSocketProvider;
23use crate::connect::{
24 sealed::{Conn, Unnameable},
25 BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
26};
27#[cfg(feature = "cookies")]
28use crate::cookie;
29#[cfg(feature = "hickory-dns")]
30use crate::dns::hickory::HickoryDnsResolver;
31use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
32use crate::error::{self, BoxError};
33use crate::into_url::try_uri;
34use crate::proxy::Matcher as ProxyMatcher;
35use crate::redirect::{self, TowerRedirectPolicy};
36#[cfg(feature = "__rustls")]
37use crate::tls::CertificateRevocationList;
38#[cfg(feature = "__tls")]
39use crate::tls::{self, TlsBackend};
40#[cfg(feature = "__tls")]
41use crate::Certificate;
42#[cfg(any(feature = "native-tls", feature = "__rustls"))]
43use crate::Identity;
44use crate::{IntoUrl, Method, Proxy, Url};
45
46use http::header::{
47 Entry, HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, PROXY_AUTHORIZATION, RANGE, USER_AGENT,
48};
49use http::uri::Scheme;
50use http::Uri;
51use hyper_util::client::legacy::connect::HttpConnector;
52#[cfg(feature = "default-tls")]
53use native_tls_crate::TlsConnector;
54use pin_project_lite::pin_project;
55#[cfg(feature = "http3")]
56use quinn::TransportConfig;
57#[cfg(feature = "http3")]
58use quinn::VarInt;
59use tokio::time::Sleep;
60use tower::util::BoxCloneSyncServiceLayer;
61use tower::{Layer, Service};
62use tower_http::follow_redirect::FollowRedirect;
63
64#[derive(Clone)]
78pub struct Client {
79 inner: Arc<ClientRef>,
80}
81
82#[must_use]
84pub struct ClientBuilder {
85 config: Config,
86}
87
88enum HttpVersionPref {
89 Http1,
90 #[cfg(feature = "http2")]
91 Http2,
92 #[cfg(feature = "http3")]
93 Http3,
94 All,
95}
96
97#[derive(Clone)]
98struct HyperService {
99 #[cfg(feature = "cookies")]
100 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
101 hyper: HyperClient,
102}
103
104impl Service<hyper::Request<crate::async_impl::body::Body>> for HyperService {
105 type Error = crate::Error;
106 type Response = http::Response<hyper::body::Incoming>;
107 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + Sync>>;
108
109 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
110 self.hyper.poll_ready(cx).map_err(crate::error::request)
111 }
112
113 #[cfg(not(feature = "cookies"))]
114 fn call(&mut self, req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
115 let clone = self.hyper.clone();
116 let mut inner = std::mem::replace(&mut self.hyper, clone);
117 Box::pin(async move { inner.call(req).await.map_err(crate::error::request) })
118 }
119
120 #[cfg(feature = "cookies")]
121 fn call(&mut self, mut req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
122 let clone = self.hyper.clone();
123 let mut inner = std::mem::replace(&mut self.hyper, clone);
124 let url = Url::parse(req.uri().to_string().as_str()).expect("invalid URL");
125
126 if let Some(cookie_store) = self.cookie_store.as_ref() {
127 if req.headers().get(crate::header::COOKIE).is_none() {
128 let headers = req.headers_mut();
129 crate::util::add_cookie_header(headers, &**cookie_store, &url);
130 }
131 }
132
133 let cookie_store = self.cookie_store.clone();
134 Box::pin(async move {
135 let res = inner.call(req).await.map_err(crate::error::request);
136
137 if let Some(ref cookie_store) = cookie_store {
138 if let Ok(res) = &res {
139 let mut cookies =
140 cookie::extract_response_cookie_headers(res.headers()).peekable();
141 if cookies.peek().is_some() {
142 cookie_store.set_cookies(&mut cookies, &url);
143 }
144 }
145 }
146
147 res
148 })
149 }
150}
151
152struct Config {
153 accepts: Accepts,
155 headers: HeaderMap,
156 #[cfg(feature = "__tls")]
157 hostname_verification: bool,
158 #[cfg(feature = "__tls")]
159 certs_verification: bool,
160 #[cfg(feature = "__tls")]
161 tls_sni: bool,
162 connect_timeout: Option<Duration>,
163 connection_verbose: bool,
164 pool_idle_timeout: Option<Duration>,
165 pool_max_idle_per_host: usize,
166 tcp_keepalive: Option<Duration>,
167 tcp_keepalive_interval: Option<Duration>,
168 tcp_keepalive_retries: Option<u32>,
169 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
170 tcp_user_timeout: Option<Duration>,
171 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
172 identity: Option<Identity>,
173 proxies: Vec<ProxyMatcher>,
174 auto_sys_proxy: bool,
175 redirect_policy: redirect::Policy,
176 retry_policy: crate::retry::Builder,
177 referer: bool,
178 read_timeout: Option<Duration>,
179 timeout: Option<Duration>,
180 #[cfg(feature = "__tls")]
181 root_certs: Vec<Certificate>,
182 #[cfg(feature = "__tls")]
183 tls_built_in_root_certs: bool,
184 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
185 tls_built_in_certs_webpki: bool,
186 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
187 tls_built_in_certs_native: bool,
188 #[cfg(feature = "__rustls")]
189 crls: Vec<CertificateRevocationList>,
190 #[cfg(feature = "__tls")]
191 min_tls_version: Option<tls::Version>,
192 #[cfg(feature = "__tls")]
193 max_tls_version: Option<tls::Version>,
194 #[cfg(feature = "__tls")]
195 tls_info: bool,
196 #[cfg(feature = "__tls")]
197 tls: TlsBackend,
198 connector_layers: Vec<BoxedConnectorLayer>,
199 http_version_pref: HttpVersionPref,
200 http09_responses: bool,
201 http1_title_case_headers: bool,
202 http1_allow_obsolete_multiline_headers_in_responses: bool,
203 http1_ignore_invalid_headers_in_responses: bool,
204 http1_allow_spaces_after_header_name_in_responses: bool,
205 #[cfg(feature = "http2")]
206 http2_initial_stream_window_size: Option<u32>,
207 #[cfg(feature = "http2")]
208 http2_initial_connection_window_size: Option<u32>,
209 #[cfg(feature = "http2")]
210 http2_adaptive_window: bool,
211 #[cfg(feature = "http2")]
212 http2_max_frame_size: Option<u32>,
213 #[cfg(feature = "http2")]
214 http2_max_header_list_size: Option<u32>,
215 #[cfg(feature = "http2")]
216 http2_keep_alive_interval: Option<Duration>,
217 #[cfg(feature = "http2")]
218 http2_keep_alive_timeout: Option<Duration>,
219 #[cfg(feature = "http2")]
220 http2_keep_alive_while_idle: bool,
221 local_address: Option<IpAddr>,
222 #[cfg(any(
223 target_os = "android",
224 target_os = "fuchsia",
225 target_os = "illumos",
226 target_os = "ios",
227 target_os = "linux",
228 target_os = "macos",
229 target_os = "solaris",
230 target_os = "tvos",
231 target_os = "visionos",
232 target_os = "watchos",
233 ))]
234 interface: Option<String>,
235 nodelay: bool,
236 #[cfg(feature = "cookies")]
237 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
238 hickory_dns: bool,
239 error: Option<crate::Error>,
240 https_only: bool,
241 #[cfg(feature = "http3")]
242 tls_enable_early_data: bool,
243 #[cfg(feature = "http3")]
244 quic_max_idle_timeout: Option<Duration>,
245 #[cfg(feature = "http3")]
246 quic_stream_receive_window: Option<VarInt>,
247 #[cfg(feature = "http3")]
248 quic_receive_window: Option<VarInt>,
249 #[cfg(feature = "http3")]
250 quic_send_window: Option<u64>,
251 #[cfg(feature = "http3")]
252 quic_congestion_bbr: bool,
253 #[cfg(feature = "http3")]
254 h3_max_field_section_size: Option<u64>,
255 #[cfg(feature = "http3")]
256 h3_send_grease: Option<bool>,
257 dns_overrides: HashMap<String, Vec<SocketAddr>>,
258 dns_resolver: Option<Arc<dyn Resolve>>,
259
260 #[cfg(unix)]
261 unix_socket: Option<Arc<std::path::Path>>,
262}
263
264impl Default for ClientBuilder {
265 fn default() -> Self {
266 Self::new()
267 }
268}
269
270impl ClientBuilder {
271 pub fn new() -> Self {
275 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
276 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
277
278 ClientBuilder {
279 config: Config {
280 error: None,
281 accepts: Accepts::default(),
282 headers,
283 #[cfg(feature = "__tls")]
284 hostname_verification: true,
285 #[cfg(feature = "__tls")]
286 certs_verification: true,
287 #[cfg(feature = "__tls")]
288 tls_sni: true,
289 connect_timeout: None,
290 connection_verbose: false,
291 pool_idle_timeout: Some(Duration::from_secs(90)),
292 pool_max_idle_per_host: usize::MAX,
293 tcp_keepalive: Some(Duration::from_secs(15)),
294 tcp_keepalive_interval: Some(Duration::from_secs(15)),
295 tcp_keepalive_retries: Some(3),
296 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
297 tcp_user_timeout: Some(Duration::from_secs(30)),
298 proxies: Vec::new(),
299 auto_sys_proxy: true,
300 redirect_policy: redirect::Policy::default(),
301 retry_policy: crate::retry::Builder::default(),
302 referer: true,
303 read_timeout: None,
304 timeout: None,
305 #[cfg(feature = "__tls")]
306 root_certs: Vec::new(),
307 #[cfg(feature = "__tls")]
308 tls_built_in_root_certs: true,
309 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
310 tls_built_in_certs_webpki: true,
311 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
312 tls_built_in_certs_native: true,
313 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
314 identity: None,
315 #[cfg(feature = "__rustls")]
316 crls: vec![],
317 #[cfg(feature = "__tls")]
318 min_tls_version: None,
319 #[cfg(feature = "__tls")]
320 max_tls_version: None,
321 #[cfg(feature = "__tls")]
322 tls_info: false,
323 #[cfg(feature = "__tls")]
324 tls: TlsBackend::default(),
325 connector_layers: Vec::new(),
326 http_version_pref: HttpVersionPref::All,
327 http09_responses: false,
328 http1_title_case_headers: false,
329 http1_allow_obsolete_multiline_headers_in_responses: false,
330 http1_ignore_invalid_headers_in_responses: false,
331 http1_allow_spaces_after_header_name_in_responses: false,
332 #[cfg(feature = "http2")]
333 http2_initial_stream_window_size: None,
334 #[cfg(feature = "http2")]
335 http2_initial_connection_window_size: None,
336 #[cfg(feature = "http2")]
337 http2_adaptive_window: false,
338 #[cfg(feature = "http2")]
339 http2_max_frame_size: None,
340 #[cfg(feature = "http2")]
341 http2_max_header_list_size: None,
342 #[cfg(feature = "http2")]
343 http2_keep_alive_interval: None,
344 #[cfg(feature = "http2")]
345 http2_keep_alive_timeout: None,
346 #[cfg(feature = "http2")]
347 http2_keep_alive_while_idle: false,
348 local_address: None,
349 #[cfg(any(
350 target_os = "android",
351 target_os = "fuchsia",
352 target_os = "illumos",
353 target_os = "ios",
354 target_os = "linux",
355 target_os = "macos",
356 target_os = "solaris",
357 target_os = "tvos",
358 target_os = "visionos",
359 target_os = "watchos",
360 ))]
361 interface: None,
362 nodelay: true,
363 hickory_dns: cfg!(feature = "hickory-dns"),
364 #[cfg(feature = "cookies")]
365 cookie_store: None,
366 https_only: false,
367 dns_overrides: HashMap::new(),
368 #[cfg(feature = "http3")]
369 tls_enable_early_data: false,
370 #[cfg(feature = "http3")]
371 quic_max_idle_timeout: None,
372 #[cfg(feature = "http3")]
373 quic_stream_receive_window: None,
374 #[cfg(feature = "http3")]
375 quic_receive_window: None,
376 #[cfg(feature = "http3")]
377 quic_send_window: None,
378 #[cfg(feature = "http3")]
379 quic_congestion_bbr: false,
380 #[cfg(feature = "http3")]
381 h3_max_field_section_size: None,
382 #[cfg(feature = "http3")]
383 h3_send_grease: None,
384 dns_resolver: None,
385 #[cfg(unix)]
386 unix_socket: None,
387 },
388 }
389 }
390}
391
392impl ClientBuilder {
393 pub fn build(self) -> crate::Result<Client> {
400 let config = self.config;
401
402 if let Some(err) = config.error {
403 return Err(err);
404 }
405
406 let mut proxies = config.proxies;
407 if config.auto_sys_proxy {
408 proxies.push(ProxyMatcher::system());
409 }
410 let proxies = Arc::new(proxies);
411
412 #[allow(unused)]
413 #[cfg(feature = "http3")]
414 let mut h3_connector = None;
415
416 let resolver = {
417 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
418 false => Arc::new(GaiResolver::new()),
419 #[cfg(feature = "hickory-dns")]
420 true => Arc::new(HickoryDnsResolver::default()),
421 #[cfg(not(feature = "hickory-dns"))]
422 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
423 };
424 if let Some(dns_resolver) = config.dns_resolver {
425 resolver = dns_resolver;
426 }
427 if !config.dns_overrides.is_empty() {
428 resolver = Arc::new(DnsResolverWithOverrides::new(
429 resolver,
430 config.dns_overrides,
431 ));
432 }
433 DynResolver::new(resolver)
434 };
435
436 let mut connector_builder = {
437 #[cfg(feature = "__tls")]
438 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
439 headers.get(USER_AGENT).cloned()
440 }
441
442 let mut http = HttpConnector::new_with_resolver(resolver.clone());
443 http.set_connect_timeout(config.connect_timeout);
444
445 #[cfg(all(feature = "http3", feature = "__rustls"))]
446 let build_h3_connector =
447 |resolver,
448 tls,
449 quic_max_idle_timeout: Option<Duration>,
450 quic_stream_receive_window,
451 quic_receive_window,
452 quic_send_window,
453 quic_congestion_bbr,
454 h3_max_field_section_size,
455 h3_send_grease,
456 local_address,
457 http_version_pref: &HttpVersionPref| {
458 let mut transport_config = TransportConfig::default();
459
460 if let Some(max_idle_timeout) = quic_max_idle_timeout {
461 transport_config.max_idle_timeout(Some(
462 max_idle_timeout.try_into().map_err(error::builder)?,
463 ));
464 }
465
466 if let Some(stream_receive_window) = quic_stream_receive_window {
467 transport_config.stream_receive_window(stream_receive_window);
468 }
469
470 if let Some(receive_window) = quic_receive_window {
471 transport_config.receive_window(receive_window);
472 }
473
474 if let Some(send_window) = quic_send_window {
475 transport_config.send_window(send_window);
476 }
477
478 if quic_congestion_bbr {
479 let factory = Arc::new(quinn::congestion::BbrConfig::default());
480 transport_config.congestion_controller_factory(factory);
481 }
482
483 let mut h3_client_config = H3ClientConfig::default();
484
485 if let Some(max_field_section_size) = h3_max_field_section_size {
486 h3_client_config.max_field_section_size = Some(max_field_section_size);
487 }
488
489 if let Some(send_grease) = h3_send_grease {
490 h3_client_config.send_grease = Some(send_grease);
491 }
492
493 let res = H3Connector::new(
494 resolver,
495 tls,
496 local_address,
497 transport_config,
498 h3_client_config,
499 );
500
501 match res {
502 Ok(connector) => Ok(Some(connector)),
503 Err(err) => {
504 if let HttpVersionPref::Http3 = http_version_pref {
505 Err(error::builder(err))
506 } else {
507 Ok(None)
508 }
509 }
510 }
511 };
512
513 #[cfg(feature = "__tls")]
514 match config.tls {
515 #[cfg(feature = "default-tls")]
516 TlsBackend::Default => {
517 let mut tls = TlsConnector::builder();
518
519 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
520 {
521 match config.http_version_pref {
522 HttpVersionPref::Http1 => {
523 tls.request_alpns(&["http/1.1"]);
524 }
525 #[cfg(feature = "http2")]
526 HttpVersionPref::Http2 => {
527 tls.request_alpns(&["h2"]);
528 }
529 HttpVersionPref::All => {
530 tls.request_alpns(&["h2", "http/1.1"]);
531 }
532 }
533 }
534
535 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
536
537 tls.danger_accept_invalid_certs(!config.certs_verification);
538
539 tls.use_sni(config.tls_sni);
540
541 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
542
543 for cert in config.root_certs {
544 cert.add_to_native_tls(&mut tls);
545 }
546
547 #[cfg(feature = "native-tls")]
548 {
549 if let Some(id) = config.identity {
550 id.add_to_native_tls(&mut tls)?;
551 }
552 }
553 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
554 {
555 if let Some(_id) = config.identity {
557 return Err(crate::error::builder("incompatible TLS identity type"));
558 }
559 }
560
561 if let Some(min_tls_version) = config.min_tls_version {
562 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
563 crate::error::builder("invalid minimum TLS version for backend")
567 })?;
568 tls.min_protocol_version(Some(protocol));
569 }
570
571 if let Some(max_tls_version) = config.max_tls_version {
572 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
573 crate::error::builder("invalid maximum TLS version for backend")
578 })?;
579 tls.max_protocol_version(Some(protocol));
580 }
581
582 ConnectorBuilder::new_default_tls(
583 http,
584 tls,
585 proxies.clone(),
586 user_agent(&config.headers),
587 config.local_address,
588 #[cfg(any(
589 target_os = "android",
590 target_os = "fuchsia",
591 target_os = "illumos",
592 target_os = "ios",
593 target_os = "linux",
594 target_os = "macos",
595 target_os = "solaris",
596 target_os = "tvos",
597 target_os = "visionos",
598 target_os = "watchos",
599 ))]
600 config.interface.as_deref(),
601 config.nodelay,
602 config.tls_info,
603 )?
604 }
605 #[cfg(feature = "native-tls")]
606 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_default_tls(
607 http,
608 conn,
609 proxies.clone(),
610 user_agent(&config.headers),
611 config.local_address,
612 #[cfg(any(
613 target_os = "android",
614 target_os = "fuchsia",
615 target_os = "illumos",
616 target_os = "ios",
617 target_os = "linux",
618 target_os = "macos",
619 target_os = "solaris",
620 target_os = "tvos",
621 target_os = "visionos",
622 target_os = "watchos",
623 ))]
624 config.interface.as_deref(),
625 config.nodelay,
626 config.tls_info,
627 ),
628 #[cfg(feature = "__rustls")]
629 TlsBackend::BuiltRustls(conn) => {
630 #[cfg(feature = "http3")]
631 {
632 h3_connector = build_h3_connector(
633 resolver.clone(),
634 conn.clone(),
635 config.quic_max_idle_timeout,
636 config.quic_stream_receive_window,
637 config.quic_receive_window,
638 config.quic_send_window,
639 config.quic_congestion_bbr,
640 config.h3_max_field_section_size,
641 config.h3_send_grease,
642 config.local_address,
643 &config.http_version_pref,
644 )?;
645 }
646
647 ConnectorBuilder::new_rustls_tls(
648 http,
649 conn,
650 proxies.clone(),
651 user_agent(&config.headers),
652 config.local_address,
653 #[cfg(any(
654 target_os = "android",
655 target_os = "fuchsia",
656 target_os = "illumos",
657 target_os = "ios",
658 target_os = "linux",
659 target_os = "macos",
660 target_os = "solaris",
661 target_os = "tvos",
662 target_os = "visionos",
663 target_os = "watchos",
664 ))]
665 config.interface.as_deref(),
666 config.nodelay,
667 config.tls_info,
668 )
669 }
670 #[cfg(feature = "__rustls")]
671 TlsBackend::Rustls => {
672 use crate::tls::{IgnoreHostname, NoVerifier};
673
674 let mut root_cert_store = rustls::RootCertStore::empty();
676 for cert in config.root_certs {
677 cert.add_to_rustls(&mut root_cert_store)?;
678 }
679
680 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
681 if config.tls_built_in_certs_webpki {
682 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
683 }
684
685 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
686 if config.tls_built_in_certs_native {
687 let mut valid_count = 0;
688 let mut invalid_count = 0;
689
690 let load_results = rustls_native_certs::load_native_certs();
691 for cert in load_results.certs {
692 match root_cert_store.add(cert.into()) {
696 Ok(_) => valid_count += 1,
697 Err(err) => {
698 invalid_count += 1;
699 log::debug!("rustls failed to parse DER certificate: {err:?}");
700 }
701 }
702 }
703 if valid_count == 0 && invalid_count > 0 {
704 let err = if load_results.errors.is_empty() {
705 crate::error::builder(
706 "zero valid certificates found in native root store",
707 )
708 } else {
709 use std::fmt::Write as _;
710 let mut acc = String::new();
711 for err in load_results.errors {
712 let _ = writeln!(&mut acc, "{err}");
713 }
714
715 crate::error::builder(acc)
716 };
717
718 return Err(err);
719 }
720 }
721
722 let mut versions = rustls::ALL_VERSIONS.to_vec();
724
725 if let Some(min_tls_version) = config.min_tls_version {
726 versions.retain(|&supported_version| {
727 match tls::Version::from_rustls(supported_version.version) {
728 Some(version) => version >= min_tls_version,
729 None => true,
732 }
733 });
734 }
735
736 if let Some(max_tls_version) = config.max_tls_version {
737 versions.retain(|&supported_version| {
738 match tls::Version::from_rustls(supported_version.version) {
739 Some(version) => version <= max_tls_version,
740 None => false,
741 }
742 });
743 }
744
745 if versions.is_empty() {
746 return Err(crate::error::builder("empty supported tls versions"));
747 }
748
749 let provider = rustls::crypto::CryptoProvider::get_default()
752 .map(|arc| arc.clone())
753 .unwrap_or_else(|| {
754 #[cfg(not(feature = "__rustls-ring"))]
755 panic!("No provider set");
756
757 #[cfg(feature = "__rustls-ring")]
758 Arc::new(rustls::crypto::ring::default_provider())
759 });
760
761 let signature_algorithms = provider.signature_verification_algorithms;
763 let config_builder =
764 rustls::ClientConfig::builder_with_provider(provider.clone())
765 .with_protocol_versions(&versions)
766 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
767
768 let config_builder = if !config.certs_verification {
769 config_builder
770 .dangerous()
771 .with_custom_certificate_verifier(Arc::new(NoVerifier))
772 } else if !config.hostname_verification {
773 config_builder
774 .dangerous()
775 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
776 root_cert_store,
777 signature_algorithms,
778 )))
779 } else {
780 if config.crls.is_empty() {
781 config_builder.with_root_certificates(root_cert_store)
782 } else {
783 let crls = config
784 .crls
785 .iter()
786 .map(|e| e.as_rustls_crl())
787 .collect::<Vec<_>>();
788 let verifier =
789 rustls::client::WebPkiServerVerifier::builder_with_provider(
790 Arc::new(root_cert_store),
791 provider,
792 )
793 .with_crls(crls)
794 .build()
795 .map_err(|_| {
796 crate::error::builder("invalid TLS verification settings")
797 })?;
798 config_builder.with_webpki_verifier(verifier)
799 }
800 };
801
802 let mut tls = if let Some(id) = config.identity {
804 id.add_to_rustls(config_builder)?
805 } else {
806 config_builder.with_no_client_auth()
807 };
808
809 tls.enable_sni = config.tls_sni;
810
811 match config.http_version_pref {
813 HttpVersionPref::Http1 => {
814 tls.alpn_protocols = vec!["http/1.1".into()];
815 }
816 #[cfg(feature = "http2")]
817 HttpVersionPref::Http2 => {
818 tls.alpn_protocols = vec!["h2".into()];
819 }
820 #[cfg(feature = "http3")]
821 HttpVersionPref::Http3 => {
822 tls.alpn_protocols = vec!["h3".into()];
823 }
824 HttpVersionPref::All => {
825 tls.alpn_protocols = vec![
826 #[cfg(feature = "http2")]
827 "h2".into(),
828 "http/1.1".into(),
829 ];
830 }
831 }
832
833 #[cfg(feature = "http3")]
834 {
835 tls.enable_early_data = config.tls_enable_early_data;
836
837 h3_connector = build_h3_connector(
838 resolver.clone(),
839 tls.clone(),
840 config.quic_max_idle_timeout,
841 config.quic_stream_receive_window,
842 config.quic_receive_window,
843 config.quic_send_window,
844 config.quic_congestion_bbr,
845 config.h3_max_field_section_size,
846 config.h3_send_grease,
847 config.local_address,
848 &config.http_version_pref,
849 )?;
850 }
851
852 ConnectorBuilder::new_rustls_tls(
853 http,
854 tls,
855 proxies.clone(),
856 user_agent(&config.headers),
857 config.local_address,
858 #[cfg(any(
859 target_os = "android",
860 target_os = "fuchsia",
861 target_os = "illumos",
862 target_os = "ios",
863 target_os = "linux",
864 target_os = "macos",
865 target_os = "solaris",
866 target_os = "tvos",
867 target_os = "visionos",
868 target_os = "watchos",
869 ))]
870 config.interface.as_deref(),
871 config.nodelay,
872 config.tls_info,
873 )
874 }
875 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
876 TlsBackend::UnknownPreconfigured => {
877 return Err(crate::error::builder(
878 "Unknown TLS backend passed to `use_preconfigured_tls`",
879 ));
880 }
881 }
882
883 #[cfg(not(feature = "__tls"))]
884 ConnectorBuilder::new(
885 http,
886 proxies.clone(),
887 config.local_address,
888 #[cfg(any(
889 target_os = "android",
890 target_os = "fuchsia",
891 target_os = "illumos",
892 target_os = "ios",
893 target_os = "linux",
894 target_os = "macos",
895 target_os = "solaris",
896 target_os = "tvos",
897 target_os = "visionos",
898 target_os = "watchos",
899 ))]
900 config.interface.as_deref(),
901 config.nodelay,
902 )
903 };
904
905 connector_builder.set_timeout(config.connect_timeout);
906 connector_builder.set_verbose(config.connection_verbose);
907 connector_builder.set_keepalive(config.tcp_keepalive);
908 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
909 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
910 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
911 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
912
913 #[cfg(feature = "socks")]
914 connector_builder.set_socks_resolver(resolver);
915
916 #[cfg(unix)]
920 connector_builder.set_unix_socket(config.unix_socket);
921
922 let mut builder =
923 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
924 #[cfg(feature = "http2")]
925 {
926 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
927 builder.http2_only(true);
928 }
929
930 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
931 {
932 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
933 }
934 if let Some(http2_initial_connection_window_size) =
935 config.http2_initial_connection_window_size
936 {
937 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
938 }
939 if config.http2_adaptive_window {
940 builder.http2_adaptive_window(true);
941 }
942 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
943 builder.http2_max_frame_size(http2_max_frame_size);
944 }
945 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
946 builder.http2_max_header_list_size(http2_max_header_list_size);
947 }
948 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
949 builder.http2_keep_alive_interval(http2_keep_alive_interval);
950 }
951 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
952 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
953 }
954 if config.http2_keep_alive_while_idle {
955 builder.http2_keep_alive_while_idle(true);
956 }
957 }
958
959 builder.timer(hyper_util::rt::TokioTimer::new());
960 builder.pool_timer(hyper_util::rt::TokioTimer::new());
961 builder.pool_idle_timeout(config.pool_idle_timeout);
962 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
963
964 if config.http09_responses {
965 builder.http09_responses(true);
966 }
967
968 if config.http1_title_case_headers {
969 builder.http1_title_case_headers(true);
970 }
971
972 if config.http1_allow_obsolete_multiline_headers_in_responses {
973 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
974 }
975
976 if config.http1_ignore_invalid_headers_in_responses {
977 builder.http1_ignore_invalid_headers_in_responses(true);
978 }
979
980 if config.http1_allow_spaces_after_header_name_in_responses {
981 builder.http1_allow_spaces_after_header_name_in_responses(true);
982 }
983
984 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
985 let proxies_maybe_http_custom_headers =
986 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
987
988 let redirect_policy_desc = if config.redirect_policy.is_default() {
989 None
990 } else {
991 Some(format!("{:?}", &config.redirect_policy))
992 };
993
994 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
995 let hyper_service = HyperService {
996 #[cfg(feature = "cookies")]
997 cookie_store: config.cookie_store.clone(),
998 hyper: hyper_client,
999 };
1000
1001 let redirect_policy = {
1002 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
1003 p.with_referer(config.referer)
1004 .with_https_only(config.https_only);
1005 p
1006 };
1007
1008 let retry_policy = config.retry_policy.into_policy();
1009
1010 let retries = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
1011
1012 let hyper = FollowRedirect::with_policy(retries, redirect_policy.clone());
1013
1014 Ok(Client {
1015 inner: Arc::new(ClientRef {
1016 accepts: config.accepts,
1017 #[cfg(feature = "cookies")]
1018 cookie_store: config.cookie_store.clone(),
1019 #[cfg(feature = "http3")]
1022 h3_client: match h3_connector {
1023 Some(h3_connector) => {
1024 #[cfg(not(feature = "cookies"))]
1025 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1026 #[cfg(feature = "cookies")]
1027 let h3_service = H3Client::new(
1028 h3_connector,
1029 config.pool_idle_timeout,
1030 config.cookie_store,
1031 );
1032 let retries = tower::retry::Retry::new(retry_policy, h3_service);
1033 Some(FollowRedirect::with_policy(retries, redirect_policy))
1034 }
1035 None => None,
1036 },
1037 headers: config.headers,
1038 referer: config.referer,
1039 read_timeout: config.read_timeout,
1040 request_timeout: RequestConfig::new(config.timeout),
1041 hyper,
1042 proxies,
1043 proxies_maybe_http_auth,
1044 proxies_maybe_http_custom_headers,
1045 https_only: config.https_only,
1046 redirect_policy_desc,
1047 }),
1048 })
1049 }
1050
1051 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1074 where
1075 V: TryInto<HeaderValue>,
1076 V::Error: Into<http::Error>,
1077 {
1078 match value.try_into() {
1079 Ok(value) => {
1080 self.config.headers.insert(USER_AGENT, value);
1081 }
1082 Err(e) => {
1083 self.config.error = Some(crate::error::builder(e.into()));
1084 }
1085 };
1086 self
1087 }
1088 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1112 for (key, value) in headers.iter() {
1113 self.config.headers.insert(key, value.clone());
1114 }
1115 self
1116 }
1117
1118 #[cfg(feature = "cookies")]
1133 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1134 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1135 if enable {
1136 self.cookie_provider(Arc::new(cookie::Jar::default()))
1137 } else {
1138 self.config.cookie_store = None;
1139 self
1140 }
1141 }
1142
1143 #[cfg(feature = "cookies")]
1157 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1158 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1159 mut self,
1160 cookie_store: Arc<C>,
1161 ) -> ClientBuilder {
1162 self.config.cookie_store = Some(cookie_store as _);
1163 self
1164 }
1165
1166 #[cfg(feature = "gzip")]
1183 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1184 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1185 self.config.accepts.gzip = enable;
1186 self
1187 }
1188
1189 #[cfg(feature = "brotli")]
1206 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1207 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1208 self.config.accepts.brotli = enable;
1209 self
1210 }
1211
1212 #[cfg(feature = "zstd")]
1229 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1230 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1231 self.config.accepts.zstd = enable;
1232 self
1233 }
1234
1235 #[cfg(feature = "deflate")]
1252 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1253 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1254 self.config.accepts.deflate = enable;
1255 self
1256 }
1257
1258 pub fn no_gzip(self) -> ClientBuilder {
1264 #[cfg(feature = "gzip")]
1265 {
1266 self.gzip(false)
1267 }
1268
1269 #[cfg(not(feature = "gzip"))]
1270 {
1271 self
1272 }
1273 }
1274
1275 pub fn no_brotli(self) -> ClientBuilder {
1281 #[cfg(feature = "brotli")]
1282 {
1283 self.brotli(false)
1284 }
1285
1286 #[cfg(not(feature = "brotli"))]
1287 {
1288 self
1289 }
1290 }
1291
1292 pub fn no_zstd(self) -> ClientBuilder {
1298 #[cfg(feature = "zstd")]
1299 {
1300 self.zstd(false)
1301 }
1302
1303 #[cfg(not(feature = "zstd"))]
1304 {
1305 self
1306 }
1307 }
1308
1309 pub fn no_deflate(self) -> ClientBuilder {
1315 #[cfg(feature = "deflate")]
1316 {
1317 self.deflate(false)
1318 }
1319
1320 #[cfg(not(feature = "deflate"))]
1321 {
1322 self
1323 }
1324 }
1325
1326 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1332 self.config.redirect_policy = policy;
1333 self
1334 }
1335
1336 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1340 self.config.referer = enable;
1341 self
1342 }
1343
1344 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1351 self.config.retry_policy = policy;
1352 self
1353 }
1354
1355 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1363 self.config.proxies.push(proxy.into_matcher());
1364 self.config.auto_sys_proxy = false;
1365 self
1366 }
1367
1368 pub fn no_proxy(mut self) -> ClientBuilder {
1376 self.config.proxies.clear();
1377 self.config.auto_sys_proxy = false;
1378 self
1379 }
1380
1381 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1390 self.config.timeout = Some(timeout);
1391 self
1392 }
1393
1394 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1402 self.config.read_timeout = Some(timeout);
1403 self
1404 }
1405
1406 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1415 self.config.connect_timeout = Some(timeout);
1416 self
1417 }
1418
1419 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1426 self.config.connection_verbose = verbose;
1427 self
1428 }
1429
1430 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1438 where
1439 D: Into<Option<Duration>>,
1440 {
1441 self.config.pool_idle_timeout = val.into();
1442 self
1443 }
1444
1445 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1447 self.config.pool_max_idle_per_host = max;
1448 self
1449 }
1450
1451 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1453 self.config.http1_title_case_headers = true;
1454 self
1455 }
1456
1457 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1463 mut self,
1464 value: bool,
1465 ) -> ClientBuilder {
1466 self.config
1467 .http1_allow_obsolete_multiline_headers_in_responses = value;
1468 self
1469 }
1470
1471 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1473 self.config.http1_ignore_invalid_headers_in_responses = value;
1474 self
1475 }
1476
1477 pub fn http1_allow_spaces_after_header_name_in_responses(
1483 mut self,
1484 value: bool,
1485 ) -> ClientBuilder {
1486 self.config
1487 .http1_allow_spaces_after_header_name_in_responses = value;
1488 self
1489 }
1490
1491 pub fn http1_only(mut self) -> ClientBuilder {
1493 self.config.http_version_pref = HttpVersionPref::Http1;
1494 self
1495 }
1496
1497 pub fn http09_responses(mut self) -> ClientBuilder {
1499 self.config.http09_responses = true;
1500 self
1501 }
1502
1503 #[cfg(feature = "http2")]
1505 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1506 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1507 self.config.http_version_pref = HttpVersionPref::Http2;
1508 self
1509 }
1510
1511 #[cfg(feature = "http3")]
1513 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1514 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1515 self.config.http_version_pref = HttpVersionPref::Http3;
1516 self
1517 }
1518
1519 #[cfg(feature = "http2")]
1523 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1524 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1525 self.config.http2_initial_stream_window_size = sz.into();
1526 self
1527 }
1528
1529 #[cfg(feature = "http2")]
1533 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1534 pub fn http2_initial_connection_window_size(
1535 mut self,
1536 sz: impl Into<Option<u32>>,
1537 ) -> ClientBuilder {
1538 self.config.http2_initial_connection_window_size = sz.into();
1539 self
1540 }
1541
1542 #[cfg(feature = "http2")]
1547 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1548 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1549 self.config.http2_adaptive_window = enabled;
1550 self
1551 }
1552
1553 #[cfg(feature = "http2")]
1557 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1558 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1559 self.config.http2_max_frame_size = sz.into();
1560 self
1561 }
1562
1563 #[cfg(feature = "http2")]
1567 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1568 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1569 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1570 self
1571 }
1572
1573 #[cfg(feature = "http2")]
1578 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1579 pub fn http2_keep_alive_interval(
1580 mut self,
1581 interval: impl Into<Option<Duration>>,
1582 ) -> ClientBuilder {
1583 self.config.http2_keep_alive_interval = interval.into();
1584 self
1585 }
1586
1587 #[cfg(feature = "http2")]
1593 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1594 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1595 self.config.http2_keep_alive_timeout = Some(timeout);
1596 self
1597 }
1598
1599 #[cfg(feature = "http2")]
1606 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1607 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1608 self.config.http2_keep_alive_while_idle = enabled;
1609 self
1610 }
1611
1612 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1618 self.config.nodelay = enabled;
1619 self
1620 }
1621
1622 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1637 where
1638 T: Into<Option<IpAddr>>,
1639 {
1640 self.config.local_address = addr.into();
1641 self
1642 }
1643
1644 #[cfg(any(
1677 target_os = "android",
1678 target_os = "fuchsia",
1679 target_os = "illumos",
1680 target_os = "ios",
1681 target_os = "linux",
1682 target_os = "macos",
1683 target_os = "solaris",
1684 target_os = "tvos",
1685 target_os = "visionos",
1686 target_os = "watchos",
1687 ))]
1688 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1689 self.config.interface = Some(interface.to_string());
1690 self
1691 }
1692
1693 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1697 where
1698 D: Into<Option<Duration>>,
1699 {
1700 self.config.tcp_keepalive = val.into();
1701 self
1702 }
1703
1704 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1708 where
1709 D: Into<Option<Duration>>,
1710 {
1711 self.config.tcp_keepalive_interval = val.into();
1712 self
1713 }
1714
1715 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1719 where
1720 C: Into<Option<u32>>,
1721 {
1722 self.config.tcp_keepalive_retries = retries.into();
1723 self
1724 }
1725
1726 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1733 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1734 where
1735 D: Into<Option<Duration>>,
1736 {
1737 self.config.tcp_user_timeout = val.into();
1738 self
1739 }
1740
1741 #[cfg(unix)]
1755 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1756 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1757 self
1758 }
1759
1760 #[cfg(feature = "__tls")]
1772 #[cfg_attr(
1773 docsrs,
1774 doc(cfg(any(
1775 feature = "default-tls",
1776 feature = "native-tls",
1777 feature = "rustls-tls"
1778 )))
1779 )]
1780 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1781 self.config.root_certs.push(cert);
1782 self
1783 }
1784
1785 #[cfg(feature = "__rustls")]
1792 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1793 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1794 self.config.crls.push(crl);
1795 self
1796 }
1797
1798 #[cfg(feature = "__rustls")]
1805 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1806 pub fn add_crls(
1807 mut self,
1808 crls: impl IntoIterator<Item = CertificateRevocationList>,
1809 ) -> ClientBuilder {
1810 self.config.crls.extend(crls);
1811 self
1812 }
1813
1814 #[cfg(feature = "__tls")]
1832 #[cfg_attr(
1833 docsrs,
1834 doc(cfg(any(
1835 feature = "default-tls",
1836 feature = "native-tls",
1837 feature = "rustls-tls"
1838 )))
1839 )]
1840 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1841 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1842
1843 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1844 {
1845 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1846 }
1847
1848 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1849 {
1850 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1851 }
1852
1853 self
1854 }
1855
1856 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1860 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1861 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1862 self.config.tls_built_in_certs_webpki = enabled;
1863 self
1864 }
1865
1866 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1870 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1871 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1872 self.config.tls_built_in_certs_native = enabled;
1873 self
1874 }
1875
1876 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1883 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1884 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1885 self.config.identity = Some(identity);
1886 self
1887 }
1888
1889 #[cfg(feature = "__tls")]
1905 #[cfg_attr(
1906 docsrs,
1907 doc(cfg(any(
1908 feature = "default-tls",
1909 feature = "native-tls",
1910 feature = "rustls-tls"
1911 )))
1912 )]
1913 pub fn danger_accept_invalid_hostnames(
1914 mut self,
1915 accept_invalid_hostname: bool,
1916 ) -> ClientBuilder {
1917 self.config.hostname_verification = !accept_invalid_hostname;
1918 self
1919 }
1920
1921 #[cfg(feature = "__tls")]
1938 #[cfg_attr(
1939 docsrs,
1940 doc(cfg(any(
1941 feature = "default-tls",
1942 feature = "native-tls",
1943 feature = "rustls-tls"
1944 )))
1945 )]
1946 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1947 self.config.certs_verification = !accept_invalid_certs;
1948 self
1949 }
1950
1951 #[cfg(feature = "__tls")]
1960 #[cfg_attr(
1961 docsrs,
1962 doc(cfg(any(
1963 feature = "default-tls",
1964 feature = "native-tls",
1965 feature = "rustls-tls"
1966 )))
1967 )]
1968 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1969 self.config.tls_sni = tls_sni;
1970 self
1971 }
1972
1973 #[cfg(feature = "__tls")]
1989 #[cfg_attr(
1990 docsrs,
1991 doc(cfg(any(
1992 feature = "default-tls",
1993 feature = "native-tls",
1994 feature = "rustls-tls"
1995 )))
1996 )]
1997 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1998 self.config.min_tls_version = Some(version);
1999 self
2000 }
2001
2002 #[cfg(feature = "__tls")]
2021 #[cfg_attr(
2022 docsrs,
2023 doc(cfg(any(
2024 feature = "default-tls",
2025 feature = "native-tls",
2026 feature = "rustls-tls"
2027 )))
2028 )]
2029 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
2030 self.config.max_tls_version = Some(version);
2031 self
2032 }
2033
2034 #[cfg(feature = "native-tls")]
2043 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2044 pub fn use_native_tls(mut self) -> ClientBuilder {
2045 self.config.tls = TlsBackend::Default;
2046 self
2047 }
2048
2049 #[cfg(feature = "__rustls")]
2058 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
2059 pub fn use_rustls_tls(mut self) -> ClientBuilder {
2060 self.config.tls = TlsBackend::Rustls;
2061 self
2062 }
2063
2064 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
2083 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
2084 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
2085 let mut tls = Some(tls);
2086 #[cfg(feature = "native-tls")]
2087 {
2088 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2089 let tls = conn.take().expect("is definitely Some");
2090 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2091 self.config.tls = tls;
2092 return self;
2093 }
2094 }
2095 #[cfg(feature = "__rustls")]
2096 {
2097 if let Some(conn) =
2098 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2099 {
2100 let tls = conn.take().expect("is definitely Some");
2101 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
2102 self.config.tls = tls;
2103 return self;
2104 }
2105 }
2106
2107 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2109 self
2110 }
2111
2112 #[cfg(feature = "__tls")]
2119 #[cfg_attr(
2120 docsrs,
2121 doc(cfg(any(
2122 feature = "default-tls",
2123 feature = "native-tls",
2124 feature = "rustls-tls"
2125 )))
2126 )]
2127 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2128 self.config.tls_info = tls_info;
2129 self
2130 }
2131
2132 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2136 self.config.https_only = enabled;
2137 self
2138 }
2139
2140 #[doc(hidden)]
2141 #[cfg(feature = "hickory-dns")]
2142 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2143 #[deprecated(note = "use `hickory_dns` instead")]
2144 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
2145 self.config.hickory_dns = enable;
2146 self
2147 }
2148
2149 #[cfg(feature = "hickory-dns")]
2163 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2164 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2165 self.config.hickory_dns = enable;
2166 self
2167 }
2168
2169 #[doc(hidden)]
2170 #[deprecated(note = "use `no_hickory_dns` instead")]
2171 pub fn no_trust_dns(self) -> ClientBuilder {
2172 self.no_hickory_dns()
2173 }
2174
2175 pub fn no_hickory_dns(self) -> ClientBuilder {
2181 #[cfg(feature = "hickory-dns")]
2182 {
2183 self.hickory_dns(false)
2184 }
2185
2186 #[cfg(not(feature = "hickory-dns"))]
2187 {
2188 self
2189 }
2190 }
2191
2192 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2197 self.resolve_to_addrs(domain, &[addr])
2198 }
2199
2200 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2205 self.config
2206 .dns_overrides
2207 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2208 self
2209 }
2210
2211 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
2217 self.config.dns_resolver = Some(resolver as _);
2218 self
2219 }
2220
2221 pub fn dns_resolver2<R>(mut self, resolver: R) -> ClientBuilder
2228 where
2229 R: crate::dns::resolve::IntoResolve,
2230 {
2231 self.config.dns_resolver = Some(resolver.into_resolve());
2232 self
2233 }
2234
2235 #[cfg(feature = "http3")]
2240 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2241 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2242 self.config.tls_enable_early_data = enabled;
2243 self
2244 }
2245
2246 #[cfg(feature = "http3")]
2252 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2253 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2254 self.config.quic_max_idle_timeout = Some(value);
2255 self
2256 }
2257
2258 #[cfg(feature = "http3")]
2269 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2270 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2271 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2272 self
2273 }
2274
2275 #[cfg(feature = "http3")]
2286 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2287 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2288 self.config.quic_receive_window = Some(value.try_into().unwrap());
2289 self
2290 }
2291
2292 #[cfg(feature = "http3")]
2298 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2299 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2300 self.config.quic_send_window = Some(value);
2301 self
2302 }
2303
2304 #[cfg(feature = "http3")]
2312 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2313 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2314 self.config.quic_congestion_bbr = true;
2315 self
2316 }
2317
2318 #[cfg(feature = "http3")]
2328 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2329 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2330 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2331 self
2332 }
2333
2334 #[cfg(feature = "http3")]
2346 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2347 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2348 self.config.h3_send_grease = Some(enabled);
2349 self
2350 }
2351
2352 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2376 where
2377 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2378 L::Service:
2379 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2380 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2381 {
2382 let layer = BoxCloneSyncServiceLayer::new(layer);
2383
2384 self.config.connector_layers.push(layer);
2385
2386 self
2387 }
2388}
2389
2390type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2391
2392impl Default for Client {
2393 fn default() -> Self {
2394 Self::new()
2395 }
2396}
2397
2398impl Client {
2399 pub fn new() -> Client {
2409 ClientBuilder::new().build().expect("Client::new()")
2410 }
2411
2412 pub fn builder() -> ClientBuilder {
2416 ClientBuilder::new()
2417 }
2418
2419 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2425 self.request(Method::GET, url)
2426 }
2427
2428 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2434 self.request(Method::POST, url)
2435 }
2436
2437 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2443 self.request(Method::PUT, url)
2444 }
2445
2446 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2452 self.request(Method::PATCH, url)
2453 }
2454
2455 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2461 self.request(Method::DELETE, url)
2462 }
2463
2464 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2470 self.request(Method::HEAD, url)
2471 }
2472
2473 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2482 let req = url.into_url().map(move |url| Request::new(method, url));
2483 RequestBuilder::new(self.clone(), req)
2484 }
2485
2486 pub fn execute(
2499 &self,
2500 request: Request,
2501 ) -> impl Future<Output = Result<Response, crate::Error>> {
2502 self.execute_request(request)
2503 }
2504
2505 pub(super) fn execute_request(&self, req: Request) -> Pending {
2506 let (method, url, mut headers, body, version, extensions) = req.pieces();
2507 if url.scheme() != "http" && url.scheme() != "https" {
2508 return Pending::new_err(error::url_bad_scheme(url));
2509 }
2510
2511 if self.inner.https_only && url.scheme() != "https" {
2513 return Pending::new_err(error::url_bad_scheme(url));
2514 }
2515
2516 for (key, value) in &self.inner.headers {
2519 if let Entry::Vacant(entry) = headers.entry(key) {
2520 entry.insert(value.clone());
2521 }
2522 }
2523
2524 let accept_encoding = self.inner.accepts.as_str();
2525
2526 if let Some(accept_encoding) = accept_encoding {
2527 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2528 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2529 }
2530 }
2531
2532 let uri = match try_uri(&url) {
2533 Ok(uri) => uri,
2534 _ => return Pending::new_err(error::url_invalid_uri(url)),
2535 };
2536
2537 let body = body.unwrap_or_else(Body::empty);
2538
2539 self.proxy_auth(&uri, &mut headers);
2540 self.proxy_custom_headers(&uri, &mut headers);
2541
2542 let builder = hyper::Request::builder()
2543 .method(method.clone())
2544 .uri(uri)
2545 .version(version);
2546
2547 let in_flight = match version {
2548 #[cfg(feature = "http3")]
2549 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2550 let mut req = builder.body(body).expect("valid request parts");
2551 *req.headers_mut() = headers.clone();
2552 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2553 ResponseFuture::H3(h3.call(req))
2554 }
2555 _ => {
2556 let mut req = builder.body(body).expect("valid request parts");
2557 *req.headers_mut() = headers.clone();
2558 let mut hyper = self.inner.hyper.clone();
2559 ResponseFuture::Default(hyper.call(req))
2560 }
2561 };
2562
2563 let total_timeout = self
2564 .inner
2565 .request_timeout
2566 .fetch(&extensions)
2567 .copied()
2568 .map(tokio::time::sleep)
2569 .map(Box::pin);
2570
2571 let read_timeout_fut = self
2572 .inner
2573 .read_timeout
2574 .map(tokio::time::sleep)
2575 .map(Box::pin);
2576
2577 Pending {
2578 inner: PendingInner::Request(Box::pin(PendingRequest {
2579 method,
2580 url,
2581 headers,
2582
2583 client: self.inner.clone(),
2584
2585 in_flight,
2586 total_timeout,
2587 read_timeout_fut,
2588 read_timeout: self.inner.read_timeout,
2589 })),
2590 }
2591 }
2592
2593 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2594 if !self.inner.proxies_maybe_http_auth {
2595 return;
2596 }
2597
2598 if dst.scheme() != Some(&Scheme::HTTP) {
2602 return;
2603 }
2604
2605 if headers.contains_key(PROXY_AUTHORIZATION) {
2606 return;
2607 }
2608
2609 for proxy in self.inner.proxies.iter() {
2610 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2611 headers.insert(PROXY_AUTHORIZATION, header);
2612 break;
2613 }
2614 }
2615 }
2616
2617 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2618 if !self.inner.proxies_maybe_http_custom_headers {
2619 return;
2620 }
2621
2622 if dst.scheme() != Some(&Scheme::HTTP) {
2623 return;
2624 }
2625
2626 for proxy in self.inner.proxies.iter() {
2627 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2628 iter.iter().for_each(|(key, value)| {
2629 headers.insert(key, value.clone());
2630 });
2631 break;
2632 }
2633 }
2634 }
2635}
2636
2637impl fmt::Debug for Client {
2638 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2639 let mut builder = f.debug_struct("Client");
2640 self.inner.fmt_fields(&mut builder);
2641 builder.finish()
2642 }
2643}
2644
2645impl tower_service::Service<Request> for Client {
2646 type Response = Response;
2647 type Error = crate::Error;
2648 type Future = Pending;
2649
2650 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2651 Poll::Ready(Ok(()))
2652 }
2653
2654 fn call(&mut self, req: Request) -> Self::Future {
2655 self.execute_request(req)
2656 }
2657}
2658
2659impl tower_service::Service<Request> for &'_ Client {
2660 type Response = Response;
2661 type Error = crate::Error;
2662 type Future = Pending;
2663
2664 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2665 Poll::Ready(Ok(()))
2666 }
2667
2668 fn call(&mut self, req: Request) -> Self::Future {
2669 self.execute_request(req)
2670 }
2671}
2672
2673impl fmt::Debug for ClientBuilder {
2674 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2675 let mut builder = f.debug_struct("ClientBuilder");
2676 self.config.fmt_fields(&mut builder);
2677 builder.finish()
2678 }
2679}
2680
2681impl Config {
2682 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2683 #[cfg(feature = "cookies")]
2687 {
2688 if let Some(_) = self.cookie_store {
2689 f.field("cookie_store", &true);
2690 }
2691 }
2692
2693 f.field("accepts", &self.accepts);
2694
2695 if !self.proxies.is_empty() {
2696 f.field("proxies", &self.proxies);
2697 }
2698
2699 if !self.redirect_policy.is_default() {
2700 f.field("redirect_policy", &self.redirect_policy);
2701 }
2702
2703 if self.referer {
2704 f.field("referer", &true);
2705 }
2706
2707 f.field("default_headers", &self.headers);
2708
2709 if self.http1_title_case_headers {
2710 f.field("http1_title_case_headers", &true);
2711 }
2712
2713 if self.http1_allow_obsolete_multiline_headers_in_responses {
2714 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2715 }
2716
2717 if self.http1_ignore_invalid_headers_in_responses {
2718 f.field("http1_ignore_invalid_headers_in_responses", &true);
2719 }
2720
2721 if self.http1_allow_spaces_after_header_name_in_responses {
2722 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2723 }
2724
2725 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2726 f.field("http1_only", &true);
2727 }
2728
2729 #[cfg(feature = "http2")]
2730 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2731 f.field("http2_prior_knowledge", &true);
2732 }
2733
2734 if let Some(ref d) = self.connect_timeout {
2735 f.field("connect_timeout", d);
2736 }
2737
2738 if let Some(ref d) = self.timeout {
2739 f.field("timeout", d);
2740 }
2741
2742 if let Some(ref v) = self.local_address {
2743 f.field("local_address", v);
2744 }
2745
2746 #[cfg(any(
2747 target_os = "android",
2748 target_os = "fuchsia",
2749 target_os = "illumos",
2750 target_os = "ios",
2751 target_os = "linux",
2752 target_os = "macos",
2753 target_os = "solaris",
2754 target_os = "tvos",
2755 target_os = "visionos",
2756 target_os = "watchos",
2757 ))]
2758 if let Some(ref v) = self.interface {
2759 f.field("interface", v);
2760 }
2761
2762 if self.nodelay {
2763 f.field("tcp_nodelay", &true);
2764 }
2765
2766 #[cfg(feature = "__tls")]
2767 {
2768 if !self.hostname_verification {
2769 f.field("danger_accept_invalid_hostnames", &true);
2770 }
2771 }
2772
2773 #[cfg(feature = "__tls")]
2774 {
2775 if !self.certs_verification {
2776 f.field("danger_accept_invalid_certs", &true);
2777 }
2778
2779 if let Some(ref min_tls_version) = self.min_tls_version {
2780 f.field("min_tls_version", min_tls_version);
2781 }
2782
2783 if let Some(ref max_tls_version) = self.max_tls_version {
2784 f.field("max_tls_version", max_tls_version);
2785 }
2786
2787 f.field("tls_sni", &self.tls_sni);
2788
2789 f.field("tls_info", &self.tls_info);
2790 }
2791
2792 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2793 {
2794 f.field("tls_backend", &self.tls);
2795 }
2796
2797 if !self.dns_overrides.is_empty() {
2798 f.field("dns_overrides", &self.dns_overrides);
2799 }
2800
2801 #[cfg(feature = "http3")]
2802 {
2803 if self.tls_enable_early_data {
2804 f.field("tls_enable_early_data", &true);
2805 }
2806 }
2807
2808 #[cfg(unix)]
2809 if let Some(ref p) = self.unix_socket {
2810 f.field("unix_socket", p);
2811 }
2812 }
2813}
2814
2815type LayeredService<T> =
2816 FollowRedirect<tower::retry::Retry<crate::retry::Policy, T>, TowerRedirectPolicy>;
2817type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
2818
2819struct ClientRef {
2820 accepts: Accepts,
2821 #[cfg(feature = "cookies")]
2822 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2823 headers: HeaderMap,
2824 hyper: LayeredService<HyperService>,
2825 #[cfg(feature = "http3")]
2826 h3_client: Option<LayeredService<H3Client>>,
2827 referer: bool,
2828 request_timeout: RequestConfig<RequestTimeout>,
2829 read_timeout: Option<Duration>,
2830 proxies: Arc<Vec<ProxyMatcher>>,
2831 proxies_maybe_http_auth: bool,
2832 proxies_maybe_http_custom_headers: bool,
2833 https_only: bool,
2834 redirect_policy_desc: Option<String>,
2835}
2836
2837impl ClientRef {
2838 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2839 #[cfg(feature = "cookies")]
2843 {
2844 if let Some(_) = self.cookie_store {
2845 f.field("cookie_store", &true);
2846 }
2847 }
2848
2849 f.field("accepts", &self.accepts);
2850
2851 if !self.proxies.is_empty() {
2852 f.field("proxies", &self.proxies);
2853 }
2854
2855 if let Some(s) = &self.redirect_policy_desc {
2856 f.field("redirect_policy", s);
2857 }
2858
2859 if self.referer {
2860 f.field("referer", &true);
2861 }
2862
2863 f.field("default_headers", &self.headers);
2864
2865 self.request_timeout.fmt_as_field(f);
2866
2867 if let Some(ref d) = self.read_timeout {
2868 f.field("read_timeout", d);
2869 }
2870 }
2871}
2872
2873pin_project! {
2874 pub struct Pending {
2875 #[pin]
2876 inner: PendingInner,
2877 }
2878}
2879
2880enum PendingInner {
2881 Request(Pin<Box<PendingRequest>>),
2882 Error(Option<crate::Error>),
2883}
2884
2885pin_project! {
2886 struct PendingRequest {
2887 method: Method,
2888 url: Url,
2889 headers: HeaderMap,
2890
2891 client: Arc<ClientRef>,
2892
2893 #[pin]
2894 in_flight: ResponseFuture,
2895 #[pin]
2896 total_timeout: Option<Pin<Box<Sleep>>>,
2897 #[pin]
2898 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2899 read_timeout: Option<Duration>,
2900 }
2901}
2902
2903enum ResponseFuture {
2904 Default(LayeredFuture<HyperService>),
2905 #[cfg(feature = "http3")]
2906 H3(LayeredFuture<H3Client>),
2907}
2908
2909impl PendingRequest {
2910 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2911 self.project().in_flight
2912 }
2913
2914 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2915 self.project().total_timeout
2916 }
2917
2918 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2919 self.project().read_timeout_fut
2920 }
2921}
2922
2923impl Pending {
2924 pub(super) fn new_err(err: crate::Error) -> Pending {
2925 Pending {
2926 inner: PendingInner::Error(Some(err)),
2927 }
2928 }
2929
2930 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2931 self.project().inner
2932 }
2933}
2934
2935impl Future for Pending {
2936 type Output = Result<Response, crate::Error>;
2937
2938 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2939 let inner = self.inner();
2940 match inner.get_mut() {
2941 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2942 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2943 .take()
2944 .expect("Pending error polled more than once"))),
2945 }
2946 }
2947}
2948
2949impl Future for PendingRequest {
2950 type Output = Result<Response, crate::Error>;
2951
2952 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2953 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
2954 if let Poll::Ready(()) = delay.poll(cx) {
2955 return Poll::Ready(Err(
2956 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2957 ));
2958 }
2959 }
2960
2961 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
2962 if let Poll::Ready(()) = delay.poll(cx) {
2963 return Poll::Ready(Err(
2964 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2965 ));
2966 }
2967 }
2968
2969 let res = match self.as_mut().in_flight().get_mut() {
2970 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
2971 Err(e) => {
2972 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
2973 }
2974 Ok(res) => res.map(super::body::boxed),
2975 },
2976 #[cfg(feature = "http3")]
2977 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
2978 Err(e) => {
2979 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
2980 }
2981 Ok(res) => res,
2982 },
2983 };
2984
2985 #[cfg(feature = "cookies")]
2986 {
2987 if let Some(ref cookie_store) = self.client.cookie_store {
2988 let mut cookies = cookie::extract_response_cookie_headers(res.headers()).peekable();
2989 if cookies.peek().is_some() {
2990 cookie_store.set_cookies(&mut cookies, &self.url);
2991 }
2992 }
2993 }
2994 if let Some(url) = &res
2995 .extensions()
2996 .get::<tower_http::follow_redirect::RequestUri>()
2997 {
2998 self.url = match Url::parse(&url.0.to_string()) {
2999 Ok(url) => url,
3000 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3001 }
3002 };
3003
3004 let res = Response::new(
3005 res,
3006 self.url.clone(),
3007 self.client.accepts,
3008 self.total_timeout.take(),
3009 self.read_timeout,
3010 );
3011 Poll::Ready(Ok(res))
3012 }
3013}
3014
3015impl fmt::Debug for Pending {
3016 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3017 match self.inner {
3018 PendingInner::Request(ref req) => f
3019 .debug_struct("Pending")
3020 .field("method", &req.method)
3021 .field("url", &req.url)
3022 .finish(),
3023 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3024 }
3025 }
3026}
3027
3028#[cfg(test)]
3029mod tests {
3030 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
3031
3032 #[tokio::test]
3033 async fn execute_request_rejects_invalid_urls() {
3034 let url_str = "hxxps://www.rust-lang.org/";
3035 let url = url::Url::parse(url_str).unwrap();
3036 let result = crate::get(url.clone()).await;
3037
3038 assert!(result.is_err());
3039 let err = result.err().unwrap();
3040 assert!(err.is_builder());
3041 assert_eq!(url_str, err.url().unwrap().as_str());
3042 }
3043
3044 #[tokio::test]
3046 async fn execute_request_rejects_invalid_hostname() {
3047 let url_str = "https://{{hostname}}/";
3048 let url = url::Url::parse(url_str).unwrap();
3049 let result = crate::get(url.clone()).await;
3050
3051 assert!(result.is_err());
3052 let err = result.err().unwrap();
3053 assert!(err.is_builder());
3054 assert_eq!(url_str, err.url().unwrap().as_str());
3055 }
3056
3057 #[test]
3058 fn test_future_size() {
3059 let s = std::mem::size_of::<super::Pending>();
3060 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3061 }
3062}