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_title_case_headers: bool,
141 http1_allow_obsolete_multiline_headers_in_responses: bool,
142 http1_ignore_invalid_headers_in_responses: bool,
143 http1_allow_spaces_after_header_name_in_responses: bool,
144 #[cfg(feature = "http2")]
145 http2_initial_stream_window_size: Option<u32>,
146 #[cfg(feature = "http2")]
147 http2_initial_connection_window_size: Option<u32>,
148 #[cfg(feature = "http2")]
149 http2_adaptive_window: bool,
150 #[cfg(feature = "http2")]
151 http2_max_frame_size: Option<u32>,
152 #[cfg(feature = "http2")]
153 http2_max_header_list_size: Option<u32>,
154 #[cfg(feature = "http2")]
155 http2_keep_alive_interval: Option<Duration>,
156 #[cfg(feature = "http2")]
157 http2_keep_alive_timeout: Option<Duration>,
158 #[cfg(feature = "http2")]
159 http2_keep_alive_while_idle: bool,
160 local_address: Option<IpAddr>,
161 #[cfg(any(
162 target_os = "android",
163 target_os = "fuchsia",
164 target_os = "illumos",
165 target_os = "ios",
166 target_os = "linux",
167 target_os = "macos",
168 target_os = "solaris",
169 target_os = "tvos",
170 target_os = "visionos",
171 target_os = "watchos",
172 ))]
173 interface: Option<String>,
174 nodelay: bool,
175 #[cfg(feature = "cookies")]
176 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
177 hickory_dns: bool,
178 error: Option<crate::Error>,
179 https_only: bool,
180 #[cfg(feature = "http3")]
181 tls_enable_early_data: bool,
182 #[cfg(feature = "http3")]
183 quic_max_idle_timeout: Option<Duration>,
184 #[cfg(feature = "http3")]
185 quic_stream_receive_window: Option<VarInt>,
186 #[cfg(feature = "http3")]
187 quic_receive_window: Option<VarInt>,
188 #[cfg(feature = "http3")]
189 quic_send_window: Option<u64>,
190 #[cfg(feature = "http3")]
191 h3_max_field_section_size: Option<u64>,
192 #[cfg(feature = "http3")]
193 h3_send_grease: Option<bool>,
194 dns_overrides: HashMap<String, Vec<SocketAddr>>,
195 dns_resolver: Option<Arc<dyn Resolve>>,
196}
197
198impl Default for ClientBuilder {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204impl ClientBuilder {
205 pub fn new() -> Self {
209 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
210 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
211
212 ClientBuilder {
213 config: Config {
214 error: None,
215 accepts: Accepts::default(),
216 headers,
217 #[cfg(feature = "__tls")]
218 hostname_verification: true,
219 #[cfg(feature = "__tls")]
220 certs_verification: true,
221 #[cfg(feature = "__tls")]
222 tls_sni: true,
223 connect_timeout: None,
224 connection_verbose: false,
225 pool_idle_timeout: Some(Duration::from_secs(90)),
226 pool_max_idle_per_host: usize::MAX,
227 tcp_keepalive: None, proxies: Vec::new(),
231 auto_sys_proxy: true,
232 redirect_policy: redirect::Policy::default(),
233 referer: true,
234 read_timeout: None,
235 timeout: None,
236 #[cfg(feature = "__tls")]
237 root_certs: Vec::new(),
238 #[cfg(feature = "__tls")]
239 tls_built_in_root_certs: true,
240 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
241 tls_built_in_certs_webpki: true,
242 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
243 tls_built_in_certs_native: true,
244 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
245 identity: None,
246 #[cfg(feature = "__rustls")]
247 crls: vec![],
248 #[cfg(feature = "__tls")]
249 min_tls_version: None,
250 #[cfg(feature = "__tls")]
251 max_tls_version: None,
252 #[cfg(feature = "__tls")]
253 tls_info: false,
254 #[cfg(feature = "__tls")]
255 tls: TlsBackend::default(),
256 connector_layers: Vec::new(),
257 http_version_pref: HttpVersionPref::All,
258 http09_responses: false,
259 http1_title_case_headers: false,
260 http1_allow_obsolete_multiline_headers_in_responses: false,
261 http1_ignore_invalid_headers_in_responses: false,
262 http1_allow_spaces_after_header_name_in_responses: false,
263 #[cfg(feature = "http2")]
264 http2_initial_stream_window_size: None,
265 #[cfg(feature = "http2")]
266 http2_initial_connection_window_size: None,
267 #[cfg(feature = "http2")]
268 http2_adaptive_window: false,
269 #[cfg(feature = "http2")]
270 http2_max_frame_size: None,
271 #[cfg(feature = "http2")]
272 http2_max_header_list_size: None,
273 #[cfg(feature = "http2")]
274 http2_keep_alive_interval: None,
275 #[cfg(feature = "http2")]
276 http2_keep_alive_timeout: None,
277 #[cfg(feature = "http2")]
278 http2_keep_alive_while_idle: false,
279 local_address: None,
280 #[cfg(any(
281 target_os = "android",
282 target_os = "fuchsia",
283 target_os = "illumos",
284 target_os = "ios",
285 target_os = "linux",
286 target_os = "macos",
287 target_os = "solaris",
288 target_os = "tvos",
289 target_os = "visionos",
290 target_os = "watchos",
291 ))]
292 interface: None,
293 nodelay: true,
294 hickory_dns: cfg!(feature = "hickory-dns"),
295 #[cfg(feature = "cookies")]
296 cookie_store: None,
297 https_only: false,
298 dns_overrides: HashMap::new(),
299 #[cfg(feature = "http3")]
300 tls_enable_early_data: false,
301 #[cfg(feature = "http3")]
302 quic_max_idle_timeout: None,
303 #[cfg(feature = "http3")]
304 quic_stream_receive_window: None,
305 #[cfg(feature = "http3")]
306 quic_receive_window: None,
307 #[cfg(feature = "http3")]
308 quic_send_window: None,
309 #[cfg(feature = "http3")]
310 h3_max_field_section_size: None,
311 #[cfg(feature = "http3")]
312 h3_send_grease: None,
313 dns_resolver: None,
314 },
315 }
316 }
317}
318
319impl ClientBuilder {
320 pub fn build(self) -> crate::Result<Client> {
327 let config = self.config;
328
329 if let Some(err) = config.error {
330 return Err(err);
331 }
332
333 let mut proxies = config.proxies;
334 if config.auto_sys_proxy {
335 proxies.push(Proxy::system());
336 }
337 let proxies = Arc::new(proxies);
338
339 #[allow(unused)]
340 #[cfg(feature = "http3")]
341 let mut h3_connector = None;
342
343 let mut connector_builder = {
344 #[cfg(feature = "__tls")]
345 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
346 headers.get(USER_AGENT).cloned()
347 }
348
349 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
350 false => Arc::new(GaiResolver::new()),
351 #[cfg(feature = "hickory-dns")]
352 true => Arc::new(HickoryDnsResolver::default()),
353 #[cfg(not(feature = "hickory-dns"))]
354 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
355 };
356 if let Some(dns_resolver) = config.dns_resolver {
357 resolver = dns_resolver;
358 }
359 if !config.dns_overrides.is_empty() {
360 resolver = Arc::new(DnsResolverWithOverrides::new(
361 resolver,
362 config.dns_overrides,
363 ));
364 }
365 let mut http = HttpConnector::new_with_resolver(DynResolver::new(resolver.clone()));
366 http.set_connect_timeout(config.connect_timeout);
367
368 #[cfg(all(feature = "http3", feature = "__rustls"))]
369 let build_h3_connector =
370 |resolver,
371 tls,
372 quic_max_idle_timeout: Option<Duration>,
373 quic_stream_receive_window,
374 quic_receive_window,
375 quic_send_window,
376 h3_max_field_section_size,
377 h3_send_grease,
378 local_address,
379 http_version_pref: &HttpVersionPref| {
380 let mut transport_config = TransportConfig::default();
381
382 if let Some(max_idle_timeout) = quic_max_idle_timeout {
383 transport_config.max_idle_timeout(Some(
384 max_idle_timeout.try_into().map_err(error::builder)?,
385 ));
386 }
387
388 if let Some(stream_receive_window) = quic_stream_receive_window {
389 transport_config.stream_receive_window(stream_receive_window);
390 }
391
392 if let Some(receive_window) = quic_receive_window {
393 transport_config.receive_window(receive_window);
394 }
395
396 if let Some(send_window) = quic_send_window {
397 transport_config.send_window(send_window);
398 }
399
400 let mut h3_client_config = H3ClientConfig::default();
401
402 if let Some(max_field_section_size) = h3_max_field_section_size {
403 h3_client_config.max_field_section_size = Some(max_field_section_size);
404 }
405
406 if let Some(send_grease) = h3_send_grease {
407 h3_client_config.send_grease = Some(send_grease);
408 }
409
410 let res = H3Connector::new(
411 DynResolver::new(resolver),
412 tls,
413 local_address,
414 transport_config,
415 h3_client_config,
416 );
417
418 match res {
419 Ok(connector) => Ok(Some(connector)),
420 Err(err) => {
421 if let HttpVersionPref::Http3 = http_version_pref {
422 Err(error::builder(err))
423 } else {
424 Ok(None)
425 }
426 }
427 }
428 };
429
430 #[cfg(feature = "__tls")]
431 match config.tls {
432 #[cfg(feature = "default-tls")]
433 TlsBackend::Default => {
434 let mut tls = TlsConnector::builder();
435
436 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
437 {
438 match config.http_version_pref {
439 HttpVersionPref::Http1 => {
440 tls.request_alpns(&["http/1.1"]);
441 }
442 #[cfg(feature = "http2")]
443 HttpVersionPref::Http2 => {
444 tls.request_alpns(&["h2"]);
445 }
446 HttpVersionPref::All => {
447 tls.request_alpns(&["h2", "http/1.1"]);
448 }
449 }
450 }
451
452 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
453
454 tls.danger_accept_invalid_certs(!config.certs_verification);
455
456 tls.use_sni(config.tls_sni);
457
458 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
459
460 for cert in config.root_certs {
461 cert.add_to_native_tls(&mut tls);
462 }
463
464 #[cfg(feature = "native-tls")]
465 {
466 if let Some(id) = config.identity {
467 id.add_to_native_tls(&mut tls)?;
468 }
469 }
470 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
471 {
472 if let Some(_id) = config.identity {
474 return Err(crate::error::builder("incompatible TLS identity type"));
475 }
476 }
477
478 if let Some(min_tls_version) = config.min_tls_version {
479 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
480 crate::error::builder("invalid minimum TLS version for backend")
484 })?;
485 tls.min_protocol_version(Some(protocol));
486 }
487
488 if let Some(max_tls_version) = config.max_tls_version {
489 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
490 crate::error::builder("invalid maximum TLS version for backend")
495 })?;
496 tls.max_protocol_version(Some(protocol));
497 }
498
499 ConnectorBuilder::new_default_tls(
500 http,
501 tls,
502 proxies.clone(),
503 user_agent(&config.headers),
504 config.local_address,
505 #[cfg(any(
506 target_os = "android",
507 target_os = "fuchsia",
508 target_os = "illumos",
509 target_os = "ios",
510 target_os = "linux",
511 target_os = "macos",
512 target_os = "solaris",
513 target_os = "tvos",
514 target_os = "visionos",
515 target_os = "watchos",
516 ))]
517 config.interface.as_deref(),
518 config.nodelay,
519 config.tls_info,
520 )?
521 }
522 #[cfg(feature = "native-tls")]
523 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_default_tls(
524 http,
525 conn,
526 proxies.clone(),
527 user_agent(&config.headers),
528 config.local_address,
529 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
530 config.interface.as_deref(),
531 config.nodelay,
532 config.tls_info,
533 ),
534 #[cfg(feature = "__rustls")]
535 TlsBackend::BuiltRustls(conn) => {
536 #[cfg(feature = "http3")]
537 {
538 h3_connector = build_h3_connector(
539 resolver,
540 conn.clone(),
541 config.quic_max_idle_timeout,
542 config.quic_stream_receive_window,
543 config.quic_receive_window,
544 config.quic_send_window,
545 config.h3_max_field_section_size,
546 config.h3_send_grease,
547 config.local_address,
548 &config.http_version_pref,
549 )?;
550 }
551
552 ConnectorBuilder::new_rustls_tls(
553 http,
554 conn,
555 proxies.clone(),
556 user_agent(&config.headers),
557 config.local_address,
558 #[cfg(any(
559 target_os = "android",
560 target_os = "fuchsia",
561 target_os = "illumos",
562 target_os = "ios",
563 target_os = "linux",
564 target_os = "macos",
565 target_os = "solaris",
566 target_os = "tvos",
567 target_os = "visionos",
568 target_os = "watchos",
569 ))]
570 config.interface.as_deref(),
571 config.nodelay,
572 config.tls_info,
573 )
574 }
575 #[cfg(feature = "__rustls")]
576 TlsBackend::Rustls => {
577 use crate::tls::{IgnoreHostname, NoVerifier};
578
579 let mut root_cert_store = rustls::RootCertStore::empty();
581 for cert in config.root_certs {
582 cert.add_to_rustls(&mut root_cert_store)?;
583 }
584
585 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
586 if config.tls_built_in_certs_webpki {
587 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
588 }
589
590 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
591 if config.tls_built_in_certs_native {
592 let mut valid_count = 0;
593 let mut invalid_count = 0;
594
595 let load_results = rustls_native_certs::load_native_certs();
596 for cert in load_results.certs {
597 match root_cert_store.add(cert.into()) {
601 Ok(_) => valid_count += 1,
602 Err(err) => {
603 invalid_count += 1;
604 log::debug!("rustls failed to parse DER certificate: {err:?}");
605 }
606 }
607 }
608 if valid_count == 0 && invalid_count > 0 {
609 let err = if load_results.errors.is_empty() {
610 crate::error::builder(
611 "zero valid certificates found in native root store",
612 )
613 } else {
614 use std::fmt::Write as _;
615 let mut acc = String::new();
616 for err in load_results.errors {
617 let _ = writeln!(&mut acc, "{err}");
618 }
619
620 crate::error::builder(acc)
621 };
622
623 return Err(err);
624 }
625 }
626
627 let mut versions = rustls::ALL_VERSIONS.to_vec();
629
630 if let Some(min_tls_version) = config.min_tls_version {
631 versions.retain(|&supported_version| {
632 match tls::Version::from_rustls(supported_version.version) {
633 Some(version) => version >= min_tls_version,
634 None => true,
637 }
638 });
639 }
640
641 if let Some(max_tls_version) = config.max_tls_version {
642 versions.retain(|&supported_version| {
643 match tls::Version::from_rustls(supported_version.version) {
644 Some(version) => version <= max_tls_version,
645 None => false,
646 }
647 });
648 }
649
650 if versions.is_empty() {
651 return Err(crate::error::builder("empty supported tls versions"));
652 }
653
654 let provider = rustls::crypto::CryptoProvider::get_default()
657 .map(|arc| arc.clone())
658 .unwrap_or_else(|| {
659 #[cfg(not(feature = "__rustls-ring"))]
660 panic!("No provider set");
661
662 #[cfg(feature = "__rustls-ring")]
663 Arc::new(rustls::crypto::ring::default_provider())
664 });
665
666 let signature_algorithms = provider.signature_verification_algorithms;
668 let config_builder =
669 rustls::ClientConfig::builder_with_provider(provider.clone())
670 .with_protocol_versions(&versions)
671 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
672
673 let config_builder = if !config.certs_verification {
674 config_builder
675 .dangerous()
676 .with_custom_certificate_verifier(Arc::new(NoVerifier))
677 } else if !config.hostname_verification {
678 config_builder
679 .dangerous()
680 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
681 root_cert_store,
682 signature_algorithms,
683 )))
684 } else {
685 if config.crls.is_empty() {
686 config_builder.with_root_certificates(root_cert_store)
687 } else {
688 let crls = config
689 .crls
690 .iter()
691 .map(|e| e.as_rustls_crl())
692 .collect::<Vec<_>>();
693 let verifier =
694 rustls::client::WebPkiServerVerifier::builder_with_provider(
695 Arc::new(root_cert_store),
696 provider,
697 )
698 .with_crls(crls)
699 .build()
700 .map_err(|_| {
701 crate::error::builder("invalid TLS verification settings")
702 })?;
703 config_builder.with_webpki_verifier(verifier)
704 }
705 };
706
707 let mut tls = if let Some(id) = config.identity {
709 id.add_to_rustls(config_builder)?
710 } else {
711 config_builder.with_no_client_auth()
712 };
713
714 tls.enable_sni = config.tls_sni;
715
716 match config.http_version_pref {
718 HttpVersionPref::Http1 => {
719 tls.alpn_protocols = vec!["http/1.1".into()];
720 }
721 #[cfg(feature = "http2")]
722 HttpVersionPref::Http2 => {
723 tls.alpn_protocols = vec!["h2".into()];
724 }
725 #[cfg(feature = "http3")]
726 HttpVersionPref::Http3 => {
727 tls.alpn_protocols = vec!["h3".into()];
728 }
729 HttpVersionPref::All => {
730 tls.alpn_protocols = vec![
731 #[cfg(feature = "http2")]
732 "h2".into(),
733 "http/1.1".into(),
734 ];
735 }
736 }
737
738 #[cfg(feature = "http3")]
739 {
740 tls.enable_early_data = config.tls_enable_early_data;
741
742 h3_connector = build_h3_connector(
743 resolver,
744 tls.clone(),
745 config.quic_max_idle_timeout,
746 config.quic_stream_receive_window,
747 config.quic_receive_window,
748 config.quic_send_window,
749 config.h3_max_field_section_size,
750 config.h3_send_grease,
751 config.local_address,
752 &config.http_version_pref,
753 )?;
754 }
755
756 ConnectorBuilder::new_rustls_tls(
757 http,
758 tls,
759 proxies.clone(),
760 user_agent(&config.headers),
761 config.local_address,
762 #[cfg(any(
763 target_os = "android",
764 target_os = "fuchsia",
765 target_os = "illumos",
766 target_os = "ios",
767 target_os = "linux",
768 target_os = "macos",
769 target_os = "solaris",
770 target_os = "tvos",
771 target_os = "visionos",
772 target_os = "watchos",
773 ))]
774 config.interface.as_deref(),
775 config.nodelay,
776 config.tls_info,
777 )
778 }
779 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
780 TlsBackend::UnknownPreconfigured => {
781 return Err(crate::error::builder(
782 "Unknown TLS backend passed to `use_preconfigured_tls`",
783 ));
784 }
785 }
786
787 #[cfg(not(feature = "__tls"))]
788 ConnectorBuilder::new(
789 http,
790 proxies.clone(),
791 config.local_address,
792 #[cfg(any(
793 target_os = "android",
794 target_os = "fuchsia",
795 target_os = "illumos",
796 target_os = "ios",
797 target_os = "linux",
798 target_os = "macos",
799 target_os = "solaris",
800 target_os = "tvos",
801 target_os = "visionos",
802 target_os = "watchos",
803 ))]
804 config.interface.as_deref(),
805 config.nodelay,
806 )
807 };
808
809 connector_builder.set_timeout(config.connect_timeout);
810 connector_builder.set_verbose(config.connection_verbose);
811 connector_builder.set_keepalive(config.tcp_keepalive);
812
813 let mut builder =
814 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
815 #[cfg(feature = "http2")]
816 {
817 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
818 builder.http2_only(true);
819 }
820
821 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
822 {
823 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
824 }
825 if let Some(http2_initial_connection_window_size) =
826 config.http2_initial_connection_window_size
827 {
828 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
829 }
830 if config.http2_adaptive_window {
831 builder.http2_adaptive_window(true);
832 }
833 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
834 builder.http2_max_frame_size(http2_max_frame_size);
835 }
836 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
837 builder.http2_max_header_list_size(http2_max_header_list_size);
838 }
839 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
840 builder.http2_keep_alive_interval(http2_keep_alive_interval);
841 }
842 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
843 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
844 }
845 if config.http2_keep_alive_while_idle {
846 builder.http2_keep_alive_while_idle(true);
847 }
848 }
849
850 builder.timer(hyper_util::rt::TokioTimer::new());
851 builder.pool_timer(hyper_util::rt::TokioTimer::new());
852 builder.pool_idle_timeout(config.pool_idle_timeout);
853 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
854
855 if config.http09_responses {
856 builder.http09_responses(true);
857 }
858
859 if config.http1_title_case_headers {
860 builder.http1_title_case_headers(true);
861 }
862
863 if config.http1_allow_obsolete_multiline_headers_in_responses {
864 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
865 }
866
867 if config.http1_ignore_invalid_headers_in_responses {
868 builder.http1_ignore_invalid_headers_in_responses(true);
869 }
870
871 if config.http1_allow_spaces_after_header_name_in_responses {
872 builder.http1_allow_spaces_after_header_name_in_responses(true);
873 }
874
875 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
876
877 Ok(Client {
878 inner: Arc::new(ClientRef {
879 accepts: config.accepts,
880 #[cfg(feature = "cookies")]
881 cookie_store: config.cookie_store,
882 #[cfg(feature = "http3")]
885 h3_client: match h3_connector {
886 Some(h3_connector) => {
887 Some(H3Client::new(h3_connector, config.pool_idle_timeout))
888 }
889 None => None,
890 },
891 hyper: builder.build(connector_builder.build(config.connector_layers)),
892 headers: config.headers,
893 redirect_policy: config.redirect_policy,
894 referer: config.referer,
895 read_timeout: config.read_timeout,
896 request_timeout: config.timeout,
897 proxies,
898 proxies_maybe_http_auth,
899 https_only: config.https_only,
900 }),
901 })
902 }
903
904 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
927 where
928 V: TryInto<HeaderValue>,
929 V::Error: Into<http::Error>,
930 {
931 match value.try_into() {
932 Ok(value) => {
933 self.config.headers.insert(USER_AGENT, value);
934 }
935 Err(e) => {
936 self.config.error = Some(crate::error::builder(e.into()));
937 }
938 };
939 self
940 }
941 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
965 for (key, value) in headers.iter() {
966 self.config.headers.insert(key, value.clone());
967 }
968 self
969 }
970
971 #[cfg(feature = "cookies")]
986 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
987 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
988 if enable {
989 self.cookie_provider(Arc::new(cookie::Jar::default()))
990 } else {
991 self.config.cookie_store = None;
992 self
993 }
994 }
995
996 #[cfg(feature = "cookies")]
1010 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
1011 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
1012 mut self,
1013 cookie_store: Arc<C>,
1014 ) -> ClientBuilder {
1015 self.config.cookie_store = Some(cookie_store as _);
1016 self
1017 }
1018
1019 #[cfg(feature = "gzip")]
1036 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
1037 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
1038 self.config.accepts.gzip = enable;
1039 self
1040 }
1041
1042 #[cfg(feature = "brotli")]
1059 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
1060 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
1061 self.config.accepts.brotli = enable;
1062 self
1063 }
1064
1065 #[cfg(feature = "zstd")]
1082 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1083 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1084 self.config.accepts.zstd = enable;
1085 self
1086 }
1087
1088 #[cfg(feature = "deflate")]
1105 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1106 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1107 self.config.accepts.deflate = enable;
1108 self
1109 }
1110
1111 pub fn no_gzip(self) -> ClientBuilder {
1117 #[cfg(feature = "gzip")]
1118 {
1119 self.gzip(false)
1120 }
1121
1122 #[cfg(not(feature = "gzip"))]
1123 {
1124 self
1125 }
1126 }
1127
1128 pub fn no_brotli(self) -> ClientBuilder {
1134 #[cfg(feature = "brotli")]
1135 {
1136 self.brotli(false)
1137 }
1138
1139 #[cfg(not(feature = "brotli"))]
1140 {
1141 self
1142 }
1143 }
1144
1145 pub fn no_zstd(self) -> ClientBuilder {
1151 #[cfg(feature = "zstd")]
1152 {
1153 self.zstd(false)
1154 }
1155
1156 #[cfg(not(feature = "zstd"))]
1157 {
1158 self
1159 }
1160 }
1161
1162 pub fn no_deflate(self) -> ClientBuilder {
1168 #[cfg(feature = "deflate")]
1169 {
1170 self.deflate(false)
1171 }
1172
1173 #[cfg(not(feature = "deflate"))]
1174 {
1175 self
1176 }
1177 }
1178
1179 pub fn redirect_policy(mut self, policy: redirect::Policy) -> ClientBuilder {
1185 self.config.redirect_policy = policy;
1186 self
1187 }
1188
1189 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1193 self.config.referer = enable;
1194 self
1195 }
1196
1197 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1205 self.config.proxies.push(proxy);
1206 self.config.auto_sys_proxy = false;
1207 self
1208 }
1209
1210 pub fn no_proxy(mut self) -> ClientBuilder {
1218 self.config.proxies.clear();
1219 self.config.auto_sys_proxy = false;
1220 self
1221 }
1222
1223 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1232 self.config.timeout = Some(timeout);
1233 self
1234 }
1235
1236 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1244 self.config.read_timeout = Some(timeout);
1245 self
1246 }
1247
1248 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1257 self.config.connect_timeout = Some(timeout);
1258 self
1259 }
1260
1261 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1268 self.config.connection_verbose = verbose;
1269 self
1270 }
1271
1272 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1280 where
1281 D: Into<Option<Duration>>,
1282 {
1283 self.config.pool_idle_timeout = val.into();
1284 self
1285 }
1286
1287 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1289 self.config.pool_max_idle_per_host = max;
1290 self
1291 }
1292
1293 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1295 self.config.http1_title_case_headers = true;
1296 self
1297 }
1298
1299 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1305 mut self,
1306 value: bool,
1307 ) -> ClientBuilder {
1308 self.config
1309 .http1_allow_obsolete_multiline_headers_in_responses = value;
1310 self
1311 }
1312
1313 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1315 self.config.http1_ignore_invalid_headers_in_responses = value;
1316 self
1317 }
1318
1319 pub fn http1_allow_spaces_after_header_name_in_responses(
1325 mut self,
1326 value: bool,
1327 ) -> ClientBuilder {
1328 self.config
1329 .http1_allow_spaces_after_header_name_in_responses = value;
1330 self
1331 }
1332
1333 pub fn http1_only(mut self) -> ClientBuilder {
1335 self.config.http_version_pref = HttpVersionPref::Http1;
1336 self
1337 }
1338
1339 pub fn http09_responses(mut self) -> ClientBuilder {
1341 self.config.http09_responses = true;
1342 self
1343 }
1344
1345 #[cfg(feature = "http2")]
1347 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1348 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1349 self.config.http_version_pref = HttpVersionPref::Http2;
1350 self
1351 }
1352
1353 #[cfg(feature = "http3")]
1355 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1356 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1357 self.config.http_version_pref = HttpVersionPref::Http3;
1358 self
1359 }
1360
1361 #[cfg(feature = "http2")]
1365 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1366 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1367 self.config.http2_initial_stream_window_size = sz.into();
1368 self
1369 }
1370
1371 #[cfg(feature = "http2")]
1375 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1376 pub fn http2_initial_connection_window_size(
1377 mut self,
1378 sz: impl Into<Option<u32>>,
1379 ) -> ClientBuilder {
1380 self.config.http2_initial_connection_window_size = sz.into();
1381 self
1382 }
1383
1384 #[cfg(feature = "http2")]
1389 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1390 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1391 self.config.http2_adaptive_window = enabled;
1392 self
1393 }
1394
1395 #[cfg(feature = "http2")]
1399 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1400 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1401 self.config.http2_max_frame_size = sz.into();
1402 self
1403 }
1404
1405 #[cfg(feature = "http2")]
1409 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1410 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1411 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1412 self
1413 }
1414
1415 #[cfg(feature = "http2")]
1420 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1421 pub fn http2_keep_alive_interval(
1422 mut self,
1423 interval: impl Into<Option<Duration>>,
1424 ) -> ClientBuilder {
1425 self.config.http2_keep_alive_interval = interval.into();
1426 self
1427 }
1428
1429 #[cfg(feature = "http2")]
1435 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1436 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1437 self.config.http2_keep_alive_timeout = Some(timeout);
1438 self
1439 }
1440
1441 #[cfg(feature = "http2")]
1448 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1449 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1450 self.config.http2_keep_alive_while_idle = enabled;
1451 self
1452 }
1453
1454 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1460 self.config.nodelay = enabled;
1461 self
1462 }
1463
1464 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1478 where
1479 T: Into<Option<IpAddr>>,
1480 {
1481 self.config.local_address = addr.into();
1482 self
1483 }
1484
1485 #[cfg(any(
1517 target_os = "android",
1518 target_os = "fuchsia",
1519 target_os = "illumos",
1520 target_os = "ios",
1521 target_os = "linux",
1522 target_os = "macos",
1523 target_os = "solaris",
1524 target_os = "tvos",
1525 target_os = "visionos",
1526 target_os = "watchos",
1527 ))]
1528 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1529 self.config.interface = Some(interface.to_string());
1530 self
1531 }
1532
1533 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1537 where
1538 D: Into<Option<Duration>>,
1539 {
1540 self.config.tcp_keepalive = val.into();
1541 self
1542 }
1543
1544 #[cfg(feature = "__tls")]
1556 #[cfg_attr(
1557 docsrs,
1558 doc(cfg(any(
1559 feature = "default-tls",
1560 feature = "native-tls",
1561 feature = "rustls-tls"
1562 )))
1563 )]
1564 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1565 self.config.root_certs.push(cert);
1566 self
1567 }
1568
1569 #[cfg(feature = "__rustls")]
1576 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1577 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1578 self.config.crls.push(crl);
1579 self
1580 }
1581
1582 #[cfg(feature = "__rustls")]
1589 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1590 pub fn add_crls(
1591 mut self,
1592 crls: impl IntoIterator<Item = CertificateRevocationList>,
1593 ) -> ClientBuilder {
1594 self.config.crls.extend(crls);
1595 self
1596 }
1597
1598 #[cfg(feature = "__tls")]
1616 #[cfg_attr(
1617 docsrs,
1618 doc(cfg(any(
1619 feature = "default-tls",
1620 feature = "native-tls",
1621 feature = "rustls-tls"
1622 )))
1623 )]
1624 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1625 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1626
1627 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1628 {
1629 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1630 }
1631
1632 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1633 {
1634 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1635 }
1636
1637 self
1638 }
1639
1640 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1644 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1645 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1646 self.config.tls_built_in_certs_webpki = enabled;
1647 self
1648 }
1649
1650 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1654 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1655 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1656 self.config.tls_built_in_certs_native = enabled;
1657 self
1658 }
1659
1660 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1667 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1668 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1669 self.config.identity = Some(identity);
1670 self
1671 }
1672
1673 #[cfg(feature = "__tls")]
1689 #[cfg_attr(
1690 docsrs,
1691 doc(cfg(any(
1692 feature = "default-tls",
1693 feature = "native-tls",
1694 feature = "rustls-tls"
1695 )))
1696 )]
1697 pub fn danger_accept_invalid_hostnames(
1698 mut self,
1699 accept_invalid_hostname: bool,
1700 ) -> ClientBuilder {
1701 self.config.hostname_verification = !accept_invalid_hostname;
1702 self
1703 }
1704
1705 #[cfg(feature = "__tls")]
1722 #[cfg_attr(
1723 docsrs,
1724 doc(cfg(any(
1725 feature = "default-tls",
1726 feature = "native-tls",
1727 feature = "rustls-tls"
1728 )))
1729 )]
1730 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1731 self.config.certs_verification = !accept_invalid_certs;
1732 self
1733 }
1734
1735 #[cfg(feature = "__tls")]
1744 #[cfg_attr(
1745 docsrs,
1746 doc(cfg(any(
1747 feature = "default-tls",
1748 feature = "native-tls",
1749 feature = "rustls-tls"
1750 )))
1751 )]
1752 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1753 self.config.tls_sni = tls_sni;
1754 self
1755 }
1756
1757 #[cfg(feature = "__tls")]
1773 #[cfg_attr(
1774 docsrs,
1775 doc(cfg(any(
1776 feature = "default-tls",
1777 feature = "native-tls",
1778 feature = "rustls-tls"
1779 )))
1780 )]
1781 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1782 self.config.min_tls_version = Some(version);
1783 self
1784 }
1785
1786 #[cfg(feature = "__tls")]
1805 #[cfg_attr(
1806 docsrs,
1807 doc(cfg(any(
1808 feature = "default-tls",
1809 feature = "native-tls",
1810 feature = "rustls-tls"
1811 )))
1812 )]
1813 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1814 self.config.max_tls_version = Some(version);
1815 self
1816 }
1817
1818 #[cfg(feature = "native-tls")]
1827 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1828 pub fn use_native_tls(mut self) -> ClientBuilder {
1829 self.config.tls = TlsBackend::Default;
1830 self
1831 }
1832
1833 #[cfg(feature = "__rustls")]
1842 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1843 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1844 self.config.tls = TlsBackend::Rustls;
1845 self
1846 }
1847
1848 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
1867 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1868 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
1869 let mut tls = Some(tls);
1870 #[cfg(feature = "native-tls")]
1871 {
1872 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
1873 let tls = conn.take().expect("is definitely Some");
1874 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
1875 self.config.tls = tls;
1876 return self;
1877 }
1878 }
1879 #[cfg(feature = "__rustls")]
1880 {
1881 if let Some(conn) =
1882 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
1883 {
1884 let tls = conn.take().expect("is definitely Some");
1885 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
1886 self.config.tls = tls;
1887 return self;
1888 }
1889 }
1890
1891 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
1893 self
1894 }
1895
1896 #[cfg(feature = "__tls")]
1903 #[cfg_attr(
1904 docsrs,
1905 doc(cfg(any(
1906 feature = "default-tls",
1907 feature = "native-tls",
1908 feature = "rustls-tls"
1909 )))
1910 )]
1911 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
1912 self.config.tls_info = tls_info;
1913 self
1914 }
1915
1916 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
1920 self.config.https_only = enabled;
1921 self
1922 }
1923
1924 #[doc(hidden)]
1925 #[cfg(feature = "hickory-dns")]
1926 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1927 #[deprecated(note = "use `hickory_dns` instead")]
1928 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
1929 self.config.hickory_dns = enable;
1930 self
1931 }
1932
1933 #[cfg(feature = "hickory-dns")]
1947 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1948 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
1949 self.config.hickory_dns = enable;
1950 self
1951 }
1952
1953 #[doc(hidden)]
1954 #[deprecated(note = "use `no_hickory_dns` instead")]
1955 pub fn no_trust_dns(self) -> ClientBuilder {
1956 self.no_hickory_dns()
1957 }
1958
1959 pub fn no_hickory_dns(self) -> ClientBuilder {
1965 #[cfg(feature = "hickory-dns")]
1966 {
1967 self.hickory_dns(false)
1968 }
1969
1970 #[cfg(not(feature = "hickory-dns"))]
1971 {
1972 self
1973 }
1974 }
1975
1976 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
1981 self.resolve_to_addrs(domain, &[addr])
1982 }
1983
1984 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
1989 self.config
1990 .dns_overrides
1991 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
1992 self
1993 }
1994
1995 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
2001 self.config.dns_resolver = Some(resolver as _);
2002 self
2003 }
2004
2005 #[cfg(feature = "http3")]
2010 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2011 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
2012 self.config.tls_enable_early_data = enabled;
2013 self
2014 }
2015
2016 #[cfg(feature = "http3")]
2022 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2023 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
2024 self.config.quic_max_idle_timeout = Some(value);
2025 self
2026 }
2027
2028 #[cfg(feature = "http3")]
2039 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2040 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
2041 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
2042 self
2043 }
2044
2045 #[cfg(feature = "http3")]
2056 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2057 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
2058 self.config.quic_receive_window = Some(value.try_into().unwrap());
2059 self
2060 }
2061
2062 #[cfg(feature = "http3")]
2068 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2069 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
2070 self.config.quic_send_window = Some(value);
2071 self
2072 }
2073
2074 #[cfg(feature = "http3")]
2084 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2085 pub fn http3_max_field_section_size(mut self, value: u64) -> ClientBuilder {
2086 self.config.h3_max_field_section_size = Some(value.try_into().unwrap());
2087 self
2088 }
2089
2090 #[cfg(feature = "http3")]
2102 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
2103 pub fn http3_send_grease(mut self, enabled: bool) -> ClientBuilder {
2104 self.config.h3_send_grease = Some(enabled);
2105 self
2106 }
2107
2108 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
2132 where
2133 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
2134 L::Service:
2135 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
2136 <L::Service as Service<Unnameable>>::Future: Send + 'static,
2137 {
2138 let layer = BoxCloneSyncServiceLayer::new(layer);
2139
2140 self.config.connector_layers.push(layer);
2141
2142 self
2143 }
2144}
2145
2146type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2147
2148impl Default for Client {
2149 fn default() -> Self {
2150 Self::new()
2151 }
2152}
2153
2154impl Client {
2155 pub fn new() -> Client {
2165 ClientBuilder::new().build().expect("Client::new()")
2166 }
2167
2168 pub fn builder() -> ClientBuilder {
2172 ClientBuilder::new()
2173 }
2174
2175 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2181 self.request(Method::GET, url)
2182 }
2183
2184 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2190 self.request(Method::POST, url)
2191 }
2192
2193 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2199 self.request(Method::PUT, url)
2200 }
2201
2202 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2208 self.request(Method::PATCH, url)
2209 }
2210
2211 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2217 self.request(Method::DELETE, url)
2218 }
2219
2220 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2226 self.request(Method::HEAD, url)
2227 }
2228
2229 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2238 let req = url.into_url().map(move |url| Request::new(method, url));
2239 RequestBuilder::new(self.clone(), req)
2240 }
2241
2242 pub fn execute(
2255 &self,
2256 request: Request,
2257 ) -> impl Future<Output = Result<Response, crate::Error>> {
2258 self.execute_request(request)
2259 }
2260
2261 pub(super) fn execute_request(&self, req: Request) -> Pending {
2262 let (method, url, mut headers, body, timeout, redirect_policy, version) = req.pieces();
2263 if url.scheme() != "http" && url.scheme() != "https" {
2264 return Pending::new_err(error::url_bad_scheme(url));
2265 }
2266
2267 if self.inner.https_only && url.scheme() != "https" {
2269 return Pending::new_err(error::url_bad_scheme(url));
2270 }
2271
2272 for (key, value) in &self.inner.headers {
2275 if let Entry::Vacant(entry) = headers.entry(key) {
2276 entry.insert(value.clone());
2277 }
2278 }
2279
2280 #[cfg(feature = "cookies")]
2282 {
2283 if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
2284 if headers.get(crate::header::COOKIE).is_none() {
2285 add_cookie_header(&mut headers, &**cookie_store, &url);
2286 }
2287 }
2288 }
2289
2290 let accept_encoding = self.inner.accepts.as_str();
2291
2292 if let Some(accept_encoding) = accept_encoding {
2293 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2294 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2295 }
2296 }
2297
2298 let uri = match try_uri(&url) {
2299 Ok(uri) => uri,
2300 _ => return Pending::new_err(error::url_invalid_uri(url)),
2301 };
2302
2303 let (reusable, body) = match body {
2304 Some(body) => {
2305 let (reusable, body) = body.try_reuse();
2306 (Some(reusable), body)
2307 }
2308 None => (None, Body::empty()),
2309 };
2310
2311 self.proxy_auth(&uri, &mut headers);
2312
2313 let builder = hyper::Request::builder()
2314 .method(method.clone())
2315 .uri(uri)
2316 .version(version);
2317
2318 let in_flight = match version {
2319 #[cfg(feature = "http3")]
2320 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2321 let mut req = builder.body(body).expect("valid request parts");
2322 *req.headers_mut() = headers.clone();
2323 ResponseFuture::H3(self.inner.h3_client.as_ref().unwrap().request(req))
2324 }
2325 _ => {
2326 let mut req = builder.body(body).expect("valid request parts");
2327 *req.headers_mut() = headers.clone();
2328 ResponseFuture::Default(self.inner.hyper.request(req))
2329 }
2330 };
2331
2332 let total_timeout = timeout
2333 .or(self.inner.request_timeout)
2334 .map(tokio::time::sleep)
2335 .map(Box::pin);
2336
2337 let read_timeout_fut = self
2338 .inner
2339 .read_timeout
2340 .map(tokio::time::sleep)
2341 .map(Box::pin);
2342
2343 Pending {
2344 inner: PendingInner::Request(PendingRequest {
2345 method,
2346 url,
2347 headers,
2348 body: reusable,
2349
2350 urls: Vec::new(),
2351
2352 retry_count: 0,
2353 redirect_policy,
2354
2355 client: self.inner.clone(),
2356
2357 in_flight,
2358 total_timeout,
2359 read_timeout_fut,
2360 read_timeout: self.inner.read_timeout,
2361 }),
2362 }
2363 }
2364
2365 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2366 if !self.inner.proxies_maybe_http_auth {
2367 return;
2368 }
2369
2370 if dst.scheme() != Some(&Scheme::HTTP) {
2374 return;
2375 }
2376
2377 if headers.contains_key(PROXY_AUTHORIZATION) {
2378 return;
2379 }
2380
2381 for proxy in self.inner.proxies.iter() {
2382 if proxy.is_match(dst) {
2383 if let Some(header) = proxy.http_basic_auth(dst) {
2384 headers.insert(PROXY_AUTHORIZATION, header);
2385 }
2386
2387 break;
2388 }
2389 }
2390 }
2391}
2392
2393impl fmt::Debug for Client {
2394 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2395 let mut builder = f.debug_struct("Client");
2396 self.inner.fmt_fields(&mut builder);
2397 builder.finish()
2398 }
2399}
2400
2401impl tower_service::Service<Request> for Client {
2402 type Response = Response;
2403 type Error = crate::Error;
2404 type Future = Pending;
2405
2406 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2407 Poll::Ready(Ok(()))
2408 }
2409
2410 fn call(&mut self, req: Request) -> Self::Future {
2411 self.execute_request(req)
2412 }
2413}
2414
2415impl tower_service::Service<Request> for &'_ Client {
2416 type Response = Response;
2417 type Error = crate::Error;
2418 type Future = Pending;
2419
2420 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2421 Poll::Ready(Ok(()))
2422 }
2423
2424 fn call(&mut self, req: Request) -> Self::Future {
2425 self.execute_request(req)
2426 }
2427}
2428
2429impl fmt::Debug for ClientBuilder {
2430 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2431 let mut builder = f.debug_struct("ClientBuilder");
2432 self.config.fmt_fields(&mut builder);
2433 builder.finish()
2434 }
2435}
2436
2437impl Config {
2438 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2439 #[cfg(feature = "cookies")]
2443 {
2444 if let Some(_) = self.cookie_store {
2445 f.field("cookie_store", &true);
2446 }
2447 }
2448
2449 f.field("accepts", &self.accepts);
2450
2451 if !self.proxies.is_empty() {
2452 f.field("proxies", &self.proxies);
2453 }
2454
2455 if !self.redirect_policy.is_default() {
2456 f.field("redirect_policy", &self.redirect_policy);
2457 }
2458
2459 if self.referer {
2460 f.field("referer", &true);
2461 }
2462
2463 f.field("default_headers", &self.headers);
2464
2465 if self.http1_title_case_headers {
2466 f.field("http1_title_case_headers", &true);
2467 }
2468
2469 if self.http1_allow_obsolete_multiline_headers_in_responses {
2470 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2471 }
2472
2473 if self.http1_ignore_invalid_headers_in_responses {
2474 f.field("http1_ignore_invalid_headers_in_responses", &true);
2475 }
2476
2477 if self.http1_allow_spaces_after_header_name_in_responses {
2478 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2479 }
2480
2481 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2482 f.field("http1_only", &true);
2483 }
2484
2485 #[cfg(feature = "http2")]
2486 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2487 f.field("http2_prior_knowledge", &true);
2488 }
2489
2490 if let Some(ref d) = self.connect_timeout {
2491 f.field("connect_timeout", d);
2492 }
2493
2494 if let Some(ref d) = self.timeout {
2495 f.field("timeout", d);
2496 }
2497
2498 if let Some(ref v) = self.local_address {
2499 f.field("local_address", v);
2500 }
2501
2502 #[cfg(any(
2503 target_os = "android",
2504 target_os = "fuchsia",
2505 target_os = "illumos",
2506 target_os = "ios",
2507 target_os = "linux",
2508 target_os = "macos",
2509 target_os = "solaris",
2510 target_os = "tvos",
2511 target_os = "visionos",
2512 target_os = "watchos",
2513 ))]
2514 if let Some(ref v) = self.interface {
2515 f.field("interface", v);
2516 }
2517
2518 if self.nodelay {
2519 f.field("tcp_nodelay", &true);
2520 }
2521
2522 #[cfg(feature = "__tls")]
2523 {
2524 if !self.hostname_verification {
2525 f.field("danger_accept_invalid_hostnames", &true);
2526 }
2527 }
2528
2529 #[cfg(feature = "__tls")]
2530 {
2531 if !self.certs_verification {
2532 f.field("danger_accept_invalid_certs", &true);
2533 }
2534
2535 if let Some(ref min_tls_version) = self.min_tls_version {
2536 f.field("min_tls_version", min_tls_version);
2537 }
2538
2539 if let Some(ref max_tls_version) = self.max_tls_version {
2540 f.field("max_tls_version", max_tls_version);
2541 }
2542
2543 f.field("tls_sni", &self.tls_sni);
2544
2545 f.field("tls_info", &self.tls_info);
2546 }
2547
2548 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2549 {
2550 f.field("tls_backend", &self.tls);
2551 }
2552
2553 if !self.dns_overrides.is_empty() {
2554 f.field("dns_overrides", &self.dns_overrides);
2555 }
2556
2557 #[cfg(feature = "http3")]
2558 {
2559 if self.tls_enable_early_data {
2560 f.field("tls_enable_early_data", &true);
2561 }
2562 }
2563 }
2564}
2565
2566struct ClientRef {
2567 accepts: Accepts,
2568 #[cfg(feature = "cookies")]
2569 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2570 headers: HeaderMap,
2571 hyper: HyperClient,
2572 #[cfg(feature = "http3")]
2573 h3_client: Option<H3Client>,
2574 redirect_policy: redirect::Policy,
2575 referer: bool,
2576 request_timeout: Option<Duration>,
2577 read_timeout: Option<Duration>,
2578 proxies: Arc<Vec<Proxy>>,
2579 proxies_maybe_http_auth: bool,
2580 https_only: bool,
2581}
2582
2583impl ClientRef {
2584 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2585 #[cfg(feature = "cookies")]
2589 {
2590 if let Some(_) = self.cookie_store {
2591 f.field("cookie_store", &true);
2592 }
2593 }
2594
2595 f.field("accepts", &self.accepts);
2596
2597 if !self.proxies.is_empty() {
2598 f.field("proxies", &self.proxies);
2599 }
2600
2601 if !self.redirect_policy.is_default() {
2602 f.field("redirect_policy", &self.redirect_policy);
2603 }
2604
2605 if self.referer {
2606 f.field("referer", &true);
2607 }
2608
2609 f.field("default_headers", &self.headers);
2610
2611 if let Some(ref d) = self.request_timeout {
2612 f.field("timeout", d);
2613 }
2614
2615 if let Some(ref d) = self.read_timeout {
2616 f.field("read_timeout", d);
2617 }
2618 }
2619}
2620
2621pin_project! {
2622 pub struct Pending {
2623 #[pin]
2624 inner: PendingInner,
2625 }
2626}
2627
2628enum PendingInner {
2629 Request(PendingRequest),
2630 Error(Option<crate::Error>),
2631}
2632
2633pin_project! {
2634 struct PendingRequest {
2635 method: Method,
2636 url: Url,
2637 headers: HeaderMap,
2638 body: Option<Option<Bytes>>,
2639
2640 urls: Vec<Url>,
2641
2642 retry_count: usize,
2643 redirect_policy: Option<redirect::Policy>,
2644
2645 client: Arc<ClientRef>,
2646
2647 #[pin]
2648 in_flight: ResponseFuture,
2649 #[pin]
2650 total_timeout: Option<Pin<Box<Sleep>>>,
2651 #[pin]
2652 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2653 read_timeout: Option<Duration>,
2654 }
2655}
2656
2657enum ResponseFuture {
2658 Default(HyperResponseFuture),
2659 #[cfg(feature = "http3")]
2660 H3(H3ResponseFuture),
2661}
2662
2663impl PendingRequest {
2664 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2665 self.project().in_flight
2666 }
2667
2668 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2669 self.project().total_timeout
2670 }
2671
2672 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2673 self.project().read_timeout_fut
2674 }
2675
2676 fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
2677 self.project().urls
2678 }
2679
2680 fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
2681 self.project().headers
2682 }
2683
2684 #[cfg(any(feature = "http2", feature = "http3"))]
2685 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2686 use log::trace;
2687
2688 if !is_retryable_error(err) {
2689 return false;
2690 }
2691
2692 trace!("can retry {err:?}");
2693
2694 let body = match self.body {
2695 Some(Some(ref body)) => Body::reusable(body.clone()),
2696 Some(None) => {
2697 debug!("error was retryable, but body not reusable");
2698 return false;
2699 }
2700 None => Body::empty(),
2701 };
2702
2703 if self.retry_count >= 2 {
2704 trace!("retry count too high");
2705 return false;
2706 }
2707 self.retry_count += 1;
2708
2709 let uri = try_uri(&self.url).expect("URL was already validated as URI");
2711
2712 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
2713 #[cfg(feature = "http3")]
2714 ResponseFuture::H3(_) => {
2715 let mut req = hyper::Request::builder()
2716 .method(self.method.clone())
2717 .uri(uri)
2718 .body(body)
2719 .expect("valid request parts");
2720 *req.headers_mut() = self.headers.clone();
2721 ResponseFuture::H3(
2722 self.client
2723 .h3_client
2724 .as_ref()
2725 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2726 .request(req),
2727 )
2728 }
2729 _ => {
2730 let mut req = hyper::Request::builder()
2731 .method(self.method.clone())
2732 .uri(uri)
2733 .body(body)
2734 .expect("valid request parts");
2735 *req.headers_mut() = self.headers.clone();
2736 ResponseFuture::Default(self.client.hyper.request(req))
2737 }
2738 };
2739
2740 true
2741 }
2742}
2743
2744#[cfg(any(feature = "http2", feature = "http3"))]
2745fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
2746 let err = if let Some(err) = err.source() {
2748 err
2749 } else {
2750 return false;
2751 };
2752
2753 #[cfg(feature = "http3")]
2754 if let Some(cause) = err.source() {
2755 if let Some(err) = cause.downcast_ref::<h3::Error>() {
2756 debug!("determining if HTTP/3 error {err} can be retried");
2757 return err.to_string().as_str() == "timeout";
2759 }
2760 }
2761
2762 #[cfg(feature = "http2")]
2763 if let Some(cause) = err.source() {
2764 if let Some(err) = cause.downcast_ref::<h2::Error>() {
2765 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
2767 return true;
2768 }
2769
2770 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
2773 {
2774 return true;
2775 }
2776 }
2777 }
2778 false
2779}
2780
2781impl Pending {
2782 pub(super) fn new_err(err: crate::Error) -> Pending {
2783 Pending {
2784 inner: PendingInner::Error(Some(err)),
2785 }
2786 }
2787
2788 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2789 self.project().inner
2790 }
2791}
2792
2793impl Future for Pending {
2794 type Output = Result<Response, crate::Error>;
2795
2796 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2797 let inner = self.inner();
2798 match inner.get_mut() {
2799 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2800 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2801 .take()
2802 .expect("Pending error polled more than once"))),
2803 }
2804 }
2805}
2806
2807impl Future for PendingRequest {
2808 type Output = Result<Response, crate::Error>;
2809
2810 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2811 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
2812 if let Poll::Ready(()) = delay.poll(cx) {
2813 return Poll::Ready(Err(
2814 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2815 ));
2816 }
2817 }
2818
2819 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
2820 if let Poll::Ready(()) = delay.poll(cx) {
2821 return Poll::Ready(Err(
2822 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2823 ));
2824 }
2825 }
2826
2827 loop {
2828 let res = match self.as_mut().in_flight().get_mut() {
2829 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
2830 Poll::Ready(Err(e)) => {
2831 #[cfg(feature = "http2")]
2832 if self.as_mut().retry_error(&e) {
2833 continue;
2834 }
2835 return Poll::Ready(Err(
2836 crate::error::request(e).with_url(self.url.clone())
2837 ));
2838 }
2839 Poll::Ready(Ok(res)) => res.map(super::body::boxed),
2840 Poll::Pending => return Poll::Pending,
2841 },
2842 #[cfg(feature = "http3")]
2843 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
2844 Poll::Ready(Err(e)) => {
2845 if self.as_mut().retry_error(&e) {
2846 continue;
2847 }
2848 return Poll::Ready(Err(
2849 crate::error::request(e).with_url(self.url.clone())
2850 ));
2851 }
2852 Poll::Ready(Ok(res)) => res,
2853 Poll::Pending => return Poll::Pending,
2854 },
2855 };
2856
2857 #[cfg(feature = "cookies")]
2858 {
2859 if let Some(ref cookie_store) = self.client.cookie_store {
2860 let mut cookies =
2861 cookie::extract_response_cookie_headers(&res.headers()).peekable();
2862 if cookies.peek().is_some() {
2863 cookie_store.set_cookies(&mut cookies, &self.url);
2864 }
2865 }
2866 }
2867 let should_redirect = match res.status() {
2868 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
2869 self.body = None;
2870 for header in &[
2871 TRANSFER_ENCODING,
2872 CONTENT_ENCODING,
2873 CONTENT_TYPE,
2874 CONTENT_LENGTH,
2875 ] {
2876 self.headers.remove(header);
2877 }
2878
2879 match self.method {
2880 Method::GET | Method::HEAD => {}
2881 _ => {
2882 self.method = Method::GET;
2883 }
2884 }
2885 true
2886 }
2887 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
2888 match self.body {
2889 Some(Some(_)) | None => true,
2890 Some(None) => false,
2891 }
2892 }
2893 _ => false,
2894 };
2895 if should_redirect {
2896 let loc = res.headers().get(LOCATION).and_then(|val| {
2897 let loc = (|| -> Option<Url> {
2898 self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
2902 })();
2903
2904 let loc = loc.and_then(|url| {
2908 if try_uri(&url).is_ok() {
2909 Some(url)
2910 } else {
2911 None
2912 }
2913 });
2914
2915 if loc.is_none() {
2916 debug!("Location header had invalid URI: {val:?}");
2917 }
2918 loc
2919 });
2920 if let Some(loc) = loc {
2921 if self.client.referer {
2922 if let Some(referer) = make_referer(&loc, &self.url) {
2923 self.headers.insert(REFERER, referer);
2924 }
2925 }
2926 let url = self.url.clone();
2927 self.as_mut().urls().push(url);
2928 let action = self
2930 .redirect_policy
2931 .as_ref()
2932 .unwrap_or(&self.client.redirect_policy)
2933 .check(res.status(), &loc, &self.urls);
2934
2935 match action {
2936 redirect::ActionKind::Follow => {
2937 debug!("redirecting '{}' to '{}'", self.url, loc);
2938
2939 if loc.scheme() != "http" && loc.scheme() != "https" {
2940 return Poll::Ready(Err(error::url_bad_scheme(loc)));
2941 }
2942
2943 if self.client.https_only && loc.scheme() != "https" {
2944 return Poll::Ready(Err(error::redirect(
2945 error::url_bad_scheme(loc.clone()),
2946 loc,
2947 )));
2948 }
2949
2950 self.url = loc;
2951 let mut headers =
2952 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
2953
2954 remove_sensitive_headers(&mut headers, &self.url, &self.urls);
2955 let uri = try_uri(&self.url)?;
2956 let body = match self.body {
2957 Some(Some(ref body)) => Body::reusable(body.clone()),
2958 _ => Body::empty(),
2959 };
2960
2961 #[cfg(feature = "cookies")]
2963 {
2964 if let Some(ref cookie_store) = self.client.cookie_store {
2965 add_cookie_header(&mut headers, &**cookie_store, &self.url);
2966 }
2967 }
2968
2969 *self.as_mut().in_flight().get_mut() =
2970 match *self.as_mut().in_flight().as_ref() {
2971 #[cfg(feature = "http3")]
2972 ResponseFuture::H3(_) => {
2973 let mut req = hyper::Request::builder()
2974 .method(self.method.clone())
2975 .uri(uri.clone())
2976 .body(body)
2977 .expect("valid request parts");
2978 *req.headers_mut() = headers.clone();
2979 std::mem::swap(self.as_mut().headers(), &mut headers);
2980 ResponseFuture::H3(self.client.h3_client
2981 .as_ref()
2982 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2983 .request(req))
2984 }
2985 _ => {
2986 let mut req = hyper::Request::builder()
2987 .method(self.method.clone())
2988 .uri(uri.clone())
2989 .body(body)
2990 .expect("valid request parts");
2991 *req.headers_mut() = headers.clone();
2992 std::mem::swap(self.as_mut().headers(), &mut headers);
2993 ResponseFuture::Default(self.client.hyper.request(req))
2994 }
2995 };
2996
2997 continue;
2998 }
2999 redirect::ActionKind::Stop => {
3000 debug!("redirect policy disallowed redirection to '{loc}'");
3001 }
3002 redirect::ActionKind::Error(err) => {
3003 return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
3004 }
3005 }
3006 }
3007 }
3008
3009 let res = Response::new(
3010 res,
3011 self.url.clone(),
3012 self.client.accepts,
3013 self.total_timeout.take(),
3014 self.read_timeout,
3015 );
3016 return Poll::Ready(Ok(res));
3017 }
3018 }
3019}
3020
3021impl fmt::Debug for Pending {
3022 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3023 match self.inner {
3024 PendingInner::Request(ref req) => f
3025 .debug_struct("Pending")
3026 .field("method", &req.method)
3027 .field("url", &req.url)
3028 .finish(),
3029 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
3030 }
3031 }
3032}
3033
3034fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
3035 if next.scheme() == "http" && previous.scheme() == "https" {
3036 return None;
3037 }
3038
3039 let mut referer = previous.clone();
3040 let _ = referer.set_username("");
3041 let _ = referer.set_password(None);
3042 referer.set_fragment(None);
3043 referer.as_str().parse().ok()
3044}
3045
3046#[cfg(feature = "cookies")]
3047fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
3048 if let Some(header) = cookie_store.cookies(url) {
3049 headers.insert(crate::header::COOKIE, header);
3050 }
3051}
3052
3053#[cfg(test)]
3054mod tests {
3055 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
3056
3057 #[tokio::test]
3058 async fn execute_request_rejects_invalid_urls() {
3059 let url_str = "hxxps://www.rust-lang.org/";
3060 let url = url::Url::parse(url_str).unwrap();
3061 let result = crate::get(url.clone()).await;
3062
3063 assert!(result.is_err());
3064 let err = result.err().unwrap();
3065 assert!(err.is_builder());
3066 assert_eq!(url_str, err.url().unwrap().as_str());
3067 }
3068
3069 #[tokio::test]
3071 async fn execute_request_rejects_invalid_hostname() {
3072 let url_str = "https://{{hostname}}/";
3073 let url = url::Url::parse(url_str).unwrap();
3074 let result = crate::get(url.clone()).await;
3075
3076 assert!(result.is_err());
3077 let err = result.err().unwrap();
3078 assert!(err.is_builder());
3079 assert_eq!(url_str, err.url().unwrap().as_str());
3080 }
3081}