1#[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2use std::any::Any;
3use std::future::Future;
4use std::net::IpAddr;
5use std::pin::Pin;
6use std::sync::Arc;
7use std::task::{ready, Context, Poll};
8use std::time::Duration;
9use std::{collections::HashMap, convert::TryInto, net::SocketAddr};
10use std::{fmt, str};
11
12use super::request::{Request, RequestBuilder};
13use super::response::Response;
14use super::Body;
15#[cfg(feature = "http3")]
16use crate::async_impl::h3_client::connect::{H3ClientConfig, H3Connector};
17#[cfg(feature = "http3")]
18use crate::async_impl::h3_client::H3Client;
19use crate::config::{RequestConfig, TotalTimeout};
20#[cfg(unix)]
21use crate::connect::uds::UnixSocketProvider;
22#[cfg(target_os = "windows")]
23use crate::connect::windows_named_pipe::WindowsNamedPipeProvider;
24use crate::connect::{
25 sealed::{Conn, Unnameable},
26 BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
27};
28#[cfg(feature = "cookies")]
29use crate::cookie;
30#[cfg(feature = "cookies")]
31use crate::cookie::service::CookieService;
32#[cfg(feature = "hickory-dns")]
33use crate::dns::hickory::HickoryDnsResolver;
34use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
35use crate::error::{self, BoxError};
36use crate::into_url::try_uri;
37use crate::proxy::Matcher as ProxyMatcher;
38use crate::redirect::{self, TowerRedirectPolicy};
39#[cfg(feature = "__rustls")]
40use crate::tls::CertificateRevocationList;
41#[cfg(feature = "__tls")]
42use crate::tls::{self, TlsBackend};
43#[cfg(feature = "__tls")]
44use crate::Certificate;
45#[cfg(any(feature = "__native-tls", feature = "__rustls"))]
46use crate::Identity;
47use crate::{IntoUrl, Method, Proxy, Url};
48
49#[cfg(feature = "http3")]
50use crate::async_impl::h3_client::connect::TransportConfig;
51use http::header::{Entry, HeaderMap, HeaderValue, ACCEPT, PROXY_AUTHORIZATION, USER_AGENT};
52use http::uri::Scheme;
53use http::Uri;
54use hyper_util::client::legacy::connect::HttpConnector;
55#[cfg(feature = "__native-tls")]
56use native_tls_crate::TlsConnector;
57use pin_project_lite::pin_project;
58
59use tokio::time::Sleep;
60use tower::util::BoxCloneSyncServiceLayer;
61use tower::{Layer, Service};
62#[cfg(any(
63 feature = "gzip",
64 feature = "brotli",
65 feature = "zstd",
66 feature = "deflate"
67))]
68use tower_http::decompression::Decompression;
69use tower_http::follow_redirect::FollowRedirect;
70
71#[derive(Clone)]
92pub struct Client {
93 inner: Arc<ClientRef>,
94}
95
96#[must_use]
98pub struct ClientBuilder {
99 config: Config,
100}
101
102enum HttpVersionPref {
103 Http1,
104 #[cfg(feature = "http2")]
105 Http2,
106 #[cfg(feature = "http3")]
107 Http3,
108 All,
109}
110
111#[derive(Clone, Copy, Debug)]
112struct Accepts {
113 #[cfg(feature = "gzip")]
114 gzip: bool,
115 #[cfg(feature = "brotli")]
116 brotli: bool,
117 #[cfg(feature = "zstd")]
118 zstd: bool,
119 #[cfg(feature = "deflate")]
120 deflate: bool,
121}
122
123#[allow(clippy::derivable_impls)] impl Default for Accepts {
125 fn default() -> Accepts {
126 Accepts {
127 #[cfg(feature = "gzip")]
128 gzip: true,
129 #[cfg(feature = "brotli")]
130 brotli: true,
131 #[cfg(feature = "zstd")]
132 zstd: true,
133 #[cfg(feature = "deflate")]
134 deflate: true,
135 }
136 }
137}
138
139#[derive(Clone)]
140struct HyperService {
141 hyper: HyperClient,
142}
143
144impl Service<hyper::Request<crate::async_impl::body::Body>> for HyperService {
145 type Error = crate::Error;
146 type Response = http::Response<hyper::body::Incoming>;
147 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + Sync>>;
148
149 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
150 self.hyper.poll_ready(cx).map_err(crate::error::request)
151 }
152
153 fn call(&mut self, req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
154 let clone = self.hyper.clone();
155 let mut inner = std::mem::replace(&mut self.hyper, clone);
156 Box::pin(async move { inner.call(req).await.map_err(crate::error::request) })
157 }
158}
159
160struct Config {
161 accepts: Accepts,
163 headers: HeaderMap,
164 #[cfg(feature = "__tls")]
165 hostname_verification: bool,
166 #[cfg(feature = "__tls")]
167 certs_verification: bool,
168 #[cfg(feature = "__tls")]
169 tls_sni: bool,
170 #[cfg(feature = "__rustls")]
171 tls_sslkeylogfile: bool,
172 connect_timeout: Option<Duration>,
173 connection_verbose: bool,
174 pool_idle_timeout: Option<Duration>,
175 pool_max_idle_per_host: usize,
176 tcp_keepalive: Option<Duration>,
177 tcp_keepalive_interval: Option<Duration>,
178 tcp_keepalive_retries: Option<u32>,
179 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
180 tcp_user_timeout: Option<Duration>,
181 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
182 identity: Option<Identity>,
183 proxies: Vec<ProxyMatcher>,
184 auto_sys_proxy: bool,
185 redirect_policy: redirect::Policy,
186 retry_policy: crate::retry::Builder,
187 referer: bool,
188 read_timeout: Option<Duration>,
189 timeout: Option<Duration>,
190 #[cfg(feature = "__tls")]
191 root_certs: Vec<Certificate>,
192 #[cfg(feature = "__tls")]
193 tls_certs_only: 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 http1_max_headers: Option<usize>,
212 #[cfg(feature = "http2")]
213 http2_initial_stream_window_size: Option<u32>,
214 #[cfg(feature = "http2")]
215 http2_initial_connection_window_size: Option<u32>,
216 #[cfg(feature = "http2")]
217 http2_adaptive_window: bool,
218 #[cfg(feature = "http2")]
219 http2_max_frame_size: Option<u32>,
220 #[cfg(feature = "http2")]
221 http2_max_header_list_size: Option<u32>,
222 #[cfg(feature = "http2")]
223 http2_keep_alive_interval: Option<Duration>,
224 #[cfg(feature = "http2")]
225 http2_keep_alive_timeout: Option<Duration>,
226 #[cfg(feature = "http2")]
227 http2_keep_alive_while_idle: bool,
228 local_address: Option<IpAddr>,
229 #[cfg(any(
230 target_os = "android",
231 target_os = "fuchsia",
232 target_os = "illumos",
233 target_os = "ios",
234 target_os = "linux",
235 target_os = "macos",
236 target_os = "solaris",
237 target_os = "tvos",
238 target_os = "visionos",
239 target_os = "watchos",
240 ))]
241 interface: Option<String>,
242 nodelay: bool,
243 #[cfg(feature = "cookies")]
244 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
245 hickory_dns: bool,
246 error: Option<crate::Error>,
247 https_only: bool,
248 #[cfg(feature = "http3")]
249 tls_enable_early_data: bool,
250 #[cfg(feature = "http3")]
251 quic_max_idle_timeout: Option<Duration>,
252 #[cfg(feature = "http3")]
253 quic_stream_receive_window: Option<u64>,
254 #[cfg(feature = "http3")]
255 quic_receive_window: Option<u64>,
256 #[cfg(feature = "http3")]
257 quic_send_window: Option<u64>,
258 #[cfg(feature = "http3")]
259 quic_congestion_bbr: bool,
260 #[cfg(feature = "http3")]
261 h3_max_field_section_size: Option<u64>,
262 #[cfg(feature = "http3")]
263 h3_send_grease: Option<bool>,
264 dns_overrides: HashMap<String, Vec<SocketAddr>>,
265 dns_resolver: Option<Arc<dyn Resolve>>,
266
267 #[cfg(unix)]
268 unix_socket: Option<Arc<std::path::Path>>,
269 #[cfg(target_os = "windows")]
270 windows_named_pipe: Option<Arc<std::ffi::OsStr>>,
271}
272
273impl Default for ClientBuilder {
274 fn default() -> Self {
275 Self::new()
276 }
277}
278
279impl ClientBuilder {
280 pub fn new() -> Self {
284 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
285 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
286
287 ClientBuilder {
288 config: Config {
289 error: None,
290 accepts: Accepts::default(),
291 headers,
292 #[cfg(feature = "__tls")]
293 hostname_verification: true,
294 #[cfg(feature = "__tls")]
295 certs_verification: true,
296 #[cfg(feature = "__tls")]
297 tls_sni: true,
298 #[cfg(feature = "__rustls")]
299 tls_sslkeylogfile: false,
300 connect_timeout: None,
301 connection_verbose: false,
302 pool_idle_timeout: Some(Duration::from_secs(90)),
303 pool_max_idle_per_host: usize::MAX,
304 tcp_keepalive: Some(Duration::from_secs(15)),
305 tcp_keepalive_interval: Some(Duration::from_secs(15)),
306 tcp_keepalive_retries: Some(3),
307 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
308 tcp_user_timeout: Some(Duration::from_secs(30)),
309 proxies: Vec::new(),
310 auto_sys_proxy: true,
311 redirect_policy: redirect::Policy::default(),
312 retry_policy: crate::retry::Builder::default(),
313 referer: true,
314 read_timeout: None,
315 timeout: None,
316 #[cfg(feature = "__tls")]
317 root_certs: Vec::new(),
318 #[cfg(feature = "__tls")]
319 tls_certs_only: false,
320 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
321 identity: None,
322 #[cfg(feature = "__rustls")]
323 crls: vec![],
324 #[cfg(feature = "__tls")]
325 min_tls_version: None,
326 #[cfg(feature = "__tls")]
327 max_tls_version: None,
328 #[cfg(feature = "__tls")]
329 tls_info: false,
330 #[cfg(feature = "__tls")]
331 tls: TlsBackend::default(),
332 connector_layers: Vec::new(),
333 http_version_pref: HttpVersionPref::All,
334 http09_responses: false,
335 http1_title_case_headers: false,
336 http1_allow_obsolete_multiline_headers_in_responses: false,
337 http1_ignore_invalid_headers_in_responses: false,
338 http1_allow_spaces_after_header_name_in_responses: false,
339 http1_max_headers: None,
340 #[cfg(feature = "http2")]
341 http2_initial_stream_window_size: None,
342 #[cfg(feature = "http2")]
343 http2_initial_connection_window_size: None,
344 #[cfg(feature = "http2")]
345 http2_adaptive_window: false,
346 #[cfg(feature = "http2")]
347 http2_max_frame_size: None,
348 #[cfg(feature = "http2")]
349 http2_max_header_list_size: None,
350 #[cfg(feature = "http2")]
351 http2_keep_alive_interval: None,
352 #[cfg(feature = "http2")]
353 http2_keep_alive_timeout: None,
354 #[cfg(feature = "http2")]
355 http2_keep_alive_while_idle: false,
356 local_address: None,
357 #[cfg(any(
358 target_os = "android",
359 target_os = "fuchsia",
360 target_os = "illumos",
361 target_os = "ios",
362 target_os = "linux",
363 target_os = "macos",
364 target_os = "solaris",
365 target_os = "tvos",
366 target_os = "visionos",
367 target_os = "watchos",
368 ))]
369 interface: None,
370 nodelay: true,
371 hickory_dns: cfg!(feature = "hickory-dns"),
372 #[cfg(feature = "cookies")]
373 cookie_store: None,
374 https_only: false,
375 dns_overrides: HashMap::new(),
376 #[cfg(feature = "http3")]
377 tls_enable_early_data: false,
378 #[cfg(feature = "http3")]
379 quic_max_idle_timeout: None,
380 #[cfg(feature = "http3")]
381 quic_stream_receive_window: None,
382 #[cfg(feature = "http3")]
383 quic_receive_window: None,
384 #[cfg(feature = "http3")]
385 quic_send_window: None,
386 #[cfg(feature = "http3")]
387 quic_congestion_bbr: false,
388 #[cfg(feature = "http3")]
389 h3_max_field_section_size: None,
390 #[cfg(feature = "http3")]
391 h3_send_grease: None,
392 dns_resolver: None,
393 #[cfg(unix)]
394 unix_socket: None,
395 #[cfg(target_os = "windows")]
396 windows_named_pipe: None,
397 },
398 }
399 }
400}
401
402impl ClientBuilder {
403 pub fn build(self) -> crate::Result<Client> {
410 let config = self.config;
411
412 if let Some(err) = config.error {
413 return Err(err);
414 }
415
416 let mut proxies = config.proxies;
417 if config.auto_sys_proxy {
418 proxies.push(ProxyMatcher::system());
419 }
420 let proxies = Arc::new(proxies);
421
422 #[allow(unused)]
423 #[cfg(feature = "http3")]
424 let mut h3_connector = None;
425
426 let resolver = {
427 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
428 false => Arc::new(GaiResolver::new()),
429 #[cfg(feature = "hickory-dns")]
430 true => Arc::new(HickoryDnsResolver::default()),
431 #[cfg(not(feature = "hickory-dns"))]
432 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
433 };
434 if let Some(dns_resolver) = config.dns_resolver {
435 resolver = dns_resolver;
436 }
437 if !config.dns_overrides.is_empty() {
438 resolver = Arc::new(DnsResolverWithOverrides::new(
439 resolver,
440 config.dns_overrides,
441 ));
442 }
443 DynResolver::new(resolver)
444 };
445
446 let mut connector_builder = {
447 #[cfg(feature = "__tls")]
448 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
449 headers.get(USER_AGENT).cloned()
450 }
451
452 let mut http = HttpConnector::new_with_resolver(resolver.clone());
453 http.set_connect_timeout(config.connect_timeout);
454
455 #[cfg(all(feature = "http3", feature = "__rustls"))]
456 let build_h3_connector =
457 |resolver,
458 tls,
459 quic_max_idle_timeout: Option<Duration>,
460 quic_stream_receive_window,
461 quic_receive_window,
462 quic_send_window,
463 quic_congestion_bbr,
464 h3_max_field_section_size,
465 h3_send_grease,
466 local_address,
467 http_version_pref: &HttpVersionPref| {
468 let mut transport_config = TransportConfig::default();
469
470 if let Some(max_idle_timeout) = quic_max_idle_timeout {
471 transport_config.max_idle_timeout(Some(max_idle_timeout));
472 }
473
474 if let Some(stream_receive_window) = quic_stream_receive_window {
475 transport_config.stream_receive_window(stream_receive_window);
476 }
477
478 if let Some(receive_window) = quic_receive_window {
479 transport_config.receive_window(receive_window);
480 }
481
482 if let Some(send_window) = quic_send_window {
483 transport_config.send_window(send_window);
484 }
485
486 if quic_congestion_bbr {
487 transport_config.bbr = true;
488 }
489
490 let mut h3_client_config = H3ClientConfig::default();
491
492 if let Some(max_field_section_size) = h3_max_field_section_size {
493 h3_client_config.max_field_section_size = Some(max_field_section_size);
494 }
495
496 if let Some(send_grease) = h3_send_grease {
497 h3_client_config.send_grease = Some(send_grease);
498 }
499
500 let res = H3Connector::new(
501 resolver,
502 tls,
503 local_address,
504 transport_config,
505 h3_client_config,
506 );
507
508 match res {
509 Ok(connector) => Ok(Some(connector)),
510 Err(err) => {
511 if let HttpVersionPref::Http3 = http_version_pref {
512 Err(error::builder(err))
513 } else {
514 Ok(None)
515 }
516 }
517 }
518 };
519
520 #[cfg(feature = "__tls")]
521 match config.tls {
522 #[cfg(feature = "__native-tls")]
523 TlsBackend::NativeTls => {
524 let mut tls = TlsConnector::builder();
525
526 #[cfg(all(feature = "__native-tls-alpn", not(feature = "http3")))]
527 {
528 match config.http_version_pref {
529 HttpVersionPref::Http1 => {
530 tls.request_alpns(&["http/1.1"]);
531 }
532 #[cfg(feature = "http2")]
533 HttpVersionPref::Http2 => {
534 tls.request_alpns(&["h2"]);
535 }
536 HttpVersionPref::All => {
537 tls.request_alpns(&[
538 #[cfg(feature = "http2")]
539 "h2",
540 "http/1.1",
541 ]);
542 }
543 }
544 }
545
546 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
547
548 tls.danger_accept_invalid_certs(!config.certs_verification);
549
550 tls.use_sni(config.tls_sni);
551
552 tls.disable_built_in_roots(config.tls_certs_only);
553
554 for cert in config.root_certs {
555 cert.add_to_native_tls(&mut tls);
556 }
557
558 #[cfg(feature = "__native-tls")]
559 {
560 if let Some(id) = config.identity {
561 id.add_to_native_tls(&mut tls)?;
562 }
563 }
564 #[cfg(all(feature = "__rustls", not(feature = "__native-tls")))]
565 {
566 if let Some(_id) = config.identity {
568 return Err(crate::error::builder("incompatible TLS identity type"));
569 }
570 }
571
572 if let Some(min_tls_version) = config.min_tls_version {
573 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
574 crate::error::builder("invalid minimum TLS version for backend")
580 })?;
581 tls.min_protocol_version(Some(protocol));
582 }
583
584 if let Some(max_tls_version) = config.max_tls_version {
585 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
586 crate::error::builder("invalid maximum TLS version for backend")
590 })?;
591 tls.max_protocol_version(Some(protocol));
592 }
593
594 ConnectorBuilder::new_native_tls(
595 http,
596 tls,
597 proxies.clone(),
598 user_agent(&config.headers),
599 config.local_address,
600 #[cfg(any(
601 target_os = "android",
602 target_os = "fuchsia",
603 target_os = "illumos",
604 target_os = "ios",
605 target_os = "linux",
606 target_os = "macos",
607 target_os = "solaris",
608 target_os = "tvos",
609 target_os = "visionos",
610 target_os = "watchos",
611 ))]
612 config.interface.as_deref(),
613 config.nodelay,
614 config.tls_info,
615 )?
616 }
617 #[cfg(feature = "__native-tls")]
618 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_native_tls(
619 http,
620 conn,
621 proxies.clone(),
622 user_agent(&config.headers),
623 config.local_address,
624 #[cfg(any(
625 target_os = "android",
626 target_os = "fuchsia",
627 target_os = "illumos",
628 target_os = "ios",
629 target_os = "linux",
630 target_os = "macos",
631 target_os = "solaris",
632 target_os = "tvos",
633 target_os = "visionos",
634 target_os = "watchos",
635 ))]
636 config.interface.as_deref(),
637 config.nodelay,
638 config.tls_info,
639 ),
640 #[cfg(feature = "__rustls")]
641 TlsBackend::BuiltBoring(conn) => {
642 let conn = crate::boring_tls::Config::preconfigured(conn);
643 #[cfg(feature = "http3")]
644 {
645 let mut h3_tls = conn.clone();
646 h3_tls.alpn_protocols = vec!["h3".into()];
647
648 h3_connector = build_h3_connector(
649 resolver.clone(),
650 h3_tls,
651 config.quic_max_idle_timeout,
652 config.quic_stream_receive_window,
653 config.quic_receive_window,
654 config.quic_send_window,
655 config.quic_congestion_bbr,
656 config.h3_max_field_section_size,
657 config.h3_send_grease,
658 config.local_address,
659 &config.http_version_pref,
660 )?;
661 }
662
663 ConnectorBuilder::new_boring_tls(
664 http,
665 conn,
666 proxies.clone(),
667 user_agent(&config.headers),
668 config.local_address,
669 #[cfg(any(
670 target_os = "android",
671 target_os = "fuchsia",
672 target_os = "illumos",
673 target_os = "ios",
674 target_os = "linux",
675 target_os = "macos",
676 target_os = "solaris",
677 target_os = "tvos",
678 target_os = "visionos",
679 target_os = "watchos",
680 ))]
681 config.interface.as_deref(),
682 config.nodelay,
683 config.tls_info,
684 )
685 }
686 #[cfg(feature = "__rustls")]
687 TlsBackend::Boring => {
688 let mut tls = crate::boring_tls::Config::new(crate::boring_tls::Settings {
689 roots: config.root_certs,
690 roots_only: config.tls_certs_only,
691 identity: config.identity,
692 crls: Arc::new(config.crls),
693 verify_certs: config.certs_verification,
694 verify_hostname: config.hostname_verification,
695 sni: config.tls_sni,
696 min: config.min_tls_version,
697 max: config.max_tls_version,
698 keylog: config.tls_sslkeylogfile,
699 })?;
700
701 match config.http_version_pref {
703 HttpVersionPref::Http1 => {
704 tls.alpn_protocols = vec!["http/1.1".into()];
705 }
706 #[cfg(feature = "http2")]
707 HttpVersionPref::Http2 => {
708 tls.alpn_protocols = vec!["h2".into()];
709 }
710 #[cfg(feature = "http3")]
711 HttpVersionPref::Http3 => {
712 }
714 HttpVersionPref::All => {
715 tls.alpn_protocols = vec![
716 #[cfg(feature = "http2")]
717 "h2".into(),
718 "http/1.1".into(),
719 ];
720 }
721 }
722
723 #[cfg(feature = "http3")]
724 {
725 let mut h3_tls = tls.clone();
726 h3_tls.enable_early_data = config.tls_enable_early_data;
727
728 h3_tls.alpn_protocols = vec!["h3".into()];
730
731 h3_connector = build_h3_connector(
732 resolver.clone(),
733 h3_tls,
734 config.quic_max_idle_timeout,
735 config.quic_stream_receive_window,
736 config.quic_receive_window,
737 config.quic_send_window,
738 config.quic_congestion_bbr,
739 config.h3_max_field_section_size,
740 config.h3_send_grease,
741 config.local_address,
742 &config.http_version_pref,
743 )?;
744 }
745
746 ConnectorBuilder::new_boring_tls(
747 http,
748 tls,
749 proxies.clone(),
750 user_agent(&config.headers),
751 config.local_address,
752 #[cfg(any(
753 target_os = "android",
754 target_os = "fuchsia",
755 target_os = "illumos",
756 target_os = "ios",
757 target_os = "linux",
758 target_os = "macos",
759 target_os = "solaris",
760 target_os = "tvos",
761 target_os = "visionos",
762 target_os = "watchos",
763 ))]
764 config.interface.as_deref(),
765 config.nodelay,
766 config.tls_info,
767 )
768 }
769 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
770 TlsBackend::UnknownPreconfigured => {
771 return Err(crate::error::builder(
772 "Unknown TLS backend passed to `use_preconfigured_tls`",
773 ));
774 }
775 }
776
777 #[cfg(not(feature = "__tls"))]
778 ConnectorBuilder::new(
779 http,
780 proxies.clone(),
781 config.local_address,
782 #[cfg(any(
783 target_os = "android",
784 target_os = "fuchsia",
785 target_os = "illumos",
786 target_os = "ios",
787 target_os = "linux",
788 target_os = "macos",
789 target_os = "solaris",
790 target_os = "tvos",
791 target_os = "visionos",
792 target_os = "watchos",
793 ))]
794 config.interface.as_deref(),
795 config.nodelay,
796 )
797 };
798
799 connector_builder.set_timeout(config.connect_timeout);
800 connector_builder.set_verbose(config.connection_verbose);
801 connector_builder.set_keepalive(config.tcp_keepalive);
802 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
803 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
804 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
805 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
806
807 #[cfg(feature = "socks")]
808 connector_builder.set_socks_resolver(resolver);
809
810 #[cfg(unix)]
814 connector_builder.set_unix_socket(config.unix_socket);
815 #[cfg(target_os = "windows")]
816 connector_builder.set_windows_named_pipe(config.windows_named_pipe.clone());
817
818 let mut builder =
819 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
820 #[cfg(feature = "http2")]
821 {
822 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
823 builder.http2_only(true);
824 }
825
826 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
827 {
828 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
829 }
830 if let Some(http2_initial_connection_window_size) =
831 config.http2_initial_connection_window_size
832 {
833 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
834 }
835 if config.http2_adaptive_window {
836 builder.http2_adaptive_window(true);
837 }
838 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
839 builder.http2_max_frame_size(http2_max_frame_size);
840 }
841 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
842 builder.http2_max_header_list_size(http2_max_header_list_size);
843 }
844 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
845 builder.http2_keep_alive_interval(http2_keep_alive_interval);
846 }
847 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
848 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
849 }
850 if config.http2_keep_alive_while_idle {
851 builder.http2_keep_alive_while_idle(true);
852 }
853 }
854
855 builder.timer(hyper_util::rt::TokioTimer::new());
856 builder.pool_timer(hyper_util::rt::TokioTimer::new());
857 builder.pool_idle_timeout(config.pool_idle_timeout);
858 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
859
860 if config.http09_responses {
861 builder.http09_responses(true);
862 }
863
864 if config.http1_title_case_headers {
865 builder.http1_title_case_headers(true);
866 }
867
868 if config.http1_allow_obsolete_multiline_headers_in_responses {
869 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
870 }
871
872 if config.http1_ignore_invalid_headers_in_responses {
873 builder.http1_ignore_invalid_headers_in_responses(true);
874 }
875
876 if config.http1_allow_spaces_after_header_name_in_responses {
877 builder.http1_allow_spaces_after_header_name_in_responses(true);
878 }
879
880 if let Some(http1_max_headers) = config.http1_max_headers {
881 builder.http1_max_headers(http1_max_headers);
882 }
883
884 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
885 let proxies_maybe_http_custom_headers =
886 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
887
888 let redirect_policy_desc = if config.redirect_policy.is_default() {
889 None
890 } else {
891 Some(format!("{:?}", &config.redirect_policy))
892 };
893
894 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
895 let hyper_service = HyperService {
896 hyper: hyper_client,
897 };
898
899 let redirect_policy = {
900 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
901 p.with_referer(config.referer)
902 .with_https_only(config.https_only);
903 p
904 };
905
906 let retry_policy = config.retry_policy.into_policy();
907
908 let svc = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
909
910 #[cfg(feature = "cookies")]
911 let svc = CookieService::new(svc, config.cookie_store.clone());
912 let hyper = FollowRedirect::with_policy(svc, redirect_policy.clone());
913 #[cfg(any(
914 feature = "gzip",
915 feature = "brotli",
916 feature = "zstd",
917 feature = "deflate"
918 ))]
919 let hyper = Decompression::new(hyper)
920 .no_gzip()
923 .no_deflate()
924 .no_br()
925 .no_zstd();
926 #[cfg(feature = "gzip")]
927 let hyper = hyper.gzip(config.accepts.gzip);
928 #[cfg(feature = "brotli")]
929 let hyper = hyper.br(config.accepts.brotli);
930 #[cfg(feature = "zstd")]
931 let hyper = hyper.zstd(config.accepts.zstd);
932 #[cfg(feature = "deflate")]
933 let hyper = hyper.deflate(config.accepts.deflate);
934
935 Ok(Client {
936 inner: Arc::new(ClientRef {
937 accepts: config.accepts,
938 #[cfg(feature = "cookies")]
939 cookie_store: config.cookie_store.clone(),
940 #[cfg(feature = "http3")]
943 h3_client: match h3_connector {
944 Some(h3_connector) => {
945 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
946 let svc = tower::retry::Retry::new(retry_policy, h3_service);
947 #[cfg(feature = "cookies")]
948 let svc = CookieService::new(svc, config.cookie_store);
949 let svc = FollowRedirect::with_policy(svc, redirect_policy);
950 #[cfg(any(
951 feature = "gzip",
952 feature = "brotli",
953 feature = "zstd",
954 feature = "deflate"
955 ))]
956 let svc = Decompression::new(svc)
957 .no_gzip()
960 .no_deflate()
961 .no_br()
962 .no_zstd();
963 #[cfg(feature = "gzip")]
964 let svc = svc.gzip(config.accepts.gzip);
965 #[cfg(feature = "brotli")]
966 let svc = svc.br(config.accepts.brotli);
967 #[cfg(feature = "zstd")]
968 let svc = svc.zstd(config.accepts.zstd);
969 #[cfg(feature = "deflate")]
970 let svc = svc.deflate(config.accepts.deflate);
971 Some(svc)
972 }
973 None => None,
974 },
975 headers: config.headers,
976 referer: config.referer,
977 read_timeout: config.read_timeout,
978 total_timeout: RequestConfig::new(config.timeout),
979 hyper,
980 proxies,
981 proxies_maybe_http_auth,
982 proxies_maybe_http_custom_headers,
983 https_only: config.https_only,
984 redirect_policy_desc,
985 }),
986 })
987 }
988
989 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1012 where
1013 V: TryInto<HeaderValue>,
1014 V::Error: Into<http::Error>,
1015 {
1016 match value.try_into() {
1017 Ok(value) => {
1018 self.config.headers.insert(USER_AGENT, value);
1019 }
1020 Err(e) => {
1021 self.config.error = Some(crate::error::builder(e.into()));
1022 }
1023 };
1024 self
1025 }
1026 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1050 for (key, value) in headers.iter() {
1051 self.config.headers.insert(key, value.clone());
1052 }
1053 self
1054 }
1055
1056 #[cfg(feature = "cookies")]
1071 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1072 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1073 if enable {
1074 self.cookie_provider(Arc::new(cookie::Jar::default()))
1075 } else {
1076 self.config.cookie_store = None;
1077 self
1078 }
1079 }
1080
1081 #[cfg(feature = "cookies")]
1095 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1096 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1097 mut self,
1098 cookie_store: Arc<C>,
1099 ) -> ClientBuilder {
1100 self.config.cookie_store = Some(cookie_store as _);
1101 self
1102 }
1103
1104 #[cfg(feature = "gzip")]
1121 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1122 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1123 self.config.accepts.gzip = enable;
1124 self
1125 }
1126
1127 #[cfg(feature = "brotli")]
1144 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1145 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1146 self.config.accepts.brotli = enable;
1147 self
1148 }
1149
1150 #[cfg(feature = "zstd")]
1167 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1168 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1169 self.config.accepts.zstd = enable;
1170 self
1171 }
1172
1173 #[cfg(feature = "deflate")]
1190 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1191 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1192 self.config.accepts.deflate = enable;
1193 self
1194 }
1195
1196 pub fn no_gzip(self) -> ClientBuilder {
1202 #[cfg(feature = "gzip")]
1203 {
1204 self.gzip(false)
1205 }
1206
1207 #[cfg(not(feature = "gzip"))]
1208 {
1209 self
1210 }
1211 }
1212
1213 pub fn no_brotli(self) -> ClientBuilder {
1219 #[cfg(feature = "brotli")]
1220 {
1221 self.brotli(false)
1222 }
1223
1224 #[cfg(not(feature = "brotli"))]
1225 {
1226 self
1227 }
1228 }
1229
1230 pub fn no_zstd(self) -> ClientBuilder {
1236 #[cfg(feature = "zstd")]
1237 {
1238 self.zstd(false)
1239 }
1240
1241 #[cfg(not(feature = "zstd"))]
1242 {
1243 self
1244 }
1245 }
1246
1247 pub fn no_deflate(self) -> ClientBuilder {
1253 #[cfg(feature = "deflate")]
1254 {
1255 self.deflate(false)
1256 }
1257
1258 #[cfg(not(feature = "deflate"))]
1259 {
1260 self
1261 }
1262 }
1263
1264 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1270 self.config.redirect_policy = policy;
1271 self
1272 }
1273
1274 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1278 self.config.referer = enable;
1279 self
1280 }
1281
1282 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1289 self.config.retry_policy = policy;
1290 self
1291 }
1292
1293 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1301 self.config.proxies.push(proxy.into_matcher());
1302 self.config.auto_sys_proxy = false;
1303 self
1304 }
1305
1306 pub fn no_proxy(mut self) -> ClientBuilder {
1314 self.config.proxies.clear();
1315 self.config.auto_sys_proxy = false;
1316 self
1317 }
1318
1319 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1328 self.config.timeout = Some(timeout);
1329 self
1330 }
1331
1332 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1340 self.config.read_timeout = Some(timeout);
1341 self
1342 }
1343
1344 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1353 self.config.connect_timeout = Some(timeout);
1354 self
1355 }
1356
1357 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1364 self.config.connection_verbose = verbose;
1365 self
1366 }
1367
1368 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1376 where
1377 D: Into<Option<Duration>>,
1378 {
1379 self.config.pool_idle_timeout = val.into();
1380 self
1381 }
1382
1383 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1387 self.config.pool_max_idle_per_host = max;
1388 self
1389 }
1390
1391 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1393 self.config.http1_title_case_headers = true;
1394 self
1395 }
1396
1397 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1403 mut self,
1404 value: bool,
1405 ) -> ClientBuilder {
1406 self.config
1407 .http1_allow_obsolete_multiline_headers_in_responses = value;
1408 self
1409 }
1410
1411 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1413 self.config.http1_ignore_invalid_headers_in_responses = value;
1414 self
1415 }
1416
1417 pub fn http1_allow_spaces_after_header_name_in_responses(
1423 mut self,
1424 value: bool,
1425 ) -> ClientBuilder {
1426 self.config
1427 .http1_allow_spaces_after_header_name_in_responses = value;
1428 self
1429 }
1430
1431 pub fn http1_max_headers(mut self, max: usize) -> ClientBuilder {
1438 self.config.http1_max_headers = Some(max);
1439 self
1440 }
1441
1442 pub fn http1_only(mut self) -> ClientBuilder {
1444 self.config.http_version_pref = HttpVersionPref::Http1;
1445 self
1446 }
1447
1448 pub fn http09_responses(mut self) -> ClientBuilder {
1450 self.config.http09_responses = true;
1451 self
1452 }
1453
1454 #[cfg(feature = "http2")]
1456 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1457 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1458 self.config.http_version_pref = HttpVersionPref::Http2;
1459 self
1460 }
1461
1462 #[cfg(feature = "http3")]
1464 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1465 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1466 self.config.http_version_pref = HttpVersionPref::Http3;
1467 self
1468 }
1469
1470 #[cfg(feature = "http2")]
1474 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1475 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1476 self.config.http2_initial_stream_window_size = sz.into();
1477 self
1478 }
1479
1480 #[cfg(feature = "http2")]
1484 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1485 pub fn http2_initial_connection_window_size(
1486 mut self,
1487 sz: impl Into<Option<u32>>,
1488 ) -> ClientBuilder {
1489 self.config.http2_initial_connection_window_size = sz.into();
1490 self
1491 }
1492
1493 #[cfg(feature = "http2")]
1498 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1499 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1500 self.config.http2_adaptive_window = enabled;
1501 self
1502 }
1503
1504 #[cfg(feature = "http2")]
1508 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1509 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1510 self.config.http2_max_frame_size = sz.into();
1511 self
1512 }
1513
1514 #[cfg(feature = "http2")]
1518 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1519 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1520 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1521 self
1522 }
1523
1524 #[cfg(feature = "http2")]
1529 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1530 pub fn http2_keep_alive_interval(
1531 mut self,
1532 interval: impl Into<Option<Duration>>,
1533 ) -> ClientBuilder {
1534 self.config.http2_keep_alive_interval = interval.into();
1535 self
1536 }
1537
1538 #[cfg(feature = "http2")]
1544 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1545 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1546 self.config.http2_keep_alive_timeout = Some(timeout);
1547 self
1548 }
1549
1550 #[cfg(feature = "http2")]
1557 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1558 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1559 self.config.http2_keep_alive_while_idle = enabled;
1560 self
1561 }
1562
1563 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1569 self.config.nodelay = enabled;
1570 self
1571 }
1572
1573 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1588 where
1589 T: Into<Option<IpAddr>>,
1590 {
1591 self.config.local_address = addr.into();
1592 self
1593 }
1594
1595 #[cfg(any(
1628 target_os = "android",
1629 target_os = "fuchsia",
1630 target_os = "illumos",
1631 target_os = "ios",
1632 target_os = "linux",
1633 target_os = "macos",
1634 target_os = "solaris",
1635 target_os = "tvos",
1636 target_os = "visionos",
1637 target_os = "watchos",
1638 ))]
1639 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1640 self.config.interface = Some(interface.to_string());
1641 self
1642 }
1643
1644 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1648 where
1649 D: Into<Option<Duration>>,
1650 {
1651 self.config.tcp_keepalive = val.into();
1652 self
1653 }
1654
1655 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1659 where
1660 D: Into<Option<Duration>>,
1661 {
1662 self.config.tcp_keepalive_interval = val.into();
1663 self
1664 }
1665
1666 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1670 where
1671 C: Into<Option<u32>>,
1672 {
1673 self.config.tcp_keepalive_retries = retries.into();
1674 self
1675 }
1676
1677 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1684 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1685 where
1686 D: Into<Option<Duration>>,
1687 {
1688 self.config.tcp_user_timeout = val.into();
1689 self
1690 }
1691
1692 #[cfg(unix)]
1706 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1707 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1708 self
1709 }
1710
1711 #[cfg(target_os = "windows")]
1723 pub fn windows_named_pipe(mut self, pipe: impl WindowsNamedPipeProvider) -> ClientBuilder {
1724 self.config.windows_named_pipe = Some(
1725 pipe.reqwest_windows_named_pipe_path(crate::connect::windows_named_pipe::Internal)
1726 .into(),
1727 );
1728 self
1729 }
1730
1731 #[cfg(feature = "__tls")]
1750 #[cfg_attr(
1751 docsrs,
1752 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1753 )]
1754 pub fn tls_certs_merge(
1755 mut self,
1756 certs: impl IntoIterator<Item = Certificate>,
1757 ) -> ClientBuilder {
1758 self.config.root_certs.extend(certs);
1759 self
1760 }
1761
1762 #[cfg(feature = "__tls")]
1775 #[cfg_attr(
1776 docsrs,
1777 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1778 )]
1779 pub fn tls_certs_only(mut self, certs: impl IntoIterator<Item = Certificate>) -> ClientBuilder {
1780 self.config.root_certs.extend(certs);
1781 self.config.tls_certs_only = true;
1782 self
1783 }
1784
1785 #[cfg(feature = "__tls")]
1788 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1789 self.config.root_certs.push(cert);
1790 self
1791 }
1792
1793 #[cfg(feature = "__rustls")]
1807 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1808 pub fn tls_crls_only(
1809 mut self,
1810 crls: impl IntoIterator<Item = CertificateRevocationList>,
1811 ) -> ClientBuilder {
1812 self.config.crls.extend(crls);
1813 self
1814 }
1815
1816 #[cfg(feature = "__rustls")]
1818 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1819 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1820 self.config.crls.push(crl);
1821 self
1822 }
1823
1824 #[cfg(feature = "__rustls")]
1826 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1827 pub fn add_crls(
1828 mut self,
1829 crls: impl IntoIterator<Item = CertificateRevocationList>,
1830 ) -> ClientBuilder {
1831 self.config.crls.extend(crls);
1832 self
1833 }
1834
1835 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
1842 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
1843 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1844 self.config.identity = Some(identity);
1845 self
1846 }
1847
1848 #[cfg(feature = "__tls")]
1869 #[cfg_attr(
1870 docsrs,
1871 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1872 )]
1873 pub fn tls_danger_accept_invalid_hostnames(
1874 mut self,
1875 accept_invalid_hostname: bool,
1876 ) -> ClientBuilder {
1877 self.config.hostname_verification = !accept_invalid_hostname;
1878 self
1879 }
1880
1881 #[cfg(feature = "__tls")]
1883 pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder {
1884 self.tls_danger_accept_invalid_hostnames(accept_invalid_hostname)
1885 }
1886
1887 #[cfg(feature = "__tls")]
1904 #[cfg_attr(
1905 docsrs,
1906 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1907 )]
1908 pub fn tls_danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1909 self.config.certs_verification = !accept_invalid_certs;
1910 self
1911 }
1912
1913 #[cfg(feature = "__tls")]
1915 pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder {
1916 self.tls_danger_accept_invalid_certs(accept_invalid_certs)
1917 }
1918
1919 #[cfg(feature = "__tls")]
1928 #[cfg_attr(
1929 docsrs,
1930 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1931 )]
1932 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1933 self.config.tls_sni = tls_sni;
1934 self
1935 }
1936
1937 #[cfg(feature = "__rustls")]
1949 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
1950 pub fn tls_sslkeylogfile(mut self, on: bool) -> ClientBuilder {
1951 self.config.tls_sslkeylogfile = on;
1952 self
1953 }
1954
1955 #[cfg(feature = "__tls")]
1968 #[cfg_attr(
1969 docsrs,
1970 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1971 )]
1972 pub fn tls_version_min(mut self, version: tls::Version) -> ClientBuilder {
1973 self.config.min_tls_version = Some(version);
1974 self
1975 }
1976
1977 #[cfg(feature = "__tls")]
1979 pub fn min_tls_version(self, version: tls::Version) -> ClientBuilder {
1980 self.tls_version_min(version)
1981 }
1982
1983 #[cfg(feature = "__tls")]
2001 #[cfg_attr(
2002 docsrs,
2003 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2004 )]
2005 pub fn tls_version_max(mut self, version: tls::Version) -> ClientBuilder {
2006 self.config.max_tls_version = Some(version);
2007 self
2008 }
2009
2010 #[cfg(feature = "__tls")]
2012 pub fn max_tls_version(self, version: tls::Version) -> ClientBuilder {
2013 self.tls_version_max(version)
2014 }
2015
2016 #[cfg(feature = "__native-tls")]
2025 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2026 pub fn tls_backend_native(mut self) -> ClientBuilder {
2027 self.config.tls = TlsBackend::NativeTls;
2028 self
2029 }
2030
2031 #[cfg(feature = "__native-tls")]
2033 pub fn use_native_tls(self) -> ClientBuilder {
2034 self.tls_backend_native()
2035 }
2036
2037 #[cfg(feature = "__rustls")]
2046 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2047 pub fn tls_backend_rustls(mut self) -> ClientBuilder {
2048 self.config.tls = TlsBackend::Boring;
2049 self
2050 }
2051
2052 #[cfg(feature = "__rustls")]
2054 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2055 pub fn use_rustls_tls(self) -> ClientBuilder {
2056 self.tls_backend_rustls()
2057 }
2058
2059 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2085 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
2086 pub fn tls_backend_preconfigured(mut self, tls: impl Any) -> ClientBuilder {
2087 let mut tls = Some(tls);
2088 #[cfg(feature = "__native-tls")]
2089 {
2090 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2091 let tls = conn.take().expect("is definitely Some");
2092 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2093 self.config.tls = tls;
2094 return self;
2095 }
2096 }
2097 #[cfg(feature = "__rustls")]
2098 {
2099 if let Some(conn) =
2100 (&mut tls as &mut dyn Any).downcast_mut::<Option<boring::ssl::SslConnector>>()
2101 {
2102 let tls = conn.take().expect("is definitely Some");
2103 let tls = crate::tls::TlsBackend::BuiltBoring(tls);
2104 self.config.tls = tls;
2105 return self;
2106 }
2107 }
2108
2109 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2111 self
2112 }
2113
2114 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2116 pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder {
2117 self.tls_backend_preconfigured(tls)
2118 }
2119
2120 #[cfg(feature = "__tls")]
2127 #[cfg_attr(
2128 docsrs,
2129 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2130 )]
2131 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2132 self.config.tls_info = tls_info;
2133 self
2134 }
2135
2136 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2140 self.config.https_only = enabled;
2141 self
2142 }
2143
2144 #[cfg(feature = "hickory-dns")]
2158 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2159 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2160 self.config.hickory_dns = enable;
2161 self
2162 }
2163
2164 pub fn no_hickory_dns(self) -> ClientBuilder {
2170 #[cfg(feature = "hickory-dns")]
2171 {
2172 self.hickory_dns(false)
2173 }
2174
2175 #[cfg(not(feature = "hickory-dns"))]
2176 {
2177 self
2178 }
2179 }
2180
2181 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2186 self.resolve_to_addrs(domain, &[addr])
2187 }
2188
2189 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2194 self.config
2195 .dns_overrides
2196 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2197 self
2198 }
2199
2200 pub fn dns_resolver<R>(mut self, resolver: R) -> ClientBuilder
2205 where
2206 R: crate::dns::resolve::IntoResolve,
2207 {
2208 self.config.dns_resolver = Some(resolver.into_resolve());
2209 self
2210 }
2211
2212 #[cfg(feature = "http3")]
2217 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2218 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2219 self.config.tls_enable_early_data = enabled;
2220 self
2221 }
2222
2223 #[cfg(feature = "http3")]
2229 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2230 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2231 self.config.quic_max_idle_timeout = Some(value);
2232 self
2233 }
2234
2235 #[cfg(feature = "http3")]
2246 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2247 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2248 assert!(
2249 value < (1 << 62),
2250 "QUIC flow control window exceeds 2^62 - 1"
2251 );
2252 self.config.quic_stream_receive_window = Some(value);
2253 self
2254 }
2255
2256 #[cfg(feature = "http3")]
2267 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2268 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2269 assert!(
2270 value < (1 << 62),
2271 "QUIC flow control window exceeds 2^62 - 1"
2272 );
2273 self.config.quic_receive_window = Some(value);
2274 self
2275 }
2276
2277 #[cfg(feature = "http3")]
2283 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2284 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2285 self.config.quic_send_window = Some(value);
2286 self
2287 }
2288
2289 #[cfg(feature = "http3")]
2297 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2298 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2299 self.config.quic_congestion_bbr = true;
2300 self
2301 }
2302
2303 #[cfg(feature = "http3")]
2313 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2314 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2315 self.config.h3_max_field_section_size = Some(value);
2316 self
2317 }
2318
2319 #[cfg(feature = "http3")]
2331 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2332 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2333 self.config.h3_send_grease = Some(enabled);
2334 self
2335 }
2336
2337 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2360 where
2361 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2362 L::Service:
2363 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2364 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2365 {
2366 let layer = BoxCloneSyncServiceLayer::new(layer);
2367
2368 self.config.connector_layers.push(layer);
2369
2370 self
2371 }
2372}
2373
2374type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2375
2376impl Default for Client {
2377 fn default() -> Self {
2378 Self::new()
2379 }
2380}
2381
2382impl Client {
2383 pub fn new() -> Client {
2393 ClientBuilder::new().build().expect("Client::new()")
2394 }
2395
2396 pub fn builder() -> ClientBuilder {
2400 ClientBuilder::new()
2401 }
2402
2403 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2409 self.request(Method::GET, url)
2410 }
2411
2412 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2418 self.request(Method::POST, url)
2419 }
2420
2421 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2427 self.request(Method::PUT, url)
2428 }
2429
2430 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2436 self.request(Method::PATCH, url)
2437 }
2438
2439 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2445 self.request(Method::DELETE, url)
2446 }
2447
2448 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2454 self.request(Method::HEAD, url)
2455 }
2456
2457 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2466 let req = url.into_url().map(move |url| Request::new(method, url));
2467 RequestBuilder::new(self.clone(), req)
2468 }
2469
2470 pub fn execute(
2483 &self,
2484 request: Request,
2485 ) -> impl Future<Output = Result<Response, crate::Error>> {
2486 self.execute_request(request)
2487 }
2488
2489 pub(super) fn execute_request(&self, req: Request) -> Pending {
2490 let (method, url, mut headers, body, version, extensions) = req.pieces();
2491 if url.scheme() != "http" && url.scheme() != "https" {
2492 return Pending::new_err(error::url_bad_scheme(url));
2493 }
2494
2495 if self.inner.https_only && url.scheme() != "https" {
2497 return Pending::new_err(error::url_bad_scheme(url));
2498 }
2499
2500 for (key, value) in &self.inner.headers {
2503 if let Entry::Vacant(entry) = headers.entry(key) {
2504 entry.insert(value.clone());
2505 }
2506 }
2507
2508 let uri = match try_uri(&url) {
2509 Ok(uri) => uri,
2510 _ => return Pending::new_err(error::url_invalid_uri(url)),
2511 };
2512
2513 let body = body.unwrap_or_else(Body::empty);
2514
2515 self.proxy_auth(&uri, &mut headers);
2516 self.proxy_custom_headers(&uri, &mut headers);
2517
2518 let builder = hyper::Request::builder()
2519 .method(method.clone())
2520 .uri(uri)
2521 .version(version);
2522
2523 let in_flight = match version {
2524 #[cfg(feature = "http3")]
2525 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2526 let mut req = builder.body(body).expect("valid request parts");
2527 *req.headers_mut() = headers.clone();
2528 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2529 ResponseFuture::H3(h3.call(req))
2530 }
2531 _ => {
2532 let mut req = builder.body(body).expect("valid request parts");
2533 *req.headers_mut() = headers.clone();
2534 let mut hyper = self.inner.hyper.clone();
2535 ResponseFuture::Default(hyper.call(req))
2536 }
2537 };
2538
2539 let total_timeout = self
2540 .inner
2541 .total_timeout
2542 .fetch(&extensions)
2543 .copied()
2544 .map(tokio::time::sleep)
2545 .map(Box::pin);
2546
2547 let read_timeout_fut = self
2548 .inner
2549 .read_timeout
2550 .map(tokio::time::sleep)
2551 .map(Box::pin);
2552
2553 Pending {
2554 inner: PendingInner::Request(Box::pin(PendingRequest {
2555 method,
2556 url,
2557 headers,
2558
2559 client: self.inner.clone(),
2560
2561 in_flight,
2562 total_timeout,
2563 read_timeout_fut,
2564 read_timeout: self.inner.read_timeout,
2565 })),
2566 }
2567 }
2568
2569 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2570 if !self.inner.proxies_maybe_http_auth {
2571 return;
2572 }
2573
2574 if dst.scheme() != Some(&Scheme::HTTP) {
2578 return;
2579 }
2580
2581 if headers.contains_key(PROXY_AUTHORIZATION) {
2582 return;
2583 }
2584
2585 for proxy in self.inner.proxies.iter() {
2586 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2587 headers.insert(PROXY_AUTHORIZATION, header);
2588 break;
2589 }
2590 }
2591 }
2592
2593 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2594 if !self.inner.proxies_maybe_http_custom_headers {
2595 return;
2596 }
2597
2598 if dst.scheme() != Some(&Scheme::HTTP) {
2599 return;
2600 }
2601
2602 for proxy in self.inner.proxies.iter() {
2603 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2604 iter.iter().for_each(|(key, value)| {
2605 headers.insert(key, value.clone());
2606 });
2607 break;
2608 }
2609 }
2610 }
2611}
2612
2613impl fmt::Debug for Client {
2614 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2615 let mut builder = f.debug_struct("Client");
2616 self.inner.fmt_fields(&mut builder);
2617 builder.finish()
2618 }
2619}
2620
2621impl tower_service::Service<Request> for Client {
2622 type Response = Response;
2623 type Error = crate::Error;
2624 type Future = Pending;
2625
2626 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2627 Poll::Ready(Ok(()))
2628 }
2629
2630 fn call(&mut self, req: Request) -> Self::Future {
2631 self.execute_request(req)
2632 }
2633}
2634
2635impl tower_service::Service<Request> for &'_ Client {
2636 type Response = Response;
2637 type Error = crate::Error;
2638 type Future = Pending;
2639
2640 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2641 Poll::Ready(Ok(()))
2642 }
2643
2644 fn call(&mut self, req: Request) -> Self::Future {
2645 self.execute_request(req)
2646 }
2647}
2648
2649impl fmt::Debug for ClientBuilder {
2650 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2651 let mut builder = f.debug_struct("ClientBuilder");
2652 self.config.fmt_fields(&mut builder);
2653 builder.finish()
2654 }
2655}
2656
2657impl Config {
2658 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2659 #[cfg(feature = "cookies")]
2663 {
2664 if self.cookie_store.is_some() {
2665 f.field("cookie_store", &true);
2666 }
2667 }
2668
2669 f.field("accepts", &self.accepts);
2670
2671 if !self.proxies.is_empty() {
2672 f.field("proxies", &self.proxies);
2673 }
2674
2675 if !self.redirect_policy.is_default() {
2676 f.field("redirect_policy", &self.redirect_policy);
2677 }
2678
2679 if self.referer {
2680 f.field("referer", &true);
2681 }
2682
2683 f.field("default_headers", &self.headers);
2684
2685 if self.http1_title_case_headers {
2686 f.field("http1_title_case_headers", &true);
2687 }
2688
2689 if self.http1_allow_obsolete_multiline_headers_in_responses {
2690 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2691 }
2692
2693 if self.http1_ignore_invalid_headers_in_responses {
2694 f.field("http1_ignore_invalid_headers_in_responses", &true);
2695 }
2696
2697 if self.http1_allow_spaces_after_header_name_in_responses {
2698 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2699 }
2700
2701 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2702 f.field("http1_only", &true);
2703 }
2704
2705 #[cfg(feature = "http2")]
2706 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2707 f.field("http2_prior_knowledge", &true);
2708 }
2709
2710 if let Some(ref d) = self.connect_timeout {
2711 f.field("connect_timeout", d);
2712 }
2713
2714 if let Some(ref d) = self.timeout {
2715 f.field("timeout", d);
2716 }
2717
2718 if let Some(ref v) = self.local_address {
2719 f.field("local_address", v);
2720 }
2721
2722 #[cfg(any(
2723 target_os = "android",
2724 target_os = "fuchsia",
2725 target_os = "illumos",
2726 target_os = "ios",
2727 target_os = "linux",
2728 target_os = "macos",
2729 target_os = "solaris",
2730 target_os = "tvos",
2731 target_os = "visionos",
2732 target_os = "watchos",
2733 ))]
2734 if let Some(ref v) = self.interface {
2735 f.field("interface", v);
2736 }
2737
2738 if self.nodelay {
2739 f.field("tcp_nodelay", &true);
2740 }
2741
2742 #[cfg(feature = "__tls")]
2743 {
2744 if !self.hostname_verification {
2745 f.field("tls_danger_accept_invalid_hostnames", &true);
2746 }
2747 }
2748
2749 #[cfg(feature = "__tls")]
2750 {
2751 if !self.certs_verification {
2752 f.field("tls_danger_accept_invalid_certs", &true);
2753 }
2754
2755 if let Some(ref min_tls_version) = self.min_tls_version {
2756 f.field("tls_version_min", min_tls_version);
2757 }
2758
2759 if let Some(ref max_tls_version) = self.max_tls_version {
2760 f.field("tls_version_max", max_tls_version);
2761 }
2762
2763 f.field("tls_sni", &self.tls_sni);
2764
2765 f.field("tls_info", &self.tls_info);
2766 }
2767
2768 #[cfg(feature = "__rustls")]
2769 {
2770 f.field("tls_sslkeylogfile", &self.tls_sslkeylogfile);
2771 }
2772
2773 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2774 {
2775 f.field("tls_backend", &self.tls);
2776 }
2777
2778 if !self.dns_overrides.is_empty() {
2779 f.field("dns_overrides", &self.dns_overrides);
2780 }
2781
2782 #[cfg(feature = "http3")]
2783 {
2784 if self.tls_enable_early_data {
2785 f.field("tls_enable_early_data", &true);
2786 }
2787 }
2788
2789 #[cfg(unix)]
2790 if let Some(ref p) = self.unix_socket {
2791 f.field("unix_socket", p);
2792 }
2793 }
2794}
2795
2796#[cfg(not(feature = "cookies"))]
2797type MaybeCookieService<T> = T;
2798
2799#[cfg(feature = "cookies")]
2800type MaybeCookieService<T> = CookieService<T>;
2801
2802#[cfg(not(any(
2803 feature = "gzip",
2804 feature = "brotli",
2805 feature = "zstd",
2806 feature = "deflate"
2807)))]
2808type MaybeDecompression<T> = T;
2809
2810#[cfg(any(
2811 feature = "gzip",
2812 feature = "brotli",
2813 feature = "zstd",
2814 feature = "deflate"
2815))]
2816type MaybeDecompression<T> = Decompression<T>;
2817
2818type LayeredService<T> = MaybeDecompression<
2819 FollowRedirect<
2820 MaybeCookieService<tower::retry::Retry<crate::retry::Policy, T>>,
2821 TowerRedirectPolicy,
2822 >,
2823>;
2824type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
2825
2826struct ClientRef {
2827 accepts: Accepts,
2828 #[cfg(feature = "cookies")]
2829 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2830 headers: HeaderMap,
2831 hyper: LayeredService<HyperService>,
2832 #[cfg(feature = "http3")]
2833 h3_client: Option<LayeredService<H3Client>>,
2834 referer: bool,
2835 total_timeout: RequestConfig<TotalTimeout>,
2836 read_timeout: Option<Duration>,
2837 proxies: Arc<Vec<ProxyMatcher>>,
2838 proxies_maybe_http_auth: bool,
2839 proxies_maybe_http_custom_headers: bool,
2840 https_only: bool,
2841 redirect_policy_desc: Option<String>,
2842}
2843
2844impl ClientRef {
2845 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2846 #[cfg(feature = "cookies")]
2850 {
2851 if self.cookie_store.is_some() {
2852 f.field("cookie_store", &true);
2853 }
2854 }
2855
2856 f.field("accepts", &self.accepts);
2857
2858 if !self.proxies.is_empty() {
2859 f.field("proxies", &self.proxies);
2860 }
2861
2862 if let Some(s) = &self.redirect_policy_desc {
2863 f.field("redirect_policy", s);
2864 }
2865
2866 if self.referer {
2867 f.field("referer", &true);
2868 }
2869
2870 f.field("default_headers", &self.headers);
2871
2872 self.total_timeout.fmt_as_field(f);
2873
2874 if let Some(ref d) = self.read_timeout {
2875 f.field("read_timeout", d);
2876 }
2877 }
2878}
2879
2880pin_project! {
2881 pub struct Pending {
2882 #[pin]
2883 inner: PendingInner,
2884 }
2885}
2886
2887enum PendingInner {
2888 Request(Pin<Box<PendingRequest>>),
2889 Error(Option<crate::Error>),
2890}
2891
2892pin_project! {
2893 struct PendingRequest {
2894 method: Method,
2895 url: Url,
2896 headers: HeaderMap,
2897
2898 client: Arc<ClientRef>,
2899
2900 #[pin]
2901 in_flight: ResponseFuture,
2902 #[pin]
2903 total_timeout: Option<Pin<Box<Sleep>>>,
2904 #[pin]
2905 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2906 read_timeout: Option<Duration>,
2907 }
2908}
2909
2910#[allow(clippy::large_enum_variant)] enum ResponseFuture {
2912 Default(LayeredFuture<HyperService>),
2913 #[cfg(feature = "http3")]
2914 H3(LayeredFuture<H3Client>),
2915}
2916
2917impl PendingRequest {
2918 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2919 self.project().in_flight
2920 }
2921
2922 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2923 self.project().total_timeout
2924 }
2925
2926 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2927 self.project().read_timeout_fut
2928 }
2929}
2930
2931impl Pending {
2932 pub(super) fn new_err(err: crate::Error) -> Pending {
2933 Pending {
2934 inner: PendingInner::Error(Some(err)),
2935 }
2936 }
2937
2938 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2939 self.project().inner
2940 }
2941}
2942
2943impl Future for Pending {
2944 type Output = Result<Response, crate::Error>;
2945
2946 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2947 let inner = self.inner();
2948 match inner.get_mut() {
2949 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2950 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2951 .take()
2952 .expect("Pending error polled more than once"))),
2953 }
2954 }
2955}
2956
2957impl Future for PendingRequest {
2958 type Output = Result<Response, crate::Error>;
2959
2960 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2961 if let Some(delay) = self.as_mut().total_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 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
2970 if let Poll::Ready(()) = delay.poll(cx) {
2971 return Poll::Ready(Err(
2972 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2973 ));
2974 }
2975 }
2976
2977 let res = match self.as_mut().in_flight().get_mut() {
2978 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
2979 Err(e) => {
2980 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
2981 }
2982 Ok(res) => res.map(super::body::boxed),
2983 },
2984 #[cfg(feature = "http3")]
2985 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
2986 Err(e) => {
2987 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
2988 }
2989 Ok(res) => res.map(super::body::boxed),
2990 },
2991 };
2992
2993 if let Some(url) = &res
2994 .extensions()
2995 .get::<tower_http::follow_redirect::RequestUri>()
2996 {
2997 self.url = match Url::parse(&url.0.to_string()) {
2998 Ok(url) => url,
2999 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3000 }
3001 };
3002
3003 let res = Response::new(
3004 res,
3005 self.url.clone(),
3006 self.total_timeout.take(),
3007 self.read_timeout,
3008 );
3009 Poll::Ready(Ok(res))
3010 }
3011}
3012
3013impl fmt::Debug for Pending {
3014 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3015 match self.inner {
3016 PendingInner::Request(ref req) => f
3017 .debug_struct("Pending")
3018 .field("method", &req.method)
3019 .field("url", &req.url)
3020 .finish(),
3021 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3022 }
3023 }
3024}
3025
3026#[cfg(test)]
3027mod tests {
3028
3029 #[tokio::test]
3030 async fn execute_request_rejects_invalid_urls() {
3031 let url_str = "hxxps://www.rust-lang.org/";
3032 let url = url::Url::parse(url_str).unwrap();
3033 let result = crate::get(url.clone()).await;
3034
3035 assert!(result.is_err());
3036 let err = result.err().unwrap();
3037 assert!(err.is_builder());
3038 assert_eq!(url_str, err.url().unwrap().as_str());
3039 }
3040
3041 #[tokio::test]
3043 async fn execute_request_rejects_invalid_hostname() {
3044 let url_str = "https://{{hostname}}/";
3045 let url = url::Url::parse(url_str).unwrap();
3046 let result = crate::get(url.clone()).await;
3047
3048 assert!(result.is_err());
3049 let err = result.err().unwrap();
3050 assert!(err.is_builder());
3051 assert_eq!(url_str, err.url().unwrap().as_str());
3052 }
3053
3054 #[test]
3055 fn test_future_size() {
3056 let s = std::mem::size_of::<super::Pending>();
3057 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3058 }
3059}