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::{ReadTimeout, RequestConfig, TotalTimeout};
20#[cfg(unix)]
21use crate::connect::uds::UnixSocketProvider;
22#[cfg(target_os = "windows")]
23use crate::connect::windows_named_pipe::WindowsNamedPipeProvider;
24use crate::connect::{
25 sealed::{Conn, Unnameable},
26 BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
27};
28#[cfg(feature = "cookies")]
29use crate::cookie;
30#[cfg(feature = "cookies")]
31use crate::cookie::service::CookieService;
32#[cfg(feature = "hickory-dns")]
33use crate::dns::hickory::HickoryDnsResolver;
34use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
35use crate::error::{self, BoxError};
36use crate::into_url::try_uri;
37use crate::proxy::Matcher as ProxyMatcher;
38use crate::redirect::{self, TowerRedirectPolicy};
39#[cfg(feature = "__rustls")]
40use crate::tls::CertificateRevocationList;
41#[cfg(feature = "__tls")]
42use crate::tls::{self, TlsBackend};
43#[cfg(feature = "__tls")]
44use crate::Certificate;
45#[cfg(any(feature = "__native-tls", feature = "__rustls"))]
46use crate::Identity;
47use crate::{IntoUrl, Method, Proxy, Url};
48
49use http::header::{Entry, HeaderMap, HeaderValue, ACCEPT, PROXY_AUTHORIZATION, USER_AGENT};
50use http::uri::Scheme;
51use http::Uri;
52use hyper_util::client::legacy::connect::HttpConnector;
53#[cfg(feature = "__native-tls")]
54use native_tls_crate::TlsConnector;
55use pin_project_lite::pin_project;
56#[cfg(feature = "http3")]
57use quinn::TransportConfig;
58#[cfg(feature = "http3")]
59use quinn::VarInt;
60use tokio::time::Sleep;
61use tower::util::BoxCloneSyncServiceLayer;
62use tower::{Layer, Service};
63#[cfg(any(
64 feature = "gzip",
65 feature = "brotli",
66 feature = "zstd",
67 feature = "deflate"
68))]
69use tower_http::decompression::Decompression;
70use tower_http::follow_redirect::FollowRedirect;
71
72#[derive(Clone)]
93pub struct Client {
94 inner: Arc<ClientRef>,
95}
96
97#[must_use]
99pub struct ClientBuilder {
100 config: Config,
101}
102
103enum HttpVersionPref {
104 Http1,
105 #[cfg(feature = "http2")]
106 Http2,
107 #[cfg(feature = "http3")]
108 Http3,
109 All,
110}
111
112#[derive(Clone, Copy, Debug)]
113struct Accepts {
114 #[cfg(feature = "gzip")]
115 gzip: bool,
116 #[cfg(feature = "brotli")]
117 brotli: bool,
118 #[cfg(feature = "zstd")]
119 zstd: bool,
120 #[cfg(feature = "deflate")]
121 deflate: bool,
122}
123
124impl Default for Accepts {
125 fn default() -> Accepts {
126 Accepts {
127 #[cfg(feature = "gzip")]
128 gzip: true,
129 #[cfg(feature = "brotli")]
130 brotli: true,
131 #[cfg(feature = "zstd")]
132 zstd: true,
133 #[cfg(feature = "deflate")]
134 deflate: true,
135 }
136 }
137}
138
139#[derive(Clone)]
140struct HyperService {
141 hyper: HyperClient,
142}
143
144impl Service<hyper::Request<crate::async_impl::body::Body>> for HyperService {
145 type Error = crate::Error;
146 type Response = http::Response<hyper::body::Incoming>;
147 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + Sync>>;
148
149 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
150 self.hyper.poll_ready(cx).map_err(crate::error::request)
151 }
152
153 fn call(&mut self, req: hyper::Request<crate::async_impl::body::Body>) -> Self::Future {
154 let clone = self.hyper.clone();
155 let mut inner = std::mem::replace(&mut self.hyper, clone);
156 Box::pin(async move { inner.call(req).await.map_err(crate::error::request) })
157 }
158}
159
160struct Config {
161 accepts: Accepts,
163 headers: HeaderMap,
164 #[cfg(feature = "__tls")]
165 hostname_verification: bool,
166 #[cfg(feature = "__tls")]
167 certs_verification: bool,
168 #[cfg(feature = "__tls")]
169 tls_sni: bool,
170 connect_timeout: Option<Duration>,
171 connection_verbose: bool,
172 pool_idle_timeout: Option<Duration>,
173 pool_max_idle_per_host: usize,
174 tcp_keepalive: Option<Duration>,
175 tcp_keepalive_interval: Option<Duration>,
176 tcp_keepalive_retries: Option<u32>,
177 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
178 tcp_user_timeout: Option<Duration>,
179 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
180 identity: Option<Identity>,
181 proxies: Vec<ProxyMatcher>,
182 auto_sys_proxy: bool,
183 redirect_policy: redirect::Policy,
184 retry_policy: crate::retry::Builder,
185 referer: bool,
186 read_timeout: Option<Duration>,
187 timeout: Option<Duration>,
188 #[cfg(feature = "__tls")]
189 root_certs: Vec<Certificate>,
190 #[cfg(feature = "__tls")]
191 tls_certs_only: bool,
192 #[cfg(feature = "__rustls")]
193 crls: Vec<CertificateRevocationList>,
194 #[cfg(feature = "__tls")]
195 min_tls_version: Option<tls::Version>,
196 #[cfg(feature = "__tls")]
197 max_tls_version: Option<tls::Version>,
198 #[cfg(feature = "__tls")]
199 tls_info: bool,
200 #[cfg(feature = "__tls")]
201 tls: TlsBackend,
202 connector_layers: Vec<BoxedConnectorLayer>,
203 http_version_pref: HttpVersionPref,
204 http09_responses: bool,
205 http1_title_case_headers: bool,
206 http1_allow_obsolete_multiline_headers_in_responses: bool,
207 http1_ignore_invalid_headers_in_responses: bool,
208 http1_allow_spaces_after_header_name_in_responses: bool,
209 #[cfg(feature = "http2")]
210 http2_initial_stream_window_size: Option<u32>,
211 #[cfg(feature = "http2")]
212 http2_initial_connection_window_size: Option<u32>,
213 #[cfg(feature = "http2")]
214 http2_adaptive_window: bool,
215 #[cfg(feature = "http2")]
216 http2_max_frame_size: Option<u32>,
217 #[cfg(feature = "http2")]
218 http2_max_header_list_size: Option<u32>,
219 #[cfg(feature = "http2")]
220 http2_keep_alive_interval: Option<Duration>,
221 #[cfg(feature = "http2")]
222 http2_keep_alive_timeout: Option<Duration>,
223 #[cfg(feature = "http2")]
224 http2_keep_alive_while_idle: bool,
225 #[cfg(feature = "http2")]
226 http2_header_table_size: Option<u32>,
227 #[cfg(feature = "http2")]
228 http2_max_concurrent_streams: Option<u32>,
229 #[cfg(feature = "http2")]
230 http2_enable_push: Option<bool>,
231 #[cfg(feature = "http2")]
232 http2_no_rfc7540_priorities: Option<bool>,
233 #[cfg(feature = "http2")]
234 http2_enable_connect_protocol: Option<u32>,
235 #[cfg(feature = "http2")]
236 http2_settings_order: Option<h2::frame::SettingsOrder>,
237 #[cfg(feature = "http2")]
238 http2_headers_pseudo_order: Option<h2::frame::PseudoOrder>,
239 #[cfg(feature = "http2")]
240 http2_headers_priority: Option<(u8, u32, bool)>,
241 #[cfg(feature = "http2")]
242 http2_headers_order: Option<Vec<http::HeaderName>>,
243 #[cfg(feature = "http2")]
244 http2_initial_stream_id: Option<u32>,
245 local_address: Option<IpAddr>,
246 #[cfg(any(
247 target_os = "android",
248 target_os = "fuchsia",
249 target_os = "illumos",
250 target_os = "ios",
251 target_os = "linux",
252 target_os = "macos",
253 target_os = "solaris",
254 target_os = "tvos",
255 target_os = "visionos",
256 target_os = "watchos",
257 ))]
258 interface: Option<String>,
259 nodelay: bool,
260 #[cfg(feature = "cookies")]
261 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
262 hickory_dns: bool,
263 error: Option<crate::Error>,
264 https_only: bool,
265 #[cfg(feature = "http3")]
266 tls_enable_early_data: bool,
267 #[cfg(feature = "http3")]
268 quic_max_idle_timeout: Option<Duration>,
269 #[cfg(feature = "http3")]
270 quic_stream_receive_window: Option<VarInt>,
271 #[cfg(feature = "http3")]
272 quic_receive_window: Option<VarInt>,
273 #[cfg(feature = "http3")]
274 quic_send_window: Option<u64>,
275 #[cfg(feature = "http3")]
276 quic_congestion_bbr: bool,
277 #[cfg(feature = "http3")]
278 h3_max_field_section_size: Option<u64>,
279 #[cfg(feature = "http3")]
280 h3_send_grease: Option<bool>,
281 dns_overrides: HashMap<String, Vec<SocketAddr>>,
282 dns_resolver: Option<Arc<dyn Resolve>>,
283
284 #[cfg(unix)]
285 unix_socket: Option<Arc<std::path::Path>>,
286 #[cfg(target_os = "windows")]
287 windows_named_pipe: Option<Arc<std::ffi::OsStr>>,
288}
289
290impl Default for ClientBuilder {
291 fn default() -> Self {
292 Self::new()
293 }
294}
295
296impl ClientBuilder {
297 pub fn new() -> Self {
301 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
302 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
303
304 ClientBuilder {
305 config: Config {
306 error: None,
307 accepts: Accepts::default(),
308 headers,
309 #[cfg(feature = "__tls")]
310 hostname_verification: true,
311 #[cfg(feature = "__tls")]
312 certs_verification: true,
313 #[cfg(feature = "__tls")]
314 tls_sni: true,
315 connect_timeout: None,
316 connection_verbose: false,
317 pool_idle_timeout: Some(Duration::from_secs(90)),
318 pool_max_idle_per_host: usize::MAX,
319 tcp_keepalive: Some(Duration::from_secs(15)),
320 tcp_keepalive_interval: Some(Duration::from_secs(15)),
321 tcp_keepalive_retries: Some(3),
322 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
323 tcp_user_timeout: Some(Duration::from_secs(30)),
324 proxies: Vec::new(),
325 auto_sys_proxy: true,
326 redirect_policy: redirect::Policy::default(),
327 retry_policy: crate::retry::Builder::default(),
328 referer: true,
329 read_timeout: None,
330 timeout: None,
331 #[cfg(feature = "__tls")]
332 root_certs: Vec::new(),
333 #[cfg(feature = "__tls")]
334 tls_certs_only: false,
335 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
336 identity: None,
337 #[cfg(feature = "__rustls")]
338 crls: vec![],
339 #[cfg(feature = "__tls")]
340 min_tls_version: None,
341 #[cfg(feature = "__tls")]
342 max_tls_version: None,
343 #[cfg(feature = "__tls")]
344 tls_info: false,
345 #[cfg(feature = "__tls")]
346 tls: TlsBackend::default(),
347 connector_layers: Vec::new(),
348 http_version_pref: HttpVersionPref::All,
349 http09_responses: false,
350 http1_title_case_headers: false,
351 http1_allow_obsolete_multiline_headers_in_responses: false,
352 http1_ignore_invalid_headers_in_responses: false,
353 http1_allow_spaces_after_header_name_in_responses: false,
354 #[cfg(feature = "http2")]
355 http2_initial_stream_window_size: None,
356 #[cfg(feature = "http2")]
357 http2_initial_connection_window_size: None,
358 #[cfg(feature = "http2")]
359 http2_adaptive_window: false,
360 #[cfg(feature = "http2")]
361 http2_max_frame_size: None,
362 #[cfg(feature = "http2")]
363 http2_max_header_list_size: None,
364 #[cfg(feature = "http2")]
365 http2_keep_alive_interval: None,
366 #[cfg(feature = "http2")]
367 http2_keep_alive_timeout: None,
368 #[cfg(feature = "http2")]
369 http2_keep_alive_while_idle: false,
370 #[cfg(feature = "http2")]
371 http2_header_table_size: None,
372 #[cfg(feature = "http2")]
373 http2_max_concurrent_streams: None,
374 #[cfg(feature = "http2")]
375 http2_enable_push: None,
376 #[cfg(feature = "http2")]
377 http2_no_rfc7540_priorities: None,
378 #[cfg(feature = "http2")]
379 http2_enable_connect_protocol: None,
380 #[cfg(feature = "http2")]
381 http2_settings_order: None,
382 #[cfg(feature = "http2")]
383 http2_headers_pseudo_order: None,
384 #[cfg(feature = "http2")]
385 http2_headers_priority: None,
386 http2_headers_order: None,
387 #[cfg(feature = "http2")]
388 http2_initial_stream_id: None,
389 local_address: None,
390 #[cfg(any(
391 target_os = "android",
392 target_os = "fuchsia",
393 target_os = "illumos",
394 target_os = "ios",
395 target_os = "linux",
396 target_os = "macos",
397 target_os = "solaris",
398 target_os = "tvos",
399 target_os = "visionos",
400 target_os = "watchos",
401 ))]
402 interface: None,
403 nodelay: true,
404 hickory_dns: cfg!(feature = "hickory-dns"),
405 #[cfg(feature = "cookies")]
406 cookie_store: None,
407 https_only: false,
408 dns_overrides: HashMap::new(),
409 #[cfg(feature = "http3")]
410 tls_enable_early_data: false,
411 #[cfg(feature = "http3")]
412 quic_max_idle_timeout: None,
413 #[cfg(feature = "http3")]
414 quic_stream_receive_window: None,
415 #[cfg(feature = "http3")]
416 quic_receive_window: None,
417 #[cfg(feature = "http3")]
418 quic_send_window: None,
419 #[cfg(feature = "http3")]
420 quic_congestion_bbr: false,
421 #[cfg(feature = "http3")]
422 h3_max_field_section_size: None,
423 #[cfg(feature = "http3")]
424 h3_send_grease: None,
425 dns_resolver: None,
426 #[cfg(unix)]
427 unix_socket: None,
428 #[cfg(target_os = "windows")]
429 windows_named_pipe: None,
430 },
431 }
432 }
433}
434
435impl ClientBuilder {
436 pub fn build(self) -> crate::Result<Client> {
443 let config = self.config;
444
445 if let Some(err) = config.error {
446 return Err(err);
447 }
448
449 let mut proxies = config.proxies;
450 if config.auto_sys_proxy {
451 proxies.push(ProxyMatcher::system());
452 }
453 let proxies = Arc::new(proxies);
454
455 #[allow(unused)]
456 #[cfg(feature = "http3")]
457 let mut h3_connector = None;
458
459 let resolver = {
460 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
461 false => Arc::new(GaiResolver::new()),
462 #[cfg(feature = "hickory-dns")]
463 true => Arc::new(HickoryDnsResolver::default()),
464 #[cfg(not(feature = "hickory-dns"))]
465 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
466 };
467 if let Some(dns_resolver) = config.dns_resolver {
468 resolver = dns_resolver;
469 }
470 if !config.dns_overrides.is_empty() {
471 resolver = Arc::new(DnsResolverWithOverrides::new(
472 resolver,
473 config.dns_overrides,
474 ));
475 }
476 DynResolver::new(Arc::new(crate::dns::cache::DnsCacheResolver::new(resolver)))
477 };
478
479 let mut connector_builder = {
480 #[cfg(feature = "__tls")]
481 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
482 headers.get(USER_AGENT).cloned()
483 }
484
485 let mut http = HttpConnector::new_with_resolver(resolver.clone());
486 http.set_connect_timeout(config.connect_timeout);
487
488 #[cfg(all(feature = "http3", feature = "__rustls"))]
489 let build_h3_connector =
490 |resolver,
491 tls,
492 quic_max_idle_timeout: Option<Duration>,
493 quic_stream_receive_window,
494 quic_receive_window,
495 quic_send_window,
496 quic_congestion_bbr,
497 h3_max_field_section_size,
498 h3_send_grease,
499 local_address,
500 http_version_pref: &HttpVersionPref| {
501 let mut transport_config = TransportConfig::default();
502
503 if let Some(max_idle_timeout) = quic_max_idle_timeout {
504 transport_config.max_idle_timeout(Some(
505 max_idle_timeout.try_into().map_err(error::builder)?,
506 ));
507 }
508
509 if let Some(stream_receive_window) = quic_stream_receive_window {
510 transport_config.stream_receive_window(stream_receive_window);
511 }
512
513 if let Some(receive_window) = quic_receive_window {
514 transport_config.receive_window(receive_window);
515 }
516
517 if let Some(send_window) = quic_send_window {
518 transport_config.send_window(send_window);
519 }
520
521 if quic_congestion_bbr {
522 let factory = Arc::new(quinn::congestion::BbrConfig::default());
523 transport_config.congestion_controller_factory(factory);
524 }
525
526 let mut h3_client_config = H3ClientConfig::default();
527
528 if let Some(max_field_section_size) = h3_max_field_section_size {
529 h3_client_config.max_field_section_size = Some(max_field_section_size);
530 }
531
532 if let Some(send_grease) = h3_send_grease {
533 h3_client_config.send_grease = Some(send_grease);
534 }
535
536 let res = H3Connector::new(
537 resolver,
538 tls,
539 local_address,
540 transport_config,
541 h3_client_config,
542 );
543
544 match res {
545 Ok(connector) => Ok(Some(connector)),
546 Err(err) => {
547 if let HttpVersionPref::Http3 = http_version_pref {
548 Err(error::builder(err))
549 } else {
550 Ok(None)
551 }
552 }
553 }
554 };
555
556 #[cfg(feature = "__tls")]
557 match config.tls {
558 #[cfg(feature = "__native-tls")]
559 TlsBackend::NativeTls => {
560 let mut tls = TlsConnector::builder();
561
562 #[cfg(all(feature = "__native-tls-alpn", not(feature = "http3")))]
563 {
564 match config.http_version_pref {
565 HttpVersionPref::Http1 => {
566 tls.request_alpns(&["http/1.1"]);
567 }
568 #[cfg(feature = "http2")]
569 HttpVersionPref::Http2 => {
570 tls.request_alpns(&["h2"]);
571 }
572 HttpVersionPref::All => {
573 tls.request_alpns(&[
574 #[cfg(feature = "http2")]
575 "h2",
576 "http/1.1",
577 ]);
578 }
579 }
580 }
581
582 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
583
584 tls.danger_accept_invalid_certs(!config.certs_verification);
585
586 tls.use_sni(config.tls_sni);
587
588 tls.disable_built_in_roots(config.tls_certs_only);
589
590 for cert in config.root_certs {
591 cert.add_to_native_tls(&mut tls);
592 }
593
594 #[cfg(feature = "__native-tls")]
595 {
596 if let Some(id) = config.identity {
597 id.add_to_native_tls(&mut tls)?;
598 }
599 }
600 #[cfg(all(feature = "__rustls", not(feature = "__native-tls")))]
601 {
602 if let Some(_id) = config.identity {
604 return Err(crate::error::builder("incompatible TLS identity type"));
605 }
606 }
607
608 if let Some(min_tls_version) = config.min_tls_version {
609 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
610 crate::error::builder("invalid minimum TLS version for backend")
614 })?;
615 tls.min_protocol_version(Some(protocol));
616 }
617
618 if let Some(max_tls_version) = config.max_tls_version {
619 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
620 crate::error::builder("invalid maximum TLS version for backend")
625 })?;
626 tls.max_protocol_version(Some(protocol));
627 }
628
629 ConnectorBuilder::new_native_tls(
630 http,
631 tls,
632 proxies.clone(),
633 user_agent(&config.headers),
634 config.local_address,
635 #[cfg(any(
636 target_os = "android",
637 target_os = "fuchsia",
638 target_os = "illumos",
639 target_os = "ios",
640 target_os = "linux",
641 target_os = "macos",
642 target_os = "solaris",
643 target_os = "tvos",
644 target_os = "visionos",
645 target_os = "watchos",
646 ))]
647 config.interface.as_deref(),
648 config.nodelay,
649 config.tls_info,
650 )?
651 }
652 #[cfg(feature = "__native-tls")]
653 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_native_tls(
654 http,
655 conn,
656 proxies.clone(),
657 user_agent(&config.headers),
658 config.local_address,
659 #[cfg(any(
660 target_os = "android",
661 target_os = "fuchsia",
662 target_os = "illumos",
663 target_os = "ios",
664 target_os = "linux",
665 target_os = "macos",
666 target_os = "solaris",
667 target_os = "tvos",
668 target_os = "visionos",
669 target_os = "watchos",
670 ))]
671 config.interface.as_deref(),
672 config.nodelay,
673 config.tls_info,
674 ),
675 #[cfg(feature = "__rustls")]
676 TlsBackend::BuiltRustls(conn) => {
677 #[cfg(feature = "http3")]
678 {
679 let mut h3_tls = conn.clone();
680 h3_tls.alpn_protocols = vec!["h3".into()];
681
682 h3_connector = build_h3_connector(
683 resolver.clone(),
684 h3_tls,
685 config.quic_max_idle_timeout,
686 config.quic_stream_receive_window,
687 config.quic_receive_window,
688 config.quic_send_window,
689 config.quic_congestion_bbr,
690 config.h3_max_field_section_size,
691 config.h3_send_grease,
692 config.local_address,
693 &config.http_version_pref,
694 )?;
695 }
696
697 ConnectorBuilder::new_rustls_tls(
698 http,
699 *conn,
700 proxies.clone(),
701 user_agent(&config.headers),
702 config.local_address,
703 #[cfg(any(
704 target_os = "android",
705 target_os = "fuchsia",
706 target_os = "illumos",
707 target_os = "ios",
708 target_os = "linux",
709 target_os = "macos",
710 target_os = "solaris",
711 target_os = "tvos",
712 target_os = "visionos",
713 target_os = "watchos",
714 ))]
715 config.interface.as_deref(),
716 config.nodelay,
717 config.tls_info,
718 )
719 }
720 #[cfg(feature = "__rustls")]
721 TlsBackend::Rustls => {
722 use crate::tls::{IgnoreHostname, NoVerifier};
723
724 let mut versions = rustls::ALL_VERSIONS.to_vec();
726
727 if let Some(min_tls_version) = config.min_tls_version {
728 versions.retain(|&supported_version| {
729 match tls::Version::from_rustls(supported_version.version) {
730 Some(version) => version >= min_tls_version,
731 None => true,
734 }
735 });
736 }
737
738 if let Some(max_tls_version) = config.max_tls_version {
739 versions.retain(|&supported_version| {
740 match tls::Version::from_rustls(supported_version.version) {
741 Some(version) => version <= max_tls_version,
742 None => false,
743 }
744 });
745 }
746
747 if versions.is_empty() {
748 return Err(crate::error::builder("empty supported tls versions"));
749 }
750
751 let provider = rustls::crypto::CryptoProvider::get_default()
754 .cloned()
755 .unwrap_or_else(default_rustls_crypto_provider);
756
757 let signature_algorithms = provider.signature_verification_algorithms;
759 let config_builder =
760 rustls::ClientConfig::builder_with_provider(provider.clone())
761 .with_protocol_versions(&versions)
762 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
763
764 let config_builder = if !config.certs_verification {
765 config_builder
766 .dangerous()
767 .with_custom_certificate_verifier(Arc::new(NoVerifier))
768 } else if !config.hostname_verification {
769 if !config.tls_certs_only {
770 return Err(crate::error::builder(
772 "disabling rustls hostname verification only allowed with tls_certs_only()"
773 ));
774 }
775
776 config_builder
777 .dangerous()
778 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
779 crate::tls::rustls_store(config.root_certs)?,
780 signature_algorithms,
781 )))
782 } else if !config.tls_certs_only {
783 if !config.crls.is_empty() {
785 return Err(crate::error::builder(
786 "CRLs only allowed with tls_certs_only()",
787 ));
788 }
789
790 let roots = if config.root_certs.is_empty() {
791 crate::tls::default_root_store().clone()
792 } else {
793 crate::tls::merged_root_store(config.root_certs)?
794 };
795
796 config_builder.with_root_certificates(roots)
797 } else if config.crls.is_empty() {
798 config_builder.with_root_certificates(crate::tls::rustls_store(
799 config.root_certs,
800 )?)
801 } else {
802 let crls = config
803 .crls
804 .iter()
805 .map(|e| e.as_rustls_crl())
806 .collect::<Vec<_>>();
807 let verifier =
808 rustls::client::WebPkiServerVerifier::builder_with_provider(
809 Arc::new(crate::tls::rustls_store(config.root_certs)?),
810 provider,
811 )
812 .with_crls(crls)
813 .build()
814 .map_err(|_| {
815 crate::error::builder("invalid TLS verification settings")
816 })?;
817 config_builder.with_webpki_verifier(verifier)
818 };
819
820 let mut tls = if let Some(id) = config.identity {
822 id.add_to_rustls(config_builder)?
823 } else {
824 config_builder.with_no_client_auth()
825 };
826
827 tls.enable_sni = config.tls_sni;
828
829 match config.http_version_pref {
831 HttpVersionPref::Http1 => {
832 tls.alpn_protocols = vec!["http/1.1".into()];
833 }
834 #[cfg(feature = "http2")]
835 HttpVersionPref::Http2 => {
836 tls.alpn_protocols = vec!["h2".into()];
837 }
838 #[cfg(feature = "http3")]
839 HttpVersionPref::Http3 => {
840 }
842 HttpVersionPref::All => {
843 tls.alpn_protocols = vec![
844 #[cfg(feature = "http2")]
845 "h2".into(),
846 "http/1.1".into(),
847 ];
848 }
849 }
850
851 #[cfg(feature = "http3")]
852 {
853 let mut h3_tls = tls.clone();
854 h3_tls.enable_early_data = config.tls_enable_early_data;
855
856 h3_tls.alpn_protocols = vec!["h3".into()];
858
859 h3_connector = build_h3_connector(
860 resolver.clone(),
861 h3_tls,
862 config.quic_max_idle_timeout,
863 config.quic_stream_receive_window,
864 config.quic_receive_window,
865 config.quic_send_window,
866 config.quic_congestion_bbr,
867 config.h3_max_field_section_size,
868 config.h3_send_grease,
869 config.local_address,
870 &config.http_version_pref,
871 )?;
872 }
873
874 ConnectorBuilder::new_rustls_tls(
875 http,
876 tls,
877 proxies.clone(),
878 user_agent(&config.headers),
879 config.local_address,
880 #[cfg(any(
881 target_os = "android",
882 target_os = "fuchsia",
883 target_os = "illumos",
884 target_os = "ios",
885 target_os = "linux",
886 target_os = "macos",
887 target_os = "solaris",
888 target_os = "tvos",
889 target_os = "visionos",
890 target_os = "watchos",
891 ))]
892 config.interface.as_deref(),
893 config.nodelay,
894 config.tls_info,
895 )
896 }
897 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
898 TlsBackend::UnknownPreconfigured => {
899 return Err(crate::error::builder(
900 "Unknown TLS backend passed to `use_preconfigured_tls`",
901 ));
902 }
903 }
904
905 #[cfg(not(feature = "__tls"))]
906 ConnectorBuilder::new(
907 http,
908 proxies.clone(),
909 config.local_address,
910 #[cfg(any(
911 target_os = "android",
912 target_os = "fuchsia",
913 target_os = "illumos",
914 target_os = "ios",
915 target_os = "linux",
916 target_os = "macos",
917 target_os = "solaris",
918 target_os = "tvos",
919 target_os = "visionos",
920 target_os = "watchos",
921 ))]
922 config.interface.as_deref(),
923 config.nodelay,
924 )
925 };
926
927 connector_builder.set_timeout(config.connect_timeout);
928 connector_builder.set_verbose(config.connection_verbose);
929 connector_builder.set_keepalive(config.tcp_keepalive);
930 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
931 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
932 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
933 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
934
935 #[cfg(feature = "socks")]
936 connector_builder.set_socks_resolver(resolver);
937
938 #[cfg(unix)]
942 connector_builder.set_unix_socket(config.unix_socket);
943 #[cfg(target_os = "windows")]
944 connector_builder.set_windows_named_pipe(config.windows_named_pipe.clone());
945
946 let mut builder =
947 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
948 #[cfg(feature = "http2")]
949 {
950 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
951 builder.http2_only(true);
952 }
953
954 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
955 {
956 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
957 }
958 if let Some(http2_initial_connection_window_size) =
959 config.http2_initial_connection_window_size
960 {
961 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
962 }
963 if config.http2_adaptive_window {
964 builder.http2_adaptive_window(true);
965 }
966 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
967 builder.http2_max_frame_size(http2_max_frame_size);
968 }
969 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
970 builder.http2_max_header_list_size(http2_max_header_list_size);
971 }
972 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
973 builder.http2_keep_alive_interval(http2_keep_alive_interval);
974 }
975 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
976 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
977 }
978 if config.http2_keep_alive_while_idle {
979 builder.http2_keep_alive_while_idle(true);
980 }
981 if let Some(http2_header_table_size) = config.http2_header_table_size {
982 builder.http2_header_table_size(http2_header_table_size);
983 }
984 if let Some(http2_max_concurrent_streams) = config.http2_max_concurrent_streams {
985 builder.http2_max_concurrent_streams(http2_max_concurrent_streams);
986 }
987 if let Some(http2_enable_push) = config.http2_enable_push {
988 builder.http2_enable_push(http2_enable_push);
989 }
990 if let Some(http2_no_rfc7540_priorities) = config.http2_no_rfc7540_priorities {
991 builder.http2_no_rfc7540_priorities(http2_no_rfc7540_priorities);
992 }
993 if let Some(http2_enable_connect_protocol) = config.http2_enable_connect_protocol {
994 builder.http2_enable_connect_protocol(http2_enable_connect_protocol);
995 }
996 if let Some(http2_settings_order) = config.http2_settings_order {
997 builder.http2_settings_order(http2_settings_order);
998 }
999 if let Some(http2_headers_pseudo_order) = config.http2_headers_pseudo_order {
1000 builder.http2_headers_pseudo_order(http2_headers_pseudo_order);
1001 }
1002 if let Some(data) = config.http2_headers_priority {
1003 builder.http2_headers_priority(Some(data));
1004 }
1005 if let Some(ref order) = config.http2_headers_order {
1006 builder.http2_headers_order(order.clone());
1007 }
1008 if let Some(stream_id) = config.http2_initial_stream_id {
1009 builder.http2_initial_stream_id(stream_id);
1010 }
1011 }
1012
1013 builder.timer(hyper_util::rt::TokioTimer::new());
1014 builder.pool_timer(hyper_util::rt::TokioTimer::new());
1015 builder.pool_idle_timeout(config.pool_idle_timeout);
1016 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
1017
1018 if config.http09_responses {
1019 builder.http09_responses(true);
1020 }
1021
1022 if config.http1_title_case_headers {
1023 builder.http1_title_case_headers(true);
1024 }
1025
1026 if config.http1_allow_obsolete_multiline_headers_in_responses {
1027 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
1028 }
1029
1030 if config.http1_ignore_invalid_headers_in_responses {
1031 builder.http1_ignore_invalid_headers_in_responses(true);
1032 }
1033
1034 if config.http1_allow_spaces_after_header_name_in_responses {
1035 builder.http1_allow_spaces_after_header_name_in_responses(true);
1036 }
1037
1038 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
1039 let proxies_maybe_http_custom_headers =
1040 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
1041
1042 let redirect_policy_desc = if config.redirect_policy.is_default() {
1043 None
1044 } else {
1045 Some(format!("{:?}", &config.redirect_policy))
1046 };
1047
1048 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
1049 let hyper_service = HyperService {
1050 hyper: hyper_client,
1051 };
1052
1053 let redirect_policy = {
1054 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
1055 p.with_referer(config.referer)
1056 .with_https_only(config.https_only);
1057 p
1058 };
1059
1060 let redirect_enabled = redirect_policy.redirect_enabled_ref();
1061
1062 let retry_policy = config.retry_policy.into_policy();
1063
1064 let svc = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
1065
1066 #[cfg(feature = "cookies")]
1067 let svc = CookieService::new(svc, config.cookie_store.clone());
1068 let hyper = FollowRedirect::with_policy(svc, redirect_policy.clone());
1069 #[cfg(any(
1070 feature = "gzip",
1071 feature = "brotli",
1072 feature = "zstd",
1073 feature = "deflate"
1074 ))]
1075 let hyper = Decompression::new(hyper)
1076 .no_gzip()
1079 .no_deflate()
1080 .no_br()
1081 .no_zstd();
1082 #[cfg(feature = "gzip")]
1083 let hyper = hyper.gzip(config.accepts.gzip);
1084 #[cfg(feature = "brotli")]
1085 let hyper = hyper.br(config.accepts.brotli);
1086 #[cfg(feature = "zstd")]
1087 let hyper = hyper.zstd(config.accepts.zstd);
1088 #[cfg(feature = "deflate")]
1089 let hyper = hyper.deflate(config.accepts.deflate);
1090
1091 Ok(Client {
1092 inner: Arc::new(ClientRef {
1093 accepts: config.accepts,
1094 #[cfg(feature = "cookies")]
1095 cookie_store: config.cookie_store.clone(),
1096 #[cfg(feature = "http3")]
1099 h3_client: match h3_connector {
1100 Some(h3_connector) => {
1101 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1102 let svc = tower::retry::Retry::new(retry_policy, h3_service);
1103 #[cfg(feature = "cookies")]
1104 let svc = CookieService::new(svc, config.cookie_store);
1105 let svc = FollowRedirect::with_policy(svc, redirect_policy);
1106 #[cfg(any(
1107 feature = "gzip",
1108 feature = "brotli",
1109 feature = "zstd",
1110 feature = "deflate"
1111 ))]
1112 let svc = Decompression::new(svc)
1113 .no_gzip()
1116 .no_deflate()
1117 .no_br()
1118 .no_zstd();
1119 #[cfg(feature = "gzip")]
1120 let svc = svc.gzip(config.accepts.gzip);
1121 #[cfg(feature = "brotli")]
1122 let svc = svc.br(config.accepts.brotli);
1123 #[cfg(feature = "zstd")]
1124 let svc = svc.zstd(config.accepts.zstd);
1125 #[cfg(feature = "deflate")]
1126 let svc = svc.deflate(config.accepts.deflate);
1127 Some(svc)
1128 }
1129 None => None,
1130 },
1131 headers: config.headers,
1132 referer: config.referer,
1133 read_timeout: RequestConfig::new(config.read_timeout),
1134 total_timeout: RequestConfig::new(config.timeout),
1135 hyper,
1136 proxies,
1137 proxies_maybe_http_auth,
1138 proxies_maybe_http_custom_headers,
1139 https_only: config.https_only,
1140 redirect_policy_desc,
1141 redirect_enabled,
1142 }),
1143 })
1144 }
1145
1146 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1169 where
1170 V: TryInto<HeaderValue>,
1171 V::Error: Into<http::Error>,
1172 {
1173 match value.try_into() {
1174 Ok(value) => {
1175 self.config.headers.insert(USER_AGENT, value);
1176 }
1177 Err(e) => {
1178 self.config.error = Some(crate::error::builder(e.into()));
1179 }
1180 };
1181 self
1182 }
1183 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1207 for (key, value) in headers.iter() {
1208 self.config.headers.insert(key, value.clone());
1209 }
1210 self
1211 }
1212
1213 #[cfg(feature = "cookies")]
1228 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1229 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1230 if enable {
1231 self.cookie_provider(Arc::new(cookie::Jar::default()))
1232 } else {
1233 self.config.cookie_store = None;
1234 self
1235 }
1236 }
1237
1238 #[cfg(feature = "cookies")]
1252 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1253 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1254 mut self,
1255 cookie_store: Arc<C>,
1256 ) -> ClientBuilder {
1257 self.config.cookie_store = Some(cookie_store as _);
1258 self
1259 }
1260
1261 #[cfg(feature = "gzip")]
1278 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1279 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1280 self.config.accepts.gzip = enable;
1281 self
1282 }
1283
1284 #[cfg(feature = "brotli")]
1301 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1302 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1303 self.config.accepts.brotli = enable;
1304 self
1305 }
1306
1307 #[cfg(feature = "zstd")]
1324 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1325 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1326 self.config.accepts.zstd = enable;
1327 self
1328 }
1329
1330 #[cfg(feature = "deflate")]
1347 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1348 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1349 self.config.accepts.deflate = enable;
1350 self
1351 }
1352
1353 pub fn no_gzip(self) -> ClientBuilder {
1359 #[cfg(feature = "gzip")]
1360 {
1361 self.gzip(false)
1362 }
1363
1364 #[cfg(not(feature = "gzip"))]
1365 {
1366 self
1367 }
1368 }
1369
1370 pub fn no_brotli(self) -> ClientBuilder {
1376 #[cfg(feature = "brotli")]
1377 {
1378 self.brotli(false)
1379 }
1380
1381 #[cfg(not(feature = "brotli"))]
1382 {
1383 self
1384 }
1385 }
1386
1387 pub fn no_zstd(self) -> ClientBuilder {
1393 #[cfg(feature = "zstd")]
1394 {
1395 self.zstd(false)
1396 }
1397
1398 #[cfg(not(feature = "zstd"))]
1399 {
1400 self
1401 }
1402 }
1403
1404 pub fn no_deflate(self) -> ClientBuilder {
1410 #[cfg(feature = "deflate")]
1411 {
1412 self.deflate(false)
1413 }
1414
1415 #[cfg(not(feature = "deflate"))]
1416 {
1417 self
1418 }
1419 }
1420
1421 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1427 self.config.redirect_policy = policy;
1428 self
1429 }
1430
1431 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1435 self.config.referer = enable;
1436 self
1437 }
1438
1439 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1446 self.config.retry_policy = policy;
1447 self
1448 }
1449
1450 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1458 self.config.proxies.push(proxy.into_matcher());
1459 self.config.auto_sys_proxy = false;
1460 self
1461 }
1462
1463 pub fn no_proxy(mut self) -> ClientBuilder {
1471 self.config.proxies.clear();
1472 self.config.auto_sys_proxy = false;
1473 self
1474 }
1475
1476 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1485 self.config.timeout = Some(timeout);
1486 self
1487 }
1488
1489 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1497 self.config.read_timeout = Some(timeout);
1498 self
1499 }
1500
1501 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1510 self.config.connect_timeout = Some(timeout);
1511 self
1512 }
1513
1514 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1521 self.config.connection_verbose = verbose;
1522 self
1523 }
1524
1525 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1533 where
1534 D: Into<Option<Duration>>,
1535 {
1536 self.config.pool_idle_timeout = val.into();
1537 self
1538 }
1539
1540 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1544 self.config.pool_max_idle_per_host = max;
1545 self
1546 }
1547
1548 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1550 self.config.http1_title_case_headers = true;
1551 self
1552 }
1553
1554 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1560 mut self,
1561 value: bool,
1562 ) -> ClientBuilder {
1563 self.config
1564 .http1_allow_obsolete_multiline_headers_in_responses = value;
1565 self
1566 }
1567
1568 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1570 self.config.http1_ignore_invalid_headers_in_responses = value;
1571 self
1572 }
1573
1574 pub fn http1_allow_spaces_after_header_name_in_responses(
1580 mut self,
1581 value: bool,
1582 ) -> ClientBuilder {
1583 self.config
1584 .http1_allow_spaces_after_header_name_in_responses = value;
1585 self
1586 }
1587
1588 pub fn http1_only(mut self) -> ClientBuilder {
1590 self.config.http_version_pref = HttpVersionPref::Http1;
1591 self
1592 }
1593
1594 pub fn http09_responses(mut self) -> ClientBuilder {
1596 self.config.http09_responses = true;
1597 self
1598 }
1599
1600 #[cfg(feature = "http2")]
1602 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1603 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1604 self.config.http_version_pref = HttpVersionPref::Http2;
1605 self
1606 }
1607
1608 #[cfg(feature = "http3")]
1610 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1611 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1612 self.config.http_version_pref = HttpVersionPref::Http3;
1613 self
1614 }
1615
1616 #[cfg(feature = "http2")]
1620 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1621 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1622 self.config.http2_initial_stream_window_size = sz.into();
1623 self
1624 }
1625
1626 #[cfg(feature = "http2")]
1630 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1631 pub fn http2_initial_connection_window_size(
1632 mut self,
1633 sz: impl Into<Option<u32>>,
1634 ) -> ClientBuilder {
1635 self.config.http2_initial_connection_window_size = sz.into();
1636 self
1637 }
1638
1639 #[cfg(feature = "http2")]
1644 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1645 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1646 self.config.http2_adaptive_window = enabled;
1647 self
1648 }
1649
1650 #[cfg(feature = "http2")]
1654 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1655 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1656 self.config.http2_max_frame_size = sz.into();
1657 self
1658 }
1659
1660 #[cfg(feature = "http2")]
1664 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1665 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1666 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1667 self
1668 }
1669
1670 #[cfg(feature = "http2")]
1675 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1676 pub fn http2_keep_alive_interval(
1677 mut self,
1678 interval: impl Into<Option<Duration>>,
1679 ) -> ClientBuilder {
1680 self.config.http2_keep_alive_interval = interval.into();
1681 self
1682 }
1683
1684 #[cfg(feature = "http2")]
1690 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1691 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1692 self.config.http2_keep_alive_timeout = Some(timeout);
1693 self
1694 }
1695
1696 #[cfg(feature = "http2")]
1703 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1704 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1705 self.config.http2_keep_alive_while_idle = enabled;
1706 self
1707 }
1708
1709 #[cfg(feature = "http2")]
1711 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1712 pub fn http2_header_table_size(mut self, size: impl Into<Option<u32>>) -> ClientBuilder {
1713 self.config.http2_header_table_size = size.into();
1714 self
1715 }
1716
1717 #[cfg(feature = "http2")]
1719 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1720 pub fn http2_max_concurrent_streams(mut self, max: impl Into<Option<u32>>) -> ClientBuilder {
1721 self.config.http2_max_concurrent_streams = max.into();
1722 self
1723 }
1724
1725 #[cfg(feature = "http2")]
1727 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1728 pub fn http2_enable_push(mut self, enabled: impl Into<Option<bool>>) -> ClientBuilder {
1729 self.config.http2_enable_push = enabled.into();
1730 self
1731 }
1732
1733 #[cfg(feature = "http2")]
1735 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1736 pub fn http2_no_rfc7540_priorities(mut self, enabled: impl Into<Option<bool>>) -> ClientBuilder {
1737 self.config.http2_no_rfc7540_priorities = enabled.into();
1738 self
1739 }
1740
1741 #[cfg(feature = "http2")]
1743 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1744 pub fn http2_enable_connect_protocol(mut self, val: impl Into<Option<u32>>) -> ClientBuilder {
1745 self.config.http2_enable_connect_protocol = val.into();
1746 self
1747 }
1748
1749 #[cfg(feature = "http2")]
1751 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1752 pub fn http2_settings_order(mut self, order: h2::frame::SettingsOrder) -> ClientBuilder {
1753 self.config.http2_settings_order = Some(order);
1754 self
1755 }
1756
1757 #[cfg(feature = "http2")]
1759 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1760 pub fn http2_headers_pseudo_order(mut self, order: h2::frame::PseudoOrder) -> ClientBuilder {
1761 self.config.http2_headers_pseudo_order = Some(order);
1762 self
1763 }
1764
1765 #[cfg(feature = "http2")]
1769 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1770 pub fn http2_headers_priority(mut self, data: Option<(u8, u32, bool)>) -> ClientBuilder {
1771 self.config.http2_headers_priority = data;
1772 self
1773 }
1774
1775 #[cfg(feature = "http2")]
1777 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1778 pub fn http2_headers_order(mut self, order: Vec<http::HeaderName>) -> ClientBuilder {
1779 self.config.http2_headers_order = Some(order);
1780 self
1781 }
1782
1783 #[cfg(feature = "http2")]
1787 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1788 pub fn http2_initial_stream_id(mut self, stream_id: u32) -> ClientBuilder {
1789 self.config.http2_initial_stream_id = Some(stream_id);
1790 self
1791 }
1792
1793 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1799 self.config.nodelay = enabled;
1800 self
1801 }
1802
1803 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1818 where
1819 T: Into<Option<IpAddr>>,
1820 {
1821 self.config.local_address = addr.into();
1822 self
1823 }
1824
1825 #[cfg(any(
1858 target_os = "android",
1859 target_os = "fuchsia",
1860 target_os = "illumos",
1861 target_os = "ios",
1862 target_os = "linux",
1863 target_os = "macos",
1864 target_os = "solaris",
1865 target_os = "tvos",
1866 target_os = "visionos",
1867 target_os = "watchos",
1868 ))]
1869 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1870 self.config.interface = Some(interface.to_string());
1871 self
1872 }
1873
1874 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1878 where
1879 D: Into<Option<Duration>>,
1880 {
1881 self.config.tcp_keepalive = val.into();
1882 self
1883 }
1884
1885 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1889 where
1890 D: Into<Option<Duration>>,
1891 {
1892 self.config.tcp_keepalive_interval = val.into();
1893 self
1894 }
1895
1896 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1900 where
1901 C: Into<Option<u32>>,
1902 {
1903 self.config.tcp_keepalive_retries = retries.into();
1904 self
1905 }
1906
1907 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1914 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1915 where
1916 D: Into<Option<Duration>>,
1917 {
1918 self.config.tcp_user_timeout = val.into();
1919 self
1920 }
1921
1922 #[cfg(unix)]
1936 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1937 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1938 self
1939 }
1940
1941 #[cfg(target_os = "windows")]
1953 pub fn windows_named_pipe(mut self, pipe: impl WindowsNamedPipeProvider) -> ClientBuilder {
1954 self.config.windows_named_pipe = Some(
1955 pipe.reqwest_windows_named_pipe_path(crate::connect::windows_named_pipe::Internal)
1956 .into(),
1957 );
1958 self
1959 }
1960
1961 #[cfg(feature = "__tls")]
1980 #[cfg_attr(
1981 docsrs,
1982 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
1983 )]
1984 pub fn tls_certs_merge(
1985 mut self,
1986 certs: impl IntoIterator<Item = Certificate>,
1987 ) -> ClientBuilder {
1988 self.config.root_certs.extend(certs);
1989 self
1990 }
1991
1992 #[cfg(feature = "__tls")]
2005 #[cfg_attr(
2006 docsrs,
2007 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2008 )]
2009 pub fn tls_certs_only(mut self, certs: impl IntoIterator<Item = Certificate>) -> ClientBuilder {
2010 self.config.root_certs.extend(certs);
2011 self.config.tls_certs_only = true;
2012 self
2013 }
2014
2015 #[cfg(feature = "__tls")]
2018 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
2019 self.config.root_certs.push(cert);
2020 self
2021 }
2022
2023 #[cfg(feature = "__rustls")]
2037 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2038 pub fn tls_crls_only(
2039 mut self,
2040 crls: impl IntoIterator<Item = CertificateRevocationList>,
2041 ) -> ClientBuilder {
2042 self.config.crls.extend(crls);
2043 self
2044 }
2045
2046 #[cfg(feature = "__rustls")]
2048 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2049 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
2050 self.config.crls.push(crl);
2051 self
2052 }
2053
2054 #[cfg(feature = "__rustls")]
2056 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2057 pub fn add_crls(
2058 mut self,
2059 crls: impl IntoIterator<Item = CertificateRevocationList>,
2060 ) -> ClientBuilder {
2061 self.config.crls.extend(crls);
2062 self
2063 }
2064
2065 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
2072 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
2073 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
2074 self.config.identity = Some(identity);
2075 self
2076 }
2077
2078 #[cfg(feature = "__tls")]
2099 #[cfg_attr(
2100 docsrs,
2101 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2102 )]
2103 pub fn tls_danger_accept_invalid_hostnames(
2104 mut self,
2105 accept_invalid_hostname: bool,
2106 ) -> ClientBuilder {
2107 self.config.hostname_verification = !accept_invalid_hostname;
2108 self
2109 }
2110
2111 #[cfg(feature = "__tls")]
2113 pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder {
2114 self.tls_danger_accept_invalid_hostnames(accept_invalid_hostname)
2115 }
2116
2117 #[cfg(feature = "__tls")]
2134 #[cfg_attr(
2135 docsrs,
2136 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2137 )]
2138 pub fn tls_danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
2139 self.config.certs_verification = !accept_invalid_certs;
2140 self
2141 }
2142
2143 #[cfg(feature = "__tls")]
2145 pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder {
2146 self.tls_danger_accept_invalid_certs(accept_invalid_certs)
2147 }
2148
2149 #[cfg(feature = "__tls")]
2158 #[cfg_attr(
2159 docsrs,
2160 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2161 )]
2162 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
2163 self.config.tls_sni = tls_sni;
2164 self
2165 }
2166
2167 #[cfg(feature = "__tls")]
2183 #[cfg_attr(
2184 docsrs,
2185 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2186 )]
2187 pub fn tls_version_min(mut self, version: tls::Version) -> ClientBuilder {
2188 self.config.min_tls_version = Some(version);
2189 self
2190 }
2191
2192 #[cfg(feature = "__tls")]
2194 pub fn min_tls_version(self, version: tls::Version) -> ClientBuilder {
2195 self.tls_version_min(version)
2196 }
2197
2198 #[cfg(feature = "__tls")]
2217 #[cfg_attr(
2218 docsrs,
2219 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2220 )]
2221 pub fn tls_version_max(mut self, version: tls::Version) -> ClientBuilder {
2222 self.config.max_tls_version = Some(version);
2223 self
2224 }
2225
2226 #[cfg(feature = "__tls")]
2228 pub fn max_tls_version(self, version: tls::Version) -> ClientBuilder {
2229 self.tls_version_max(version)
2230 }
2231
2232 #[cfg(feature = "__native-tls")]
2241 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2242 pub fn tls_backend_native(mut self) -> ClientBuilder {
2243 self.config.tls = TlsBackend::NativeTls;
2244 self
2245 }
2246
2247 #[cfg(feature = "__native-tls")]
2249 pub fn use_native_tls(self) -> ClientBuilder {
2250 self.tls_backend_native()
2251 }
2252
2253 #[cfg(feature = "__rustls")]
2262 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2263 pub fn tls_backend_rustls(mut self) -> ClientBuilder {
2264 self.config.tls = TlsBackend::Rustls;
2265 self
2266 }
2267
2268 #[cfg(feature = "__rustls")]
2270 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2271 pub fn use_rustls_tls(self) -> ClientBuilder {
2272 self.tls_backend_rustls()
2273 }
2274
2275 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2301 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
2302 pub fn tls_backend_preconfigured(mut self, tls: impl Any) -> ClientBuilder {
2303 let mut tls = Some(tls);
2304 #[cfg(feature = "__native-tls")]
2305 {
2306 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2307 let tls = conn.take().expect("is definitely Some");
2308 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2309 self.config.tls = tls;
2310 return self;
2311 }
2312 }
2313 #[cfg(feature = "__rustls")]
2314 {
2315 if let Some(conn) =
2316 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2317 {
2318 let tls = conn.take().expect("is definitely Some");
2319 let tls = crate::tls::TlsBackend::BuiltRustls(Box::new(tls));
2320 self.config.tls = tls;
2321 return self;
2322 }
2323 }
2324
2325 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2327 self
2328 }
2329
2330 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2332 pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder {
2333 self.tls_backend_preconfigured(tls)
2334 }
2335
2336 #[cfg(feature = "__tls")]
2343 #[cfg_attr(
2344 docsrs,
2345 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2346 )]
2347 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2348 self.config.tls_info = tls_info;
2349 self
2350 }
2351
2352 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2356 self.config.https_only = enabled;
2357 self
2358 }
2359
2360 #[cfg(feature = "hickory-dns")]
2374 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2375 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2376 self.config.hickory_dns = enable;
2377 self
2378 }
2379
2380 pub fn no_hickory_dns(self) -> ClientBuilder {
2386 #[cfg(feature = "hickory-dns")]
2387 {
2388 self.hickory_dns(false)
2389 }
2390
2391 #[cfg(not(feature = "hickory-dns"))]
2392 {
2393 self
2394 }
2395 }
2396
2397 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2402 self.resolve_to_addrs(domain, &[addr])
2403 }
2404
2405 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2410 self.config
2411 .dns_overrides
2412 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2413 self
2414 }
2415
2416 pub fn dns_resolver<R>(mut self, resolver: R) -> ClientBuilder
2421 where
2422 R: crate::dns::resolve::IntoResolve,
2423 {
2424 self.config.dns_resolver = Some(resolver.into_resolve());
2425 self
2426 }
2427
2428 #[cfg(feature = "http3")]
2433 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2434 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2435 self.config.tls_enable_early_data = enabled;
2436 self
2437 }
2438
2439 #[cfg(feature = "http3")]
2445 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2446 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2447 self.config.quic_max_idle_timeout = Some(value);
2448 self
2449 }
2450
2451 #[cfg(feature = "http3")]
2462 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2463 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2464 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2465 self
2466 }
2467
2468 #[cfg(feature = "http3")]
2479 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2480 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2481 self.config.quic_receive_window = Some(value.try_into().unwrap());
2482 self
2483 }
2484
2485 #[cfg(feature = "http3")]
2491 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2492 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2493 self.config.quic_send_window = Some(value);
2494 self
2495 }
2496
2497 #[cfg(feature = "http3")]
2505 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2506 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2507 self.config.quic_congestion_bbr = true;
2508 self
2509 }
2510
2511 #[cfg(feature = "http3")]
2521 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2522 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2523 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2524 self
2525 }
2526
2527 #[cfg(feature = "http3")]
2539 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2540 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2541 self.config.h3_send_grease = Some(enabled);
2542 self
2543 }
2544
2545 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2569 where
2570 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2571 L::Service:
2572 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2573 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2574 {
2575 let layer = BoxCloneSyncServiceLayer::new(layer);
2576
2577 self.config.connector_layers.push(layer);
2578
2579 self
2580 }
2581}
2582
2583type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2584
2585impl Default for Client {
2586 fn default() -> Self {
2587 Self::new()
2588 }
2589}
2590
2591#[cfg(feature = "__rustls")]
2592fn default_rustls_crypto_provider() -> Arc<rustls::crypto::CryptoProvider> {
2593 #[cfg(not(feature = "__rustls-aws-lc-rs"))]
2594 panic!("No provider set");
2595
2596 #[cfg(feature = "__rustls-aws-lc-rs")]
2597 Arc::new(rustls::crypto::aws_lc_rs::default_provider())
2598}
2599
2600impl Client {
2601 pub fn new() -> Client {
2611 ClientBuilder::new().build().expect("Client::new()")
2612 }
2613
2614 pub fn builder() -> ClientBuilder {
2618 ClientBuilder::new()
2619 }
2620
2621 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2627 self.request(Method::GET, url)
2628 }
2629
2630 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2636 self.request(Method::POST, url)
2637 }
2638
2639 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2645 self.request(Method::PUT, url)
2646 }
2647
2648 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2654 self.request(Method::PATCH, url)
2655 }
2656
2657 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2663 self.request(Method::DELETE, url)
2664 }
2665
2666 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2672 self.request(Method::HEAD, url)
2673 }
2674
2675 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2684 let req = url.into_url().map(move |url| Request::new(method, url));
2685 RequestBuilder::new(self.clone(), req)
2686 }
2687
2688 pub fn execute(
2701 &self,
2702 request: Request,
2703 ) -> impl Future<Output = Result<Response, crate::Error>> {
2704 self.execute_request(request)
2705 }
2706
2707 pub fn headers(&self) -> &HeaderMap {
2709 &self.inner.headers
2710 }
2711
2712 pub fn headers_mut(&mut self) -> &mut HeaderMap {
2714 &mut Arc::make_mut(&mut self.inner).headers
2715 }
2716
2717 #[cfg(feature = "cookies")]
2719 pub fn get_cookies(&self, url: &Url) -> Option<HeaderValue> {
2720 self.inner.cookie_store.as_ref().and_then(|store| {
2721 store.cookies(url)
2722 })
2723 }
2724
2725 #[cfg(feature = "cookies")]
2727 pub fn set_cookies(&self, url: &Url, cookies: Vec<HeaderValue>) {
2728 if let Some(store) = self.inner.cookie_store.as_ref() {
2729 let mut iter = cookies.iter();
2730 store.set_cookies(&mut iter, url);
2731 }
2732 }
2733
2734 pub fn set_proxies(&mut self, proxies: Vec<Proxy>) {
2736 let proxy_matchers: Vec<ProxyMatcher> = proxies
2737 .into_iter()
2738 .map(|p| p.into_matcher())
2739 .collect();
2740 Arc::make_mut(&mut self.inner).proxies = Arc::new(proxy_matchers);
2741 }
2742
2743 pub fn set_redirect_policy(&mut self, policy: redirect::Policy) {
2748 let enabled = !matches!(policy.inner, crate::redirect::PolicyKind::None);
2749 self.inner
2750 .redirect_enabled
2751 .store(enabled, std::sync::atomic::Ordering::Relaxed);
2752 let desc = if policy.is_default() {
2753 None
2754 } else {
2755 Some(format!("{:?}", &policy))
2756 };
2757 Arc::make_mut(&mut self.inner).redirect_policy_desc = desc;
2758 }
2759
2760 pub(super) fn execute_request(&self, req: Request) -> Pending {
2761 let (method, url, mut headers, body, version, extensions) = req.pieces();
2762 if url.scheme() != "http" && url.scheme() != "https" {
2763 return Pending::new_err(error::url_bad_scheme(url));
2764 }
2765
2766 if self.inner.https_only && url.scheme() != "https" {
2768 return Pending::new_err(error::url_bad_scheme(url));
2769 }
2770
2771 for (key, value) in &self.inner.headers {
2774 if let Entry::Vacant(entry) = headers.entry(key) {
2775 entry.insert(value.clone());
2776 }
2777 }
2778
2779 let uri = match try_uri(&url) {
2780 Ok(uri) => uri,
2781 _ => return Pending::new_err(error::url_invalid_uri(url)),
2782 };
2783
2784 let body = body.unwrap_or_else(Body::empty);
2785
2786 self.proxy_auth(&uri, &mut headers);
2787 self.proxy_custom_headers(&uri, &mut headers);
2788
2789 let builder = hyper::Request::builder()
2790 .method(method.clone())
2791 .uri(uri)
2792 .version(version);
2793
2794 let in_flight = match version {
2795 #[cfg(feature = "http3")]
2796 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2797 let mut req = builder.body(body).expect("valid request parts");
2798 *req.headers_mut() = headers.clone();
2799 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2800 ResponseFuture::H3(h3.call(req))
2801 }
2802 _ => {
2803 let mut req = builder.body(body).expect("valid request parts");
2804 *req.headers_mut() = headers.clone();
2805 let mut hyper = self.inner.hyper.clone();
2806 ResponseFuture::Default(hyper.call(req))
2807 }
2808 };
2809
2810 let total_timeout = self
2811 .inner
2812 .total_timeout
2813 .fetch(&extensions)
2814 .copied()
2815 .map(tokio::time::sleep)
2816 .map(Box::pin);
2817
2818 let read_timeout_duration = self
2819 .inner
2820 .read_timeout
2821 .fetch(&extensions)
2822 .copied();
2823 let read_timeout_fut = read_timeout_duration
2824 .map(tokio::time::sleep)
2825 .map(Box::pin);
2826
2827 Pending {
2828 inner: PendingInner::Request(Box::pin(PendingRequest {
2829 method,
2830 url,
2831 headers,
2832
2833 client: self.inner.clone(),
2834
2835 in_flight,
2836 total_timeout,
2837 read_timeout_fut,
2838 read_timeout: read_timeout_duration,
2839 })),
2840 }
2841 }
2842
2843 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2844 if !self.inner.proxies_maybe_http_auth {
2845 return;
2846 }
2847
2848 if dst.scheme() != Some(&Scheme::HTTP) {
2852 return;
2853 }
2854
2855 if headers.contains_key(PROXY_AUTHORIZATION) {
2856 return;
2857 }
2858
2859 for proxy in self.inner.proxies.iter() {
2860 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2861 headers.insert(PROXY_AUTHORIZATION, header);
2862 break;
2863 }
2864 }
2865 }
2866
2867 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2868 if !self.inner.proxies_maybe_http_custom_headers {
2869 return;
2870 }
2871
2872 if dst.scheme() != Some(&Scheme::HTTP) {
2873 return;
2874 }
2875
2876 for proxy in self.inner.proxies.iter() {
2877 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2878 iter.iter().for_each(|(key, value)| {
2879 headers.insert(key, value.clone());
2880 });
2881 break;
2882 }
2883 }
2884 }
2885}
2886
2887impl fmt::Debug for Client {
2888 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2889 let mut builder = f.debug_struct("Client");
2890 self.inner.fmt_fields(&mut builder);
2891 builder.finish()
2892 }
2893}
2894
2895impl tower_service::Service<Request> for Client {
2896 type Response = Response;
2897 type Error = crate::Error;
2898 type Future = Pending;
2899
2900 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2901 Poll::Ready(Ok(()))
2902 }
2903
2904 fn call(&mut self, req: Request) -> Self::Future {
2905 self.execute_request(req)
2906 }
2907}
2908
2909impl tower_service::Service<Request> for &'_ Client {
2910 type Response = Response;
2911 type Error = crate::Error;
2912 type Future = Pending;
2913
2914 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2915 Poll::Ready(Ok(()))
2916 }
2917
2918 fn call(&mut self, req: Request) -> Self::Future {
2919 self.execute_request(req)
2920 }
2921}
2922
2923impl fmt::Debug for ClientBuilder {
2924 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2925 let mut builder = f.debug_struct("ClientBuilder");
2926 self.config.fmt_fields(&mut builder);
2927 builder.finish()
2928 }
2929}
2930
2931impl Config {
2932 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2933 #[cfg(feature = "cookies")]
2937 {
2938 if self.cookie_store.is_some() {
2939 f.field("cookie_store", &true);
2940 }
2941 }
2942
2943 f.field("accepts", &self.accepts);
2944
2945 if !self.proxies.is_empty() {
2946 f.field("proxies", &self.proxies);
2947 }
2948
2949 if !self.redirect_policy.is_default() {
2950 f.field("redirect_policy", &self.redirect_policy);
2951 }
2952
2953 if self.referer {
2954 f.field("referer", &true);
2955 }
2956
2957 f.field("default_headers", &self.headers);
2958
2959 if self.http1_title_case_headers {
2960 f.field("http1_title_case_headers", &true);
2961 }
2962
2963 if self.http1_allow_obsolete_multiline_headers_in_responses {
2964 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2965 }
2966
2967 if self.http1_ignore_invalid_headers_in_responses {
2968 f.field("http1_ignore_invalid_headers_in_responses", &true);
2969 }
2970
2971 if self.http1_allow_spaces_after_header_name_in_responses {
2972 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2973 }
2974
2975 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2976 f.field("http1_only", &true);
2977 }
2978
2979 #[cfg(feature = "http2")]
2980 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2981 f.field("http2_prior_knowledge", &true);
2982 }
2983
2984 if let Some(ref d) = self.connect_timeout {
2985 f.field("connect_timeout", d);
2986 }
2987
2988 if let Some(ref d) = self.timeout {
2989 f.field("timeout", d);
2990 }
2991
2992 if let Some(ref v) = self.local_address {
2993 f.field("local_address", v);
2994 }
2995
2996 #[cfg(any(
2997 target_os = "android",
2998 target_os = "fuchsia",
2999 target_os = "illumos",
3000 target_os = "ios",
3001 target_os = "linux",
3002 target_os = "macos",
3003 target_os = "solaris",
3004 target_os = "tvos",
3005 target_os = "visionos",
3006 target_os = "watchos",
3007 ))]
3008 if let Some(ref v) = self.interface {
3009 f.field("interface", v);
3010 }
3011
3012 if self.nodelay {
3013 f.field("tcp_nodelay", &true);
3014 }
3015
3016 #[cfg(feature = "__tls")]
3017 {
3018 if !self.hostname_verification {
3019 f.field("tls_danger_accept_invalid_hostnames", &true);
3020 }
3021 }
3022
3023 #[cfg(feature = "__tls")]
3024 {
3025 if !self.certs_verification {
3026 f.field("tls_danger_accept_invalid_certs", &true);
3027 }
3028
3029 if let Some(ref min_tls_version) = self.min_tls_version {
3030 f.field("tls_version_min", min_tls_version);
3031 }
3032
3033 if let Some(ref max_tls_version) = self.max_tls_version {
3034 f.field("tls_version_max", max_tls_version);
3035 }
3036
3037 f.field("tls_sni", &self.tls_sni);
3038
3039 f.field("tls_info", &self.tls_info);
3040 }
3041
3042 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
3043 {
3044 f.field("tls_backend", &self.tls);
3045 }
3046
3047 if !self.dns_overrides.is_empty() {
3048 f.field("dns_overrides", &self.dns_overrides);
3049 }
3050
3051 #[cfg(feature = "http3")]
3052 {
3053 if self.tls_enable_early_data {
3054 f.field("tls_enable_early_data", &true);
3055 }
3056 }
3057
3058 #[cfg(unix)]
3059 if let Some(ref p) = self.unix_socket {
3060 f.field("unix_socket", p);
3061 }
3062 }
3063}
3064
3065#[cfg(not(feature = "cookies"))]
3066type MaybeCookieService<T> = T;
3067
3068#[cfg(feature = "cookies")]
3069type MaybeCookieService<T> = CookieService<T>;
3070
3071#[cfg(not(any(
3072 feature = "gzip",
3073 feature = "brotli",
3074 feature = "zstd",
3075 feature = "deflate"
3076)))]
3077type MaybeDecompression<T> = T;
3078
3079#[cfg(any(
3080 feature = "gzip",
3081 feature = "brotli",
3082 feature = "zstd",
3083 feature = "deflate"
3084))]
3085type MaybeDecompression<T> = Decompression<T>;
3086
3087type LayeredService<T> = MaybeDecompression<
3088 FollowRedirect<
3089 MaybeCookieService<tower::retry::Retry<crate::retry::Policy, T>>,
3090 TowerRedirectPolicy,
3091 >,
3092>;
3093type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
3094
3095#[derive(Clone)]
3096struct ClientRef {
3097 accepts: Accepts,
3098 #[cfg(feature = "cookies")]
3099 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
3100 headers: HeaderMap,
3101 hyper: LayeredService<HyperService>,
3102 #[cfg(feature = "http3")]
3103 h3_client: Option<LayeredService<H3Client>>,
3104 referer: bool,
3105 total_timeout: RequestConfig<TotalTimeout>,
3106 read_timeout: RequestConfig<ReadTimeout>,
3107 proxies: Arc<Vec<ProxyMatcher>>,
3108 proxies_maybe_http_auth: bool,
3109 proxies_maybe_http_custom_headers: bool,
3110 https_only: bool,
3111 redirect_policy_desc: Option<String>,
3112 redirect_enabled: Arc<std::sync::atomic::AtomicBool>,
3113}
3114
3115impl ClientRef {
3116 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
3117 #[cfg(feature = "cookies")]
3121 {
3122 if self.cookie_store.is_some() {
3123 f.field("cookie_store", &true);
3124 }
3125 }
3126
3127 f.field("accepts", &self.accepts);
3128
3129 if !self.proxies.is_empty() {
3130 f.field("proxies", &self.proxies);
3131 }
3132
3133 if let Some(s) = &self.redirect_policy_desc {
3134 f.field("redirect_policy", s);
3135 }
3136
3137 if self.referer {
3138 f.field("referer", &true);
3139 }
3140
3141 f.field("default_headers", &self.headers);
3142
3143 self.total_timeout.fmt_as_field(f);
3144
3145 self.read_timeout.fmt_as_field(f);
3146 }
3147}
3148
3149pin_project! {
3150 pub struct Pending {
3151 #[pin]
3152 inner: PendingInner,
3153 }
3154}
3155
3156enum PendingInner {
3157 Request(Pin<Box<PendingRequest>>),
3158 Error(Option<crate::Error>),
3159}
3160
3161pin_project! {
3162 struct PendingRequest {
3163 method: Method,
3164 url: Url,
3165 headers: HeaderMap,
3166
3167 client: Arc<ClientRef>,
3168
3169 #[pin]
3170 in_flight: ResponseFuture,
3171 #[pin]
3172 total_timeout: Option<Pin<Box<Sleep>>>,
3173 #[pin]
3174 read_timeout_fut: Option<Pin<Box<Sleep>>>,
3175 read_timeout: Option<Duration>,
3176 }
3177}
3178
3179enum ResponseFuture {
3180 Default(LayeredFuture<HyperService>),
3181 #[cfg(feature = "http3")]
3182 H3(LayeredFuture<H3Client>),
3183}
3184
3185impl PendingRequest {
3186 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
3187 self.project().in_flight
3188 }
3189
3190 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3191 self.project().total_timeout
3192 }
3193
3194 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3195 self.project().read_timeout_fut
3196 }
3197}
3198
3199impl Pending {
3200 pub(super) fn new_err(err: crate::Error) -> Pending {
3201 Pending {
3202 inner: PendingInner::Error(Some(err)),
3203 }
3204 }
3205
3206 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
3207 self.project().inner
3208 }
3209}
3210
3211impl Future for Pending {
3212 type Output = Result<Response, crate::Error>;
3213
3214 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3215 let inner = self.inner();
3216 match inner.get_mut() {
3217 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
3218 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
3219 .take()
3220 .expect("Pending error polled more than once"))),
3221 }
3222 }
3223}
3224
3225impl Future for PendingRequest {
3226 type Output = Result<Response, crate::Error>;
3227
3228 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3229 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
3230 if let Poll::Ready(()) = delay.poll(cx) {
3231 return Poll::Ready(Err(
3232 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3233 ));
3234 }
3235 }
3236
3237 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
3238 if let Poll::Ready(()) = delay.poll(cx) {
3239 return Poll::Ready(Err(
3240 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3241 ));
3242 }
3243 }
3244
3245 let res = match self.as_mut().in_flight().get_mut() {
3246 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
3247 Err(e) => {
3248 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
3249 }
3250 Ok(res) => res.map(super::body::boxed),
3251 },
3252 #[cfg(feature = "http3")]
3253 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
3254 Err(e) => {
3255 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
3256 }
3257 Ok(res) => res.map(super::body::boxed),
3258 },
3259 };
3260
3261 if let Some(url) = &res
3262 .extensions()
3263 .get::<tower_http::follow_redirect::RequestUri>()
3264 {
3265 self.url = match Url::parse(&url.0.to_string()) {
3266 Ok(url) => url,
3267 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3268 }
3269 };
3270
3271 let res = Response::new(
3272 res,
3273 self.url.clone(),
3274 self.total_timeout.take(),
3275 self.read_timeout,
3276 );
3277 Poll::Ready(Ok(res))
3278 }
3279}
3280
3281impl fmt::Debug for Pending {
3282 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3283 match self.inner {
3284 PendingInner::Request(ref req) => f
3285 .debug_struct("Pending")
3286 .field("method", &req.method)
3287 .field("url", &req.url)
3288 .finish(),
3289 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3290 }
3291 }
3292}
3293
3294#[cfg(test)]
3295mod tests {
3296 #![cfg(not(feature = "rustls-no-provider"))]
3297
3298 #[tokio::test]
3299 async fn execute_request_rejects_invalid_urls() {
3300 let url_str = "hxxps://www.rust-lang.org/";
3301 let url = url::Url::parse(url_str).unwrap();
3302 let result = crate::get(url.clone()).await;
3303
3304 assert!(result.is_err());
3305 let err = result.err().unwrap();
3306 assert!(err.is_builder());
3307 assert_eq!(url_str, err.url().unwrap().as_str());
3308 }
3309
3310 #[tokio::test]
3312 async fn execute_request_rejects_invalid_hostname() {
3313 let url_str = "https://{{hostname}}/";
3314 let url = url::Url::parse(url_str).unwrap();
3315 let result = crate::get(url.clone()).await;
3316
3317 assert!(result.is_err());
3318 let err = result.err().unwrap();
3319 assert!(err.is_builder());
3320 assert_eq!(url_str, err.url().unwrap().as_str());
3321 }
3322
3323 #[test]
3324 fn test_future_size() {
3325 let s = std::mem::size_of::<super::Pending>();
3326 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3327 }
3328}