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
663 #[cfg(feature = "http2")]
664 HttpVersionPref::Http2 => {
665 tls.alpn_protocols = vec!["h2".into()];
666 }
667
668 #[cfg(feature = "http3")]
669 HttpVersionPref::Http3 => {
670 tls.alpn_protocols = vec!["h3".into()];
671 }
672
673 HttpVersionPref::All => {
674 tls.alpn_protocols = vec![
675 #[cfg(feature = "http2")]
676 "h2".into(),
677 "http/1.1".into(),
678 ];
679 }
680 }
681
682 #[cfg(feature = "http3")]
683 {
684 tls.enable_early_data = config.tls_enable_early_data;
685
686 h3_connector = build_h3_connector(
687 resolver,
688 tls.clone(),
689 config.quic_max_idle_timeout,
690 config.quic_stream_receive_window,
691 config.quic_receive_window,
692 config.quic_send_window,
693 config.local_address,
694 &config.http_version_pref,
695 )?;
696 }
697
698 ConnectorBuilder::new_rustls_tls(
699 http,
700 tls,
701 proxies.clone(),
702 user_agent(&config.headers),
703 config.local_address,
704 #[cfg(any(
705 target_os = "android",
706 target_os = "fuchsia",
707 target_os = "linux"
708 ))]
709 config.interface.as_deref(),
710 config.nodelay,
711 config.tls_info,
712 )
713 }
714 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
715 TlsBackend::UnknownPreconfigured => {
716 return Err(crate::error::builder(
717 "Unknown TLS backend passed to `use_preconfigured_tls`",
718 ));
719 }
720 }
721
722 #[cfg(not(feature = "__tls"))]
723 ConnectorBuilder::new(
724 http,
725 proxies.clone(),
726 config.local_address,
727 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
728 config.interface.as_deref(),
729 config.nodelay,
730 )
731 };
732
733 connector_builder.set_timeout(config.connect_timeout);
734 connector_builder.set_verbose(config.connection_verbose);
735 connector_builder.set_keepalive(config.tcp_keepalive);
736
737 let mut builder =
738 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
739 #[cfg(feature = "http2")]
740 {
741 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
742 builder.http2_only(true);
743 }
744
745 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
746 {
747 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
748 }
749 if let Some(http2_initial_connection_window_size) =
750 config.http2_initial_connection_window_size
751 {
752 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
753 }
754 if config.http2_adaptive_window {
755 builder.http2_adaptive_window(true);
756 }
757 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
758 builder.http2_max_frame_size(http2_max_frame_size);
759 }
760 if let Some(http2_max_header_list_size) = config.http2_max_header_list_size {
761 builder.http2_max_header_list_size(http2_max_header_list_size);
762 }
763 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
764 builder.http2_keep_alive_interval(http2_keep_alive_interval);
765 }
766 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
767 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
768 }
769 if config.http2_keep_alive_while_idle {
770 builder.http2_keep_alive_while_idle(true);
771 }
772 }
773
774 builder.timer(hyper_util::rt::TokioTimer::new());
775 builder.pool_timer(hyper_util::rt::TokioTimer::new());
776 builder.pool_idle_timeout(config.pool_idle_timeout);
777 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
778
779 if config.http09_responses {
780 builder.http09_responses(true);
781 }
782
783 if config.http1_title_case_headers {
784 builder.http1_title_case_headers(true);
785 }
786
787 if config.http1_allow_obsolete_multiline_headers_in_responses {
788 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
789 }
790
791 if config.http1_ignore_invalid_headers_in_responses {
792 builder.http1_ignore_invalid_headers_in_responses(true);
793 }
794
795 if config.http1_allow_spaces_after_header_name_in_responses {
796 builder.http1_allow_spaces_after_header_name_in_responses(true);
797 }
798
799 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
800
801 Ok(Client {
802 inner: Arc::new(ClientRef {
803 accepts: config.accepts,
804 #[cfg(feature = "cookies")]
805 cookie_store: config.cookie_store,
806
807 #[cfg(feature = "http3")]
810 h3_client: match h3_connector {
811 Some(h3_connector) => {
812 Some(H3Client::new(h3_connector, config.pool_idle_timeout))
813 }
814 None => None,
815 },
816hyper: builder.build(connector_builder.build(config.connector_layers)),
818 headers: config.headers,
819 redirect_policy: config.redirect_policy,
820 referer: config.referer,
821 read_timeout: config.read_timeout,
822 request_timeout: config.timeout,
823 proxies,
824 proxies_maybe_http_auth,
825 https_only: config.https_only,
826 }),
827 })
828 }
829
830 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
853 where
854 V: TryInto<HeaderValue>,
855 V::Error: Into<http::Error>,
856 {
857 match value.try_into() {
858 Ok(value) => {
859 self.config.headers.insert(USER_AGENT, value);
860 }
861 Err(e) => {
862 self.config.error = Some(crate::error::builder(e.into()));
863 }
864 };
865 self
866 }
867 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
891 for (key, value) in headers.iter() {
892 self.config.headers.insert(key, value.clone());
893 }
894 self
895 }
896
897 #[cfg(feature = "cookies")]
912 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
913 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
914 if enable {
915 self.cookie_provider(Arc::new(cookie::Jar::default()))
916 } else {
917 self.config.cookie_store = None;
918 self
919 }
920 }
921
922 #[cfg(feature = "cookies")]
936 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
937 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
938 mut self,
939 cookie_store: Arc<C>,
940 ) -> ClientBuilder {
941 self.config.cookie_store = Some(cookie_store as _);
942 self
943 }
944
945 #[cfg(feature = "gzip")]
962 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
963 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
964 self.config.accepts.gzip = enable;
965 self
966 }
967
968 #[cfg(feature = "brotli")]
985 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
986 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
987 self.config.accepts.brotli = enable;
988 self
989 }
990
991 #[cfg(feature = "zstd")]
1008 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
1009 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
1010 self.config.accepts.zstd = enable;
1011 self
1012 }
1013
1014 #[cfg(feature = "deflate")]
1031 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
1032 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
1033 self.config.accepts.deflate = enable;
1034 self
1035 }
1036
1037 pub fn no_gzip(self) -> ClientBuilder {
1043 #[cfg(feature = "gzip")]
1044 {
1045 self.gzip(false)
1046 }
1047
1048 #[cfg(not(feature = "gzip"))]
1049 {
1050 self
1051 }
1052 }
1053
1054 pub fn no_brotli(self) -> ClientBuilder {
1060 #[cfg(feature = "brotli")]
1061 {
1062 self.brotli(false)
1063 }
1064
1065 #[cfg(not(feature = "brotli"))]
1066 {
1067 self
1068 }
1069 }
1070
1071 pub fn no_zstd(self) -> ClientBuilder {
1077 #[cfg(feature = "zstd")]
1078 {
1079 self.zstd(false)
1080 }
1081
1082 #[cfg(not(feature = "zstd"))]
1083 {
1084 self
1085 }
1086 }
1087
1088 pub fn no_deflate(self) -> ClientBuilder {
1094 #[cfg(feature = "deflate")]
1095 {
1096 self.deflate(false)
1097 }
1098
1099 #[cfg(not(feature = "deflate"))]
1100 {
1101 self
1102 }
1103 }
1104
1105 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1111 self.config.redirect_policy = policy;
1112 self
1113 }
1114
1115 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1119 self.config.referer = enable;
1120 self
1121 }
1122
1123 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1131 self.config.proxies.push(proxy);
1132 self.config.auto_sys_proxy = false;
1133 self
1134 }
1135
1136 pub fn no_proxy(mut self) -> ClientBuilder {
1144 self.config.proxies.clear();
1145 self.config.auto_sys_proxy = false;
1146 self
1147 }
1148
1149 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1158 self.config.timeout = Some(timeout);
1159 self
1160 }
1161
1162 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1170 self.config.read_timeout = Some(timeout);
1171 self
1172 }
1173
1174 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1183 self.config.connect_timeout = Some(timeout);
1184 self
1185 }
1186
1187 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1194 self.config.connection_verbose = verbose;
1195 self
1196 }
1197
1198 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1206 where
1207 D: Into<Option<Duration>>,
1208 {
1209 self.config.pool_idle_timeout = val.into();
1210 self
1211 }
1212
1213 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1215 self.config.pool_max_idle_per_host = max;
1216 self
1217 }
1218
1219 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1221 self.config.http1_title_case_headers = true;
1222 self
1223 }
1224
1225 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1231 mut self,
1232 value: bool,
1233 ) -> ClientBuilder {
1234 self.config
1235 .http1_allow_obsolete_multiline_headers_in_responses = value;
1236 self
1237 }
1238
1239 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1241 self.config.http1_ignore_invalid_headers_in_responses = value;
1242 self
1243 }
1244
1245 pub fn http1_allow_spaces_after_header_name_in_responses(
1251 mut self,
1252 value: bool,
1253 ) -> ClientBuilder {
1254 self.config
1255 .http1_allow_spaces_after_header_name_in_responses = value;
1256 self
1257 }
1258
1259 pub fn http1_only(mut self) -> ClientBuilder {
1261 self.config.http_version_pref = HttpVersionPref::Http1;
1262 self
1263 }
1264
1265 pub fn http09_responses(mut self) -> ClientBuilder {
1267 self.config.http09_responses = true;
1268 self
1269 }
1270
1271 #[cfg(feature = "http2")]
1273 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1274 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1275 self.config.http_version_pref = HttpVersionPref::Http2;
1276 self
1277 }
1278
1279 #[cfg(feature = "http3")]
1281 #[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "http3",))))]
1282 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1283 self.config.http_version_pref = HttpVersionPref::Http3;
1284 self
1285 }
1286
1287 #[cfg(feature = "http2")]
1291 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1292 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1293 self.config.http2_initial_stream_window_size = sz.into();
1294 self
1295 }
1296
1297 #[cfg(feature = "http2")]
1301 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1302 pub fn http2_initial_connection_window_size(
1303 mut self,
1304 sz: impl Into<Option<u32>>,
1305 ) -> ClientBuilder {
1306 self.config.http2_initial_connection_window_size = sz.into();
1307 self
1308 }
1309
1310 #[cfg(feature = "http2")]
1315 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1316 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1317 self.config.http2_adaptive_window = enabled;
1318 self
1319 }
1320
1321 #[cfg(feature = "http2")]
1325 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1326 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1327 self.config.http2_max_frame_size = sz.into();
1328 self
1329 }
1330
1331 #[cfg(feature = "http2")]
1335 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1336 pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder {
1337 self.config.http2_max_header_list_size = Some(max_header_size_bytes);
1338 self
1339 }
1340
1341 #[cfg(feature = "http2")]
1346 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1347 pub fn http2_keep_alive_interval(
1348 mut self,
1349 interval: impl Into<Option<Duration>>,
1350 ) -> ClientBuilder {
1351 self.config.http2_keep_alive_interval = interval.into();
1352 self
1353 }
1354
1355 #[cfg(feature = "http2")]
1361 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1362 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1363 self.config.http2_keep_alive_timeout = Some(timeout);
1364 self
1365 }
1366
1367 #[cfg(feature = "http2")]
1374 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1375 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1376 self.config.http2_keep_alive_while_idle = enabled;
1377 self
1378 }
1379
1380 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1386 self.config.nodelay = enabled;
1387 self
1388 }
1389
1390 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1404 where
1405 T: Into<Option<IpAddr>>,
1406 {
1407 self.config.local_address = addr.into();
1408 self
1409 }
1410
1411 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1424 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1425 self.config.interface = Some(interface.to_string());
1426 self
1427 }
1428
1429 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1433 where
1434 D: Into<Option<Duration>>,
1435 {
1436 self.config.tcp_keepalive = val.into();
1437 self
1438 }
1439
1440 #[cfg(feature = "__tls")]
1452 #[cfg_attr(
1453 docsrs,
1454 doc(cfg(any(
1455 feature = "default-tls",
1456 feature = "native-tls",
1457 feature = "rustls-tls"
1458 )))
1459 )]
1460 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1461 self.config.root_certs.push(cert);
1462 self
1463 }
1464
1465 #[cfg(feature = "__rustls")]
1472 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1473 pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder {
1474 self.config.crls.push(crl);
1475 self
1476 }
1477
1478 #[cfg(feature = "__rustls")]
1485 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1486 pub fn add_crls(
1487 mut self,
1488 crls: impl IntoIterator<Item = CertificateRevocationList>,
1489 ) -> ClientBuilder {
1490 self.config.crls.extend(crls);
1491 self
1492 }
1493
1494 #[cfg(feature = "__tls")]
1512 #[cfg_attr(
1513 docsrs,
1514 doc(cfg(any(
1515 feature = "default-tls",
1516 feature = "native-tls",
1517 feature = "rustls-tls"
1518 )))
1519 )]
1520 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1521 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1522
1523 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1524 {
1525 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1526 }
1527
1528 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1529 {
1530 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1531 }
1532
1533 self
1534 }
1535
1536 #[cfg(feature = "rustls-tls-webpki-roots-no-provider")]
1540 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))]
1541 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1542 self.config.tls_built_in_certs_webpki = enabled;
1543 self
1544 }
1545
1546 #[cfg(feature = "rustls-tls-native-roots-no-provider")]
1550 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))]
1551 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1552 self.config.tls_built_in_certs_native = enabled;
1553 self
1554 }
1555
1556 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1563 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1564 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1565 self.config.identity = Some(identity);
1566 self
1567 }
1568
1569 #[cfg(feature = "__tls")]
1585 #[cfg_attr(
1586 docsrs,
1587 doc(cfg(any(
1588 feature = "default-tls",
1589 feature = "native-tls",
1590 feature = "rustls-tls"
1591 )))
1592 )]
1593 pub fn danger_accept_invalid_hostnames(
1594 mut self,
1595 accept_invalid_hostname: bool,
1596 ) -> ClientBuilder {
1597 self.config.hostname_verification = !accept_invalid_hostname;
1598 self
1599 }
1600
1601 #[cfg(feature = "__tls")]
1618 #[cfg_attr(
1619 docsrs,
1620 doc(cfg(any(
1621 feature = "default-tls",
1622 feature = "native-tls",
1623 feature = "rustls-tls"
1624 )))
1625 )]
1626 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1627 self.config.certs_verification = !accept_invalid_certs;
1628 self
1629 }
1630
1631 #[cfg(feature = "__tls")]
1640 #[cfg_attr(
1641 docsrs,
1642 doc(cfg(any(
1643 feature = "default-tls",
1644 feature = "native-tls",
1645 feature = "rustls-tls"
1646 )))
1647 )]
1648 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1649 self.config.tls_sni = tls_sni;
1650 self
1651 }
1652
1653 #[cfg(feature = "__tls")]
1669 #[cfg_attr(
1670 docsrs,
1671 doc(cfg(any(
1672 feature = "default-tls",
1673 feature = "native-tls",
1674 feature = "rustls-tls"
1675 )))
1676 )]
1677 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1678 self.config.min_tls_version = Some(version);
1679 self
1680 }
1681
1682 #[cfg(feature = "__tls")]
1701 #[cfg_attr(
1702 docsrs,
1703 doc(cfg(any(
1704 feature = "default-tls",
1705 feature = "native-tls",
1706 feature = "rustls-tls"
1707 )))
1708 )]
1709 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1710 self.config.max_tls_version = Some(version);
1711 self
1712 }
1713
1714 #[cfg(feature = "native-tls")]
1723 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1724 pub fn use_native_tls(mut self) -> ClientBuilder {
1725 self.config.tls = TlsBackend::Default;
1726 self
1727 }
1728
1729 #[cfg(feature = "__rustls")]
1738 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1739 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1740 self.config.tls = TlsBackend::Rustls;
1741 self
1742 }
1743
1744 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
1763 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1764 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
1765 let mut tls = Some(tls);
1766 #[cfg(feature = "native-tls")]
1767 {
1768 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
1769 let tls = conn.take().expect("is definitely Some");
1770 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
1771 self.config.tls = tls;
1772 return self;
1773 }
1774 }
1775 #[cfg(feature = "__rustls")]
1776 {
1777 if let Some(conn) =
1778 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
1779 {
1780 let tls = conn.take().expect("is definitely Some");
1781 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
1782 self.config.tls = tls;
1783 return self;
1784 }
1785 }
1786
1787 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
1789 self
1790 }
1791
1792 #[cfg(feature = "__tls")]
1799 #[cfg_attr(
1800 docsrs,
1801 doc(cfg(any(
1802 feature = "default-tls",
1803 feature = "native-tls",
1804 feature = "rustls-tls"
1805 )))
1806 )]
1807 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
1808 self.config.tls_info = tls_info;
1809 self
1810 }
1811
1812 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
1816 self.config.https_only = enabled;
1817 self
1818 }
1819
1820 #[doc(hidden)]
1821 #[cfg(feature = "hickory-dns")]
1822 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1823 #[deprecated(note = "use `hickory_dns` instead")]
1824 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
1825 self.config.hickory_dns = enable;
1826 self
1827 }
1828
1829 #[cfg(feature = "hickory-dns")]
1843 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1844 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
1845 self.config.hickory_dns = enable;
1846 self
1847 }
1848
1849 #[doc(hidden)]
1850 #[deprecated(note = "use `no_hickory_dns` instead")]
1851 pub fn no_trust_dns(self) -> ClientBuilder {
1852 self.no_hickory_dns()
1853 }
1854
1855 pub fn no_hickory_dns(self) -> ClientBuilder {
1861 #[cfg(feature = "hickory-dns")]
1862 {
1863 self.hickory_dns(false)
1864 }
1865
1866 #[cfg(not(feature = "hickory-dns"))]
1867 {
1868 self
1869 }
1870 }
1871
1872 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
1877 self.resolve_to_addrs(domain, &[addr])
1878 }
1879
1880 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
1885 self.config
1886 .dns_overrides
1887 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
1888 self
1889 }
1890
1891 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
1897 self.config.dns_resolver = Some(resolver as _);
1898 self
1899 }
1900
1901 #[cfg(feature = "http3")]
1906 #[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "http3",))))]
1907 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
1908 self.config.tls_enable_early_data = enabled;
1909 self
1910 }
1911
1912 #[cfg(feature = "http3")]
1918 #[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "http3",))))]
1919 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
1920 self.config.quic_max_idle_timeout = Some(value);
1921 self
1922 }
1923
1924 #[cfg(feature = "http3")]
1935 #[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "http3",))))]
1936 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
1937 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
1938 self
1939 }
1940
1941 #[cfg(feature = "http3")]
1952 #[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "http3",))))]
1953 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
1954 self.config.quic_receive_window = Some(value.try_into().unwrap());
1955 self
1956 }
1957
1958 #[cfg(feature = "http3")]
1964 #[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "http3",))))]
1965 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
1966 self.config.quic_send_window = Some(value);
1967 self
1968 }
1969
1970 pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
1994 where
1995 L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
1996 L::Service:
1997 Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
1998 <L::Service as Service<Unnameable>>::Future: Send + 'static,
1999 {
2000 let layer = BoxCloneSyncServiceLayer::new(layer);
2001
2002 self.config.connector_layers.push(layer);
2003
2004 self
2005 }
2006}
2007
2008type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
2009
2010impl Default for Client {
2011 fn default() -> Self {
2012 Self::new()
2013 }
2014}
2015
2016impl Client {
2017 pub fn new() -> Client {
2027 ClientBuilder::new().build().expect("Client::new()")
2028 }
2029
2030 pub fn builder() -> ClientBuilder {
2034 ClientBuilder::new()
2035 }
2036
2037 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2043 self.request(Method::GET, url)
2044 }
2045
2046 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2052 self.request(Method::POST, url)
2053 }
2054
2055 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2061 self.request(Method::PUT, url)
2062 }
2063
2064 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2070 self.request(Method::PATCH, url)
2071 }
2072
2073 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2079 self.request(Method::DELETE, url)
2080 }
2081
2082 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
2088 self.request(Method::HEAD, url)
2089 }
2090
2091 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
2100 let req = url.into_url().map(move |url| Request::new(method, url));
2101 RequestBuilder::new(self.clone(), req)
2102 }
2103
2104 pub fn execute(
2117 &self,
2118 request: Request,
2119 ) -> impl Future<Output = Result<Response, crate::Error>> {
2120 self.execute_request(request)
2121 }
2122
2123 pub(super) fn execute_request(&self, req: Request) -> Pending {
2124 let (method, url, mut headers, body, timeout, mut version) = req.pieces();
2125
2126 #[cfg(feature = "http3")]
2127 if self.inner.h3_client.is_some() {
2128 version = http::Version::HTTP_3;
2129 }
2130
2131 if url.scheme() != "http" && url.scheme() != "https" {
2132 return Pending::new_err(error::url_bad_scheme(url));
2133 }
2134
2135 if self.inner.https_only && url.scheme() != "https" {
2137 return Pending::new_err(error::url_bad_scheme(url));
2138 }
2139
2140 for (key, value) in &self.inner.headers {
2143 if let Entry::Vacant(entry) = headers.entry(key) {
2144 entry.insert(value.clone());
2145 }
2146 }
2147
2148 #[cfg(feature = "cookies")]
2150 {
2151 if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
2152 if headers.get(crate::header::COOKIE).is_none() {
2153 add_cookie_header(&mut headers, &**cookie_store, &url);
2154 }
2155 }
2156 }
2157
2158 let accept_encoding = self.inner.accepts.as_str();
2159
2160 if let Some(accept_encoding) = accept_encoding {
2161 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2162 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2163 }
2164 }
2165
2166 let uri = match try_uri(&url) {
2167 Ok(uri) => uri,
2168 _ => return Pending::new_err(error::url_invalid_uri(url)),
2169 };
2170
2171 let (reusable, body) = match body {
2172 Some(body) => {
2173 let (reusable, body) = body.try_reuse();
2174 (Some(reusable), body)
2175 }
2176 None => (None, Body::empty()),
2177 };
2178
2179 self.proxy_auth(&uri, &mut headers);
2180
2181 let builder = hyper::Request::builder()
2182 .method(method.clone())
2183 .uri(uri)
2184 .version(version);
2185
2186 let in_flight = match version {
2187 #[cfg(feature = "http3")]
2188 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2189 let mut req = builder.body(body).expect("valid request parts");
2190 *req.headers_mut() = headers.clone();
2191 ResponseFuture::H3(self.inner.h3_client.as_ref().unwrap().request(req))
2192 }
2193 _ => {
2194 let mut req = builder.body(body).expect("valid request parts");
2195 *req.headers_mut() = headers.clone();
2196 ResponseFuture::Default(self.inner.hyper.request(req))
2197 }
2198 };
2199
2200 let total_timeout = timeout
2201 .or(self.inner.request_timeout)
2202 .map(tokio::time::sleep)
2203 .map(Box::pin);
2204
2205 let read_timeout_fut = self
2206 .inner
2207 .read_timeout
2208 .map(tokio::time::sleep)
2209 .map(Box::pin);
2210
2211 Pending {
2212 inner: PendingInner::Request(PendingRequest {
2213 method,
2214 url,
2215 headers,
2216 body: reusable,
2217
2218 urls: Vec::new(),
2219
2220 retry_count: 0,
2221
2222 client: self.inner.clone(),
2223
2224 in_flight,
2225 total_timeout,
2226 read_timeout_fut,
2227 read_timeout: self.inner.read_timeout,
2228 }),
2229 }
2230 }
2231
2232 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2233 if !self.inner.proxies_maybe_http_auth {
2234 return;
2235 }
2236
2237 if dst.scheme() != Some(&Scheme::HTTP) {
2241 return;
2242 }
2243
2244 if headers.contains_key(PROXY_AUTHORIZATION) {
2245 return;
2246 }
2247
2248 for proxy in self.inner.proxies.iter() {
2249 if proxy.is_match(dst) {
2250 if let Some(header) = proxy.http_basic_auth(dst) {
2251 headers.insert(PROXY_AUTHORIZATION, header);
2252 }
2253
2254 break;
2255 }
2256 }
2257 }
2258}
2259
2260impl fmt::Debug for Client {
2261 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2262 let mut builder = f.debug_struct("Client");
2263 self.inner.fmt_fields(&mut builder);
2264 builder.finish()
2265 }
2266}
2267
2268impl tower_service::Service<Request> for Client {
2269 type Response = Response;
2270 type Error = crate::Error;
2271 type Future = Pending;
2272
2273 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2274 Poll::Ready(Ok(()))
2275 }
2276
2277 fn call(&mut self, req: Request) -> Self::Future {
2278 self.execute_request(req)
2279 }
2280}
2281
2282impl tower_service::Service<Request> for &'_ Client {
2283 type Response = Response;
2284 type Error = crate::Error;
2285 type Future = Pending;
2286
2287 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2288 Poll::Ready(Ok(()))
2289 }
2290
2291 fn call(&mut self, req: Request) -> Self::Future {
2292 self.execute_request(req)
2293 }
2294}
2295
2296impl fmt::Debug for ClientBuilder {
2297 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2298 let mut builder = f.debug_struct("ClientBuilder");
2299 self.config.fmt_fields(&mut builder);
2300 builder.finish()
2301 }
2302}
2303
2304impl Config {
2305 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2306 #[cfg(feature = "cookies")]
2310 {
2311 if let Some(_) = self.cookie_store {
2312 f.field("cookie_store", &true);
2313 }
2314 }
2315
2316 f.field("accepts", &self.accepts);
2317
2318 if !self.proxies.is_empty() {
2319 f.field("proxies", &self.proxies);
2320 }
2321
2322 if !self.redirect_policy.is_default() {
2323 f.field("redirect_policy", &self.redirect_policy);
2324 }
2325
2326 if self.referer {
2327 f.field("referer", &true);
2328 }
2329
2330 f.field("default_headers", &self.headers);
2331
2332 if self.http1_title_case_headers {
2333 f.field("http1_title_case_headers", &true);
2334 }
2335
2336 if self.http1_allow_obsolete_multiline_headers_in_responses {
2337 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2338 }
2339
2340 if self.http1_ignore_invalid_headers_in_responses {
2341 f.field("http1_ignore_invalid_headers_in_responses", &true);
2342 }
2343
2344 if self.http1_allow_spaces_after_header_name_in_responses {
2345 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2346 }
2347
2348 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2349 f.field("http1_only", &true);
2350 }
2351
2352 #[cfg(feature = "http2")]
2353 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2354 f.field("http2_prior_knowledge", &true);
2355 }
2356
2357 if let Some(ref d) = self.connect_timeout {
2358 f.field("connect_timeout", d);
2359 }
2360
2361 if let Some(ref d) = self.timeout {
2362 f.field("timeout", d);
2363 }
2364
2365 if let Some(ref v) = self.local_address {
2366 f.field("local_address", v);
2367 }
2368
2369 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
2370 if let Some(ref v) = self.interface {
2371 f.field("interface", v);
2372 }
2373
2374 if self.nodelay {
2375 f.field("tcp_nodelay", &true);
2376 }
2377
2378 #[cfg(feature = "__tls")]
2379 {
2380 if !self.hostname_verification {
2381 f.field("danger_accept_invalid_hostnames", &true);
2382 }
2383 }
2384
2385 #[cfg(feature = "__tls")]
2386 {
2387 if !self.certs_verification {
2388 f.field("danger_accept_invalid_certs", &true);
2389 }
2390
2391 if let Some(ref min_tls_version) = self.min_tls_version {
2392 f.field("min_tls_version", min_tls_version);
2393 }
2394
2395 if let Some(ref max_tls_version) = self.max_tls_version {
2396 f.field("max_tls_version", max_tls_version);
2397 }
2398
2399 f.field("tls_sni", &self.tls_sni);
2400
2401 f.field("tls_info", &self.tls_info);
2402 }
2403
2404 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2405 {
2406 f.field("tls_backend", &self.tls);
2407 }
2408
2409 if !self.dns_overrides.is_empty() {
2410 f.field("dns_overrides", &self.dns_overrides);
2411 }
2412
2413 #[cfg(feature = "http3")]
2414 {
2415 if self.tls_enable_early_data {
2416 f.field("tls_enable_early_data", &true);
2417 }
2418 }
2419 }
2420}
2421
2422struct ClientRef {
2423 accepts: Accepts,
2424 #[cfg(feature = "cookies")]
2425 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2426 headers: HeaderMap,
2427 hyper: HyperClient,
2428 #[cfg(feature = "http3")]
2429 h3_client: Option<H3Client>,
2430 redirect_policy: redirect::Policy,
2431 referer: bool,
2432 request_timeout: Option<Duration>,
2433 read_timeout: Option<Duration>,
2434 proxies: Arc<Vec<Proxy>>,
2435 proxies_maybe_http_auth: bool,
2436 https_only: bool,
2437}
2438
2439impl ClientRef {
2440 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2441 #[cfg(feature = "cookies")]
2445 {
2446 if let Some(_) = self.cookie_store {
2447 f.field("cookie_store", &true);
2448 }
2449 }
2450
2451 f.field("accepts", &self.accepts);
2452
2453 if !self.proxies.is_empty() {
2454 f.field("proxies", &self.proxies);
2455 }
2456
2457 if !self.redirect_policy.is_default() {
2458 f.field("redirect_policy", &self.redirect_policy);
2459 }
2460
2461 if self.referer {
2462 f.field("referer", &true);
2463 }
2464
2465 f.field("default_headers", &self.headers);
2466
2467 if let Some(ref d) = self.request_timeout {
2468 f.field("timeout", d);
2469 }
2470
2471 if let Some(ref d) = self.read_timeout {
2472 f.field("read_timeout", d);
2473 }
2474 }
2475}
2476
2477pin_project! {
2478 pub struct Pending {
2479 #[pin]
2480 inner: PendingInner,
2481 }
2482}
2483
2484enum PendingInner {
2485 Request(PendingRequest),
2486 Error(Option<crate::Error>),
2487}
2488
2489pin_project! {
2490 struct PendingRequest {
2491 method: Method,
2492 url: Url,
2493 headers: HeaderMap,
2494 body: Option<Option<Bytes>>,
2495
2496 urls: Vec<Url>,
2497
2498 retry_count: usize,
2499
2500 client: Arc<ClientRef>,
2501
2502 #[pin]
2503 in_flight: ResponseFuture,
2504 #[pin]
2505 total_timeout: Option<Pin<Box<Sleep>>>,
2506 #[pin]
2507 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2508 read_timeout: Option<Duration>,
2509 }
2510}
2511
2512enum ResponseFuture {
2513 Default(HyperResponseFuture),
2514 #[cfg(feature = "http3")]
2515 H3(H3ResponseFuture),
2516}
2517
2518impl PendingRequest {
2519 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2520 self.project().in_flight
2521 }
2522
2523 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2524 self.project().total_timeout
2525 }
2526
2527 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2528 self.project().read_timeout_fut
2529 }
2530
2531 fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
2532 self.project().urls
2533 }
2534
2535 fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
2536 self.project().headers
2537 }
2538
2539 #[cfg(any(feature = "http2", feature = "http3"))]
2540 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2541 use log::trace;
2542
2543 if !is_retryable_error(err) {
2544 return false;
2545 }
2546
2547 trace!("can retry {err:?}");
2548
2549 let body = match self.body {
2550 Some(Some(ref body)) => Body::reusable(body.clone()),
2551 Some(None) => {
2552 debug!("error was retryable, but body not reusable");
2553 return false;
2554 }
2555 None => Body::empty(),
2556 };
2557
2558 if self.retry_count >= 2 {
2559 trace!("retry count too high");
2560 return false;
2561 }
2562 self.retry_count += 1;
2563
2564 let uri = try_uri(&self.url).expect("URL was already validated as URI");
2566
2567 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
2568 #[cfg(feature = "http3")]
2569 ResponseFuture::H3(_) => {
2570 let mut req = hyper::Request::builder()
2571 .method(self.method.clone())
2572 .uri(uri)
2573 .body(body)
2574 .expect("valid request parts");
2575 *req.headers_mut() = self.headers.clone();
2576 ResponseFuture::H3(
2577 self.client
2578 .h3_client
2579 .as_ref()
2580 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2581 .request(req),
2582 )
2583 }
2584 _ => {
2585 let mut req = hyper::Request::builder()
2586 .method(self.method.clone())
2587 .uri(uri)
2588 .body(body)
2589 .expect("valid request parts");
2590 *req.headers_mut() = self.headers.clone();
2591 ResponseFuture::Default(self.client.hyper.request(req))
2592 }
2593 };
2594
2595 true
2596 }
2597}
2598
2599#[cfg(any(feature = "http2", feature = "http3"))]
2600fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
2601 let err = if let Some(err) = err.source() {
2603 err
2604 } else {
2605 return false;
2606 };
2607
2608 #[cfg(feature = "http3")]
2609 if let Some(cause) = err.source() {
2610 if let Some(err) = cause.downcast_ref::<h3::Error>() {
2611 debug!("determining if HTTP/3 error {err} can be retried");
2612 return err.to_string().as_str() == "timeout";
2614 }
2615 }
2616
2617 #[cfg(feature = "http2")]
2618 if let Some(cause) = err.source() {
2619 if let Some(err) = cause.downcast_ref::<h2::Error>() {
2620 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
2622 return true;
2623 }
2624
2625 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
2628 {
2629 return true;
2630 }
2631 }
2632 }
2633 false
2634}
2635
2636impl Pending {
2637 pub(super) fn new_err(err: crate::Error) -> Pending {
2638 Pending {
2639 inner: PendingInner::Error(Some(err)),
2640 }
2641 }
2642
2643 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2644 self.project().inner
2645 }
2646}
2647
2648impl Future for Pending {
2649 type Output = Result<Response, crate::Error>;
2650
2651 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2652 let inner = self.inner();
2653 match inner.get_mut() {
2654 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2655 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2656 .take()
2657 .expect("Pending error polled more than once"))),
2658 }
2659 }
2660}
2661
2662impl Future for PendingRequest {
2663 type Output = Result<Response, crate::Error>;
2664
2665 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2666 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
2667 if let Poll::Ready(()) = delay.poll(cx) {
2668 return Poll::Ready(Err(
2669 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2670 ));
2671 }
2672 }
2673
2674 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
2675 if let Poll::Ready(()) = delay.poll(cx) {
2676 return Poll::Ready(Err(
2677 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2678 ));
2679 }
2680 }
2681
2682 loop {
2683 let res = match self.as_mut().in_flight().get_mut() {
2684 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
2685 Poll::Ready(Err(e)) => {
2686 #[cfg(feature = "http2")]
2687 if self.as_mut().retry_error(&e) {
2688 continue;
2689 }
2690 return Poll::Ready(Err(
2691 crate::error::request(e).with_url(self.url.clone())
2692 ));
2693 }
2694 Poll::Ready(Ok(res)) => res.map(super::body::boxed),
2695 Poll::Pending => return Poll::Pending,
2696 },
2697 #[cfg(feature = "http3")]
2698 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
2699 Poll::Ready(Err(e)) => {
2700 if self.as_mut().retry_error(&e) {
2701 continue;
2702 }
2703 return Poll::Ready(Err(
2704 crate::error::request(e).with_url(self.url.clone())
2705 ));
2706 }
2707 Poll::Ready(Ok(res)) => res,
2708 Poll::Pending => return Poll::Pending,
2709 },
2710 };
2711
2712 #[cfg(feature = "cookies")]
2713 {
2714 if let Some(ref cookie_store) = self.client.cookie_store {
2715 let mut cookies =
2716 cookie::extract_response_cookie_headers(&res.headers()).peekable();
2717 if cookies.peek().is_some() {
2718 cookie_store.set_cookies(&mut cookies, &self.url);
2719 }
2720 }
2721 }
2722 let should_redirect = match res.status() {
2723 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
2724 self.body = None;
2725 for header in &[
2726 TRANSFER_ENCODING,
2727 CONTENT_ENCODING,
2728 CONTENT_TYPE,
2729 CONTENT_LENGTH,
2730 ] {
2731 self.headers.remove(header);
2732 }
2733
2734 match self.method {
2735 Method::GET | Method::HEAD => {}
2736 _ => {
2737 self.method = Method::GET;
2738 }
2739 }
2740 true
2741 }
2742 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
2743 match self.body {
2744 Some(Some(_)) | None => true,
2745 Some(None) => false,
2746 }
2747 }
2748 _ => false,
2749 };
2750 if should_redirect {
2751 let loc = res.headers().get(LOCATION).and_then(|val| {
2752 let loc = (|| -> Option<Url> {
2753 self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
2757 })();
2758
2759 let loc = loc.and_then(|url| {
2763 if try_uri(&url).is_ok() {
2764 Some(url)
2765 } else {
2766 None
2767 }
2768 });
2769
2770 if loc.is_none() {
2771 debug!("Location header had invalid URI: {val:?}");
2772 }
2773 loc
2774 });
2775 if let Some(loc) = loc {
2776 if self.client.referer {
2777 if let Some(referer) = make_referer(&loc, &self.url) {
2778 self.headers.insert(REFERER, referer);
2779 }
2780 }
2781 let url = self.url.clone();
2782 self.as_mut().urls().push(url);
2783 let action = self
2784 .client
2785 .redirect_policy
2786 .check(res.status(), &loc, &self.urls);
2787
2788 match action {
2789 redirect::ActionKind::Follow => {
2790 debug!("redirecting '{}' to '{}'", self.url, loc);
2791
2792 if loc.scheme() != "http" && loc.scheme() != "https" {
2793 return Poll::Ready(Err(error::url_bad_scheme(loc)));
2794 }
2795
2796 if self.client.https_only && loc.scheme() != "https" {
2797 return Poll::Ready(Err(error::redirect(
2798 error::url_bad_scheme(loc.clone()),
2799 loc,
2800 )));
2801 }
2802
2803 self.url = loc;
2804 let mut headers =
2805 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
2806
2807 remove_sensitive_headers(&mut headers, &self.url, &self.urls);
2808 let uri = try_uri(&self.url)?;
2809 let body = match self.body {
2810 Some(Some(ref body)) => Body::reusable(body.clone()),
2811 _ => Body::empty(),
2812 };
2813
2814 #[cfg(feature = "cookies")]
2816 {
2817 if let Some(ref cookie_store) = self.client.cookie_store {
2818 add_cookie_header(&mut headers, &**cookie_store, &self.url);
2819 }
2820 }
2821
2822 *self.as_mut().in_flight().get_mut() =
2823 match *self.as_mut().in_flight().as_ref() {
2824 #[cfg(feature = "http3")]
2825 ResponseFuture::H3(_) => {
2826 let mut req = hyper::Request::builder()
2827 .method(self.method.clone())
2828 .uri(uri.clone())
2829 .body(body)
2830 .expect("valid request parts");
2831 *req.headers_mut() = headers.clone();
2832 std::mem::swap(self.as_mut().headers(), &mut headers);
2833 ResponseFuture::H3(self.client.h3_client
2834 .as_ref()
2835 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2836 .request(req))
2837 }
2838 _ => {
2839 let mut req = hyper::Request::builder()
2840 .method(self.method.clone())
2841 .uri(uri.clone())
2842 .body(body)
2843 .expect("valid request parts");
2844 *req.headers_mut() = headers.clone();
2845 std::mem::swap(self.as_mut().headers(), &mut headers);
2846 ResponseFuture::Default(self.client.hyper.request(req))
2847 }
2848 };
2849
2850 continue;
2851 }
2852 redirect::ActionKind::Stop => {
2853 debug!("redirect policy disallowed redirection to '{loc}'");
2854 }
2855 redirect::ActionKind::Error(err) => {
2856 return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
2857 }
2858 }
2859 }
2860 }
2861
2862 let res = Response::new(
2863 res,
2864 self.url.clone(),
2865 self.client.accepts,
2866 self.total_timeout.take(),
2867 self.read_timeout,
2868 );
2869 return Poll::Ready(Ok(res));
2870 }
2871 }
2872}
2873
2874impl fmt::Debug for Pending {
2875 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2876 match self.inner {
2877 PendingInner::Request(ref req) => f
2878 .debug_struct("Pending")
2879 .field("method", &req.method)
2880 .field("url", &req.url)
2881 .finish(),
2882 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
2883 }
2884 }
2885}
2886
2887fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
2888 if next.scheme() == "http" && previous.scheme() == "https" {
2889 return None;
2890 }
2891
2892 let mut referer = previous.clone();
2893 let _ = referer.set_username("");
2894 let _ = referer.set_password(None);
2895 referer.set_fragment(None);
2896 referer.as_str().parse().ok()
2897}
2898
2899#[cfg(feature = "cookies")]
2900fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
2901 if let Some(header) = cookie_store.cookies(url) {
2902 headers.insert(crate::header::COOKIE, header);
2903 }
2904}
2905
2906#[cfg(test)]
2907mod tests {
2908 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
2909
2910 #[tokio::test]
2911 async fn execute_request_rejects_invalid_urls() {
2912 let url_str = "hxxps://www.rust-lang.org/";
2913 let url = url::Url::parse(url_str).unwrap();
2914 let result = crate::get(url.clone()).await;
2915
2916 assert!(result.is_err());
2917 let err = result.err().unwrap();
2918 assert!(err.is_builder());
2919 assert_eq!(url_str, err.url().unwrap().as_str());
2920 }
2921
2922 #[tokio::test]
2924 async fn execute_request_rejects_invalid_hostname() {
2925 let url_str = "https://{{hostname}}/";
2926 let url = url::Url::parse(url_str).unwrap();
2927 let result = crate::get(url.clone()).await;
2928
2929 assert!(result.is_err());
2930 let err = result.err().unwrap();
2931 assert!(err.is_builder());
2932 assert_eq!(url_str, err.url().unwrap().as_str());
2933 }
2934}