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::{Context, Poll};
8use std::time::Duration;
9use std::{collections::HashMap, convert::TryInto, net::SocketAddr};
10use std::{fmt, str};
11
12use super::decoder::Accepts;
13use super::request::{Request, RequestBuilder};
14use super::response::Response;
15use super::Body;
16#[cfg(feature = "http3")]
17use crate::async_impl::h3_client::connect::{H3ClientConfig, H3Connector};
18#[cfg(feature = "http3")]
19use crate::async_impl::h3_client::{H3Client, H3ResponseFuture};
20use crate::connect::{
21 sealed::{Conn, Unnameable},
22 BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
23};
24#[cfg(feature = "cookies")]
25use crate::cookie;
26#[cfg(feature = "hickory-dns")]
27use crate::dns::hickory::HickoryDnsResolver;
28use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
29use crate::error::{self, BoxError};
30use crate::into_url::try_uri;
31use crate::redirect::{self, remove_sensitive_headers};
32#[cfg(feature = "__rustls")]
33use crate::tls::CertificateRevocationList;
34#[cfg(feature = "__tls")]
35use crate::tls::{self, TlsBackend};
36#[cfg(feature = "__tls")]
37use crate::Certificate;
38#[cfg(any(feature = "native-tls", feature = "__rustls"))]
39use crate::Identity;
40use crate::{IntoUrl, Method, Proxy, StatusCode, Url};
41use bytes::Bytes;
42use http::header::{
43 Entry, HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH,
44 CONTENT_TYPE, LOCATION, PROXY_AUTHORIZATION, RANGE, REFERER, TRANSFER_ENCODING, USER_AGENT,
45};
46use http::uri::Scheme;
47use http::Uri;
48use hyper_util::client::legacy::connect::HttpConnector;
49use log::debug;
50#[cfg(feature = "default-tls")]
51use native_tls_crate::TlsConnector;
52use pin_project_lite::pin_project;
53#[cfg(feature = "http3")]
54use quinn::TransportConfig;
55#[cfg(feature = "http3")]
56use quinn::VarInt;
57use tokio::time::Sleep;
58use tower::util::BoxCloneSyncServiceLayer;
59use tower::{Layer, Service};
60
61type HyperResponseFuture = hyper_util::client::legacy::ResponseFuture;
62
63#[derive(Clone)]
77pub struct Client {
78 inner: Arc<ClientRef>,
79}
80
81#[must_use]
83pub struct ClientBuilder {
84 config: Config,
85}
86
87enum HttpVersionPref {
88 Http1,
89 #[cfg(feature = "http2")]
90 Http2,
91 #[cfg(feature = "http3")]
92 Http3,
93 All,
94}
95
96struct Config {
97 accepts: Accepts,
99 headers: HeaderMap,
100 #[cfg(feature = "__tls")]
101 hostname_verification: bool,
102 #[cfg(feature = "__tls")]
103 certs_verification: bool,
104 #[cfg(feature = "__tls")]
105 tls_sni: bool,
106 connect_timeout: Option<Duration>,
107 connection_verbose: bool,
108 pool_idle_timeout: Option<Duration>,
109 pool_max_idle_per_host: usize,
110 tcp_keepalive: Option<Duration>,
111 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
112 identity: Option<Identity>,
113 proxies: Vec<Proxy>,
114 auto_sys_proxy: bool,
115 redirect_policy: redirect::Policy,
116 referer: bool,
117 read_timeout: Option<Duration>,
118 timeout: Option<Duration>,
119 #[cfg(feature = "__tls")]
120 root_certs: Vec<Certificate>,
121 #[cfg(feature = "__tls")]
122 tls_built_in_root_certs: bool,
123 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
124 tls_built_in_certs_webpki: bool,
125 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
126 tls_built_in_certs_native: bool,
127 #[cfg(feature = "__rustls")]
128 crls: Vec<CertificateRevocationList>,
129 #[cfg(feature = "__tls")]
130 min_tls_version: Option<tls::Version>,
131 #[cfg(feature = "__tls")]
132 max_tls_version: Option<tls::Version>,
133 #[cfg(feature = "__tls")]
134 tls_info: bool,
135 #[cfg(feature = "__tls")]
136 tls: TlsBackend,
137 connector_layers: Vec<BoxedConnectorLayer>,
138 http_version_pref: HttpVersionPref,
139 http09_responses: bool,
140 http1_preserve_header_case: bool,
141 http1_title_case_headers: bool,
142 http1_preserve_header_order: bool,
143 http1_allow_obsolete_multiline_headers_in_responses: bool,
144 http1_ignore_invalid_headers_in_responses: bool,
145 http1_allow_spaces_after_header_name_in_responses: bool,
146 #[cfg(feature = "http2")]
147 http2_initial_stream_window_size: Option<u32>,
148 #[cfg(feature = "http2")]
149 http2_initial_connection_window_size: Option<u32>,
150 #[cfg(feature = "http2")]
151 http2_adaptive_window: bool,
152 #[cfg(feature = "http2")]
153 http2_max_frame_size: Option<u32>,
154 #[cfg(feature = "http2")]
155 http2_max_header_list_size: Option<u32>,
156 #[cfg(feature = "http2")]
157 http2_keep_alive_interval: Option<Duration>,
158 #[cfg(feature = "http2")]
159 http2_keep_alive_timeout: Option<Duration>,
160 #[cfg(feature = "http2")]
161 http2_keep_alive_while_idle: bool,
162 set_host: bool,
163 local_address: Option<IpAddr>,
164 #[cfg(any(
165 target_os = "android",
166 target_os = "fuchsia",
167 target_os = "illumos",
168 target_os = "ios",
169 target_os = "linux",
170 target_os = "macos",
171 target_os = "solaris",
172 target_os = "tvos",
173 target_os = "visionos",
174 target_os = "watchos",
175 ))]
176 interface: Option<String>,
177 nodelay: bool,
178 #[cfg(feature = "cookies")]
179 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
180 hickory_dns: bool,
181 error: Option<crate::Error>,
182 https_only: bool,
183 #[cfg(feature = "http3")]
184 tls_enable_early_data: bool,
185 #[cfg(feature = "http3")]
186 quic_max_idle_timeout: Option<Duration>,
187 #[cfg(feature = "http3")]
188 quic_stream_receive_window: Option<VarInt>,
189 #[cfg(feature = "http3")]
190 quic_receive_window: Option<VarInt>,
191 #[cfg(feature = "http3")]
192 quic_send_window: Option<u64>,
193 #[cfg(feature = "http3")]
194 quic_congestion_bbr: bool,
195 #[cfg(feature = "http3")]
196 h3_max_field_section_size: Option<u64>,
197 #[cfg(feature = "http3")]
198 h3_send_grease: Option<bool>,
199 dns_overrides: HashMap<String, Vec<SocketAddr>>,
200 dns_resolver: Option<Arc<dyn Resolve>>,
201}
202
203impl Default for ClientBuilder {
204 fn default() -> Self {
205 Self::new()
206 }
207}
208
209impl ClientBuilder {
210 pub fn new() -> Self {
214 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
215 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
216
217 ClientBuilder {
218 config: Config {
219 error: None,
220 accepts: Accepts::default(),
221 headers,
222 #[cfg(feature = "__tls")]
223 hostname_verification: true,
224 #[cfg(feature = "__tls")]
225 certs_verification: true,
226 #[cfg(feature = "__tls")]
227 tls_sni: true,
228 connect_timeout: None,
229 connection_verbose: false,
230 pool_idle_timeout: Some(Duration::from_secs(90)),
231 pool_max_idle_per_host: usize::MAX,
232 tcp_keepalive: None, proxies: Vec::new(),
236 auto_sys_proxy: true,
237 redirect_policy: redirect::Policy::default(),
238 referer: true,
239 read_timeout: None,
240 set_host: true,
241 timeout: None,
242 #[cfg(feature = "__tls")]
243 root_certs: Vec::new(),
244 #[cfg(feature = "__tls")]
245 tls_built_in_root_certs: true,
246 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
247 tls_built_in_certs_webpki: true,
248 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
249 tls_built_in_certs_native: true,
250 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
251 identity: None,
252 #[cfg(feature = "__rustls")]
253 crls: vec![],
254 #[cfg(feature = "__tls")]
255 min_tls_version: None,
256 #[cfg(feature = "__tls")]
257 max_tls_version: None,
258 #[cfg(feature = "__tls")]
259 tls_info: false,
260 #[cfg(feature = "__tls")]
261 tls: TlsBackend::default(),
262 connector_layers: Vec::new(),
263 http_version_pref: HttpVersionPref::All,
264 http09_responses: false,
265 http1_preserve_header_case: true,
266 http1_title_case_headers: false,
267 http1_preserve_header_order: false,
268 http1_allow_obsolete_multiline_headers_in_responses: false,
269 http1_ignore_invalid_headers_in_responses: false,
270 http1_allow_spaces_after_header_name_in_responses: false,
271 #[cfg(feature = "http2")]
272 http2_initial_stream_window_size: None,
273 #[cfg(feature = "http2")]
274 http2_initial_connection_window_size: None,
275 #[cfg(feature = "http2")]
276 http2_adaptive_window: false,
277 #[cfg(feature = "http2")]
278 http2_max_frame_size: None,
279 #[cfg(feature = "http2")]
280 http2_max_header_list_size: None,
281 #[cfg(feature = "http2")]
282 http2_keep_alive_interval: None,
283 #[cfg(feature = "http2")]
284 http2_keep_alive_timeout: None,
285 #[cfg(feature = "http2")]
286 http2_keep_alive_while_idle: false,
287 local_address: None,
288 #[cfg(any(
289 target_os = "android",
290 target_os = "fuchsia",
291 target_os = "illumos",
292 target_os = "ios",
293 target_os = "linux",
294 target_os = "macos",
295 target_os = "solaris",
296 target_os = "tvos",
297 target_os = "visionos",
298 target_os = "watchos",
299 ))]
300 interface: None,
301 nodelay: true,
302 hickory_dns: cfg!(feature = "hickory-dns"),
303 #[cfg(feature = "cookies")]
304 cookie_store: None,
305 https_only: false,
306 dns_overrides: HashMap::new(),
307 #[cfg(feature = "http3")]
308 tls_enable_early_data: false,
309 #[cfg(feature = "http3")]
310 quic_max_idle_timeout: None,
311 #[cfg(feature = "http3")]
312 quic_stream_receive_window: None,
313 #[cfg(feature = "http3")]
314 quic_receive_window: None,
315 #[cfg(feature = "http3")]
316 quic_send_window: None,
317 #[cfg(feature = "http3")]
318 quic_congestion_bbr: false,
319 #[cfg(feature = "http3")]
320 h3_max_field_section_size: None,
321 #[cfg(feature = "http3")]
322 h3_send_grease: None,
323 dns_resolver: None,
324 },
325 }
326 }
327}
328
329impl ClientBuilder {
330 pub fn build(self) -> crate::Result<Client> {
337 let config = self.config;
338
339 if let Some(err) = config.error {
340 return Err(err);
341 }
342
343 let mut proxies = config.proxies;
344 if config.auto_sys_proxy {
345 proxies.push(Proxy::system());
346 }
347 let proxies = Arc::new(proxies);
348
349 #[allow(unused)]
350 #[cfg(feature = "http3")]
351 let mut h3_connector = None;
352
353 let mut connector_builder = {
354 #[cfg(feature = "__tls")]
355 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
356 headers.get(USER_AGENT).cloned()
357 }
358
359 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
360 false => Arc::new(GaiResolver::new()),
361 #[cfg(feature = "hickory-dns")]
362 true => Arc::new(HickoryDnsResolver::default()),
363 #[cfg(not(feature = "hickory-dns"))]
364 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
365 };
366 if let Some(dns_resolver) = config.dns_resolver {
367 resolver = dns_resolver;
368 }
369 if !config.dns_overrides.is_empty() {
370 resolver = Arc::new(DnsResolverWithOverrides::new(
371 resolver,
372 config.dns_overrides,
373 ));
374 }
375 let mut http = HttpConnector::new_with_resolver(DynResolver::new(resolver.clone()));
376 http.set_connect_timeout(config.connect_timeout);
377
378 #[cfg(all(feature = "http3", feature = "__rustls"))]
379 let build_h3_connector =
380 |resolver,
381 tls,
382 quic_max_idle_timeout: Option<Duration>,
383 quic_stream_receive_window,
384 quic_receive_window,
385 quic_send_window,
386 quic_congestion_bbr,
387 h3_max_field_section_size,
388 h3_send_grease,
389 local_address,
390 http_version_pref: &HttpVersionPref| {
391 let mut transport_config = TransportConfig::default();
392
393 if let Some(max_idle_timeout) = quic_max_idle_timeout {
394 transport_config.max_idle_timeout(Some(
395 max_idle_timeout.try_into().map_err(error::builder)?,
396 ));
397 }
398
399 if let Some(stream_receive_window) = quic_stream_receive_window {
400 transport_config.stream_receive_window(stream_receive_window);
401 }
402
403 if let Some(receive_window) = quic_receive_window {
404 transport_config.receive_window(receive_window);
405 }
406
407 if let Some(send_window) = quic_send_window {
408 transport_config.send_window(send_window);
409 }
410
411 if quic_congestion_bbr {
412 let factory = Arc::new(quinn::congestion::BbrConfig::default());
413 transport_config.congestion_controller_factory(factory);
414 }
415
416 let mut h3_client_config = H3ClientConfig::default();
417
418 if let Some(max_field_section_size) = h3_max_field_section_size {
419 h3_client_config.max_field_section_size = Some(max_field_section_size);
420 }
421
422 if let Some(send_grease) = h3_send_grease {
423 h3_client_config.send_grease = Some(send_grease);
424 }
425
426 let res = H3Connector::new(
427 DynResolver::new(resolver),
428 tls,
429 local_address,
430 transport_config,
431 h3_client_config,
432 );
433
434 match res {
435 Ok(connector) => Ok(Some(connector)),
436 Err(err) => {
437 if let HttpVersionPref::Http3 = http_version_pref {
438 Err(error::builder(err))
439 } else {
440 Ok(None)
441 }
442 }
443 }
444 };
445
446 #[cfg(feature = "__tls")]
447 match config.tls {
448 #[cfg(feature = "default-tls")]
449 TlsBackend::Default => {
450 let mut tls = TlsConnector::builder();
451
452 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
453 {
454 match config.http_version_pref {
455 HttpVersionPref::Http1 => {
456 tls.request_alpns(&["http/1.1"]);
457 }
458 #[cfg(feature = "http2")]
459 HttpVersionPref::Http2 => {
460 tls.request_alpns(&["h2"]);
461 }
462 HttpVersionPref::All => {
463 tls.request_alpns(&["h2", "http/1.1"]);
464 }
465 }
466 }
467
468 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
469
470 tls.danger_accept_invalid_certs(!config.certs_verification);
471
472 tls.use_sni(config.tls_sni);
473
474 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
475
476 for cert in config.root_certs {
477 cert.add_to_native_tls(&mut tls);
478 }
479
480 #[cfg(feature = "native-tls")]
481 {
482 if let Some(id) = config.identity {
483 id.add_to_native_tls(&mut tls)?;
484 }
485 }
486 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
487 {
488 if let Some(_id) = config.identity {
490 return Err(crate::error::builder("incompatible TLS identity type"));
491 }
492 }
493
494 if let Some(min_tls_version) = config.min_tls_version {
495 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
496 crate::error::builder("invalid minimum TLS version for backend")
500 })?;
501 tls.min_protocol_version(Some(protocol));
502 }
503
504 if let Some(max_tls_version) = config.max_tls_version {
505 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
506 crate::error::builder("invalid maximum TLS version for backend")
511 })?;
512 tls.max_protocol_version(Some(protocol));
513 }
514
515 ConnectorBuilder::new_default_tls(
516 http,
517 tls,
518 proxies.clone(),
519 user_agent(&config.headers),
520 config.local_address,
521 #[cfg(any(
522 target_os = "android",
523 target_os = "fuchsia",
524 target_os = "illumos",
525 target_os = "ios",
526 target_os = "linux",
527 target_os = "macos",
528 target_os = "solaris",
529 target_os = "tvos",
530 target_os = "visionos",
531 target_os = "watchos",
532 ))]
533 config.interface.as_deref(),
534 config.nodelay,
535 config.tls_info,
536 )?
537 }
538 #[cfg(feature = "native-tls")]
539 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_default_tls(
540 http,
541 conn,
542 proxies.clone(),
543 user_agent(&config.headers),
544 config.local_address,
545 #[cfg(any(feature ="native-tls-alpn", target_os = "android", target_os = "fuchsia", target_os = "linux"))]
546 config.interface.as_deref(),
547 config.nodelay,
548 config.tls_info,
549 ),
550 #[cfg(feature = "__rustls")]
551 TlsBackend::BuiltRustls(conn) => {
552 #[cfg(feature = "http3")]
553 {
554 h3_connector = build_h3_connector(
555 resolver,
556 conn.clone(),
557 config.quic_max_idle_timeout,
558 config.quic_stream_receive_window,
559 config.quic_receive_window,
560 config.quic_send_window,
561 config.quic_congestion_bbr,
562 config.h3_max_field_section_size,
563 config.h3_send_grease,
564 config.local_address,
565 &config.http_version_pref,
566 )?;
567 }
568
569 ConnectorBuilder::new_rustls_tls(
570 http,
571 conn,
572 proxies.clone(),
573 user_agent(&config.headers),
574 config.local_address,
575 #[cfg(any(
576 target_os = "android",
577 target_os = "fuchsia",
578 target_os = "illumos",
579 target_os = "ios",
580 target_os = "linux",
581 target_os = "macos",
582 target_os = "solaris",
583 target_os = "tvos",
584 target_os = "visionos",
585 target_os = "watchos",
586 ))]
587 config.interface.as_deref(),
588 config.nodelay,
589 config.tls_info,
590 )
591 }
592 #[cfg(feature = "__rustls")]
593 TlsBackend::Rustls => {
594 use crate::tls::{IgnoreHostname, NoVerifier};
595
596 let mut root_cert_store = rustls::RootCertStore::empty();
598 for cert in config.root_certs {
599 cert.add_to_rustls(&mut root_cert_store)?;
600 }
601
602 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
603 if config.tls_built_in_certs_webpki {
604 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
605 }
606
607 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
608 if config.tls_built_in_certs_native {
609 let mut valid_count = 0;
610 let mut invalid_count = 0;
611
612 let load_results = rustls_native_certs::load_native_certs();
613 for cert in load_results.certs {
614 match root_cert_store.add(cert.into()) {
618 Ok(_) => valid_count += 1,
619 Err(err) => {
620 invalid_count += 1;
621 log::debug!("rustls failed to parse DER certificate: {err:?}");
622 }
623 }
624 }
625 if valid_count == 0 && invalid_count > 0 {
626 let err = if load_results.errors.is_empty() {
627 crate::error::builder(
628 "zero valid certificates found in native root store",
629 )
630 } else {
631 use std::fmt::Write as _;
632 let mut acc = String::new();
633 for err in load_results.errors {
634 let _ = writeln!(&mut acc, "{err}");
635 }
636
637 crate::error::builder(acc)
638 };
639
640 return Err(err);
641 }
642 }
643
644 let mut versions = rustls::ALL_VERSIONS.to_vec();
646
647 if let Some(min_tls_version) = config.min_tls_version {
648 versions.retain(|&supported_version| {
649 match tls::Version::from_rustls(supported_version.version) {
650 Some(version) => version >= min_tls_version,
651 None => true,
654 }
655 });
656 }
657
658 if let Some(max_tls_version) = config.max_tls_version {
659 versions.retain(|&supported_version| {
660 match tls::Version::from_rustls(supported_version.version) {
661 Some(version) => version <= max_tls_version,
662 None => false,
663 }
664 });
665 }
666
667 if versions.is_empty() {
668 return Err(crate::error::builder("empty supported tls versions"));
669 }
670
671 let provider = rustls::crypto::CryptoProvider::get_default()
674 .map(|arc| arc.clone())
675 .unwrap_or_else(|| {
676 #[cfg(not(feature = "__rustls-ring"))]
677 panic!("No provider set");
678
679 #[cfg(feature = "__rustls-ring")]
680 Arc::new(rustls::crypto::ring::default_provider())
681 });
682
683 let signature_algorithms = provider.signature_verification_algorithms;
685 let config_builder =
686 rustls::ClientConfig::builder_with_provider(provider.clone())
687 .with_protocol_versions(&versions)
688 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
689
690 let config_builder = if !config.certs_verification {
691 config_builder
692 .dangerous()
693 .with_custom_certificate_verifier(Arc::new(NoVerifier))
694 } else if !config.hostname_verification {
695 config_builder
696 .dangerous()
697 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
698 root_cert_store,
699 signature_algorithms,
700 )))
701 } else {
702 if config.crls.is_empty() {
703 config_builder.with_root_certificates(root_cert_store)
704 } else {
705 let crls = config
706 .crls
707 .iter()
708 .map(|e| e.as_rustls_crl())
709 .collect::<Vec<_>>();
710 let verifier =
711 rustls::client::WebPkiServerVerifier::builder_with_provider(
712 Arc::new(root_cert_store),
713 provider,
714 )
715 .with_crls(crls)
716 .build()
717 .map_err(|_| {
718 crate::error::builder("invalid TLS verification settings")
719 })?;
720 config_builder.with_webpki_verifier(verifier)
721 }
722 };
723
724 let mut tls = if let Some(id) = config.identity {
726 id.add_to_rustls(config_builder)?
727 } else {
728 config_builder.with_no_client_auth()
729 };
730
731 tls.enable_sni = config.tls_sni;
732
733 match config.http_version_pref {
735 HttpVersionPref::Http1 => {
736 tls.alpn_protocols = vec!["http/1.1".into()];
737 }
738 #[cfg(feature = "http2")]
739 HttpVersionPref::Http2 => {
740 tls.alpn_protocols = vec!["h2".into()];
741 }
742 #[cfg(feature = "http3")]
743 HttpVersionPref::Http3 => {
744 tls.alpn_protocols = vec!["h3".into()];
745 }
746 HttpVersionPref::All => {
747 tls.alpn_protocols = vec![
748 #[cfg(feature = "http2")]
749 "h2".into(),
750 "http/1.1".into(),
751 ];
752 }
753 }
754
755 #[cfg(feature = "http3")]
756 {
757 tls.enable_early_data = config.tls_enable_early_data;
758
759 h3_connector = build_h3_connector(
760 resolver,
761 tls.clone(),
762 config.quic_max_idle_timeout,
763 config.quic_stream_receive_window,
764 config.quic_receive_window,
765 config.quic_send_window,
766 config.quic_congestion_bbr,
767 config.h3_max_field_section_size,
768 config.h3_send_grease,
769 config.local_address,
770 &config.http_version_pref,
771 )?;
772 }
773
774 ConnectorBuilder::new_rustls_tls(
775 http,
776 tls,
777 proxies.clone(),
778 user_agent(&config.headers),
779 config.local_address,
780 #[cfg(any(
781 target_os = "android",
782 target_os = "fuchsia",
783 target_os = "illumos",
784 target_os = "ios",
785 target_os = "linux",
786 target_os = "macos",
787 target_os = "solaris",
788 target_os = "tvos",
789 target_os = "visionos",
790 target_os = "watchos",
791 ))]
792 config.interface.as_deref(),
793 config.nodelay,
794 config.tls_info,
795 )
796 }
797 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
798 TlsBackend::UnknownPreconfigured => {
799 return Err(crate::error::builder(
800 "Unknown TLS backend passed to `use_preconfigured_tls`",
801 ));
802 }
803 }
804
805 #[cfg(not(feature = "__tls"))]
806 ConnectorBuilder::new(
807 http,
808 proxies.clone(),
809 config.local_address,
810 #[cfg(any(
811 target_os = "android",
812 target_os = "fuchsia",
813 target_os = "illumos",
814 target_os = "ios",
815 target_os = "linux",
816 target_os = "macos",
817 target_os = "solaris",
818 target_os = "tvos",
819 target_os = "visionos",
820 target_os = "watchos",
821 ))]
822 config.interface.as_deref(),
823 config.nodelay,
824 )
825 };
826
827 connector_builder.set_timeout(config.connect_timeout);
828 connector_builder.set_verbose(config.connection_verbose);
829 connector_builder.set_keepalive(config.tcp_keepalive);
830
831 let mut builder =
832 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
833 #[cfg(feature = "http2")]
834 {
835 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
836 builder.http2_only(true);
837 }
838
839 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
840 {
841 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
842 }
843 if let Some(http2_initial_connection_window_size) =
844 config.http2_initial_connection_window_size
845 {
846 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
847 }
848 if config.http2_adaptive_window {
849 builder.http2_adaptive_window(true);
850 }
851 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
852 builder.http2_max_frame_size(http2_max_frame_size);
853 }
854 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
855 builder.http2_max_header_list_size(http2_max_header_list_size);
856 }
857 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
858 builder.http2_keep_alive_interval(http2_keep_alive_interval);
859 }
860 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
861 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
862 }
863 if config.http2_keep_alive_while_idle {
864 builder.http2_keep_alive_while_idle(true);
865 }
866 }
867
868 builder.timer(hyper_util::rt::TokioTimer::new());
869 builder.pool_timer(hyper_util::rt::TokioTimer::new());
870 builder.pool_idle_timeout(config.pool_idle_timeout);
871 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
872 builder.set_host(config.set_host);
873
874 if config.http1_preserve_header_case {
875 builder.http1_preserve_header_case(true);
876 }
877
878 if config.http1_preserve_header_order {
879 builder.http1_preserve_header_order(true);
880 }
881
882 if config.http09_responses {
883 builder.http09_responses(true);
884 }
885
886 if config.http1_title_case_headers {
887 builder.http1_title_case_headers(true);
888 }
889
890 if config.http1_allow_obsolete_multiline_headers_in_responses {
891 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
892 }
893
894 if config.http1_ignore_invalid_headers_in_responses {
895 builder.http1_ignore_invalid_headers_in_responses(true);
896 }
897
898 if config.http1_allow_spaces_after_header_name_in_responses {
899 builder.http1_allow_spaces_after_header_name_in_responses(true);
900 }
901
902 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
903
904 Ok(Client {
905 inner: Arc::new(ClientRef {
906 accepts: config.accepts,
907 #[cfg(feature = "cookies")]
908 cookie_store: config.cookie_store,
909 #[cfg(feature = "http3")]
912 h3_client: match h3_connector {
913 Some(h3_connector) => {
914 Some(H3Client::new(h3_connector, config.pool_idle_timeout))
915 }
916 None => None,
917 },
918 hyper: builder.build(connector_builder.build(config.connector_layers)),
919 headers: config.headers,
920 redirect_policy: config.redirect_policy,
921 referer: config.referer,
922 read_timeout: config.read_timeout,
923 request_timeout: config.timeout,
924 proxies,
925 proxies_maybe_http_auth,
926 https_only: config.https_only,
927 }),
928 })
929 }
930
931 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
954 where
955 V: TryInto<HeaderValue>,
956 V::Error: Into<http::Error>,
957 {
958 match value.try_into() {
959 Ok(value) => {
960 self.config.headers.insert(USER_AGENT, value);
961 }
962 Err(e) => {
963 self.config.error = Some(crate::error::builder(e.into()));
964 }
965 };
966 self
967 }
968 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
992 for (key, value) in headers.iter() {
993 self.config.headers.insert(key, value.clone());
994 }
995 self
996 }
997
998 pub fn set_default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
1022 self.config.headers = headers;
1023 self
1024 }
1025
1026 #[cfg(feature = "cookies")]
1041 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1042 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
1043 if enable {
1044 self.cookie_provider(Arc::new(cookie::Jar::default()))
1045 } else {
1046 self.config.cookie_store = None;
1047 self
1048 }
1049 }
1050
1051 #[cfg(feature = "cookies")]
1065 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1066 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1067 mut self,
1068 cookie_store: Arc<C>,
1069 ) -> ClientBuilder {
1070 self.config.cookie_store = Some(cookie_store as _);
1071 self
1072 }
1073
1074 #[cfg(feature = "gzip")]
1091 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1092 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1093 self.config.accepts.gzip = enable;
1094 self
1095 }
1096
1097 #[cfg(feature = "brotli")]
1114 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1115 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1116 self.config.accepts.brotli = enable;
1117 self
1118 }
1119
1120 #[cfg(feature = "zstd")]
1137 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1138 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1139 self.config.accepts.zstd = enable;
1140 self
1141 }
1142
1143 #[cfg(feature = "deflate")]
1160 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1161 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1162 self.config.accepts.deflate = enable;
1163 self
1164 }
1165
1166 pub fn no_gzip(self) -> ClientBuilder {
1172 #[cfg(feature = "gzip")]
1173 {
1174 self.gzip(false)
1175 }
1176
1177 #[cfg(not(feature = "gzip"))]
1178 {
1179 self
1180 }
1181 }
1182
1183 pub fn no_brotli(self) -> ClientBuilder {
1189 #[cfg(feature = "brotli")]
1190 {
1191 self.brotli(false)
1192 }
1193
1194 #[cfg(not(feature = "brotli"))]
1195 {
1196 self
1197 }
1198 }
1199
1200 pub fn no_zstd(self) -> ClientBuilder {
1206 #[cfg(feature = "zstd")]
1207 {
1208 self.zstd(false)
1209 }
1210
1211 #[cfg(not(feature = "zstd"))]
1212 {
1213 self
1214 }
1215 }
1216
1217 pub fn no_deflate(self) -> ClientBuilder {
1223 #[cfg(feature = "deflate")]
1224 {
1225 self.deflate(false)
1226 }
1227
1228 #[cfg(not(feature = "deflate"))]
1229 {
1230 self
1231 }
1232 }
1233
1234 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1240 self.config.redirect_policy = policy;
1241 self
1242 }
1243
1244 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1248 self.config.referer = enable;
1249 self
1250 }
1251
1252 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1260 self.config.proxies.push(proxy);
1261 self.config.auto_sys_proxy = false;
1262 self
1263 }
1264
1265 pub fn no_proxy(mut self) -> ClientBuilder {
1273 self.config.proxies.clear();
1274 self.config.auto_sys_proxy = false;
1275 self
1276 }
1277
1278 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1287 self.config.timeout = Some(timeout);
1288 self
1289 }
1290
1291 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1299 self.config.read_timeout = Some(timeout);
1300 self
1301 }
1302
1303 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1312 self.config.connect_timeout = Some(timeout);
1313 self
1314 }
1315
1316 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1323 self.config.connection_verbose = verbose;
1324 self
1325 }
1326
1327 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1335 where
1336 D: Into<Option<Duration>>,
1337 {
1338 self.config.pool_idle_timeout = val.into();
1339 self
1340 }
1341
1342 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1344 self.config.pool_max_idle_per_host = max;
1345 self
1346 }
1347
1348 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1350 self.config.http1_title_case_headers = true;
1351 self
1352 }
1353
1354 pub fn http1_preserve_header_case(mut self) -> ClientBuilder {
1356 self.config.http1_preserve_header_case = true;
1357 self
1358 }
1359
1360 pub fn http1_preserve_header_order(mut self) -> ClientBuilder {
1362 self.config.http1_preserve_header_order = true;
1363 self
1364 }
1365
1366 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1372 mut self,
1373 value: bool,
1374 ) -> ClientBuilder {
1375 self.config
1376 .http1_allow_obsolete_multiline_headers_in_responses = value;
1377 self
1378 }
1379
1380 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1382 self.config.http1_ignore_invalid_headers_in_responses = value;
1383 self
1384 }
1385
1386 pub fn http1_allow_spaces_after_header_name_in_responses(
1392 mut self,
1393 value: bool,
1394 ) -> ClientBuilder {
1395 self.config
1396 .http1_allow_spaces_after_header_name_in_responses = value;
1397 self
1398 }
1399
1400 pub fn http1_only(mut self) -> ClientBuilder {
1402 self.config.http_version_pref = HttpVersionPref::Http1;
1403 self
1404 }
1405
1406 pub fn http09_responses(mut self) -> ClientBuilder {
1408 self.config.http09_responses = true;
1409 self
1410 }
1411
1412 #[cfg(feature = "http2")]
1414 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1415 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1416 self.config.http_version_pref = HttpVersionPref::Http2;
1417 self
1418 }
1419
1420 #[cfg(feature = "http3")]
1422 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1423 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1424 self.config.http_version_pref = HttpVersionPref::Http3;
1425 self
1426 }
1427
1428 #[cfg(feature = "http2")]
1432 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1433 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1434 self.config.http2_initial_stream_window_size = sz.into();
1435 self
1436 }
1437
1438 #[cfg(feature = "http2")]
1442 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1443 pub fn http2_initial_connection_window_size(
1444 mut self,
1445 sz: impl Into<Option<u32>>,
1446 ) -> ClientBuilder {
1447 self.config.http2_initial_connection_window_size = sz.into();
1448 self
1449 }
1450
1451 #[cfg(feature = "http2")]
1456 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1457 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1458 self.config.http2_adaptive_window = enabled;
1459 self
1460 }
1461
1462 #[cfg(feature = "http2")]
1466 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1467 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1468 self.config.http2_max_frame_size = sz.into();
1469 self
1470 }
1471
1472 #[cfg(feature = "http2")]
1476 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1477 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1478 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1479 self
1480 }
1481
1482 #[cfg(feature = "http2")]
1487 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1488 pub fn http2_keep_alive_interval(
1489 mut self,
1490 interval: impl Into<Option<Duration>>,
1491 ) -> ClientBuilder {
1492 self.config.http2_keep_alive_interval = interval.into();
1493 self
1494 }
1495
1496 #[cfg(feature = "http2")]
1502 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1503 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1504 self.config.http2_keep_alive_timeout = Some(timeout);
1505 self
1506 }
1507
1508 #[cfg(feature = "http2")]
1515 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1516 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1517 self.config.http2_keep_alive_while_idle = enabled;
1518 self
1519 }
1520
1521 #[cfg(feature = "http2")]
1524 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1525 pub fn set_host(mut self, enabled: bool) -> ClientBuilder {
1526 self.config.set_host = enabled;
1527 self
1528 }
1529
1530
1531 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1537 self.config.nodelay = enabled;
1538 self
1539 }
1540
1541 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1555 where
1556 T: Into<Option<IpAddr>>,
1557 {
1558 self.config.local_address = addr.into();
1559 self
1560 }
1561
1562 #[cfg(any(
1594 target_os = "android",
1595 target_os = "fuchsia",
1596 target_os = "illumos",
1597 target_os = "ios",
1598 target_os = "linux",
1599 target_os = "macos",
1600 target_os = "solaris",
1601 target_os = "tvos",
1602 target_os = "visionos",
1603 target_os = "watchos",
1604 ))]
1605 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1606 self.config.interface = Some(interface.to_string());
1607 self
1608 }
1609
1610 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1614 where
1615 D: Into<Option<Duration>>,
1616 {
1617 self.config.tcp_keepalive = val.into();
1618 self
1619 }
1620
1621 #[cfg(feature = "__tls")]
1633 #[cfg_attr(
1634 docsrs,
1635 doc(cfg(any(
1636 feature = "default-tls",
1637 feature = "native-tls",
1638 feature = "rustls-tls"
1639 )))
1640 )]
1641 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1642 self.config.root_certs.push(cert);
1643 self
1644 }
1645
1646 #[cfg(feature = "__rustls")]
1653 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1654 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1655 self.config.crls.push(crl);
1656 self
1657 }
1658
1659 #[cfg(feature = "__rustls")]
1666 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1667 pub fn add_crls(
1668 mut self,
1669 crls: impl IntoIterator<Item = CertificateRevocationList>,
1670 ) -> ClientBuilder {
1671 self.config.crls.extend(crls);
1672 self
1673 }
1674
1675 #[cfg(feature = "__tls")]
1693 #[cfg_attr(
1694 docsrs,
1695 doc(cfg(any(
1696 feature = "default-tls",
1697 feature = "native-tls",
1698 feature = "rustls-tls"
1699 )))
1700 )]
1701 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1702 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1703
1704 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1705 {
1706 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1707 }
1708
1709 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1710 {
1711 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1712 }
1713
1714 self
1715 }
1716
1717 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1721 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1722 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1723 self.config.tls_built_in_certs_webpki = enabled;
1724 self
1725 }
1726
1727 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1731 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1732 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1733 self.config.tls_built_in_certs_native = enabled;
1734 self
1735 }
1736
1737 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1744 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1745 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1746 self.config.identity = Some(identity);
1747 self
1748 }
1749
1750 #[cfg(feature = "__tls")]
1766 #[cfg_attr(
1767 docsrs,
1768 doc(cfg(any(
1769 feature = "default-tls",
1770 feature = "native-tls",
1771 feature = "rustls-tls"
1772 )))
1773 )]
1774 pub fn danger_accept_invalid_hostnames(
1775 mut self,
1776 accept_invalid_hostname: bool,
1777 ) -> ClientBuilder {
1778 self.config.hostname_verification = !accept_invalid_hostname;
1779 self
1780 }
1781
1782 #[cfg(feature = "__tls")]
1799 #[cfg_attr(
1800 docsrs,
1801 doc(cfg(any(
1802 feature = "default-tls",
1803 feature = "native-tls",
1804 feature = "rustls-tls"
1805 )))
1806 )]
1807 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1808 self.config.certs_verification = !accept_invalid_certs;
1809 self
1810 }
1811
1812 #[cfg(feature = "__tls")]
1821 #[cfg_attr(
1822 docsrs,
1823 doc(cfg(any(
1824 feature = "default-tls",
1825 feature = "native-tls",
1826 feature = "rustls-tls"
1827 )))
1828 )]
1829 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1830 self.config.tls_sni = tls_sni;
1831 self
1832 }
1833
1834 #[cfg(feature = "__tls")]
1850 #[cfg_attr(
1851 docsrs,
1852 doc(cfg(any(
1853 feature = "default-tls",
1854 feature = "native-tls",
1855 feature = "rustls-tls"
1856 )))
1857 )]
1858 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1859 self.config.min_tls_version = Some(version);
1860 self
1861 }
1862
1863 #[cfg(feature = "__tls")]
1882 #[cfg_attr(
1883 docsrs,
1884 doc(cfg(any(
1885 feature = "default-tls",
1886 feature = "native-tls",
1887 feature = "rustls-tls"
1888 )))
1889 )]
1890 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1891 self.config.max_tls_version = Some(version);
1892 self
1893 }
1894
1895 #[cfg(feature = "native-tls")]
1904 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1905 pub fn use_native_tls(mut self) -> ClientBuilder {
1906 self.config.tls = TlsBackend::Default;
1907 self
1908 }
1909
1910 #[cfg(feature = "__rustls")]
1919 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1920 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1921 self.config.tls = TlsBackend::Rustls;
1922 self
1923 }
1924
1925 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
1944 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1945 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
1946 let mut tls = Some(tls);
1947 #[cfg(feature = "native-tls")]
1948 {
1949 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
1950 let tls = conn.take().expect("is definitely Some");
1951 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
1952 self.config.tls = tls;
1953 return self;
1954 }
1955 }
1956 #[cfg(feature = "__rustls")]
1957 {
1958 if let Some(conn) =
1959 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
1960 {
1961 let tls = conn.take().expect("is definitely Some");
1962 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
1963 self.config.tls = tls;
1964 return self;
1965 }
1966 }
1967
1968 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
1970 self
1971 }
1972
1973 #[cfg(feature = "__tls")]
1980 #[cfg_attr(
1981 docsrs,
1982 doc(cfg(any(
1983 feature = "default-tls",
1984 feature = "native-tls",
1985 feature = "rustls-tls"
1986 )))
1987 )]
1988 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
1989 self.config.tls_info = tls_info;
1990 self
1991 }
1992
1993 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
1997 self.config.https_only = enabled;
1998 self
1999 }
2000
2001 #[doc(hidden)]
2002 #[cfg(feature = "hickory-dns")]
2003 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2004 #[deprecated(note = "use `hickory_dns` instead")]
2005 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
2006 self.config.hickory_dns = enable;
2007 self
2008 }
2009
2010 #[cfg(feature = "hickory-dns")]
2024 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
2025 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
2026 self.config.hickory_dns = enable;
2027 self
2028 }
2029
2030 #[doc(hidden)]
2031 #[deprecated(note = "use `no_hickory_dns` instead")]
2032 pub fn no_trust_dns(self) -> ClientBuilder {
2033 self.no_hickory_dns()
2034 }
2035
2036 pub fn no_hickory_dns(self) -> ClientBuilder {
2042 #[cfg(feature = "hickory-dns")]
2043 {
2044 self.hickory_dns(false)
2045 }
2046
2047 #[cfg(not(feature = "hickory-dns"))]
2048 {
2049 self
2050 }
2051 }
2052
2053 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
2058 self.resolve_to_addrs(domain, &[addr])
2059 }
2060
2061 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
2066 self.config
2067 .dns_overrides
2068 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
2069 self
2070 }
2071
2072 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
2078 self.config.dns_resolver = Some(resolver as _);
2079 self
2080 }
2081
2082 #[cfg(feature = "http3")]
2087 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2088 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2089 self.config.tls_enable_early_data = enabled;
2090 self
2091 }
2092
2093 #[cfg(feature = "http3")]
2099 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2100 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2101 self.config.quic_max_idle_timeout = Some(value);
2102 self
2103 }
2104
2105 #[cfg(feature = "http3")]
2116 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2117 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2118 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2119 self
2120 }
2121
2122 #[cfg(feature = "http3")]
2133 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2134 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2135 self.config.quic_receive_window = Some(value.try_into().unwrap());
2136 self
2137 }
2138
2139 #[cfg(feature = "http3")]
2145 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2146 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2147 self.config.quic_send_window = Some(value);
2148 self
2149 }
2150
2151 #[cfg(feature = "http3")]
2159 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2160 pub fn http3_congestion_bbr(mut self) -> ClientBuilder {
2161 self.config.quic_congestion_bbr = true;
2162 self
2163 }
2164
2165 #[cfg(feature = "http3")]
2175 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2176 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2177 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2178 self
2179 }
2180
2181 #[cfg(feature = "http3")]
2193 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2194 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2195 self.config.h3_send_grease = Some(enabled);
2196 self
2197 }
2198
2199 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2223 where
2224 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2225 L::Service:
2226 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2227 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2228 {
2229 let layer = BoxCloneSyncServiceLayer::new(layer);
2230
2231 self.config.connector_layers.push(layer);
2232
2233 self
2234 }
2235}
2236
2237type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2238
2239impl Default for Client {
2240 fn default() -> Self {
2241 Self::new()
2242 }
2243}
2244
2245impl Client {
2246 pub fn new() -> Client {
2256 ClientBuilder::new().build().expect("Client::new()")
2257 }
2258
2259 pub fn builder() -> ClientBuilder {
2263 ClientBuilder::new()
2264 }
2265
2266 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2272 self.request(Method::GET, url)
2273 }
2274
2275 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2281 self.request(Method::POST, url)
2282 }
2283
2284 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2290 self.request(Method::PUT, url)
2291 }
2292
2293 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2299 self.request(Method::PATCH, url)
2300 }
2301
2302 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2308 self.request(Method::DELETE, url)
2309 }
2310
2311 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2317 self.request(Method::HEAD, url)
2318 }
2319
2320 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2329 let req = url.into_url().map(move |url| Request::new(method, url));
2330 RequestBuilder::new(self.clone(), req)
2331 }
2332
2333 pub fn execute(
2346 &self,
2347 request: Request,
2348 ) -> impl Future<Output = Result<Response, crate::Error>> {
2349 self.execute_request(request)
2350 }
2351
2352 pub(super) fn execute_request(&self, req: Request) -> Pending {
2353 let (method, url, mut headers, body, timeout, version, _extensions) = req.pieces();
2354 if url.scheme() != "http" && url.scheme() != "https" {
2355 return Pending::new_err(error::url_bad_scheme(url));
2356 }
2357
2358 if self.inner.https_only && url.scheme() != "https" {
2360 return Pending::new_err(error::url_bad_scheme(url));
2361 }
2362
2363 for (key, value) in &self.inner.headers {
2366 if let Entry::Vacant(entry) = headers.entry(key) {
2367 entry.insert(value.clone());
2368 }
2369 }
2370
2371 #[cfg(feature = "cookies")]
2373 {
2374 if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
2375 if headers.get(crate::header::COOKIE).is_none() {
2376 add_cookie_header(&mut headers, &**cookie_store, &url);
2377 }
2378 }
2379 }
2380
2381 let accept_encoding = self.inner.accepts.as_str();
2382
2383 if let Some(accept_encoding) = accept_encoding {
2384 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2385 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2386 }
2387 }
2388
2389 let uri = match try_uri(&url) {
2390 Ok(uri) => uri,
2391 _ => return Pending::new_err(error::url_invalid_uri(url)),
2392 };
2393
2394 let (reusable, body) = match body {
2395 Some(body) => {
2396 let (reusable, body) = body.try_reuse();
2397 (Some(reusable), body)
2398 }
2399 None => (None, Body::empty()),
2400 };
2401
2402 self.proxy_auth(&uri, &mut headers);
2403
2404 let builder = hyper::Request::builder()
2405 .method(method.clone())
2406 .uri(uri)
2407 .version(version);
2408
2409 let in_flight = match version {
2410 #[cfg(feature = "http3")]
2411 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2412 let mut req = builder.body(body).expect("valid request parts");
2413 *req.headers_mut() = headers.clone();
2414 ResponseFuture::H3(self.inner.h3_client.as_ref().unwrap().request(req))
2415 }
2416 _ => {
2417 let mut req = builder.body(body).expect("valid request parts");
2418 *req.headers_mut() = headers.clone();
2419 ResponseFuture::Default(self.inner.hyper.request(req))
2420 }
2421 };
2422
2423 let total_timeout = timeout
2424 .or(self.inner.request_timeout)
2425 .map(tokio::time::sleep)
2426 .map(Box::pin);
2427
2428 let read_timeout_fut = self
2429 .inner
2430 .read_timeout
2431 .map(tokio::time::sleep)
2432 .map(Box::pin);
2433
2434 Pending {
2435 inner: PendingInner::Request(PendingRequest {
2436 method,
2437 url,
2438 headers,
2439 body: reusable,
2440
2441 urls: Vec::new(),
2442
2443 retry_count: 0,
2444
2445 client: self.inner.clone(),
2446
2447 in_flight,
2448 total_timeout,
2449 read_timeout_fut,
2450 read_timeout: self.inner.read_timeout,
2451 }),
2452 }
2453 }
2454
2455 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2456 if !self.inner.proxies_maybe_http_auth {
2457 return;
2458 }
2459
2460 if dst.scheme() != Some(&Scheme::HTTP) {
2464 return;
2465 }
2466
2467 if headers.contains_key(PROXY_AUTHORIZATION) {
2468 return;
2469 }
2470
2471 for proxy in self.inner.proxies.iter() {
2472 if proxy.is_match(dst) {
2473 if let Some(header) = proxy.http_basic_auth(dst) {
2474 headers.insert(PROXY_AUTHORIZATION, header);
2475 }
2476
2477 break;
2478 }
2479 }
2480 }
2481}
2482
2483impl fmt::Debug for Client {
2484 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2485 let mut builder = f.debug_struct("Client");
2486 self.inner.fmt_fields(&mut builder);
2487 builder.finish()
2488 }
2489}
2490
2491impl tower_service::Service<Request> for Client {
2492 type Response = Response;
2493 type Error = crate::Error;
2494 type Future = Pending;
2495
2496 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2497 Poll::Ready(Ok(()))
2498 }
2499
2500 fn call(&mut self, req: Request) -> Self::Future {
2501 self.execute_request(req)
2502 }
2503}
2504
2505impl tower_service::Service<Request> for &'_ Client {
2506 type Response = Response;
2507 type Error = crate::Error;
2508 type Future = Pending;
2509
2510 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2511 Poll::Ready(Ok(()))
2512 }
2513
2514 fn call(&mut self, req: Request) -> Self::Future {
2515 self.execute_request(req)
2516 }
2517}
2518
2519impl fmt::Debug for ClientBuilder {
2520 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2521 let mut builder = f.debug_struct("ClientBuilder");
2522 self.config.fmt_fields(&mut builder);
2523 builder.finish()
2524 }
2525}
2526
2527impl Config {
2528 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2529 #[cfg(feature = "cookies")]
2533 {
2534 if let Some(_) = self.cookie_store {
2535 f.field("cookie_store", &true);
2536 }
2537 }
2538
2539 f.field("accepts", &self.accepts);
2540
2541 if !self.proxies.is_empty() {
2542 f.field("proxies", &self.proxies);
2543 }
2544
2545 if !self.redirect_policy.is_default() {
2546 f.field("redirect_policy", &self.redirect_policy);
2547 }
2548
2549 if self.referer {
2550 f.field("referer", &true);
2551 }
2552
2553 f.field("default_headers", &self.headers);
2554
2555 if self.http1_title_case_headers {
2556 f.field("http1_title_case_headers", &true);
2557 }
2558
2559 if self.http1_preserve_header_case {
2560 f.field("http1_preserve_header_case", &true);
2561 }
2562
2563 if self.http1_allow_obsolete_multiline_headers_in_responses {
2564 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2565 }
2566
2567 if self.http1_ignore_invalid_headers_in_responses {
2568 f.field("http1_ignore_invalid_headers_in_responses", &true);
2569 }
2570
2571 if self.http1_allow_spaces_after_header_name_in_responses {
2572 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2573 }
2574
2575 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2576 f.field("http1_only", &true);
2577 }
2578
2579 #[cfg(feature = "http2")]
2580 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2581 f.field("http2_prior_knowledge", &true);
2582 }
2583
2584 if let Some(ref d) = self.connect_timeout {
2585 f.field("connect_timeout", d);
2586 }
2587
2588 if let Some(ref d) = self.timeout {
2589 f.field("timeout", d);
2590 }
2591
2592 if let Some(ref v) = self.local_address {
2593 f.field("local_address", v);
2594 }
2595
2596 #[cfg(any(
2597 target_os = "android",
2598 target_os = "fuchsia",
2599 target_os = "illumos",
2600 target_os = "ios",
2601 target_os = "linux",
2602 target_os = "macos",
2603 target_os = "solaris",
2604 target_os = "tvos",
2605 target_os = "visionos",
2606 target_os = "watchos",
2607 ))]
2608 if let Some(ref v) = self.interface {
2609 f.field("interface", v);
2610 }
2611
2612 if self.nodelay {
2613 f.field("tcp_nodelay", &true);
2614 }
2615
2616 #[cfg(feature = "__tls")]
2617 {
2618 if !self.hostname_verification {
2619 f.field("danger_accept_invalid_hostnames", &true);
2620 }
2621 }
2622
2623 #[cfg(feature = "__tls")]
2624 {
2625 if !self.certs_verification {
2626 f.field("danger_accept_invalid_certs", &true);
2627 }
2628
2629 if let Some(ref min_tls_version) = self.min_tls_version {
2630 f.field("min_tls_version", min_tls_version);
2631 }
2632
2633 if let Some(ref max_tls_version) = self.max_tls_version {
2634 f.field("max_tls_version", max_tls_version);
2635 }
2636
2637 f.field("tls_sni", &self.tls_sni);
2638
2639 f.field("tls_info", &self.tls_info);
2640 }
2641
2642 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2643 {
2644 f.field("tls_backend", &self.tls);
2645 }
2646
2647 if !self.dns_overrides.is_empty() {
2648 f.field("dns_overrides", &self.dns_overrides);
2649 }
2650
2651 #[cfg(feature = "http3")]
2652 {
2653 if self.tls_enable_early_data {
2654 f.field("tls_enable_early_data", &true);
2655 }
2656 }
2657 }
2658}
2659
2660struct ClientRef {
2661 accepts: Accepts,
2662 #[cfg(feature = "cookies")]
2663 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2664 headers: HeaderMap,
2665 hyper: HyperClient,
2666 #[cfg(feature = "http3")]
2667 h3_client: Option<H3Client>,
2668 redirect_policy: redirect::Policy,
2669 referer: bool,
2670 request_timeout: Option<Duration>,
2671 read_timeout: Option<Duration>,
2672 proxies: Arc<Vec<Proxy>>,
2673 proxies_maybe_http_auth: bool,
2674 https_only: bool,
2675}
2676
2677impl ClientRef {
2678 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2679 #[cfg(feature = "cookies")]
2683 {
2684 if let Some(_) = self.cookie_store {
2685 f.field("cookie_store", &true);
2686 }
2687 }
2688
2689 f.field("accepts", &self.accepts);
2690
2691 if !self.proxies.is_empty() {
2692 f.field("proxies", &self.proxies);
2693 }
2694
2695 if !self.redirect_policy.is_default() {
2696 f.field("redirect_policy", &self.redirect_policy);
2697 }
2698
2699 if self.referer {
2700 f.field("referer", &true);
2701 }
2702
2703 f.field("default_headers", &self.headers);
2704
2705 if let Some(ref d) = self.request_timeout {
2706 f.field("timeout", d);
2707 }
2708
2709 if let Some(ref d) = self.read_timeout {
2710 f.field("read_timeout", d);
2711 }
2712 }
2713}
2714
2715pin_project! {
2716 pub struct Pending {
2717 #[pin]
2718 inner: PendingInner,
2719 }
2720}
2721
2722enum PendingInner {
2723 Request(PendingRequest),
2724 Error(Option<crate::Error>),
2725}
2726
2727pin_project! {
2728 struct PendingRequest {
2729 method: Method,
2730 url: Url,
2731 headers: HeaderMap,
2732 body: Option<Option<Bytes>>,
2733
2734 urls: Vec<Url>,
2735
2736 retry_count: usize,
2737
2738 client: Arc<ClientRef>,
2739
2740 #[pin]
2741 in_flight: ResponseFuture,
2742 #[pin]
2743 total_timeout: Option<Pin<Box<Sleep>>>,
2744 #[pin]
2745 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2746 read_timeout: Option<Duration>,
2747 }
2748}
2749
2750enum ResponseFuture {
2751 Default(HyperResponseFuture),
2752 #[cfg(feature = "http3")]
2753 H3(H3ResponseFuture),
2754}
2755
2756impl PendingRequest {
2757 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2758 self.project().in_flight
2759 }
2760
2761 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2762 self.project().total_timeout
2763 }
2764
2765 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2766 self.project().read_timeout_fut
2767 }
2768
2769 fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
2770 self.project().urls
2771 }
2772
2773 fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
2774 self.project().headers
2775 }
2776
2777 #[cfg(any(feature = "http2", feature = "http3"))]
2778 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2779 use log::trace;
2780
2781 if !is_retryable_error(err) {
2782 return false;
2783 }
2784
2785 trace!("can retry {err:?}");
2786
2787 let body = match self.body {
2788 Some(Some(ref body)) => Body::reusable(body.clone()),
2789 Some(None) => {
2790 debug!("error was retryable, but body not reusable");
2791 return false;
2792 }
2793 None => Body::empty(),
2794 };
2795
2796 if self.retry_count >= 2 {
2797 trace!("retry count too high");
2798 return false;
2799 }
2800 self.retry_count += 1;
2801
2802 let uri = try_uri(&self.url).expect("URL was already validated as URI");
2804
2805 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
2806 #[cfg(feature = "http3")]
2807 ResponseFuture::H3(_) => {
2808 let mut req = hyper::Request::builder()
2809 .method(self.method.clone())
2810 .uri(uri)
2811 .body(body)
2812 .expect("valid request parts");
2813 *req.headers_mut() = self.headers.clone();
2814 ResponseFuture::H3(
2815 self.client
2816 .h3_client
2817 .as_ref()
2818 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2819 .request(req),
2820 )
2821 }
2822 _ => {
2823 let mut req = hyper::Request::builder()
2824 .method(self.method.clone())
2825 .uri(uri)
2826 .body(body)
2827 .expect("valid request parts");
2828 *req.headers_mut() = self.headers.clone();
2829 ResponseFuture::Default(self.client.hyper.request(req))
2830 }
2831 };
2832
2833 true
2834 }
2835}
2836
2837#[cfg(any(feature = "http2", feature = "http3"))]
2838fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
2839 let err = if let Some(err) = err.source() {
2841 err
2842 } else {
2843 return false;
2844 };
2845
2846 #[cfg(feature = "http3")]
2847 if let Some(cause) = err.source() {
2848 if let Some(err) = cause.downcast_ref::<h3::Error>() {
2849 debug!("determining if HTTP/3 error {err} can be retried");
2850 return err.to_string().as_str() == "timeout";
2852 }
2853 }
2854
2855 #[cfg(feature = "http2")]
2856 if let Some(cause) = err.source() {
2857 if let Some(err) = cause.downcast_ref::<h2::Error>() {
2858 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
2860 return true;
2861 }
2862
2863 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
2866 {
2867 return true;
2868 }
2869 }
2870 }
2871 false
2872}
2873
2874impl Pending {
2875 pub(super) fn new_err(err: crate::Error) -> Pending {
2876 Pending {
2877 inner: PendingInner::Error(Some(err)),
2878 }
2879 }
2880
2881 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2882 self.project().inner
2883 }
2884}
2885
2886impl Future for Pending {
2887 type Output = Result<Response, crate::Error>;
2888
2889 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2890 let inner = self.inner();
2891 match inner.get_mut() {
2892 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2893 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2894 .take()
2895 .expect("Pending error polled more than once"))),
2896 }
2897 }
2898}
2899
2900impl Future for PendingRequest {
2901 type Output = Result<Response, crate::Error>;
2902
2903 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2904 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
2905 if let Poll::Ready(()) = delay.poll(cx) {
2906 return Poll::Ready(Err(
2907 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2908 ));
2909 }
2910 }
2911
2912 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
2913 if let Poll::Ready(()) = delay.poll(cx) {
2914 return Poll::Ready(Err(
2915 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2916 ));
2917 }
2918 }
2919
2920 loop {
2921 let res = match self.as_mut().in_flight().get_mut() {
2922 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
2923 Poll::Ready(Err(e)) => {
2924 #[cfg(feature = "http2")]
2925 if self.as_mut().retry_error(&e) {
2926 continue;
2927 }
2928 return Poll::Ready(Err(
2929 crate::error::request(e).with_url(self.url.clone())
2930 ));
2931 }
2932 Poll::Ready(Ok(res)) => res.map(super::body::boxed),
2933 Poll::Pending => return Poll::Pending,
2934 },
2935 #[cfg(feature = "http3")]
2936 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
2937 Poll::Ready(Err(e)) => {
2938 if self.as_mut().retry_error(&e) {
2939 continue;
2940 }
2941 return Poll::Ready(Err(
2942 crate::error::request(e).with_url(self.url.clone())
2943 ));
2944 }
2945 Poll::Ready(Ok(res)) => res,
2946 Poll::Pending => return Poll::Pending,
2947 },
2948 };
2949
2950 #[cfg(feature = "cookies")]
2951 {
2952 if let Some(ref cookie_store) = self.client.cookie_store {
2953 let mut cookies =
2954 cookie::extract_response_cookie_headers(&res.headers()).peekable();
2955 if cookies.peek().is_some() {
2956 cookie_store.set_cookies(&mut cookies, &self.url);
2957 }
2958 }
2959 }
2960 let should_redirect = match res.status() {
2961 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
2962 self.body = None;
2963 for header in &[
2964 TRANSFER_ENCODING,
2965 CONTENT_ENCODING,
2966 CONTENT_TYPE,
2967 CONTENT_LENGTH,
2968 ] {
2969 self.headers.remove(header);
2970 }
2971
2972 match self.method {
2973 Method::GET | Method::HEAD => {}
2974 _ => {
2975 self.method = Method::GET;
2976 }
2977 }
2978 true
2979 }
2980 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
2981 match self.body {
2982 Some(Some(_)) | None => true,
2983 Some(None) => false,
2984 }
2985 }
2986 _ => false,
2987 };
2988 if should_redirect {
2989 let loc = res.headers().get(LOCATION).and_then(|val| {
2990 let loc = (|| -> Option<Url> {
2991 self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
2995 })();
2996
2997 let loc = loc.and_then(|url| {
3001 if try_uri(&url).is_ok() {
3002 Some(url)
3003 } else {
3004 None
3005 }
3006 });
3007
3008 if loc.is_none() {
3009 debug!("Location header had invalid URI: {val:?}");
3010 }
3011 loc
3012 });
3013 if let Some(loc) = loc {
3014 if self.client.referer {
3015 if let Some(referer) = make_referer(&loc, &self.url) {
3016 self.headers.insert(REFERER, referer);
3017 }
3018 }
3019 let url = self.url.clone();
3020 self.as_mut().urls().push(url);
3021 let action = self
3022 .client
3023 .redirect_policy
3024 .check(res.status(), &loc, &self.urls);
3025
3026 match action {
3027 redirect::ActionKind::Follow => {
3028 debug!("redirecting '{}' to '{}'", self.url, loc);
3029
3030 if loc.scheme() != "http" && loc.scheme() != "https" {
3031 return Poll::Ready(Err(error::url_bad_scheme(loc)));
3032 }
3033
3034 if self.client.https_only && loc.scheme() != "https" {
3035 return Poll::Ready(Err(error::redirect(
3036 error::url_bad_scheme(loc.clone()),
3037 loc,
3038 )));
3039 }
3040
3041 self.url = loc;
3042 let mut headers =
3043 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
3044
3045 remove_sensitive_headers(&mut headers, &self.url, &self.urls);
3046 let uri = try_uri(&self.url)?;
3047 let body = match self.body {
3048 Some(Some(ref body)) => Body::reusable(body.clone()),
3049 _ => Body::empty(),
3050 };
3051
3052 #[cfg(feature = "cookies")]
3054 {
3055 if let Some(ref cookie_store) = self.client.cookie_store {
3056 add_cookie_header(&mut headers, &**cookie_store, &self.url);
3057 }
3058 }
3059
3060 *self.as_mut().in_flight().get_mut() =
3061 match *self.as_mut().in_flight().as_ref() {
3062 #[cfg(feature = "http3")]
3063 ResponseFuture::H3(_) => {
3064 let mut req = hyper::Request::builder()
3065 .method(self.method.clone())
3066 .uri(uri.clone())
3067 .body(body)
3068 .expect("valid request parts");
3069 *req.headers_mut() = headers.clone();
3070 std::mem::swap(self.as_mut().headers(), &mut headers);
3071 ResponseFuture::H3(self.client.h3_client
3072 .as_ref()
3073 .expect("H3 client must exists, otherwise we can't have a h3 request here")
3074 .request(req))
3075 }
3076 _ => {
3077 let mut req = hyper::Request::builder()
3078 .method(self.method.clone())
3079 .uri(uri.clone())
3080 .body(body)
3081 .expect("valid request parts");
3082 *req.headers_mut() = headers.clone();
3083 std::mem::swap(self.as_mut().headers(), &mut headers);
3084 ResponseFuture::Default(self.client.hyper.request(req))
3085 }
3086 };
3087
3088 continue;
3089 }
3090 redirect::ActionKind::Stop => {
3091 debug!("redirect policy disallowed redirection to '{loc}'");
3092 }
3093 redirect::ActionKind::Error(err) => {
3094 return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
3095 }
3096 }
3097 }
3098 }
3099
3100 let res = Response::new(
3101 res,
3102 self.url.clone(),
3103 self.client.accepts,
3104 self.total_timeout.take(),
3105 self.read_timeout,
3106 );
3107 return Poll::Ready(Ok(res));
3108 }
3109 }
3110}
3111
3112impl fmt::Debug for Pending {
3113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3114 match self.inner {
3115 PendingInner::Request(ref req) => f
3116 .debug_struct("Pending")
3117 .field("method", &req.method)
3118 .field("url", &req.url)
3119 .finish(),
3120 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3121 }
3122 }
3123}
3124
3125fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
3126 if next.scheme() == "http" && previous.scheme() == "https" {
3127 return None;
3128 }
3129
3130 let mut referer = previous.clone();
3131 let _ = referer.set_username("");
3132 let _ = referer.set_password(None);
3133 referer.set_fragment(None);
3134 referer.as_str().parse().ok()
3135}
3136
3137#[cfg(feature = "cookies")]
3138fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
3139 if let Some(header) = cookie_store.cookies(url) {
3140 headers.insert(crate::header::COOKIE, header);
3141 }
3142}
3143
3144#[cfg(test)]
3145mod tests {
3146 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
3147
3148 #[tokio::test]
3149 async fn execute_request_rejects_invalid_urls() {
3150 let url_str = "hxxps://www.rust-lang.org/";
3151 let url = url::Url::parse(url_str).unwrap();
3152 let result = crate::get(url.clone()).await;
3153
3154 assert!(result.is_err());
3155 let err = result.err().unwrap();
3156 assert!(err.is_builder());
3157 assert_eq!(url_str, err.url().unwrap().as_str());
3158 }
3159
3160 #[tokio::test]
3162 async fn execute_request_rejects_invalid_hostname() {
3163 let url_str = "https://{{hostname}}/";
3164 let url = url::Url::parse(url_str).unwrap();
3165 let result = crate::get(url.clone()).await;
3166
3167 assert!(result.is_err());
3168 let err = result.err().unwrap();
3169 assert!(err.is_builder());
3170 assert_eq!(url_str, err.url().unwrap().as_str());
3171 }
3172}