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::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(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
162 interface: Option<String>,
163 nodelay: bool,
164 #[cfg(feature = "cookies")]
165 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
166 hickory_dns: bool,
167 error: Option<crate::Error>,
168 https_only: bool,
169 #[cfg(feature = "http3")]
170 tls_enable_early_data: bool,
171 #[cfg(feature = "http3")]
172 quic_max_idle_timeout: Option<Duration>,
173 #[cfg(feature = "http3")]
174 quic_stream_receive_window: Option<VarInt>,
175 #[cfg(feature = "http3")]
176 quic_receive_window: Option<VarInt>,
177 #[cfg(feature = "http3")]
178 quic_send_window: Option<u64>,
179 dns_overrides: HashMap<String, Vec<SocketAddr>>,
180 dns_resolver: Option<Arc<dyn Resolve>>,
181}
182
183impl Default for ClientBuilder {
184 fn default() -> Self {
185 Self::new()
186 }
187}
188
189impl ClientBuilder {
190 pub fn new() -> Self {
194 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
195 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
196
197 ClientBuilder {
198 config: Config {
199 error: None,
200 accepts: Accepts::default(),
201 headers,
202 #[cfg(feature = "__tls")]
203 hostname_verification: true,
204 #[cfg(feature = "__tls")]
205 certs_verification: true,
206 #[cfg(feature = "__tls")]
207 tls_sni: true,
208 connect_timeout: None,
209 connection_verbose: false,
210 pool_idle_timeout: Some(Duration::from_secs(90)),
211 pool_max_idle_per_host: usize::MAX,
212 tcp_keepalive: None, proxies: Vec::new(),
216 auto_sys_proxy: true,
217 redirect_policy: redirect::Policy::default(),
218 referer: true,
219 read_timeout: None,
220 timeout: None,
221 #[cfg(feature = "__tls")]
222 root_certs: Vec::new(),
223 #[cfg(feature = "__tls")]
224 tls_built_in_root_certs: true,
225 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
226 tls_built_in_certs_webpki: true,
227 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
228 tls_built_in_certs_native: true,
229 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
230 identity: None,
231 #[cfg(feature = "__rustls")]
232 crls: vec![],
233 #[cfg(feature = "__tls")]
234 min_tls_version: None,
235 #[cfg(feature = "__tls")]
236 max_tls_version: None,
237 #[cfg(feature = "__tls")]
238 tls_info: false,
239 #[cfg(feature = "__tls")]
240 tls: TlsBackend::default(),
241 connector_layers: Vec::new(),
242 http_version_pref: HttpVersionPref::All,
243 http09_responses: false,
244 http1_title_case_headers: false,
245 http1_allow_obsolete_multiline_headers_in_responses: false,
246 http1_ignore_invalid_headers_in_responses: false,
247 http1_allow_spaces_after_header_name_in_responses: false,
248 #[cfg(feature = "http2")]
249 http2_initial_stream_window_size: None,
250 #[cfg(feature = "http2")]
251 http2_initial_connection_window_size: None,
252 #[cfg(feature = "http2")]
253 http2_adaptive_window: false,
254 #[cfg(feature = "http2")]
255 http2_max_frame_size: None,
256 #[cfg(feature = "http2")]
257 http2_max_header_list_size: None,
258 #[cfg(feature = "http2")]
259 http2_keep_alive_interval: None,
260 #[cfg(feature = "http2")]
261 http2_keep_alive_timeout: None,
262 #[cfg(feature = "http2")]
263 http2_keep_alive_while_idle: false,
264 local_address: None,
265 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
266 interface: None,
267 nodelay: true,
268 hickory_dns: cfg!(feature = "hickory-dns"),
269 #[cfg(feature = "cookies")]
270 cookie_store: None,
271 https_only: false,
272 dns_overrides: HashMap::new(),
273 #[cfg(feature = "http3")]
274 tls_enable_early_data: false,
275 #[cfg(feature = "http3")]
276 quic_max_idle_timeout: None,
277 #[cfg(feature = "http3")]
278 quic_stream_receive_window: None,
279 #[cfg(feature = "http3")]
280 quic_receive_window: None,
281 #[cfg(feature = "http3")]
282 quic_send_window: None,
283 dns_resolver: None,
284 },
285 }
286 }
287}
288
289impl ClientBuilder {
290 pub fn build(self) -> crate::Result<Client> {
297 let config = self.config;
298
299 if let Some(err) = config.error {
300 return Err(err);
301 }
302
303 let mut proxies = config.proxies;
304 if config.auto_sys_proxy {
305 proxies.push(Proxy::system());
306 }
307 let proxies = Arc::new(proxies);
308
309 #[allow(unused)]
310 #[cfg(feature = "http3")]
311 let mut h3_connector = None;
312
313 let mut connector_builder = {
314 #[cfg(feature = "__tls")]
315 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
316 headers.get(USER_AGENT).cloned()
317 }
318
319 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
320 false => Arc::new(GaiResolver::new()),
321 #[cfg(feature = "hickory-dns")]
322 true => Arc::new(HickoryDnsResolver::default()),
323 #[cfg(not(feature = "hickory-dns"))]
324 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
325 };
326 if let Some(dns_resolver) = config.dns_resolver {
327 resolver = dns_resolver;
328 }
329 if !config.dns_overrides.is_empty() {
330 resolver = Arc::new(DnsResolverWithOverrides::new(
331 resolver,
332 config.dns_overrides,
333 ));
334 }
335 let mut http = HttpConnector::new_with_resolver(DynResolver::new(resolver.clone()));
336 http.set_connect_timeout(config.connect_timeout);
337
338 #[cfg(all(feature = "http3", feature = "__rustls"))]
339 let build_h3_connector =
340 |resolver,
341 tls,
342 quic_max_idle_timeout: Option<Duration>,
343 quic_stream_receive_window,
344 quic_receive_window,
345 quic_send_window,
346 local_address,
347 http_version_pref: &HttpVersionPref| {
348 let mut transport_config = TransportConfig::default();
349
350 if let Some(max_idle_timeout) = quic_max_idle_timeout {
351 transport_config.max_idle_timeout(Some(
352 max_idle_timeout.try_into().map_err(error::builder)?,
353 ));
354 }
355
356 if let Some(stream_receive_window) = quic_stream_receive_window {
357 transport_config.stream_receive_window(stream_receive_window);
358 }
359
360 if let Some(receive_window) = quic_receive_window {
361 transport_config.receive_window(receive_window);
362 }
363
364 if let Some(send_window) = quic_send_window {
365 transport_config.send_window(send_window);
366 }
367
368 let res = H3Connector::new(
369 DynResolver::new(resolver),
370 tls,
371 local_address,
372 transport_config,
373 );
374
375 match res {
376 Ok(connector) => Ok(Some(connector)),
377 Err(err) => {
378 if let HttpVersionPref::Http3 = http_version_pref {
379 Err(error::builder(err))
380 } else {
381 Ok(None)
382 }
383 }
384 }
385 };
386
387 #[cfg(feature = "__tls")]
388 match config.tls {
389 #[cfg(feature = "default-tls")]
390 TlsBackend::Default => {
391 let mut tls = TlsConnector::builder();
392
393 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
394 {
395 match config.http_version_pref {
396 HttpVersionPref::Http1 => {
397 tls.request_alpns(&["http/1.1"]);
398 }
399 #[cfg(feature = "http2")]
400 HttpVersionPref::Http2 => {
401 tls.request_alpns(&["h2"]);
402 }
403 HttpVersionPref::All => {
404 tls.request_alpns(&["h2", "http/1.1"]);
405 }
406 }
407 }
408
409 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
410
411 tls.danger_accept_invalid_certs(!config.certs_verification);
412
413 tls.use_sni(config.tls_sni);
414
415 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
416
417 for cert in config.root_certs {
418 cert.add_to_native_tls(&mut tls);
419 }
420
421 #[cfg(feature = "native-tls")]
422 {
423 if let Some(id) = config.identity {
424 id.add_to_native_tls(&mut tls)?;
425 }
426 }
427 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
428 {
429 if let Some(_id) = config.identity {
431 return Err(crate::error::builder("incompatible TLS identity type"));
432 }
433 }
434
435 if let Some(min_tls_version) = config.min_tls_version {
436 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
437 crate::error::builder("invalid minimum TLS version for backend")
441 })?;
442 tls.min_protocol_version(Some(protocol));
443 }
444
445 if let Some(max_tls_version) = config.max_tls_version {
446 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
447 crate::error::builder("invalid maximum TLS version for backend")
452 })?;
453 tls.max_protocol_version(Some(protocol));
454 }
455
456 ConnectorBuilder::new_default_tls(
457 http,
458 tls,
459 proxies.clone(),
460 user_agent(&config.headers),
461 config.local_address,
462 #[cfg(any(
463 target_os = "android",
464 target_os = "fuchsia",
465 target_os = "linux"
466 ))]
467 config.interface.as_deref(),
468 config.nodelay,
469 config.tls_info,
470 )?
471 }
472 #[cfg(feature = "native-tls")]
473 TlsBackend::BuiltNativeTls(conn) => ConnectorBuilder::from_built_default_tls(
474 http,
475 conn,
476 proxies.clone(),
477 user_agent(&config.headers),
478 config.local_address,
479 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
480 config.interface.as_deref(),
481 config.nodelay,
482 config.tls_info,
483 ),
484 #[cfg(feature = "__rustls")]
485 TlsBackend::BuiltRustls(conn) => {
486 #[cfg(feature = "http3")]
487 {
488 h3_connector = build_h3_connector(
489 resolver,
490 conn.clone(),
491 config.quic_max_idle_timeout,
492 config.quic_stream_receive_window,
493 config.quic_receive_window,
494 config.quic_send_window,
495 config.local_address,
496 &config.http_version_pref,
497 )?;
498 }
499
500 ConnectorBuilder::new_rustls_tls(
501 http,
502 conn,
503 proxies.clone(),
504 user_agent(&config.headers),
505 config.local_address,
506 #[cfg(any(
507 target_os = "android",
508 target_os = "fuchsia",
509 target_os = "linux"
510 ))]
511 config.interface.as_deref(),
512 config.nodelay,
513 config.tls_info,
514 )
515 }
516 #[cfg(feature = "__rustls")]
517 TlsBackend::Rustls => {
518 use crate::tls::{IgnoreHostname, NoVerifier};
519
520 let mut root_cert_store = rustls::RootCertStore::empty();
522 for cert in config.root_certs {
523 cert.add_to_rustls(&mut root_cert_store)?;
524 }
525
526 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
527 if config.tls_built_in_certs_webpki {
528 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
529 }
530
531 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
532 if config.tls_built_in_certs_native {
533 let mut valid_count = 0;
534 let mut invalid_count = 0;
535
536 let load_results = rustls_native_certs::load_native_certs();
537 for cert in load_results.certs {
538 match root_cert_store.add(cert.into()) {
542 Ok(_) => valid_count += 1,
543 Err(err) => {
544 invalid_count += 1;
545 log::debug!("rustls failed to parse DER certificate: {err:?}");
546 }
547 }
548 }
549 if valid_count == 0 && invalid_count > 0 {
550 let err = if load_results.errors.is_empty() {
551 crate::error::builder(
552 "zero valid certificates found in native root store",
553 )
554 } else {
555 use std::fmt::Write as _;
556 let mut acc = String::new();
557 for err in load_results.errors {
558 let _ = writeln!(&mut acc, "{err}");
559 }
560
561 crate::error::builder(acc)
562 };
563
564 return Err(err);
565 }
566 }
567
568 let mut versions = rustls::ALL_VERSIONS.to_vec();
570
571 if let Some(min_tls_version) = config.min_tls_version {
572 versions.retain(|&supported_version| {
573 match tls::Version::from_rustls(supported_version.version) {
574 Some(version) => version >= min_tls_version,
575 None => true,
578 }
579 });
580 }
581
582 if let Some(max_tls_version) = config.max_tls_version {
583 versions.retain(|&supported_version| {
584 match tls::Version::from_rustls(supported_version.version) {
585 Some(version) => version <= max_tls_version,
586 None => false,
587 }
588 });
589 }
590
591 if versions.is_empty() {
592 return Err(crate::error::builder("empty supported tls versions"));
593 }
594
595 let provider = rustls::crypto::CryptoProvider::get_default()
598 .map(|arc| arc.clone())
599 .unwrap_or_else(|| {
600 #[cfg(not(feature = "__rustls-ring"))]
601 panic!("No provider set");
602
603 #[cfg(feature = "__rustls-ring")]
604 Arc::new(rustls::crypto::ring::default_provider())
605 });
606
607 let signature_algorithms = provider.signature_verification_algorithms;
609 let config_builder =
610 rustls::ClientConfig::builder_with_provider(provider.clone())
611 .with_protocol_versions(&versions)
612 .map_err(|_| crate::error::builder("invalid TLS versions"))?;
613
614 let config_builder = if !config.certs_verification {
615 config_builder
616 .dangerous()
617 .with_custom_certificate_verifier(Arc::new(NoVerifier))
618 } else if !config.hostname_verification {
619 config_builder
620 .dangerous()
621 .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
622 root_cert_store,
623 signature_algorithms,
624 )))
625 } else {
626 if config.crls.is_empty() {
627 config_builder.with_root_certificates(root_cert_store)
628 } else {
629 let crls = config
630 .crls
631 .iter()
632 .map(|e| e.as_rustls_crl())
633 .collect::<Vec<_>>();
634 let verifier =
635 rustls::client::WebPkiServerVerifier::builder_with_provider(
636 Arc::new(root_cert_store),
637 provider,
638 )
639 .with_crls(crls)
640 .build()
641 .map_err(|_| {
642 crate::error::builder("invalid TLS verification settings")
643 })?;
644 config_builder.with_webpki_verifier(verifier)
645 }
646 };
647
648 let mut tls = if let Some(id) = config.identity {
650 id.add_to_rustls(config_builder)?
651 } else {
652 config_builder.with_no_client_auth()
653 };
654
655 tls.enable_sni = config.tls_sni;
656
657 match config.http_version_pref {
659 HttpVersionPref::Http1 => {
660 tls.alpn_protocols = vec!["http/1.1".into()];
661 }
662 #[cfg(feature = "http2")]
663 HttpVersionPref::Http2 => {
664 tls.alpn_protocols = vec!["h2".into()];
665 }
666 #[cfg(feature = "http3")]
667 HttpVersionPref::Http3 => {
668 tls.alpn_protocols = vec!["h3".into()];
669 }
670 HttpVersionPref::All => {
671 tls.alpn_protocols = vec![
672 #[cfg(feature = "http2")]
673 "h2".into(),
674 "http/1.1".into(),
675 ];
676 }
677 }
678
679 #[cfg(feature = "http3")]
680 {
681 tls.enable_early_data = config.tls_enable_early_data;
682
683 h3_connector = build_h3_connector(
684 resolver,
685 tls.clone(),
686 config.quic_max_idle_timeout,
687 config.quic_stream_receive_window,
688 config.quic_receive_window,
689 config.quic_send_window,
690 config.local_address,
691 &config.http_version_pref,
692 )?;
693 }
694
695 ConnectorBuilder::new_rustls_tls(
696 http,
697 tls,
698 proxies.clone(),
699 user_agent(&config.headers),
700 config.local_address,
701 #[cfg(any(
702 target_os = "android",
703 target_os = "fuchsia",
704 target_os = "linux"
705 ))]
706 config.interface.as_deref(),
707 config.nodelay,
708 config.tls_info,
709 )
710 }
711 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
712 TlsBackend::UnknownPreconfigured => {
713 return Err(crate::error::builder(
714 "Unknown TLS backend passed to `use_preconfigured_tls`",
715 ));
716 }
717 }
718
719 #[cfg(not(feature = "__tls"))]
720 ConnectorBuilder::new(
721 http,
722 proxies.clone(),
723 config.local_address,
724 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
725 config.interface.as_deref(),
726 config.nodelay,
727 )
728 };
729
730 connector_builder.set_timeout(config.connect_timeout);
731 connector_builder.set_verbose(config.connection_verbose);
732 connector_builder.set_keepalive(config.tcp_keepalive);
733
734 let mut builder =
735 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
736 #[cfg(feature = "http2")]
737 {
738 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
739 builder.http2_only(true);
740 }
741
742 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
743 {
744 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
745 }
746 if let Some(http2_initial_connection_window_size) =
747 config.http2_initial_connection_window_size
748 {
749 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
750 }
751 if config.http2_adaptive_window {
752 builder.http2_adaptive_window(true);
753 }
754 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
755 builder.http2_max_frame_size(http2_max_frame_size);
756 }
757 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
758 builder.http2_max_header_list_size(http2_max_header_list_size);
759 }
760 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
761 builder.http2_keep_alive_interval(http2_keep_alive_interval);
762 }
763 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
764 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
765 }
766 if config.http2_keep_alive_while_idle {
767 builder.http2_keep_alive_while_idle(true);
768 }
769 }
770
771 builder.timer(hyper_util::rt::TokioTimer::new());
772 builder.pool_timer(hyper_util::rt::TokioTimer::new());
773 builder.pool_idle_timeout(config.pool_idle_timeout);
774 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
775
776 if config.http09_responses {
777 builder.http09_responses(true);
778 }
779
780 if config.http1_title_case_headers {
781 builder.http1_title_case_headers(true);
782 }
783
784 if config.http1_allow_obsolete_multiline_headers_in_responses {
785 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
786 }
787
788 if config.http1_ignore_invalid_headers_in_responses {
789 builder.http1_ignore_invalid_headers_in_responses(true);
790 }
791
792 if config.http1_allow_spaces_after_header_name_in_responses {
793 builder.http1_allow_spaces_after_header_name_in_responses(true);
794 }
795
796 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
797
798 Ok(Client {
799 inner: Arc::new(ClientRef {
800 accepts: config.accepts,
801 #[cfg(feature = "cookies")]
802 cookie_store: config.cookie_store,
803 #[cfg(feature = "http3")]
806 h3_client: match h3_connector {
807 Some(h3_connector) => {
808 Some(H3Client::new(h3_connector, config.pool_idle_timeout))
809 }
810 None => None,
811 },
812 hyper: builder.build(connector_builder.build(config.connector_layers)),
813 headers: config.headers,
814 redirect_policy: config.redirect_policy,
815 referer: config.referer,
816 read_timeout: config.read_timeout,
817 request_timeout: config.timeout,
818 proxies,
819 proxies_maybe_http_auth,
820 https_only: config.https_only,
821 }),
822 })
823 }
824
825 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
848 where
849 V: TryInto<HeaderValue>,
850 V::Error: Into<http::Error>,
851 {
852 match value.try_into() {
853 Ok(value) => {
854 self.config.headers.insert(USER_AGENT, value);
855 }
856 Err(e) => {
857 self.config.error = Some(crate::error::builder(e.into()));
858 }
859 };
860 self
861 }
862 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
886 for (key, value) in headers.iter() {
887 self.config.headers.insert(key, value.clone());
888 }
889 self
890 }
891
892 #[cfg(feature = "cookies")]
907 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
908 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
909 if enable {
910 self.cookie_provider(Arc::new(cookie::Jar::default()))
911 } else {
912 self.config.cookie_store = None;
913 self
914 }
915 }
916
917 #[cfg(feature = "cookies")]
931 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
932 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
933 mut self,
934 cookie_store: Arc<C>,
935 ) -> ClientBuilder {
936 self.config.cookie_store = Some(cookie_store as _);
937 self
938 }
939
940 #[cfg(feature = "gzip")]
957 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
958 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
959 self.config.accepts.gzip = enable;
960 self
961 }
962
963 #[cfg(feature = "brotli")]
980 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
981 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
982 self.config.accepts.brotli = enable;
983 self
984 }
985
986 #[cfg(feature = "zstd")]
1003 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1004 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1005 self.config.accepts.zstd = enable;
1006 self
1007 }
1008
1009 #[cfg(feature = "deflate")]
1026 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1027 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1028 self.config.accepts.deflate = enable;
1029 self
1030 }
1031
1032 pub fn no_gzip(self) -> ClientBuilder {
1038 #[cfg(feature = "gzip")]
1039 {
1040 self.gzip(false)
1041 }
1042
1043 #[cfg(not(feature = "gzip"))]
1044 {
1045 self
1046 }
1047 }
1048
1049 pub fn no_brotli(self) -> ClientBuilder {
1055 #[cfg(feature = "brotli")]
1056 {
1057 self.brotli(false)
1058 }
1059
1060 #[cfg(not(feature = "brotli"))]
1061 {
1062 self
1063 }
1064 }
1065
1066 pub fn no_zstd(self) -> ClientBuilder {
1072 #[cfg(feature = "zstd")]
1073 {
1074 self.zstd(false)
1075 }
1076
1077 #[cfg(not(feature = "zstd"))]
1078 {
1079 self
1080 }
1081 }
1082
1083 pub fn no_deflate(self) -> ClientBuilder {
1089 #[cfg(feature = "deflate")]
1090 {
1091 self.deflate(false)
1092 }
1093
1094 #[cfg(not(feature = "deflate"))]
1095 {
1096 self
1097 }
1098 }
1099
1100 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1106 self.config.redirect_policy = policy;
1107 self
1108 }
1109
1110 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1114 self.config.referer = enable;
1115 self
1116 }
1117
1118 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1126 self.config.proxies.push(proxy);
1127 self.config.auto_sys_proxy = false;
1128 self
1129 }
1130
1131 pub fn no_proxy(mut self) -> ClientBuilder {
1139 self.config.proxies.clear();
1140 self.config.auto_sys_proxy = false;
1141 self
1142 }
1143
1144 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1153 self.config.timeout = Some(timeout);
1154 self
1155 }
1156
1157 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1165 self.config.read_timeout = Some(timeout);
1166 self
1167 }
1168
1169 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1178 self.config.connect_timeout = Some(timeout);
1179 self
1180 }
1181
1182 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1189 self.config.connection_verbose = verbose;
1190 self
1191 }
1192
1193 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1201 where
1202 D: Into<Option<Duration>>,
1203 {
1204 self.config.pool_idle_timeout = val.into();
1205 self
1206 }
1207
1208 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1210 self.config.pool_max_idle_per_host = max;
1211 self
1212 }
1213
1214 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1216 self.config.http1_title_case_headers = true;
1217 self
1218 }
1219
1220 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1226 mut self,
1227 value: bool,
1228 ) -> ClientBuilder {
1229 self.config
1230 .http1_allow_obsolete_multiline_headers_in_responses = value;
1231 self
1232 }
1233
1234 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1236 self.config.http1_ignore_invalid_headers_in_responses = value;
1237 self
1238 }
1239
1240 pub fn http1_allow_spaces_after_header_name_in_responses(
1246 mut self,
1247 value: bool,
1248 ) -> ClientBuilder {
1249 self.config
1250 .http1_allow_spaces_after_header_name_in_responses = value;
1251 self
1252 }
1253
1254 pub fn http1_only(mut self) -> ClientBuilder {
1256 self.config.http_version_pref = HttpVersionPref::Http1;
1257 self
1258 }
1259
1260 pub fn http09_responses(mut self) -> ClientBuilder {
1262 self.config.http09_responses = true;
1263 self
1264 }
1265
1266 #[cfg(feature = "http2")]
1268 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1269 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1270 self.config.http_version_pref = HttpVersionPref::Http2;
1271 self
1272 }
1273
1274 #[cfg(feature = "http3")]
1276 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1277 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1278 self.config.http_version_pref = HttpVersionPref::Http3;
1279 self
1280 }
1281
1282 #[cfg(feature = "http2")]
1286 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1287 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1288 self.config.http2_initial_stream_window_size = sz.into();
1289 self
1290 }
1291
1292 #[cfg(feature = "http2")]
1296 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1297 pub fn http2_initial_connection_window_size(
1298 mut self,
1299 sz: impl Into<Option<u32>>,
1300 ) -> ClientBuilder {
1301 self.config.http2_initial_connection_window_size = sz.into();
1302 self
1303 }
1304
1305 #[cfg(feature = "http2")]
1310 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1311 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1312 self.config.http2_adaptive_window = enabled;
1313 self
1314 }
1315
1316 #[cfg(feature = "http2")]
1320 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1321 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1322 self.config.http2_max_frame_size = sz.into();
1323 self
1324 }
1325
1326 #[cfg(feature = "http2")]
1330 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1331 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1332 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1333 self
1334 }
1335
1336 #[cfg(feature = "http2")]
1341 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1342 pub fn http2_keep_alive_interval(
1343 mut self,
1344 interval: impl Into<Option<Duration>>,
1345 ) -> ClientBuilder {
1346 self.config.http2_keep_alive_interval = interval.into();
1347 self
1348 }
1349
1350 #[cfg(feature = "http2")]
1356 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1357 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1358 self.config.http2_keep_alive_timeout = Some(timeout);
1359 self
1360 }
1361
1362 #[cfg(feature = "http2")]
1369 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1370 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1371 self.config.http2_keep_alive_while_idle = enabled;
1372 self
1373 }
1374
1375 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1381 self.config.nodelay = enabled;
1382 self
1383 }
1384
1385 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1399 where
1400 T: Into<Option<IpAddr>>,
1401 {
1402 self.config.local_address = addr.into();
1403 self
1404 }
1405
1406 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1419 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1420 self.config.interface = Some(interface.to_string());
1421 self
1422 }
1423
1424 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1428 where
1429 D: Into<Option<Duration>>,
1430 {
1431 self.config.tcp_keepalive = val.into();
1432 self
1433 }
1434
1435 #[cfg(feature = "__tls")]
1447 #[cfg_attr(
1448 docsrs,
1449 doc(cfg(any(
1450 feature = "default-tls",
1451 feature = "native-tls",
1452 feature = "rustls-tls"
1453 )))
1454 )]
1455 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1456 self.config.root_certs.push(cert);
1457 self
1458 }
1459
1460 #[cfg(feature = "__rustls")]
1467 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1468 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1469 self.config.crls.push(crl);
1470 self
1471 }
1472
1473 #[cfg(feature = "__rustls")]
1480 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1481 pub fn add_crls(
1482 mut self,
1483 crls: impl IntoIterator<Item = CertificateRevocationList>,
1484 ) -> ClientBuilder {
1485 self.config.crls.extend(crls);
1486 self
1487 }
1488
1489 #[cfg(feature = "__tls")]
1507 #[cfg_attr(
1508 docsrs,
1509 doc(cfg(any(
1510 feature = "default-tls",
1511 feature = "native-tls",
1512 feature = "rustls-tls"
1513 )))
1514 )]
1515 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1516 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1517
1518 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1519 {
1520 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1521 }
1522
1523 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1524 {
1525 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1526 }
1527
1528 self
1529 }
1530
1531 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1535 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1536 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1537 self.config.tls_built_in_certs_webpki = enabled;
1538 self
1539 }
1540
1541 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1545 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1546 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1547 self.config.tls_built_in_certs_native = enabled;
1548 self
1549 }
1550
1551 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1558 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1559 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1560 self.config.identity = Some(identity);
1561 self
1562 }
1563
1564 #[cfg(feature = "__tls")]
1580 #[cfg_attr(
1581 docsrs,
1582 doc(cfg(any(
1583 feature = "default-tls",
1584 feature = "native-tls",
1585 feature = "rustls-tls"
1586 )))
1587 )]
1588 pub fn danger_accept_invalid_hostnames(
1589 mut self,
1590 accept_invalid_hostname: bool,
1591 ) -> ClientBuilder {
1592 self.config.hostname_verification = !accept_invalid_hostname;
1593 self
1594 }
1595
1596 #[cfg(feature = "__tls")]
1613 #[cfg_attr(
1614 docsrs,
1615 doc(cfg(any(
1616 feature = "default-tls",
1617 feature = "native-tls",
1618 feature = "rustls-tls"
1619 )))
1620 )]
1621 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1622 self.config.certs_verification = !accept_invalid_certs;
1623 self
1624 }
1625
1626 #[cfg(feature = "__tls")]
1635 #[cfg_attr(
1636 docsrs,
1637 doc(cfg(any(
1638 feature = "default-tls",
1639 feature = "native-tls",
1640 feature = "rustls-tls"
1641 )))
1642 )]
1643 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1644 self.config.tls_sni = tls_sni;
1645 self
1646 }
1647
1648 #[cfg(feature = "__tls")]
1664 #[cfg_attr(
1665 docsrs,
1666 doc(cfg(any(
1667 feature = "default-tls",
1668 feature = "native-tls",
1669 feature = "rustls-tls"
1670 )))
1671 )]
1672 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1673 self.config.min_tls_version = Some(version);
1674 self
1675 }
1676
1677 #[cfg(feature = "__tls")]
1696 #[cfg_attr(
1697 docsrs,
1698 doc(cfg(any(
1699 feature = "default-tls",
1700 feature = "native-tls",
1701 feature = "rustls-tls"
1702 )))
1703 )]
1704 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1705 self.config.max_tls_version = Some(version);
1706 self
1707 }
1708
1709 #[cfg(feature = "native-tls")]
1718 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1719 pub fn use_native_tls(mut self) -> ClientBuilder {
1720 self.config.tls = TlsBackend::Default;
1721 self
1722 }
1723
1724 #[cfg(feature = "__rustls")]
1733 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1734 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1735 self.config.tls = TlsBackend::Rustls;
1736 self
1737 }
1738
1739 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
1758 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1759 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
1760 let mut tls = Some(tls);
1761 #[cfg(feature = "native-tls")]
1762 {
1763 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
1764 let tls = conn.take().expect("is definitely Some");
1765 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
1766 self.config.tls = tls;
1767 return self;
1768 }
1769 }
1770 #[cfg(feature = "__rustls")]
1771 {
1772 if let Some(conn) =
1773 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
1774 {
1775 let tls = conn.take().expect("is definitely Some");
1776 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
1777 self.config.tls = tls;
1778 return self;
1779 }
1780 }
1781
1782 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
1784 self
1785 }
1786
1787 #[cfg(feature = "__tls")]
1794 #[cfg_attr(
1795 docsrs,
1796 doc(cfg(any(
1797 feature = "default-tls",
1798 feature = "native-tls",
1799 feature = "rustls-tls"
1800 )))
1801 )]
1802 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
1803 self.config.tls_info = tls_info;
1804 self
1805 }
1806
1807 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
1811 self.config.https_only = enabled;
1812 self
1813 }
1814
1815 #[doc(hidden)]
1816 #[cfg(feature = "hickory-dns")]
1817 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1818 #[deprecated(note = "use `hickory_dns` instead")]
1819 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
1820 self.config.hickory_dns = enable;
1821 self
1822 }
1823
1824 #[cfg(feature = "hickory-dns")]
1838 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1839 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
1840 self.config.hickory_dns = enable;
1841 self
1842 }
1843
1844 #[doc(hidden)]
1845 #[deprecated(note = "use `no_hickory_dns` instead")]
1846 pub fn no_trust_dns(self) -> ClientBuilder {
1847 self.no_hickory_dns()
1848 }
1849
1850 pub fn no_hickory_dns(self) -> ClientBuilder {
1856 #[cfg(feature = "hickory-dns")]
1857 {
1858 self.hickory_dns(false)
1859 }
1860
1861 #[cfg(not(feature = "hickory-dns"))]
1862 {
1863 self
1864 }
1865 }
1866
1867 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
1872 self.resolve_to_addrs(domain, &[addr])
1873 }
1874
1875 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
1880 self.config
1881 .dns_overrides
1882 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
1883 self
1884 }
1885
1886 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
1892 self.config.dns_resolver = Some(resolver as _);
1893 self
1894 }
1895
1896 #[cfg(feature = "http3")]
1901 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1902 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
1903 self.config.tls_enable_early_data = enabled;
1904 self
1905 }
1906
1907 #[cfg(feature = "http3")]
1913 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1914 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
1915 self.config.quic_max_idle_timeout = Some(value);
1916 self
1917 }
1918
1919 #[cfg(feature = "http3")]
1930 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1931 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
1932 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
1933 self
1934 }
1935
1936 #[cfg(feature = "http3")]
1947 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1948 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
1949 self.config.quic_receive_window = Some(value.try_into().unwrap());
1950 self
1951 }
1952
1953 #[cfg(feature = "http3")]
1959 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1960 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
1961 self.config.quic_send_window = Some(value);
1962 self
1963 }
1964
1965 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
1989 where
1990 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
1991 L::Service:
1992 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
1993 <L::Service as Service<Unnameable>>::Future: Send + 'static,
1994 {
1995 let layer = BoxCloneSyncServiceLayer::new(layer);
1996
1997 self.config.connector_layers.push(layer);
1998
1999 self
2000 }
2001}
2002
2003type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2004
2005impl Default for Client {
2006 fn default() -> Self {
2007 Self::new()
2008 }
2009}
2010
2011impl Client {
2012 pub fn new() -> Client {
2022 ClientBuilder::new().build().expect("Client::new()")
2023 }
2024
2025 pub fn builder() -> ClientBuilder {
2029 ClientBuilder::new()
2030 }
2031
2032 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2038 self.request(Method::GET, url)
2039 }
2040
2041 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2047 self.request(Method::POST, url)
2048 }
2049
2050 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2056 self.request(Method::PUT, url)
2057 }
2058
2059 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2065 self.request(Method::PATCH, url)
2066 }
2067
2068 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2074 self.request(Method::DELETE, url)
2075 }
2076
2077 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2083 self.request(Method::HEAD, url)
2084 }
2085
2086 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2095 let req = url.into_url().map(move |url| Request::new(method, url));
2096 RequestBuilder::new(self.clone(), req)
2097 }
2098
2099 pub fn execute(
2112 &self,
2113 request: Request,
2114 ) -> impl Future<Output = Result<Response, crate::Error>> {
2115 self.execute_request(request)
2116 }
2117
2118 pub(super) fn execute_request(&self, req: Request) -> Pending {
2119 let (method, url, mut headers, body, timeout, version) = req.pieces();
2120 if url.scheme() != "http" && url.scheme() != "https" {
2121 return Pending::new_err(error::url_bad_scheme(url));
2122 }
2123
2124 if self.inner.https_only && url.scheme() != "https" {
2126 return Pending::new_err(error::url_bad_scheme(url));
2127 }
2128
2129 for (key, value) in &self.inner.headers {
2132 if let Entry::Vacant(entry) = headers.entry(key) {
2133 entry.insert(value.clone());
2134 }
2135 }
2136
2137 #[cfg(feature = "cookies")]
2139 {
2140 if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
2141 if headers.get(crate::header::COOKIE).is_none() {
2142 add_cookie_header(&mut headers, &**cookie_store, &url);
2143 }
2144 }
2145 }
2146
2147 let accept_encoding = self.inner.accepts.as_str();
2148
2149 if let Some(accept_encoding) = accept_encoding {
2150 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2151 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2152 }
2153 }
2154
2155 let uri = match try_uri(&url) {
2156 Ok(uri) => uri,
2157 _ => return Pending::new_err(error::url_invalid_uri(url)),
2158 };
2159
2160 let (reusable, body) = match body {
2161 Some(body) => {
2162 let (reusable, body) = body.try_reuse();
2163 (Some(reusable), body)
2164 }
2165 None => (None, Body::empty()),
2166 };
2167
2168 self.proxy_auth(&uri, &mut headers);
2169
2170 let builder = hyper::Request::builder()
2171 .method(method.clone())
2172 .uri(uri)
2173 .version(version);
2174
2175 let in_flight = match version {
2176 #[cfg(feature = "http3")]
2177 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2178 let mut req = builder.body(body).expect("valid request parts");
2179 *req.headers_mut() = headers.clone();
2180 ResponseFuture::H3(self.inner.h3_client.as_ref().unwrap().request(req))
2181 }
2182 _ => {
2183 let mut req = builder.body(body).expect("valid request parts");
2184 *req.headers_mut() = headers.clone();
2185 ResponseFuture::Default(self.inner.hyper.request(req))
2186 }
2187 };
2188
2189 let total_timeout = timeout
2190 .or(self.inner.request_timeout)
2191 .map(tokio::time::sleep)
2192 .map(Box::pin);
2193
2194 let read_timeout_fut = self
2195 .inner
2196 .read_timeout
2197 .map(tokio::time::sleep)
2198 .map(Box::pin);
2199
2200 Pending {
2201 inner: PendingInner::Request(PendingRequest {
2202 method,
2203 url,
2204 headers,
2205 body: reusable,
2206
2207 urls: Vec::new(),
2208
2209 retry_count: 0,
2210
2211 client: self.inner.clone(),
2212
2213 in_flight,
2214 total_timeout,
2215 read_timeout_fut,
2216 read_timeout: self.inner.read_timeout,
2217 }),
2218 }
2219 }
2220
2221 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2222 if !self.inner.proxies_maybe_http_auth {
2223 return;
2224 }
2225
2226 if dst.scheme() != Some(&Scheme::HTTP) {
2230 return;
2231 }
2232
2233 if headers.contains_key(PROXY_AUTHORIZATION) {
2234 return;
2235 }
2236
2237 for proxy in self.inner.proxies.iter() {
2238 if proxy.is_match(dst) {
2239 if let Some(header) = proxy.http_basic_auth(dst) {
2240 headers.insert(PROXY_AUTHORIZATION, header);
2241 }
2242
2243 break;
2244 }
2245 }
2246 }
2247}
2248
2249impl fmt::Debug for Client {
2250 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2251 let mut builder = f.debug_struct("Client");
2252 self.inner.fmt_fields(&mut builder);
2253 builder.finish()
2254 }
2255}
2256
2257impl tower_service::Service<Request> for Client {
2258 type Response = Response;
2259 type Error = crate::Error;
2260 type Future = Pending;
2261
2262 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2263 Poll::Ready(Ok(()))
2264 }
2265
2266 fn call(&mut self, req: Request) -> Self::Future {
2267 self.execute_request(req)
2268 }
2269}
2270
2271impl tower_service::Service<Request> for &'_ Client {
2272 type Response = Response;
2273 type Error = crate::Error;
2274 type Future = Pending;
2275
2276 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2277 Poll::Ready(Ok(()))
2278 }
2279
2280 fn call(&mut self, req: Request) -> Self::Future {
2281 self.execute_request(req)
2282 }
2283}
2284
2285impl fmt::Debug for ClientBuilder {
2286 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2287 let mut builder = f.debug_struct("ClientBuilder");
2288 self.config.fmt_fields(&mut builder);
2289 builder.finish()
2290 }
2291}
2292
2293impl Config {
2294 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2295 #[cfg(feature = "cookies")]
2299 {
2300 if let Some(_) = self.cookie_store {
2301 f.field("cookie_store", &true);
2302 }
2303 }
2304
2305 f.field("accepts", &self.accepts);
2306
2307 if !self.proxies.is_empty() {
2308 f.field("proxies", &self.proxies);
2309 }
2310
2311 if !self.redirect_policy.is_default() {
2312 f.field("redirect_policy", &self.redirect_policy);
2313 }
2314
2315 if self.referer {
2316 f.field("referer", &true);
2317 }
2318
2319 f.field("default_headers", &self.headers);
2320
2321 if self.http1_title_case_headers {
2322 f.field("http1_title_case_headers", &true);
2323 }
2324
2325 if self.http1_allow_obsolete_multiline_headers_in_responses {
2326 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2327 }
2328
2329 if self.http1_ignore_invalid_headers_in_responses {
2330 f.field("http1_ignore_invalid_headers_in_responses", &true);
2331 }
2332
2333 if self.http1_allow_spaces_after_header_name_in_responses {
2334 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2335 }
2336
2337 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2338 f.field("http1_only", &true);
2339 }
2340
2341 #[cfg(feature = "http2")]
2342 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2343 f.field("http2_prior_knowledge", &true);
2344 }
2345
2346 if let Some(ref d) = self.connect_timeout {
2347 f.field("connect_timeout", d);
2348 }
2349
2350 if let Some(ref d) = self.timeout {
2351 f.field("timeout", d);
2352 }
2353
2354 if let Some(ref v) = self.local_address {
2355 f.field("local_address", v);
2356 }
2357
2358 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
2359 if let Some(ref v) = self.interface {
2360 f.field("interface", v);
2361 }
2362
2363 if self.nodelay {
2364 f.field("tcp_nodelay", &true);
2365 }
2366
2367 #[cfg(feature = "__tls")]
2368 {
2369 if !self.hostname_verification {
2370 f.field("danger_accept_invalid_hostnames", &true);
2371 }
2372 }
2373
2374 #[cfg(feature = "__tls")]
2375 {
2376 if !self.certs_verification {
2377 f.field("danger_accept_invalid_certs", &true);
2378 }
2379
2380 if let Some(ref min_tls_version) = self.min_tls_version {
2381 f.field("min_tls_version", min_tls_version);
2382 }
2383
2384 if let Some(ref max_tls_version) = self.max_tls_version {
2385 f.field("max_tls_version", max_tls_version);
2386 }
2387
2388 f.field("tls_sni", &self.tls_sni);
2389
2390 f.field("tls_info", &self.tls_info);
2391 }
2392
2393 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2394 {
2395 f.field("tls_backend", &self.tls);
2396 }
2397
2398 if !self.dns_overrides.is_empty() {
2399 f.field("dns_overrides", &self.dns_overrides);
2400 }
2401
2402 #[cfg(feature = "http3")]
2403 {
2404 if self.tls_enable_early_data {
2405 f.field("tls_enable_early_data", &true);
2406 }
2407 }
2408 }
2409}
2410
2411struct ClientRef {
2412 accepts: Accepts,
2413 #[cfg(feature = "cookies")]
2414 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2415 headers: HeaderMap,
2416 hyper: HyperClient,
2417 #[cfg(feature = "http3")]
2418 h3_client: Option<H3Client>,
2419 redirect_policy: redirect::Policy,
2420 referer: bool,
2421 request_timeout: Option<Duration>,
2422 read_timeout: Option<Duration>,
2423 proxies: Arc<Vec<Proxy>>,
2424 proxies_maybe_http_auth: bool,
2425 https_only: bool,
2426}
2427
2428impl ClientRef {
2429 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2430 #[cfg(feature = "cookies")]
2434 {
2435 if let Some(_) = self.cookie_store {
2436 f.field("cookie_store", &true);
2437 }
2438 }
2439
2440 f.field("accepts", &self.accepts);
2441
2442 if !self.proxies.is_empty() {
2443 f.field("proxies", &self.proxies);
2444 }
2445
2446 if !self.redirect_policy.is_default() {
2447 f.field("redirect_policy", &self.redirect_policy);
2448 }
2449
2450 if self.referer {
2451 f.field("referer", &true);
2452 }
2453
2454 f.field("default_headers", &self.headers);
2455
2456 if let Some(ref d) = self.request_timeout {
2457 f.field("timeout", d);
2458 }
2459
2460 if let Some(ref d) = self.read_timeout {
2461 f.field("read_timeout", d);
2462 }
2463 }
2464}
2465
2466pin_project! {
2467 pub struct Pending {
2468 #[pin]
2469 inner: PendingInner,
2470 }
2471}
2472
2473enum PendingInner {
2474 Request(PendingRequest),
2475 Error(Option<crate::Error>),
2476}
2477
2478pin_project! {
2479 struct PendingRequest {
2480 method: Method,
2481 url: Url,
2482 headers: HeaderMap,
2483 body: Option<Option<Bytes>>,
2484
2485 urls: Vec<Url>,
2486
2487 retry_count: usize,
2488
2489 client: Arc<ClientRef>,
2490
2491 #[pin]
2492 in_flight: ResponseFuture,
2493 #[pin]
2494 total_timeout: Option<Pin<Box<Sleep>>>,
2495 #[pin]
2496 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2497 read_timeout: Option<Duration>,
2498 }
2499}
2500
2501enum ResponseFuture {
2502 Default(HyperResponseFuture),
2503 #[cfg(feature = "http3")]
2504 H3(H3ResponseFuture),
2505}
2506
2507impl PendingRequest {
2508 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2509 self.project().in_flight
2510 }
2511
2512 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2513 self.project().total_timeout
2514 }
2515
2516 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2517 self.project().read_timeout_fut
2518 }
2519
2520 fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
2521 self.project().urls
2522 }
2523
2524 fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
2525 self.project().headers
2526 }
2527
2528 #[cfg(any(feature = "http2", feature = "http3"))]
2529 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2530 use log::trace;
2531
2532 if !is_retryable_error(err) {
2533 return false;
2534 }
2535
2536 trace!("can retry {err:?}");
2537
2538 let body = match self.body {
2539 Some(Some(ref body)) => Body::reusable(body.clone()),
2540 Some(None) => {
2541 debug!("error was retryable, but body not reusable");
2542 return false;
2543 }
2544 None => Body::empty(),
2545 };
2546
2547 if self.retry_count >= 2 {
2548 trace!("retry count too high");
2549 return false;
2550 }
2551 self.retry_count += 1;
2552
2553 let uri = try_uri(&self.url).expect("URL was already validated as URI");
2555
2556 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
2557 #[cfg(feature = "http3")]
2558 ResponseFuture::H3(_) => {
2559 let mut req = hyper::Request::builder()
2560 .method(self.method.clone())
2561 .uri(uri)
2562 .body(body)
2563 .expect("valid request parts");
2564 *req.headers_mut() = self.headers.clone();
2565 ResponseFuture::H3(
2566 self.client
2567 .h3_client
2568 .as_ref()
2569 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2570 .request(req),
2571 )
2572 }
2573 _ => {
2574 let mut req = hyper::Request::builder()
2575 .method(self.method.clone())
2576 .uri(uri)
2577 .body(body)
2578 .expect("valid request parts");
2579 *req.headers_mut() = self.headers.clone();
2580 ResponseFuture::Default(self.client.hyper.request(req))
2581 }
2582 };
2583
2584 true
2585 }
2586}
2587
2588#[cfg(any(feature = "http2", feature = "http3"))]
2589fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
2590 let err = if let Some(err) = err.source() {
2592 err
2593 } else {
2594 return false;
2595 };
2596
2597 #[cfg(feature = "http3")]
2598 if let Some(cause) = err.source() {
2599 if let Some(err) = cause.downcast_ref::<h3::Error>() {
2600 debug!("determining if HTTP/3 error {err} can be retried");
2601 return err.to_string().as_str() == "timeout";
2603 }
2604 }
2605
2606 #[cfg(feature = "http2")]
2607 if let Some(cause) = err.source() {
2608 if let Some(err) = cause.downcast_ref::<h2::Error>() {
2609 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
2611 return true;
2612 }
2613
2614 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
2617 {
2618 return true;
2619 }
2620 }
2621 }
2622 false
2623}
2624
2625impl Pending {
2626 pub(super) fn new_err(err: crate::Error) -> Pending {
2627 Pending {
2628 inner: PendingInner::Error(Some(err)),
2629 }
2630 }
2631
2632 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2633 self.project().inner
2634 }
2635}
2636
2637impl Future for Pending {
2638 type Output = Result<Response, crate::Error>;
2639
2640 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2641 let inner = self.inner();
2642 match inner.get_mut() {
2643 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2644 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2645 .take()
2646 .expect("Pending error polled more than once"))),
2647 }
2648 }
2649}
2650
2651impl Future for PendingRequest {
2652 type Output = Result<Response, crate::Error>;
2653
2654 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2655 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
2656 if let Poll::Ready(()) = delay.poll(cx) {
2657 return Poll::Ready(Err(
2658 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2659 ));
2660 }
2661 }
2662
2663 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
2664 if let Poll::Ready(()) = delay.poll(cx) {
2665 return Poll::Ready(Err(
2666 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2667 ));
2668 }
2669 }
2670
2671 loop {
2672 let res = match self.as_mut().in_flight().get_mut() {
2673 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
2674 Poll::Ready(Err(e)) => {
2675 #[cfg(feature = "http2")]
2676 if self.as_mut().retry_error(&e) {
2677 continue;
2678 }
2679 return Poll::Ready(Err(
2680 crate::error::request(e).with_url(self.url.clone())
2681 ));
2682 }
2683 Poll::Ready(Ok(res)) => res.map(super::body::boxed),
2684 Poll::Pending => return Poll::Pending,
2685 },
2686 #[cfg(feature = "http3")]
2687 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
2688 Poll::Ready(Err(e)) => {
2689 if self.as_mut().retry_error(&e) {
2690 continue;
2691 }
2692 return Poll::Ready(Err(
2693 crate::error::request(e).with_url(self.url.clone())
2694 ));
2695 }
2696 Poll::Ready(Ok(res)) => res,
2697 Poll::Pending => return Poll::Pending,
2698 },
2699 };
2700
2701 #[cfg(feature = "cookies")]
2702 {
2703 if let Some(ref cookie_store) = self.client.cookie_store {
2704 let mut cookies =
2705 cookie::extract_response_cookie_headers(&res.headers()).peekable();
2706 if cookies.peek().is_some() {
2707 cookie_store.set_cookies(&mut cookies, &self.url);
2708 }
2709 }
2710 }
2711 let should_redirect = match res.status() {
2712 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
2713 self.body = None;
2714 for header in &[
2715 TRANSFER_ENCODING,
2716 CONTENT_ENCODING,
2717 CONTENT_TYPE,
2718 CONTENT_LENGTH,
2719 ] {
2720 self.headers.remove(header);
2721 }
2722
2723 match self.method {
2724 Method::GET | Method::HEAD => {}
2725 _ => {
2726 self.method = Method::GET;
2727 }
2728 }
2729 true
2730 }
2731 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
2732 match self.body {
2733 Some(Some(_)) | None => true,
2734 Some(None) => false,
2735 }
2736 }
2737 _ => false,
2738 };
2739 if should_redirect {
2740 let loc = res.headers().get(LOCATION).and_then(|val| {
2741 let loc = (|| -> Option<Url> {
2742 self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
2746 })();
2747
2748 let loc = loc.and_then(|url| {
2752 if try_uri(&url).is_ok() {
2753 Some(url)
2754 } else {
2755 None
2756 }
2757 });
2758
2759 if loc.is_none() {
2760 debug!("Location header had invalid URI: {val:?}");
2761 }
2762 loc
2763 });
2764 if let Some(loc) = loc {
2765 if self.client.referer {
2766 if let Some(referer) = make_referer(&loc, &self.url) {
2767 self.headers.insert(REFERER, referer);
2768 }
2769 }
2770 let url = self.url.clone();
2771 self.as_mut().urls().push(url);
2772 let action = self
2773 .client
2774 .redirect_policy
2775 .check(res.status(), &loc, &self.urls);
2776
2777 match action {
2778 redirect::ActionKind::Follow => {
2779 debug!("redirecting '{}' to '{}'", self.url, loc);
2780
2781 if loc.scheme() != "http" && loc.scheme() != "https" {
2782 return Poll::Ready(Err(error::url_bad_scheme(loc)));
2783 }
2784
2785 if self.client.https_only && loc.scheme() != "https" {
2786 return Poll::Ready(Err(error::redirect(
2787 error::url_bad_scheme(loc.clone()),
2788 loc,
2789 )));
2790 }
2791
2792 self.url = loc;
2793 let mut headers =
2794 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
2795
2796 remove_sensitive_headers(&mut headers, &self.url, &self.urls);
2797 let uri = try_uri(&self.url)?;
2798 let body = match self.body {
2799 Some(Some(ref body)) => Body::reusable(body.clone()),
2800 _ => Body::empty(),
2801 };
2802
2803 #[cfg(feature = "cookies")]
2805 {
2806 if let Some(ref cookie_store) = self.client.cookie_store {
2807 add_cookie_header(&mut headers, &**cookie_store, &self.url);
2808 }
2809 }
2810
2811 *self.as_mut().in_flight().get_mut() =
2812 match *self.as_mut().in_flight().as_ref() {
2813 #[cfg(feature = "http3")]
2814 ResponseFuture::H3(_) => {
2815 let mut req = hyper::Request::builder()
2816 .method(self.method.clone())
2817 .uri(uri.clone())
2818 .body(body)
2819 .expect("valid request parts");
2820 *req.headers_mut() = headers.clone();
2821 std::mem::swap(self.as_mut().headers(), &mut headers);
2822 ResponseFuture::H3(self.client.h3_client
2823 .as_ref()
2824 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2825 .request(req))
2826 }
2827 _ => {
2828 let mut req = hyper::Request::builder()
2829 .method(self.method.clone())
2830 .uri(uri.clone())
2831 .body(body)
2832 .expect("valid request parts");
2833 *req.headers_mut() = headers.clone();
2834 std::mem::swap(self.as_mut().headers(), &mut headers);
2835 ResponseFuture::Default(self.client.hyper.request(req))
2836 }
2837 };
2838
2839 continue;
2840 }
2841 redirect::ActionKind::Stop => {
2842 debug!("redirect policy disallowed redirection to '{loc}'");
2843 }
2844 redirect::ActionKind::Error(err) => {
2845 return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
2846 }
2847 }
2848 }
2849 }
2850
2851 let res = Response::new(
2852 res,
2853 self.url.clone(),
2854 self.client.accepts,
2855 self.total_timeout.take(),
2856 self.read_timeout,
2857 );
2858 return Poll::Ready(Ok(res));
2859 }
2860 }
2861}
2862
2863impl fmt::Debug for Pending {
2864 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2865 match self.inner {
2866 PendingInner::Request(ref req) => f
2867 .debug_struct("Pending")
2868 .field("method", &req.method)
2869 .field("url", &req.url)
2870 .finish(),
2871 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
2872 }
2873 }
2874}
2875
2876fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
2877 if next.scheme() == "http" && previous.scheme() == "https" {
2878 return None;
2879 }
2880
2881 let mut referer = previous.clone();
2882 let _ = referer.set_username("");
2883 let _ = referer.set_password(None);
2884 referer.set_fragment(None);
2885 referer.as_str().parse().ok()
2886}
2887
2888#[cfg(feature = "cookies")]
2889fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
2890 if let Some(header) = cookie_store.cookies(url) {
2891 headers.insert(crate::header::COOKIE, header);
2892 }
2893}
2894
2895#[cfg(test)]
2896mod tests {
2897 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
2898
2899 #[tokio::test]
2900 async fn execute_request_rejects_invalid_urls() {
2901 let url_str = "hxxps://www.rust-lang.org/";
2902 let url = url::Url::parse(url_str).unwrap();
2903 let result = crate::get(url.clone()).await;
2904
2905 assert!(result.is_err());
2906 let err = result.err().unwrap();
2907 assert!(err.is_builder());
2908 assert_eq!(url_str, err.url().unwrap().as_str());
2909 }
2910
2911 #[tokio::test]
2913 async fn execute_request_rejects_invalid_hostname() {
2914 let url_str = "https://{{hostname}}/";
2915 let url = url::Url::parse(url_str).unwrap();
2916 let result = crate::get(url.clone()).await;
2917
2918 assert!(result.is_err());
2919 let err = result.err().unwrap();
2920 assert!(err.is_builder());
2921 assert_eq!(url_str, err.url().unwrap().as_str());
2922 }
2923}