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 #[cfg(feature = "http2")]
246 http2_initial_stream_window_size_increment: Option<u32>,
247 local_address: Option<IpAddr>,
248 #[cfg(any(
249 target_os = "android",
250 target_os = "fuchsia",
251 target_os = "illumos",
252 target_os = "ios",
253 target_os = "linux",
254 target_os = "macos",
255 target_os = "solaris",
256 target_os = "tvos",
257 target_os = "visionos",
258 target_os = "watchos",
259 ))]
260 interface: Option<String>,
261 nodelay: bool,
262 #[cfg(feature = "cookies")]
263 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
264 hickory_dns: bool,
265 error: Option<crate::Error>,
266 https_only: bool,
267 #[cfg(feature = "http3")]
268 tls_enable_early_data: bool,
269 #[cfg(feature = "http3")]
270 quic_max_idle_timeout: Option<Duration>,
271 #[cfg(feature = "http3")]
272 quic_stream_receive_window: Option<VarInt>,
273 #[cfg(feature = "http3")]
274 quic_receive_window: Option<VarInt>,
275 #[cfg(feature = "http3")]
276 quic_send_window: Option<u64>,
277 #[cfg(feature = "http3")]
278 quic_congestion_bbr: bool,
279 #[cfg(feature = "http3")]
280 h3_max_field_section_size: Option<u64>,
281 #[cfg(feature = "http3")]
282 h3_send_grease: Option<bool>,
283 dns_overrides: HashMap<String, Vec<SocketAddr>>,
284 dns_resolver: Option<Arc<dyn Resolve>>,
285
286 #[cfg(unix)]
287 unix_socket: Option<Arc<std::path::Path>>,
288 #[cfg(target_os = "windows")]
289 windows_named_pipe: Option<Arc<std::ffi::OsStr>>,
290}
291
292impl Default for ClientBuilder {
293 fn default() -> Self {
294 Self::new()
295 }
296}
297
298impl ClientBuilder {
299 pub fn new() -> Self {
303 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
304 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
305
306 ClientBuilder {
307 config: Config {
308 error: None,
309 accepts: Accepts::default(),
310 headers,
311 #[cfg(feature = "__tls")]
312 hostname_verification: true,
313 #[cfg(feature = "__tls")]
314 certs_verification: true,
315 #[cfg(feature = "__tls")]
316 tls_sni: true,
317 connect_timeout: None,
318 connection_verbose: false,
319 pool_idle_timeout: Some(Duration::from_secs(90)),
320 pool_max_idle_per_host: usize::MAX,
321 tcp_keepalive: Some(Duration::from_secs(15)),
322 tcp_keepalive_interval: Some(Duration::from_secs(15)),
323 tcp_keepalive_retries: Some(3),
324 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
325 tcp_user_timeout: Some(Duration::from_secs(30)),
326 proxies: Vec::new(),
327 auto_sys_proxy: true,
328 redirect_policy: redirect::Policy::default(),
329 retry_policy: crate::retry::Builder::default(),
330 referer: true,
331 read_timeout: None,
332 timeout: None,
333 #[cfg(feature = "__tls")]
334 root_certs: Vec::new(),
335 #[cfg(feature = "__tls")]
336 tls_certs_only: false,
337 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
338 identity: None,
339 #[cfg(feature = "__rustls")]
340 crls: vec![],
341 #[cfg(feature = "__tls")]
342 min_tls_version: None,
343 #[cfg(feature = "__tls")]
344 max_tls_version: None,
345 #[cfg(feature = "__tls")]
346 tls_info: false,
347 #[cfg(feature = "__tls")]
348 tls: TlsBackend::default(),
349 connector_layers: Vec::new(),
350 http_version_pref: HttpVersionPref::All,
351 http09_responses: false,
352 http1_title_case_headers: false,
353 http1_allow_obsolete_multiline_headers_in_responses: false,
354 http1_ignore_invalid_headers_in_responses: false,
355 http1_allow_spaces_after_header_name_in_responses: false,
356 #[cfg(feature = "http2")]
357 http2_initial_stream_window_size: None,
358 #[cfg(feature = "http2")]
359 http2_initial_connection_window_size: None,
360 #[cfg(feature = "http2")]
361 http2_adaptive_window: false,
362 #[cfg(feature = "http2")]
363 http2_max_frame_size: None,
364 #[cfg(feature = "http2")]
365 http2_max_header_list_size: None,
366 #[cfg(feature = "http2")]
367 http2_keep_alive_interval: None,
368 #[cfg(feature = "http2")]
369 http2_keep_alive_timeout: None,
370 #[cfg(feature = "http2")]
371 http2_keep_alive_while_idle: false,
372 #[cfg(feature = "http2")]
373 http2_header_table_size: None,
374 #[cfg(feature = "http2")]
375 http2_max_concurrent_streams: None,
376 #[cfg(feature = "http2")]
377 http2_enable_push: None,
378 #[cfg(feature = "http2")]
379 http2_no_rfc7540_priorities: None,
380 #[cfg(feature = "http2")]
381 http2_enable_connect_protocol: None,
382 #[cfg(feature = "http2")]
383 http2_settings_order: None,
384 #[cfg(feature = "http2")]
385 http2_headers_pseudo_order: None,
386 #[cfg(feature = "http2")]
387 http2_headers_priority: None,
388 #[cfg(feature = "http2")]
389 http2_headers_order: None,
390 #[cfg(feature = "http2")]
391 http2_initial_stream_id: None,
392 #[cfg(feature = "http2")]
393 http2_initial_stream_window_size_increment: None,
394 local_address: None,
395 #[cfg(any(
396 target_os = "android",
397 target_os = "fuchsia",
398 target_os = "illumos",
399 target_os = "ios",
400 target_os = "linux",
401 target_os = "macos",
402 target_os = "solaris",
403 target_os = "tvos",
404 target_os = "visionos",
405 target_os = "watchos",
406 ))]
407 interface: None,
408 nodelay: true,
409 hickory_dns: cfg!(feature = "hickory-dns"),
410 #[cfg(feature = "cookies")]
411 cookie_store: None,
412 https_only: false,
413 dns_overrides: HashMap::new(),
414 #[cfg(feature = "http3")]
415 tls_enable_early_data: false,
416 #[cfg(feature = "http3")]
417 quic_max_idle_timeout: None,
418 #[cfg(feature = "http3")]
419 quic_stream_receive_window: None,
420 #[cfg(feature = "http3")]
421 quic_receive_window: None,
422 #[cfg(feature = "http3")]
423 quic_send_window: None,
424 #[cfg(feature = "http3")]
425 quic_congestion_bbr: false,
426 #[cfg(feature = "http3")]
427 h3_max_field_section_size: None,
428 #[cfg(feature = "http3")]
429 h3_send_grease: None,
430 dns_resolver: None,
431 #[cfg(unix)]
432 unix_socket: None,
433 #[cfg(target_os = "windows")]
434 windows_named_pipe: None,
435 },
436 }
437 }
438}
439
440impl ClientBuilder {
441 pub fn build(self) -> crate::Result<Client> {
448 let config = self.config;
449
450 if let Some(err) = config.error {
451 return Err(err);
452 }
453
454 let mut proxies = config.proxies;
455 if config.auto_sys_proxy {
456 proxies.push(ProxyMatcher::system());
457 }
458 let proxies = Arc::new(proxies);
459
460 #[allow(unused)]
461 #[cfg(feature = "http3")]
462 let mut h3_connector = None;
463
464 let resolver = {
465 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
466 false => Arc::new(GaiResolver::new()),
467 #[cfg(feature = "hickory-dns")]
468 true => Arc::new(HickoryDnsResolver::default()),
469 #[cfg(not(feature = "hickory-dns"))]
470 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
471 };
472 if let Some(dns_resolver) = config.dns_resolver {
473 resolver = dns_resolver;
474 }
475 if !config.dns_overrides.is_empty() {
476 resolver = Arc::new(DnsResolverWithOverrides::new(
477 resolver,
478 config.dns_overrides,
479 ));
480 }
481 DynResolver::new(Arc::new(crate::dns::cache::DnsCacheResolver::new(resolver)))
482 };
483
484 let mut connector_builder = {
485 #[cfg(feature = "__tls")]
486 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
487 headers.get(USER_AGENT).cloned()
488 }
489
490 let mut http = HttpConnector::new_with_resolver(resolver.clone());
491 http.set_connect_timeout(config.connect_timeout);
492
493 #[cfg(all(feature = "http3", feature = "__rustls"))]
494 let build_h3_connector =
495 |resolver,
496 tls,
497 quic_max_idle_timeout: Option<Duration>,
498 quic_stream_receive_window,
499 quic_receive_window,
500 quic_send_window,
501 quic_congestion_bbr,
502 h3_max_field_section_size,
503 h3_send_grease,
504 local_address,
505 http_version_pref: &HttpVersionPref| {
506 let mut transport_config = TransportConfig::default();
507
508 if let Some(max_idle_timeout) = quic_max_idle_timeout {
509 transport_config.max_idle_timeout(Some(
510 max_idle_timeout.try_into().map_err(error::builder)?,
511 ));
512 }
513
514 if let Some(stream_receive_window) = quic_stream_receive_window {
515 transport_config.stream_receive_window(stream_receive_window);
516 }
517
518 if let Some(receive_window) = quic_receive_window {
519 transport_config.receive_window(receive_window);
520 }
521
522 if let Some(send_window) = quic_send_window {
523 transport_config.send_window(send_window);
524 }
525
526 if quic_congestion_bbr {
527 let factory = Arc::new(quinn::congestion::BbrConfig::default());
528 transport_config.congestion_controller_factory(factory);
529 }
530
531 let mut h3_client_config = H3ClientConfig::default();
532
533 if let Some(max_field_section_size) = h3_max_field_section_size {
534 h3_client_config.max_field_section_size = Some(max_field_section_size);
535 }
536
537 if let Some(send_grease) = h3_send_grease {
538 h3_client_config.send_grease = Some(send_grease);
539 }
540
541 let res = H3Connector::new(
542 resolver,
543 tls,
544 local_address,
545 transport_config,
546 h3_client_config,
547 );
548
549 match res {
550 Ok(connector) => Ok(Some(connector)),
551 Err(err) => {
552 if let HttpVersionPref::Http3 = http_version_pref {
553 Err(error::builder(err))
554 } else {
555 Ok(None)
556 }
557 }
558 }
559 };
560
561 #[cfg(feature = "__tls")]
562 match config.tls {
563 #[cfg(feature = "__native-tls")]
564 TlsBackend::NativeTls => {
565 let mut tls = TlsConnector::builder();
566
567 #[cfg(all(feature = "__native-tls-alpn", not(feature = "http3")))]
568 {
569 match config.http_version_pref {
570 HttpVersionPref::Http1 => {
571 tls.request_alpns(&["http/1.1"]);
572 }
573 #[cfg(feature = "http2")]
574 HttpVersionPref::Http2 => {
575 tls.request_alpns(&["h2"]);
576 }
577 HttpVersionPref::All => {
578 tls.request_alpns(&[
579 #[cfg(feature = "http2")]
580 "h2",
581 "http/1.1",
582 ]);
583 }
584 }
585 }
586
587 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
588
589 tls.danger_accept_invalid_certs(!config.certs_verification);
590
591 tls.use_sni(config.tls_sni);
592
593 tls.disable_built_in_roots(config.tls_certs_only);
594
595 for cert in config.root_certs {
596 cert.add_to_native_tls(&mut tls);
597 }
598
599 #[cfg(feature = "__native-tls")]
600 {
601 if let Some(id) = config.identity {
602 id.add_to_native_tls(&mut tls)?;
603 }
604 }
605 #[cfg(all(feature = "__rustls", not(feature = "__native-tls")))]
606 {
607 if let Some(_id) = config.identity {
609 return Err(crate::error::builder("incompatible TLS identity type"));
610 }
611 }
612
613 if let Some(min_tls_version) = config.min_tls_version {
614 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
615 crate::error::builder("invalid minimum TLS version for backend")
619 })?;
620 tls.min_protocol_version(Some(protocol));
621 }
622
623 if let Some(max_tls_version) = config.max_tls_version {
624 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
625 crate::error::builder("invalid maximum TLS version for backend")
630 })?;
631 tls.max_protocol_version(Some(protocol));
632 }
633
634 ConnectorBuilder::new_native_tls(
635 http,
636 tls,
637 proxies.clone(),
638 user_agent(&config.headers),
639 config.local_address,
640 #[cfg(any(
641 target_os = "android",
642 target_os = "fuchsia",
643 target_os = "illumos",
644 target_os = "ios",
645 target_os = "linux",
646 target_os = "macos",
647 target_os = "solaris",
648 target_os = "tvos",
649 target_os = "visionos",
650 target_os = "watchos",
651 ))]
652 config.interface.as_deref(),
653 config.nodelay,
654 config.tls_info,
655 )?
656 }
657 #[cfg(feature = "__native-tls")]
658 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_native_tls(
659 http,
660 conn,
661 proxies.clone(),
662 user_agent(&config.headers),
663 config.local_address,
664 #[cfg(any(
665 target_os = "android",
666 target_os = "fuchsia",
667 target_os = "illumos",
668 target_os = "ios",
669 target_os = "linux",
670 target_os = "macos",
671 target_os = "solaris",
672 target_os = "tvos",
673 target_os = "visionos",
674 target_os = "watchos",
675 ))]
676 config.interface.as_deref(),
677 config.nodelay,
678 config.tls_info,
679 ),
680 #[cfg(feature = "__rustls")]
681 TlsBackend::BuiltRustls(conn) => {
682 #[cfg(feature = "http3")]
683 {
684 let mut h3_tls = (**conn).clone();
685 h3_tls.alpn_protocols = vec!["h3".into()];
686
687 h3_connector = build_h3_connector(
688 resolver.clone(),
689 h3_tls,
690 config.quic_max_idle_timeout,
691 config.quic_stream_receive_window,
692 config.quic_receive_window,
693 config.quic_send_window,
694 config.quic_congestion_bbr,
695 config.h3_max_field_section_size,
696 config.h3_send_grease,
697 config.local_address,
698 &config.http_version_pref,
699 )?;
700 }
701
702 ConnectorBuilder::new_rustls_tls(
703 http,
704 *conn,
705 proxies.clone(),
706 user_agent(&config.headers),
707 config.local_address,
708 #[cfg(any(
709 target_os = "android",
710 target_os = "fuchsia",
711 target_os = "illumos",
712 target_os = "ios",
713 target_os = "linux",
714 target_os = "macos",
715 target_os = "solaris",
716 target_os = "tvos",
717 target_os = "visionos",
718 target_os = "watchos",
719 ))]
720 config.interface.as_deref(),
721 config.nodelay,
722 config.tls_info,
723 )
724 }
725 #[cfg(feature = "__rustls")]
726 TlsBackend::Rustls => {
727 use crate::tls::{IgnoreHostname, NoVerifier};
728
729 let mut versions = rustls::ALL_VERSIONS.to_vec();
731
732 if let Some(min_tls_version) = config.min_tls_version {
733 versions.retain(|&supported_version| {
734 match tls::Version::from_rustls(supported_version.version) {
735 Some(version) => version >= min_tls_version,
736 None => true,
739 }
740 });
741 }
742
743 if let Some(max_tls_version) = config.max_tls_version {
744 versions.retain(|&supported_version| {
745 match tls::Version::from_rustls(supported_version.version) {
746 Some(version) => version <= max_tls_version,
747 None => false,
748 }
749 });
750 }
751
752 if versions.is_empty() {
753 return Err(crate::error::builder("empty supported tls versions"));
754 }
755
756 let provider = rustls::crypto::CryptoProvider::get_default()
759 .cloned()
760 .unwrap_or_else(default_rustls_crypto_provider);
761
762 let signature_algorithms = provider.signature_verification_algorithms;
764 let config_builder =
765 rustls::ClientConfig::builder_with_provider(provider.clone())
766 .with_protocol_versions(&versions)
767 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
768
769 let config_builder = if !config.certs_verification {
770 config_builder
771 .dangerous()
772 .with_custom_certificate_verifier(Arc::new(NoVerifier))
773 } else if !config.hostname_verification {
774 if !config.tls_certs_only {
775 return Err(crate::error::builder(
777 "disabling rustls hostname verification only allowed with tls_certs_only()"
778 ));
779 }
780
781 config_builder
782 .dangerous()
783 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
784 crate::tls::rustls_store(config.root_certs)?,
785 signature_algorithms,
786 )))
787 } else if !config.tls_certs_only {
788 if !config.crls.is_empty() {
790 return Err(crate::error::builder(
791 "CRLs only allowed with tls_certs_only()",
792 ));
793 }
794
795 let roots = if config.root_certs.is_empty() {
796 crate::tls::default_root_store().clone()
797 } else {
798 crate::tls::merged_root_store(config.root_certs)?
799 };
800
801 config_builder.with_root_certificates(roots)
802 } else if config.crls.is_empty() {
803 config_builder.with_root_certificates(crate::tls::rustls_store(
804 config.root_certs,
805 )?)
806 } else {
807 let crls = config
808 .crls
809 .iter()
810 .map(|e| e.as_rustls_crl())
811 .collect::<Vec<_>>();
812 let verifier =
813 rustls::client::WebPkiServerVerifier::builder_with_provider(
814 Arc::new(crate::tls::rustls_store(config.root_certs)?),
815 provider,
816 )
817 .with_crls(crls)
818 .build()
819 .map_err(|_| {
820 crate::error::builder("invalid TLS verification settings")
821 })?;
822 config_builder.with_webpki_verifier(verifier)
823 };
824
825 let mut tls = if let Some(id) = config.identity {
827 id.add_to_rustls(config_builder)?
828 } else {
829 config_builder.with_no_client_auth()
830 };
831
832 tls.enable_sni = config.tls_sni;
833
834 match config.http_version_pref {
836 HttpVersionPref::Http1 => {
837 tls.alpn_protocols = vec!["http/1.1".into()];
838 }
839 #[cfg(feature = "http2")]
840 HttpVersionPref::Http2 => {
841 tls.alpn_protocols = vec!["h2".into()];
842 }
843 #[cfg(feature = "http3")]
844 HttpVersionPref::Http3 => {
845 }
847 HttpVersionPref::All => {
848 tls.alpn_protocols = vec![
849 #[cfg(feature = "http2")]
850 "h2".into(),
851 "http/1.1".into(),
852 ];
853 }
854 }
855
856 #[cfg(feature = "http3")]
857 {
858 let mut h3_tls = tls.clone();
859 h3_tls.enable_early_data = config.tls_enable_early_data;
860
861 h3_tls.alpn_protocols = vec!["h3".into()];
863
864 h3_connector = build_h3_connector(
865 resolver.clone(),
866 h3_tls,
867 config.quic_max_idle_timeout,
868 config.quic_stream_receive_window,
869 config.quic_receive_window,
870 config.quic_send_window,
871 config.quic_congestion_bbr,
872 config.h3_max_field_section_size,
873 config.h3_send_grease,
874 config.local_address,
875 &config.http_version_pref,
876 )?;
877 }
878
879 ConnectorBuilder::new_rustls_tls(
880 http,
881 tls,
882 proxies.clone(),
883 user_agent(&config.headers),
884 config.local_address,
885 #[cfg(any(
886 target_os = "android",
887 target_os = "fuchsia",
888 target_os = "illumos",
889 target_os = "ios",
890 target_os = "linux",
891 target_os = "macos",
892 target_os = "solaris",
893 target_os = "tvos",
894 target_os = "visionos",
895 target_os = "watchos",
896 ))]
897 config.interface.as_deref(),
898 config.nodelay,
899 config.tls_info,
900 )
901 }
902 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
903 TlsBackend::UnknownPreconfigured => {
904 return Err(crate::error::builder(
905 "Unknown TLS backend passed to `use_preconfigured_tls`",
906 ));
907 }
908 }
909
910 #[cfg(not(feature = "__tls"))]
911 ConnectorBuilder::new(
912 http,
913 proxies.clone(),
914 config.local_address,
915 #[cfg(any(
916 target_os = "android",
917 target_os = "fuchsia",
918 target_os = "illumos",
919 target_os = "ios",
920 target_os = "linux",
921 target_os = "macos",
922 target_os = "solaris",
923 target_os = "tvos",
924 target_os = "visionos",
925 target_os = "watchos",
926 ))]
927 config.interface.as_deref(),
928 config.nodelay,
929 )
930 };
931
932 connector_builder.set_timeout(config.connect_timeout);
933 connector_builder.set_verbose(config.connection_verbose);
934 connector_builder.set_keepalive(config.tcp_keepalive);
935 connector_builder.set_keepalive_interval(config.tcp_keepalive_interval);
936 connector_builder.set_keepalive_retries(config.tcp_keepalive_retries);
937 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
938 connector_builder.set_tcp_user_timeout(config.tcp_user_timeout);
939
940 #[cfg(feature = "socks")]
941 connector_builder.set_socks_resolver(resolver);
942
943 #[cfg(unix)]
947 connector_builder.set_unix_socket(config.unix_socket);
948 #[cfg(target_os = "windows")]
949 connector_builder.set_windows_named_pipe(config.windows_named_pipe.clone());
950
951 let mut builder =
952 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
953 #[cfg(feature = "http2")]
954 {
955 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
956 builder.http2_only(true);
957 }
958
959 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
960 {
961 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
962 }
963 if let Some(http2_initial_connection_window_size) =
964 config.http2_initial_connection_window_size
965 {
966 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
967 }
968 if config.http2_adaptive_window {
969 builder.http2_adaptive_window(true);
970 }
971 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
972 builder.http2_max_frame_size(http2_max_frame_size);
973 }
974 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
975 builder.http2_max_header_list_size(http2_max_header_list_size);
976 }
977 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
978 builder.http2_keep_alive_interval(http2_keep_alive_interval);
979 }
980 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
981 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
982 }
983 if config.http2_keep_alive_while_idle {
984 builder.http2_keep_alive_while_idle(true);
985 }
986 if let Some(http2_header_table_size) = config.http2_header_table_size {
987 builder.http2_header_table_size(http2_header_table_size);
988 }
989 if let Some(http2_max_concurrent_streams) = config.http2_max_concurrent_streams {
990 builder.http2_max_concurrent_streams(http2_max_concurrent_streams);
991 }
992 if let Some(http2_enable_push) = config.http2_enable_push {
993 builder.http2_enable_push(http2_enable_push);
994 }
995 if let Some(http2_no_rfc7540_priorities) = config.http2_no_rfc7540_priorities {
996 builder.http2_no_rfc7540_priorities(http2_no_rfc7540_priorities);
997 }
998 if let Some(http2_enable_connect_protocol) = config.http2_enable_connect_protocol {
999 builder.http2_enable_connect_protocol(http2_enable_connect_protocol);
1000 }
1001 if let Some(http2_settings_order) = config.http2_settings_order {
1002 builder.http2_settings_order(http2_settings_order);
1003 }
1004 if let Some(http2_headers_pseudo_order) = config.http2_headers_pseudo_order {
1005 builder.http2_headers_pseudo_order(http2_headers_pseudo_order);
1006 }
1007 if let Some(data) = config.http2_headers_priority {
1008 builder.http2_headers_priority(Some(data));
1009 }
1010 if let Some(ref order) = config.http2_headers_order {
1011 builder.http2_headers_order(order.clone());
1012 }
1013 if let Some(stream_id) = config.http2_initial_stream_id {
1014 builder.http2_initial_stream_id(stream_id);
1015 }
1016 if let Some(incr) = config.http2_initial_stream_window_size_increment {
1017 builder.http2_initial_stream_window_size_increment(incr);
1018 }
1019 }
1020
1021 builder.timer(hyper_util::rt::TokioTimer::new());
1022 builder.pool_timer(hyper_util::rt::TokioTimer::new());
1023 builder.pool_idle_timeout(config.pool_idle_timeout);
1024 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
1025
1026 if config.http09_responses {
1027 builder.http09_responses(true);
1028 }
1029
1030 if config.http1_title_case_headers {
1031 builder.http1_title_case_headers(true);
1032 }
1033
1034 if config.http1_allow_obsolete_multiline_headers_in_responses {
1035 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
1036 }
1037
1038 if config.http1_ignore_invalid_headers_in_responses {
1039 builder.http1_ignore_invalid_headers_in_responses(true);
1040 }
1041
1042 if config.http1_allow_spaces_after_header_name_in_responses {
1043 builder.http1_allow_spaces_after_header_name_in_responses(true);
1044 }
1045
1046 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
1047 let proxies_maybe_http_custom_headers =
1048 proxies.iter().any(|p| p.maybe_has_http_custom_headers());
1049
1050 let redirect_policy_desc = if config.redirect_policy.is_default() {
1051 None
1052 } else {
1053 Some(format!("{:?}", &config.redirect_policy))
1054 };
1055
1056 let hyper_client = builder.build(connector_builder.build(config.connector_layers));
1057 let hyper_service = HyperService {
1058 hyper: hyper_client,
1059 };
1060
1061 let redirect_policy = {
1062 let mut p = TowerRedirectPolicy::new(config.redirect_policy);
1063 p.with_referer(config.referer)
1064 .with_https_only(config.https_only);
1065 p
1066 };
1067
1068 let redirect_enabled = redirect_policy.redirect_enabled_ref();
1069
1070 let retry_policy = config.retry_policy.into_policy();
1071
1072 let svc = tower::retry::Retry::new(retry_policy.clone(), hyper_service);
1073
1074 #[cfg(feature = "cookies")]
1075 let svc = CookieService::new(svc, config.cookie_store.clone());
1076 let hyper = FollowRedirect::with_policy(svc, redirect_policy.clone());
1077 #[cfg(any(
1078 feature = "gzip",
1079 feature = "brotli",
1080 feature = "zstd",
1081 feature = "deflate"
1082 ))]
1083 let hyper = Decompression::new(hyper)
1084 .no_gzip()
1087 .no_deflate()
1088 .no_br()
1089 .no_zstd();
1090 #[cfg(feature = "gzip")]
1091 let hyper = hyper.gzip(config.accepts.gzip);
1092 #[cfg(feature = "brotli")]
1093 let hyper = hyper.br(config.accepts.brotli);
1094 #[cfg(feature = "zstd")]
1095 let hyper = hyper.zstd(config.accepts.zstd);
1096 #[cfg(feature = "deflate")]
1097 let hyper = hyper.deflate(config.accepts.deflate);
1098
1099 Ok(Client {
1100 inner: Arc::new(ClientRef {
1101 accepts: config.accepts,
1102 #[cfg(feature = "cookies")]
1103 cookie_store: config.cookie_store.clone(),
1104 #[cfg(feature = "http3")]
1107 h3_client: match h3_connector {
1108 Some(h3_connector) => {
1109 let h3_service = H3Client::new(h3_connector, config.pool_idle_timeout);
1110 let svc = tower::retry::Retry::new(retry_policy, h3_service);
1111 #[cfg(feature = "cookies")]
1112 let svc = CookieService::new(svc, config.cookie_store);
1113 let svc = FollowRedirect::with_policy(svc, redirect_policy);
1114 #[cfg(any(
1115 feature = "gzip",
1116 feature = "brotli",
1117 feature = "zstd",
1118 feature = "deflate"
1119 ))]
1120 let svc = Decompression::new(svc)
1121 .no_gzip()
1124 .no_deflate()
1125 .no_br()
1126 .no_zstd();
1127 #[cfg(feature = "gzip")]
1128 let svc = svc.gzip(config.accepts.gzip);
1129 #[cfg(feature = "brotli")]
1130 let svc = svc.br(config.accepts.brotli);
1131 #[cfg(feature = "zstd")]
1132 let svc = svc.zstd(config.accepts.zstd);
1133 #[cfg(feature = "deflate")]
1134 let svc = svc.deflate(config.accepts.deflate);
1135 Some(svc)
1136 }
1137 None => None,
1138 },
1139 headers: config.headers,
1140 referer: config.referer,
1141 read_timeout: RequestConfig::new(config.read_timeout),
1142 total_timeout: RequestConfig::new(config.timeout),
1143 hyper,
1144 proxies,
1145 proxies_maybe_http_auth,
1146 proxies_maybe_http_custom_headers,
1147 https_only: config.https_only,
1148 redirect_policy_desc,
1149 redirect_enabled,
1150 }),
1151 })
1152 }
1153
1154 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
1177 where
1178 V: TryInto<HeaderValue>,
1179 V::Error: Into<http::Error>,
1180 {
1181 match value.try_into() {
1182 Ok(value) => {
1183 self.config.headers.insert(USER_AGENT, value);
1184 }
1185 Err(e) => {
1186 self.config.error = Some(crate::error::builder(e.into()));
1187 }
1188 };
1189 self
1190 }
1191 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1215 for (key, value) in headers.iter() {
1216 self.config.headers.insert(key, value.clone());
1217 }
1218 self
1219 }
1220
1221 #[cfg(feature = "cookies")]
1236 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1237 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1238 if enable {
1239 self.cookie_provider(Arc::new(cookie::Jar::default()))
1240 } else {
1241 self.config.cookie_store = None;
1242 self
1243 }
1244 }
1245
1246 #[cfg(feature = "cookies")]
1260 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1261 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1262 mut self,
1263 cookie_store: Arc<C>,
1264 ) -> ClientBuilder {
1265 self.config.cookie_store = Some(cookie_store as _);
1266 self
1267 }
1268
1269 #[cfg(feature = "gzip")]
1286 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1287 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1288 self.config.accepts.gzip = enable;
1289 self
1290 }
1291
1292 #[cfg(feature = "brotli")]
1309 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1310 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1311 self.config.accepts.brotli = enable;
1312 self
1313 }
1314
1315 #[cfg(feature = "zstd")]
1332 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1333 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1334 self.config.accepts.zstd = enable;
1335 self
1336 }
1337
1338 #[cfg(feature = "deflate")]
1355 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1356 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1357 self.config.accepts.deflate = enable;
1358 self
1359 }
1360
1361 pub fn no_gzip(self) -> ClientBuilder {
1367 #[cfg(feature = "gzip")]
1368 {
1369 self.gzip(false)
1370 }
1371
1372 #[cfg(not(feature = "gzip"))]
1373 {
1374 self
1375 }
1376 }
1377
1378 pub fn no_brotli(self) -> ClientBuilder {
1384 #[cfg(feature = "brotli")]
1385 {
1386 self.brotli(false)
1387 }
1388
1389 #[cfg(not(feature = "brotli"))]
1390 {
1391 self
1392 }
1393 }
1394
1395 pub fn no_zstd(self) -> ClientBuilder {
1401 #[cfg(feature = "zstd")]
1402 {
1403 self.zstd(false)
1404 }
1405
1406 #[cfg(not(feature = "zstd"))]
1407 {
1408 self
1409 }
1410 }
1411
1412 pub fn no_deflate(self) -> ClientBuilder {
1418 #[cfg(feature = "deflate")]
1419 {
1420 self.deflate(false)
1421 }
1422
1423 #[cfg(not(feature = "deflate"))]
1424 {
1425 self
1426 }
1427 }
1428
1429 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1435 self.config.redirect_policy = policy;
1436 self
1437 }
1438
1439 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1443 self.config.referer = enable;
1444 self
1445 }
1446
1447 pub fn retry(mut self, policy: crate::retry::Builder) -> ClientBuilder {
1454 self.config.retry_policy = policy;
1455 self
1456 }
1457
1458 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1466 self.config.proxies.push(proxy.into_matcher());
1467 self.config.auto_sys_proxy = false;
1468 self
1469 }
1470
1471 pub fn no_proxy(mut self) -> ClientBuilder {
1479 self.config.proxies.clear();
1480 self.config.auto_sys_proxy = false;
1481 self
1482 }
1483
1484 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1493 self.config.timeout = Some(timeout);
1494 self
1495 }
1496
1497 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1505 self.config.read_timeout = Some(timeout);
1506 self
1507 }
1508
1509 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1518 self.config.connect_timeout = Some(timeout);
1519 self
1520 }
1521
1522 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1529 self.config.connection_verbose = verbose;
1530 self
1531 }
1532
1533 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1541 where
1542 D: Into<Option<Duration>>,
1543 {
1544 self.config.pool_idle_timeout = val.into();
1545 self
1546 }
1547
1548 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1552 self.config.pool_max_idle_per_host = max;
1553 self
1554 }
1555
1556 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1558 self.config.http1_title_case_headers = true;
1559 self
1560 }
1561
1562 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1568 mut self,
1569 value: bool,
1570 ) -> ClientBuilder {
1571 self.config
1572 .http1_allow_obsolete_multiline_headers_in_responses = value;
1573 self
1574 }
1575
1576 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1578 self.config.http1_ignore_invalid_headers_in_responses = value;
1579 self
1580 }
1581
1582 pub fn http1_allow_spaces_after_header_name_in_responses(
1588 mut self,
1589 value: bool,
1590 ) -> ClientBuilder {
1591 self.config
1592 .http1_allow_spaces_after_header_name_in_responses = value;
1593 self
1594 }
1595
1596 pub fn http1_only(mut self) -> ClientBuilder {
1598 self.config.http_version_pref = HttpVersionPref::Http1;
1599 self
1600 }
1601
1602 pub fn http09_responses(mut self) -> ClientBuilder {
1604 self.config.http09_responses = true;
1605 self
1606 }
1607
1608 #[cfg(feature = "http2")]
1610 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1611 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1612 self.config.http_version_pref = HttpVersionPref::Http2;
1613 self
1614 }
1615
1616 #[cfg(feature = "http3")]
1618 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1619 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1620 self.config.http_version_pref = HttpVersionPref::Http3;
1621 self
1622 }
1623
1624 #[cfg(feature = "http2")]
1628 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1629 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1630 self.config.http2_initial_stream_window_size = sz.into();
1631 self
1632 }
1633
1634 #[cfg(feature = "http2")]
1638 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1639 pub fn http2_initial_connection_window_size(
1640 mut self,
1641 sz: impl Into<Option<u32>>,
1642 ) -> ClientBuilder {
1643 self.config.http2_initial_connection_window_size = sz.into();
1644 self
1645 }
1646
1647 #[cfg(feature = "http2")]
1652 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1653 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1654 self.config.http2_adaptive_window = enabled;
1655 self
1656 }
1657
1658 #[cfg(feature = "http2")]
1662 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1663 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1664 self.config.http2_max_frame_size = sz.into();
1665 self
1666 }
1667
1668 #[cfg(feature = "http2")]
1672 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1673 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1674 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1675 self
1676 }
1677
1678 #[cfg(feature = "http2")]
1683 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1684 pub fn http2_keep_alive_interval(
1685 mut self,
1686 interval: impl Into<Option<Duration>>,
1687 ) -> ClientBuilder {
1688 self.config.http2_keep_alive_interval = interval.into();
1689 self
1690 }
1691
1692 #[cfg(feature = "http2")]
1698 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1699 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1700 self.config.http2_keep_alive_timeout = Some(timeout);
1701 self
1702 }
1703
1704 #[cfg(feature = "http2")]
1711 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1712 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1713 self.config.http2_keep_alive_while_idle = enabled;
1714 self
1715 }
1716
1717 #[cfg(feature = "http2")]
1719 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1720 pub fn http2_header_table_size(mut self, size: impl Into<Option<u32>>) -> ClientBuilder {
1721 self.config.http2_header_table_size = size.into();
1722 self
1723 }
1724
1725 #[cfg(feature = "http2")]
1727 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1728 pub fn http2_max_concurrent_streams(mut self, max: impl Into<Option<u32>>) -> ClientBuilder {
1729 self.config.http2_max_concurrent_streams = max.into();
1730 self
1731 }
1732
1733 #[cfg(feature = "http2")]
1735 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1736 pub fn http2_enable_push(mut self, enabled: impl Into<Option<bool>>) -> ClientBuilder {
1737 self.config.http2_enable_push = enabled.into();
1738 self
1739 }
1740
1741 #[cfg(feature = "http2")]
1743 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1744 pub fn http2_no_rfc7540_priorities(mut self, enabled: impl Into<Option<bool>>) -> ClientBuilder {
1745 self.config.http2_no_rfc7540_priorities = enabled.into();
1746 self
1747 }
1748
1749 #[cfg(feature = "http2")]
1751 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1752 pub fn http2_enable_connect_protocol(mut self, val: impl Into<Option<u32>>) -> ClientBuilder {
1753 self.config.http2_enable_connect_protocol = val.into();
1754 self
1755 }
1756
1757 #[cfg(feature = "http2")]
1759 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1760 pub fn http2_settings_order(mut self, order: h2::frame::SettingsOrder) -> ClientBuilder {
1761 self.config.http2_settings_order = Some(order);
1762 self
1763 }
1764
1765 #[cfg(feature = "http2")]
1767 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1768 pub fn http2_headers_pseudo_order(mut self, order: h2::frame::PseudoOrder) -> ClientBuilder {
1769 self.config.http2_headers_pseudo_order = Some(order);
1770 self
1771 }
1772
1773 #[cfg(feature = "http2")]
1777 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1778 pub fn http2_headers_priority(mut self, data: Option<(u8, u32, bool)>) -> ClientBuilder {
1779 self.config.http2_headers_priority = data;
1780 self
1781 }
1782
1783 #[cfg(feature = "http2")]
1785 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1786 pub fn http2_headers_order(mut self, order: Vec<http::HeaderName>) -> ClientBuilder {
1787 self.config.http2_headers_order = Some(order);
1788 self
1789 }
1790
1791 #[cfg(feature = "http2")]
1795 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1796 pub fn http2_initial_stream_id(mut self, stream_id: u32) -> ClientBuilder {
1797 self.config.http2_initial_stream_id = Some(stream_id);
1798 self
1799 }
1800
1801 #[cfg(feature = "http2")]
1808 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1809 pub fn http2_initial_stream_window_size_increment(
1810 mut self,
1811 sz: impl Into<Option<u32>>,
1812 ) -> ClientBuilder {
1813 self.config.http2_initial_stream_window_size_increment = sz.into();
1814 self
1815 }
1816
1817 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1823 self.config.nodelay = enabled;
1824 self
1825 }
1826
1827 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1842 where
1843 T: Into<Option<IpAddr>>,
1844 {
1845 self.config.local_address = addr.into();
1846 self
1847 }
1848
1849 #[cfg(any(
1882 target_os = "android",
1883 target_os = "fuchsia",
1884 target_os = "illumos",
1885 target_os = "ios",
1886 target_os = "linux",
1887 target_os = "macos",
1888 target_os = "solaris",
1889 target_os = "tvos",
1890 target_os = "visionos",
1891 target_os = "watchos",
1892 ))]
1893 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1894 self.config.interface = Some(interface.to_string());
1895 self
1896 }
1897
1898 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1902 where
1903 D: Into<Option<Duration>>,
1904 {
1905 self.config.tcp_keepalive = val.into();
1906 self
1907 }
1908
1909 pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
1913 where
1914 D: Into<Option<Duration>>,
1915 {
1916 self.config.tcp_keepalive_interval = val.into();
1917 self
1918 }
1919
1920 pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
1924 where
1925 C: Into<Option<u32>>,
1926 {
1927 self.config.tcp_keepalive_retries = retries.into();
1928 self
1929 }
1930
1931 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1938 pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
1939 where
1940 D: Into<Option<Duration>>,
1941 {
1942 self.config.tcp_user_timeout = val.into();
1943 self
1944 }
1945
1946 #[cfg(unix)]
1960 pub fn unix_socket(mut self, path: impl UnixSocketProvider) -> ClientBuilder {
1961 self.config.unix_socket = Some(path.reqwest_uds_path(crate::connect::uds::Internal).into());
1962 self
1963 }
1964
1965 #[cfg(target_os = "windows")]
1977 pub fn windows_named_pipe(mut self, pipe: impl WindowsNamedPipeProvider) -> ClientBuilder {
1978 self.config.windows_named_pipe = Some(
1979 pipe.reqwest_windows_named_pipe_path(crate::connect::windows_named_pipe::Internal)
1980 .into(),
1981 );
1982 self
1983 }
1984
1985 #[cfg(feature = "__tls")]
2004 #[cfg_attr(
2005 docsrs,
2006 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2007 )]
2008 pub fn tls_certs_merge(
2009 mut self,
2010 certs: impl IntoIterator<Item = Certificate>,
2011 ) -> ClientBuilder {
2012 self.config.root_certs.extend(certs);
2013 self
2014 }
2015
2016 #[cfg(feature = "__tls")]
2029 #[cfg_attr(
2030 docsrs,
2031 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2032 )]
2033 pub fn tls_certs_only(mut self, certs: impl IntoIterator<Item = Certificate>) -> ClientBuilder {
2034 self.config.root_certs.extend(certs);
2035 self.config.tls_certs_only = true;
2036 self
2037 }
2038
2039 #[cfg(feature = "__tls")]
2042 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
2043 self.config.root_certs.push(cert);
2044 self
2045 }
2046
2047 #[cfg(feature = "__rustls")]
2061 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2062 pub fn tls_crls_only(
2063 mut self,
2064 crls: impl IntoIterator<Item = CertificateRevocationList>,
2065 ) -> ClientBuilder {
2066 self.config.crls.extend(crls);
2067 self
2068 }
2069
2070 #[cfg(feature = "__rustls")]
2072 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2073 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
2074 self.config.crls.push(crl);
2075 self
2076 }
2077
2078 #[cfg(feature = "__rustls")]
2080 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2081 pub fn add_crls(
2082 mut self,
2083 crls: impl IntoIterator<Item = CertificateRevocationList>,
2084 ) -> ClientBuilder {
2085 self.config.crls.extend(crls);
2086 self
2087 }
2088
2089 #[cfg(any(feature = "__native-tls", feature = "__rustls"))]
2096 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
2097 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
2098 self.config.identity = Some(identity);
2099 self
2100 }
2101
2102 #[cfg(feature = "__tls")]
2123 #[cfg_attr(
2124 docsrs,
2125 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2126 )]
2127 pub fn tls_danger_accept_invalid_hostnames(
2128 mut self,
2129 accept_invalid_hostname: bool,
2130 ) -> ClientBuilder {
2131 self.config.hostname_verification = !accept_invalid_hostname;
2132 self
2133 }
2134
2135 #[cfg(feature = "__tls")]
2137 pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder {
2138 self.tls_danger_accept_invalid_hostnames(accept_invalid_hostname)
2139 }
2140
2141 #[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_danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
2163 self.config.certs_verification = !accept_invalid_certs;
2164 self
2165 }
2166
2167 #[cfg(feature = "__tls")]
2169 pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder {
2170 self.tls_danger_accept_invalid_certs(accept_invalid_certs)
2171 }
2172
2173 #[cfg(feature = "__tls")]
2182 #[cfg_attr(
2183 docsrs,
2184 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2185 )]
2186 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
2187 self.config.tls_sni = tls_sni;
2188 self
2189 }
2190
2191 #[cfg(feature = "__tls")]
2207 #[cfg_attr(
2208 docsrs,
2209 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2210 )]
2211 pub fn tls_version_min(mut self, version: tls::Version) -> ClientBuilder {
2212 self.config.min_tls_version = Some(version);
2213 self
2214 }
2215
2216 #[cfg(feature = "__tls")]
2218 pub fn min_tls_version(self, version: tls::Version) -> ClientBuilder {
2219 self.tls_version_min(version)
2220 }
2221
2222 #[cfg(feature = "__tls")]
2241 #[cfg_attr(
2242 docsrs,
2243 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2244 )]
2245 pub fn tls_version_max(mut self, version: tls::Version) -> ClientBuilder {
2246 self.config.max_tls_version = Some(version);
2247 self
2248 }
2249
2250 #[cfg(feature = "__tls")]
2252 pub fn max_tls_version(self, version: tls::Version) -> ClientBuilder {
2253 self.tls_version_max(version)
2254 }
2255
2256 #[cfg(feature = "__native-tls")]
2265 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
2266 pub fn tls_backend_native(mut self) -> ClientBuilder {
2267 self.config.tls = TlsBackend::NativeTls;
2268 self
2269 }
2270
2271 #[cfg(feature = "__native-tls")]
2273 pub fn use_native_tls(self) -> ClientBuilder {
2274 self.tls_backend_native()
2275 }
2276
2277 #[cfg(feature = "__rustls")]
2286 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2287 pub fn tls_backend_rustls(mut self) -> ClientBuilder {
2288 self.config.tls = TlsBackend::Rustls;
2289 self
2290 }
2291
2292 #[cfg(feature = "__rustls")]
2294 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
2295 pub fn use_rustls_tls(self) -> ClientBuilder {
2296 self.tls_backend_rustls()
2297 }
2298
2299 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2325 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
2326 pub fn tls_backend_preconfigured(mut self, tls: impl Any) -> ClientBuilder {
2327 let mut tls = Some(tls);
2328 #[cfg(feature = "__native-tls")]
2329 {
2330 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
2331 let tls = conn.take().expect("is definitely Some");
2332 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
2333 self.config.tls = tls;
2334 return self;
2335 }
2336 }
2337 #[cfg(feature = "__rustls")]
2338 {
2339 if let Some(conn) =
2340 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
2341 {
2342 let tls = conn.take().expect("is definitely Some");
2343 let tls = crate::tls::TlsBackend::BuiltRustls(Box::new(tls));
2344 self.config.tls = tls;
2345 return self;
2346 }
2347 }
2348
2349 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
2351 self
2352 }
2353
2354 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
2356 pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder {
2357 self.tls_backend_preconfigured(tls)
2358 }
2359
2360 #[cfg(feature = "__tls")]
2367 #[cfg_attr(
2368 docsrs,
2369 doc(cfg(any(feature = "default-tls", feature = "native-tls", feature = "rustls")))
2370 )]
2371 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
2372 self.config.tls_info = tls_info;
2373 self
2374 }
2375
2376 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
2380 self.config.https_only = enabled;
2381 self
2382 }
2383
2384 #[cfg(feature = "hickory-dns")]
2398 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2399 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2400 self.config.hickory_dns = enable;
2401 self
2402 }
2403
2404 pub fn no_hickory_dns(self) -> ClientBuilder {
2410 #[cfg(feature = "hickory-dns")]
2411 {
2412 self.hickory_dns(false)
2413 }
2414
2415 #[cfg(not(feature = "hickory-dns"))]
2416 {
2417 self
2418 }
2419 }
2420
2421 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2426 self.resolve_to_addrs(domain, &[addr])
2427 }
2428
2429 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2434 self.config
2435 .dns_overrides
2436 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2437 self
2438 }
2439
2440 pub fn dns_resolver<R>(mut self, resolver: R) -> ClientBuilder
2445 where
2446 R: crate::dns::resolve::IntoResolve,
2447 {
2448 self.config.dns_resolver = Some(resolver.into_resolve());
2449 self
2450 }
2451
2452 #[cfg(feature = "http3")]
2457 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2458 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2459 self.config.tls_enable_early_data = enabled;
2460 self
2461 }
2462
2463 #[cfg(feature = "http3")]
2469 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2470 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2471 self.config.quic_max_idle_timeout = Some(value);
2472 self
2473 }
2474
2475 #[cfg(feature = "http3")]
2486 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2487 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2488 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2489 self
2490 }
2491
2492 #[cfg(feature = "http3")]
2503 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2504 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2505 self.config.quic_receive_window = Some(value.try_into().unwrap());
2506 self
2507 }
2508
2509 #[cfg(feature = "http3")]
2515 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2516 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2517 self.config.quic_send_window = Some(value);
2518 self
2519 }
2520
2521 #[cfg(feature = "http3")]
2529 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2530 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2531 self.config.quic_congestion_bbr = true;
2532 self
2533 }
2534
2535 #[cfg(feature = "http3")]
2545 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2546 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2547 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2548 self
2549 }
2550
2551 #[cfg(feature = "http3")]
2563 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2564 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2565 self.config.h3_send_grease = Some(enabled);
2566 self
2567 }
2568
2569 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2593 where
2594 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2595 L::Service:
2596 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2597 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2598 {
2599 let layer = BoxCloneSyncServiceLayer::new(layer);
2600
2601 self.config.connector_layers.push(layer);
2602
2603 self
2604 }
2605}
2606
2607type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2608
2609impl Default for Client {
2610 fn default() -> Self {
2611 Self::new()
2612 }
2613}
2614
2615#[cfg(feature = "__rustls")]
2616fn default_rustls_crypto_provider() -> Arc<rustls::crypto::CryptoProvider> {
2617 #[cfg(not(feature = "__rustls-aws-lc-rs"))]
2618 panic!("No provider set");
2619
2620 #[cfg(feature = "__rustls-aws-lc-rs")]
2621 Arc::new(rustls::crypto::aws_lc_rs::default_provider())
2622}
2623
2624impl Client {
2625 pub fn new() -> Client {
2635 ClientBuilder::new().build().expect("Client::new()")
2636 }
2637
2638 pub fn builder() -> ClientBuilder {
2642 ClientBuilder::new()
2643 }
2644
2645 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2651 self.request(Method::GET, url)
2652 }
2653
2654 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2660 self.request(Method::POST, url)
2661 }
2662
2663 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2669 self.request(Method::PUT, url)
2670 }
2671
2672 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2678 self.request(Method::PATCH, url)
2679 }
2680
2681 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2687 self.request(Method::DELETE, url)
2688 }
2689
2690 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2696 self.request(Method::HEAD, url)
2697 }
2698
2699 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2708 let req = url.into_url().map(move |url| Request::new(method, url));
2709 RequestBuilder::new(self.clone(), req)
2710 }
2711
2712 pub fn execute(
2725 &self,
2726 request: Request,
2727 ) -> impl Future<Output = Result<Response, crate::Error>> {
2728 self.execute_request(request)
2729 }
2730
2731 pub fn headers(&self) -> &HeaderMap {
2733 &self.inner.headers
2734 }
2735
2736 pub fn headers_mut(&mut self) -> &mut HeaderMap {
2738 &mut Arc::make_mut(&mut self.inner).headers
2739 }
2740
2741 #[cfg(feature = "cookies")]
2743 pub fn get_cookies(&self, url: &Url) -> Option<HeaderValue> {
2744 self.inner.cookie_store.as_ref().and_then(|store| {
2745 store.cookies(url)
2746 })
2747 }
2748
2749 #[cfg(feature = "cookies")]
2751 pub fn set_cookies(&self, url: &Url, cookies: Vec<HeaderValue>) {
2752 if let Some(store) = self.inner.cookie_store.as_ref() {
2753 let mut iter = cookies.iter();
2754 store.set_cookies(&mut iter, url);
2755 }
2756 }
2757
2758 pub fn set_proxies(&mut self, proxies: Vec<Proxy>) {
2760 let proxy_matchers: Vec<ProxyMatcher> = proxies
2761 .into_iter()
2762 .map(|p| p.into_matcher())
2763 .collect();
2764 Arc::make_mut(&mut self.inner).proxies = Arc::new(proxy_matchers);
2765 }
2766
2767 pub fn set_redirect_policy(&mut self, policy: redirect::Policy) {
2772 let enabled = !matches!(policy.inner, crate::redirect::PolicyKind::None);
2773 self.inner
2774 .redirect_enabled
2775 .store(enabled, std::sync::atomic::Ordering::Relaxed);
2776 let desc = if policy.is_default() {
2777 None
2778 } else {
2779 Some(format!("{:?}", &policy))
2780 };
2781 Arc::make_mut(&mut self.inner).redirect_policy_desc = desc;
2782 }
2783
2784 pub(super) fn execute_request(&self, req: Request) -> Pending {
2785 let (method, url, mut headers, body, version, extensions) = req.pieces();
2786 if url.scheme() != "http" && url.scheme() != "https" {
2787 return Pending::new_err(error::url_bad_scheme(url));
2788 }
2789
2790 if self.inner.https_only && url.scheme() != "https" {
2792 return Pending::new_err(error::url_bad_scheme(url));
2793 }
2794
2795 for (key, value) in &self.inner.headers {
2798 if let Entry::Vacant(entry) = headers.entry(key) {
2799 entry.insert(value.clone());
2800 }
2801 }
2802
2803 let uri = match try_uri(&url) {
2804 Ok(uri) => uri,
2805 _ => return Pending::new_err(error::url_invalid_uri(url)),
2806 };
2807
2808 let body = body.unwrap_or_else(Body::empty);
2809
2810 self.proxy_auth(&uri, &mut headers);
2811 self.proxy_custom_headers(&uri, &mut headers);
2812
2813 let builder = hyper::Request::builder()
2814 .method(method.clone())
2815 .uri(uri)
2816 .version(version);
2817
2818 let in_flight = match version {
2819 #[cfg(feature = "http3")]
2820 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2821 let mut req = builder.body(body).expect("valid request parts");
2822 *req.headers_mut() = headers.clone();
2823 let mut h3 = self.inner.h3_client.as_ref().unwrap().clone();
2824 ResponseFuture::H3(h3.call(req))
2825 }
2826 _ => {
2827 let mut req = builder.body(body).expect("valid request parts");
2828 *req.headers_mut() = headers.clone();
2829 let mut hyper = self.inner.hyper.clone();
2830 ResponseFuture::Default(hyper.call(req))
2831 }
2832 };
2833
2834 let total_timeout = self
2835 .inner
2836 .total_timeout
2837 .fetch(&extensions)
2838 .copied()
2839 .map(tokio::time::sleep)
2840 .map(Box::pin);
2841
2842 let read_timeout_duration = self
2843 .inner
2844 .read_timeout
2845 .fetch(&extensions)
2846 .copied();
2847 let read_timeout_fut = read_timeout_duration
2848 .map(tokio::time::sleep)
2849 .map(Box::pin);
2850
2851 Pending {
2852 inner: PendingInner::Request(Box::pin(PendingRequest {
2853 method,
2854 url,
2855 headers,
2856
2857 client: self.inner.clone(),
2858
2859 in_flight,
2860 total_timeout,
2861 read_timeout_fut,
2862 read_timeout: read_timeout_duration,
2863 })),
2864 }
2865 }
2866
2867 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2868 if !self.inner.proxies_maybe_http_auth {
2869 return;
2870 }
2871
2872 if dst.scheme() != Some(&Scheme::HTTP) {
2876 return;
2877 }
2878
2879 if headers.contains_key(PROXY_AUTHORIZATION) {
2880 return;
2881 }
2882
2883 for proxy in self.inner.proxies.iter() {
2884 if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2885 headers.insert(PROXY_AUTHORIZATION, header);
2886 break;
2887 }
2888 }
2889 }
2890
2891 fn proxy_custom_headers(&self, dst: &Uri, headers: &mut HeaderMap) {
2892 if !self.inner.proxies_maybe_http_custom_headers {
2893 return;
2894 }
2895
2896 if dst.scheme() != Some(&Scheme::HTTP) {
2897 return;
2898 }
2899
2900 for proxy in self.inner.proxies.iter() {
2901 if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2902 iter.iter().for_each(|(key, value)| {
2903 headers.insert(key, value.clone());
2904 });
2905 break;
2906 }
2907 }
2908 }
2909}
2910
2911impl fmt::Debug for Client {
2912 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2913 let mut builder = f.debug_struct("Client");
2914 self.inner.fmt_fields(&mut builder);
2915 builder.finish()
2916 }
2917}
2918
2919impl tower_service::Service<Request> for Client {
2920 type Response = Response;
2921 type Error = crate::Error;
2922 type Future = Pending;
2923
2924 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2925 Poll::Ready(Ok(()))
2926 }
2927
2928 fn call(&mut self, req: Request) -> Self::Future {
2929 self.execute_request(req)
2930 }
2931}
2932
2933impl tower_service::Service<Request> for &'_ Client {
2934 type Response = Response;
2935 type Error = crate::Error;
2936 type Future = Pending;
2937
2938 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2939 Poll::Ready(Ok(()))
2940 }
2941
2942 fn call(&mut self, req: Request) -> Self::Future {
2943 self.execute_request(req)
2944 }
2945}
2946
2947impl fmt::Debug for ClientBuilder {
2948 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2949 let mut builder = f.debug_struct("ClientBuilder");
2950 self.config.fmt_fields(&mut builder);
2951 builder.finish()
2952 }
2953}
2954
2955impl Config {
2956 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2957 #[cfg(feature = "cookies")]
2961 {
2962 if self.cookie_store.is_some() {
2963 f.field("cookie_store", &true);
2964 }
2965 }
2966
2967 f.field("accepts", &self.accepts);
2968
2969 if !self.proxies.is_empty() {
2970 f.field("proxies", &self.proxies);
2971 }
2972
2973 if !self.redirect_policy.is_default() {
2974 f.field("redirect_policy", &self.redirect_policy);
2975 }
2976
2977 if self.referer {
2978 f.field("referer", &true);
2979 }
2980
2981 f.field("default_headers", &self.headers);
2982
2983 if self.http1_title_case_headers {
2984 f.field("http1_title_case_headers", &true);
2985 }
2986
2987 if self.http1_allow_obsolete_multiline_headers_in_responses {
2988 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2989 }
2990
2991 if self.http1_ignore_invalid_headers_in_responses {
2992 f.field("http1_ignore_invalid_headers_in_responses", &true);
2993 }
2994
2995 if self.http1_allow_spaces_after_header_name_in_responses {
2996 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2997 }
2998
2999 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
3000 f.field("http1_only", &true);
3001 }
3002
3003 #[cfg(feature = "http2")]
3004 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
3005 f.field("http2_prior_knowledge", &true);
3006 }
3007
3008 if let Some(ref d) = self.connect_timeout {
3009 f.field("connect_timeout", d);
3010 }
3011
3012 if let Some(ref d) = self.timeout {
3013 f.field("timeout", d);
3014 }
3015
3016 if let Some(ref v) = self.local_address {
3017 f.field("local_address", v);
3018 }
3019
3020 #[cfg(any(
3021 target_os = "android",
3022 target_os = "fuchsia",
3023 target_os = "illumos",
3024 target_os = "ios",
3025 target_os = "linux",
3026 target_os = "macos",
3027 target_os = "solaris",
3028 target_os = "tvos",
3029 target_os = "visionos",
3030 target_os = "watchos",
3031 ))]
3032 if let Some(ref v) = self.interface {
3033 f.field("interface", v);
3034 }
3035
3036 if self.nodelay {
3037 f.field("tcp_nodelay", &true);
3038 }
3039
3040 #[cfg(feature = "__tls")]
3041 {
3042 if !self.hostname_verification {
3043 f.field("tls_danger_accept_invalid_hostnames", &true);
3044 }
3045 }
3046
3047 #[cfg(feature = "__tls")]
3048 {
3049 if !self.certs_verification {
3050 f.field("tls_danger_accept_invalid_certs", &true);
3051 }
3052
3053 if let Some(ref min_tls_version) = self.min_tls_version {
3054 f.field("tls_version_min", min_tls_version);
3055 }
3056
3057 if let Some(ref max_tls_version) = self.max_tls_version {
3058 f.field("tls_version_max", max_tls_version);
3059 }
3060
3061 f.field("tls_sni", &self.tls_sni);
3062
3063 f.field("tls_info", &self.tls_info);
3064 }
3065
3066 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
3067 {
3068 f.field("tls_backend", &self.tls);
3069 }
3070
3071 if !self.dns_overrides.is_empty() {
3072 f.field("dns_overrides", &self.dns_overrides);
3073 }
3074
3075 #[cfg(feature = "http3")]
3076 {
3077 if self.tls_enable_early_data {
3078 f.field("tls_enable_early_data", &true);
3079 }
3080 }
3081
3082 #[cfg(unix)]
3083 if let Some(ref p) = self.unix_socket {
3084 f.field("unix_socket", p);
3085 }
3086 }
3087}
3088
3089#[cfg(not(feature = "cookies"))]
3090type MaybeCookieService<T> = T;
3091
3092#[cfg(feature = "cookies")]
3093type MaybeCookieService<T> = CookieService<T>;
3094
3095#[cfg(not(any(
3096 feature = "gzip",
3097 feature = "brotli",
3098 feature = "zstd",
3099 feature = "deflate"
3100)))]
3101type MaybeDecompression<T> = T;
3102
3103#[cfg(any(
3104 feature = "gzip",
3105 feature = "brotli",
3106 feature = "zstd",
3107 feature = "deflate"
3108))]
3109type MaybeDecompression<T> = Decompression<T>;
3110
3111type LayeredService<T> = MaybeDecompression<
3112 FollowRedirect<
3113 MaybeCookieService<tower::retry::Retry<crate::retry::Policy, T>>,
3114 TowerRedirectPolicy,
3115 >,
3116>;
3117type LayeredFuture<T> = <LayeredService<T> as Service<http::Request<Body>>>::Future;
3118
3119#[derive(Clone)]
3120struct ClientRef {
3121 accepts: Accepts,
3122 #[cfg(feature = "cookies")]
3123 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
3124 headers: HeaderMap,
3125 hyper: LayeredService<HyperService>,
3126 #[cfg(feature = "http3")]
3127 h3_client: Option<LayeredService<H3Client>>,
3128 referer: bool,
3129 total_timeout: RequestConfig<TotalTimeout>,
3130 read_timeout: RequestConfig<ReadTimeout>,
3131 proxies: Arc<Vec<ProxyMatcher>>,
3132 proxies_maybe_http_auth: bool,
3133 proxies_maybe_http_custom_headers: bool,
3134 https_only: bool,
3135 redirect_policy_desc: Option<String>,
3136 redirect_enabled: Arc<std::sync::atomic::AtomicBool>,
3137}
3138
3139impl ClientRef {
3140 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
3141 #[cfg(feature = "cookies")]
3145 {
3146 if self.cookie_store.is_some() {
3147 f.field("cookie_store", &true);
3148 }
3149 }
3150
3151 f.field("accepts", &self.accepts);
3152
3153 if !self.proxies.is_empty() {
3154 f.field("proxies", &self.proxies);
3155 }
3156
3157 if let Some(s) = &self.redirect_policy_desc {
3158 f.field("redirect_policy", s);
3159 }
3160
3161 if self.referer {
3162 f.field("referer", &true);
3163 }
3164
3165 f.field("default_headers", &self.headers);
3166
3167 self.total_timeout.fmt_as_field(f);
3168
3169 self.read_timeout.fmt_as_field(f);
3170 }
3171}
3172
3173pin_project! {
3174 pub struct Pending {
3175 #[pin]
3176 inner: PendingInner,
3177 }
3178}
3179
3180enum PendingInner {
3181 Request(Pin<Box<PendingRequest>>),
3182 Error(Option<crate::Error>),
3183}
3184
3185pin_project! {
3186 struct PendingRequest {
3187 method: Method,
3188 url: Url,
3189 headers: HeaderMap,
3190
3191 client: Arc<ClientRef>,
3192
3193 #[pin]
3194 in_flight: ResponseFuture,
3195 #[pin]
3196 total_timeout: Option<Pin<Box<Sleep>>>,
3197 #[pin]
3198 read_timeout_fut: Option<Pin<Box<Sleep>>>,
3199 read_timeout: Option<Duration>,
3200 }
3201}
3202
3203enum ResponseFuture {
3204 Default(LayeredFuture<HyperService>),
3205 #[cfg(feature = "http3")]
3206 H3(LayeredFuture<H3Client>),
3207}
3208
3209impl PendingRequest {
3210 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
3211 self.project().in_flight
3212 }
3213
3214 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3215 self.project().total_timeout
3216 }
3217
3218 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
3219 self.project().read_timeout_fut
3220 }
3221}
3222
3223impl Pending {
3224 pub(super) fn new_err(err: crate::Error) -> Pending {
3225 Pending {
3226 inner: PendingInner::Error(Some(err)),
3227 }
3228 }
3229
3230 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
3231 self.project().inner
3232 }
3233}
3234
3235impl Future for Pending {
3236 type Output = Result<Response, crate::Error>;
3237
3238 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3239 let inner = self.inner();
3240 match inner.get_mut() {
3241 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
3242 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
3243 .take()
3244 .expect("Pending error polled more than once"))),
3245 }
3246 }
3247}
3248
3249impl Future for PendingRequest {
3250 type Output = Result<Response, crate::Error>;
3251
3252 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3253 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
3254 if let Poll::Ready(()) = delay.poll(cx) {
3255 return Poll::Ready(Err(
3256 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3257 ));
3258 }
3259 }
3260
3261 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
3262 if let Poll::Ready(()) = delay.poll(cx) {
3263 return Poll::Ready(Err(
3264 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
3265 ));
3266 }
3267 }
3268
3269 let res = match self.as_mut().in_flight().get_mut() {
3270 ResponseFuture::Default(r) => match ready!(Pin::new(r).poll(cx)) {
3271 Err(e) => {
3272 return Poll::Ready(Err(e.if_no_url(|| self.url.clone())));
3273 }
3274 Ok(res) => res.map(super::body::boxed),
3275 },
3276 #[cfg(feature = "http3")]
3277 ResponseFuture::H3(r) => match ready!(Pin::new(r).poll(cx)) {
3278 Err(e) => {
3279 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
3280 }
3281 Ok(res) => res.map(super::body::boxed),
3282 },
3283 };
3284
3285 if let Some(url) = &res
3286 .extensions()
3287 .get::<tower_http::follow_redirect::RequestUri>()
3288 {
3289 self.url = match Url::parse(&url.0.to_string()) {
3290 Ok(url) => url,
3291 Err(e) => return Poll::Ready(Err(crate::error::decode(e))),
3292 }
3293 };
3294
3295 let res = Response::new(
3296 res,
3297 self.url.clone(),
3298 self.total_timeout.take(),
3299 self.read_timeout,
3300 );
3301 Poll::Ready(Ok(res))
3302 }
3303}
3304
3305impl fmt::Debug for Pending {
3306 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3307 match self.inner {
3308 PendingInner::Request(ref req) => f
3309 .debug_struct("Pending")
3310 .field("method", &req.method)
3311 .field("url", &req.url)
3312 .finish(),
3313 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3314 }
3315 }
3316}
3317
3318#[cfg(test)]
3319#[cfg(not(feature = "rustls-no-provider"))]
3320mod tests {
3321
3322 #[tokio::test]
3323 async fn execute_request_rejects_invalid_urls() {
3324 let url_str = "hxxps://www.rust-lang.org/";
3325 let url = url::Url::parse(url_str).unwrap();
3326 let result = crate::get(url.clone()).await;
3327
3328 assert!(result.is_err());
3329 let err = result.err().unwrap();
3330 assert!(err.is_builder());
3331 assert_eq!(url_str, err.url().unwrap().as_str());
3332 }
3333
3334 #[tokio::test]
3336 async fn execute_request_rejects_invalid_hostname() {
3337 let url_str = "https://{{hostname}}/";
3338 let url = url::Url::parse(url_str).unwrap();
3339 let result = crate::get(url.clone()).await;
3340
3341 assert!(result.is_err());
3342 let err = result.err().unwrap();
3343 assert!(err.is_builder());
3344 assert_eq!(url_str, err.url().unwrap().as_str());
3345 }
3346
3347 #[test]
3348 fn test_future_size() {
3349 let s = std::mem::size_of::<super::Pending>();
3350 assert!(s < 128, "size_of::<Pending>() == {s}, too big");
3351 }
3352}