1#[cfg(any(feature = "native-tls", feature = "__rustls",))]
2use std::any::Any;
3use std::net::IpAddr;
4use std::sync::Arc;
5use std::time::Duration;
6use std::{collections::HashMap, convert::TryInto, net::SocketAddr};
7use std::{fmt, str};
8
9use bytes::Bytes;
10use http::header::{
11 Entry, HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH,
12 CONTENT_TYPE, LOCATION, PROXY_AUTHORIZATION, RANGE, REFERER, TRANSFER_ENCODING, USER_AGENT,
13};
14use http::uri::Scheme;
15use http::Uri;
16use hyper_util::client::legacy::connect::HttpConnector;
17#[cfg(feature = "default-tls")]
18use native_tls_crate::TlsConnector;
19use pin_project_lite::pin_project;
20use std::future::Future;
21use std::pin::Pin;
22use std::task::{Context, Poll};
23use tokio::time::Sleep;
24
25use super::decoder::Accepts;
26use super::request::{Request, RequestBuilder};
27use super::response::Response;
28use super::Body;
29#[cfg(feature = "http3")]
30use crate::async_impl::h3_client::connect::H3Connector;
31#[cfg(feature = "http3")]
32use crate::async_impl::h3_client::{H3Client, H3ResponseFuture};
33use crate::connect::Connector;
34#[cfg(feature = "cookies")]
35use crate::cookie;
36#[cfg(feature = "hickory-dns")]
37use crate::dns::hickory::HickoryDnsResolver;
38use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
39use crate::error;
40use crate::into_url::try_uri;
41use crate::redirect::{self, remove_sensitive_headers};
42#[cfg(feature = "__tls")]
43use crate::tls::{self, TlsBackend};
44#[cfg(feature = "__tls")]
45use crate::Certificate;
46#[cfg(any(feature = "native-tls", feature = "__rustls"))]
47use crate::Identity;
48use crate::{IntoUrl, Method, Proxy, StatusCode, Url};
49use log::debug;
50#[cfg(feature = "http3")]
51use quinn::TransportConfig;
52#[cfg(feature = "http3")]
53use quinn::VarInt;
54
55type HyperResponseFuture = hyper_util::client::legacy::ResponseFuture;
56
57#[derive(Clone)]
71pub struct Client {
72 inner: Arc<ClientRef>,
73}
74
75#[must_use]
77pub struct ClientBuilder {
78 config: Config,
79}
80
81enum HttpVersionPref {
82 Http1,
83 #[cfg(feature = "http2")]
84 Http2,
85 #[cfg(feature = "http3")]
86 Http3,
87 All,
88}
89
90struct Config {
91 accepts: Accepts,
93 headers: HeaderMap,
94 #[cfg(feature = "native-tls")]
95 hostname_verification: bool,
96 #[cfg(feature = "__tls")]
97 certs_verification: bool,
98 #[cfg(feature = "__tls")]
99 tls_sni: bool,
100 connect_timeout: Option<Duration>,
101 connection_verbose: bool,
102 pool_idle_timeout: Option<Duration>,
103 pool_max_idle_per_host: usize,
104 tcp_keepalive: Option<Duration>,
105 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
106 identity: Option<Identity>,
107 proxies: Vec<Proxy>,
108 auto_sys_proxy: bool,
109 redirect_policy: redirect::Policy,
110 referer: bool,
111 read_timeout: Option<Duration>,
112 timeout: Option<Duration>,
113 #[cfg(feature = "__tls")]
114 root_certs: Vec<Certificate>,
115 #[cfg(feature = "__tls")]
116 tls_built_in_root_certs: bool,
117 #[cfg(feature = "rustls-tls-webpki-roots")]
118 tls_built_in_certs_webpki: bool,
119 #[cfg(feature = "rustls-tls-native-roots")]
120 tls_built_in_certs_native: bool,
121 #[cfg(feature = "__tls")]
122 min_tls_version: Option<tls::Version>,
123 #[cfg(feature = "__tls")]
124 max_tls_version: Option<tls::Version>,
125 #[cfg(feature = "__tls")]
126 tls_info: bool,
127 #[cfg(feature = "__tls")]
128 tls: TlsBackend,
129 http_version_pref: HttpVersionPref,
130 http09_responses: bool,
131 http1_title_case_headers: bool,
132 http1_allow_obsolete_multiline_headers_in_responses: bool,
133 http1_ignore_invalid_headers_in_responses: bool,
134 http1_allow_spaces_after_header_name_in_responses: bool,
135 #[cfg(feature = "http2")]
136 http2_initial_stream_window_size: Option<u32>,
137 #[cfg(feature = "http2")]
138 http2_initial_connection_window_size: Option<u32>,
139 #[cfg(feature = "http2")]
140 http2_adaptive_window: bool,
141 #[cfg(feature = "http2")]
142 http2_max_frame_size: Option<u32>,
143 #[cfg(feature = "http2")]
144 http2_keep_alive_interval: Option<Duration>,
145 #[cfg(feature = "http2")]
146 http2_keep_alive_timeout: Option<Duration>,
147 #[cfg(feature = "http2")]
148 http2_keep_alive_while_idle: bool,
149 local_address: Option<IpAddr>,
150 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
151 interface: Option<String>,
152 nodelay: bool,
153 #[cfg(feature = "cookies")]
154 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
155 hickory_dns: bool,
156 error: Option<crate::Error>,
157 https_only: bool,
158 #[cfg(feature = "http3")]
159 tls_enable_early_data: bool,
160 #[cfg(feature = "http3")]
161 quic_max_idle_timeout: Option<Duration>,
162 #[cfg(feature = "http3")]
163 quic_stream_receive_window: Option<VarInt>,
164 #[cfg(feature = "http3")]
165 quic_receive_window: Option<VarInt>,
166 #[cfg(feature = "http3")]
167 quic_send_window: Option<u64>,
168 dns_overrides: HashMap<String, Vec<SocketAddr>>,
169 dns_resolver: Option<Arc<dyn Resolve>>,
170}
171
172impl Default for ClientBuilder {
173 fn default() -> Self {
174 Self::new()
175 }
176}
177
178impl ClientBuilder {
179 pub fn new() -> ClientBuilder {
183 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
184 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
185
186 ClientBuilder {
187 config: Config {
188 error: None,
189 accepts: Accepts::default(),
190 headers,
191 #[cfg(feature = "native-tls")]
192 hostname_verification: true,
193 #[cfg(feature = "__tls")]
194 certs_verification: true,
195 #[cfg(feature = "__tls")]
196 tls_sni: true,
197 connect_timeout: None,
198 connection_verbose: false,
199 pool_idle_timeout: Some(Duration::from_secs(90)),
200 pool_max_idle_per_host: std::usize::MAX,
201 tcp_keepalive: None, proxies: Vec::new(),
205 auto_sys_proxy: true,
206 redirect_policy: redirect::Policy::default(),
207 referer: true,
208 read_timeout: None,
209 timeout: None,
210 #[cfg(feature = "__tls")]
211 root_certs: Vec::new(),
212 #[cfg(feature = "__tls")]
213 tls_built_in_root_certs: true,
214 #[cfg(feature = "rustls-tls-webpki-roots")]
215 tls_built_in_certs_webpki: true,
216 #[cfg(feature = "rustls-tls-native-roots")]
217 tls_built_in_certs_native: true,
218 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
219 identity: None,
220 #[cfg(feature = "__tls")]
221 min_tls_version: None,
222 #[cfg(feature = "__tls")]
223 max_tls_version: None,
224 #[cfg(feature = "__tls")]
225 tls_info: false,
226 #[cfg(feature = "__tls")]
227 tls: TlsBackend::default(),
228 http_version_pref: HttpVersionPref::All,
229 http09_responses: false,
230 http1_title_case_headers: false,
231 http1_allow_obsolete_multiline_headers_in_responses: false,
232 http1_ignore_invalid_headers_in_responses: false,
233 http1_allow_spaces_after_header_name_in_responses: false,
234 #[cfg(feature = "http2")]
235 http2_initial_stream_window_size: None,
236 #[cfg(feature = "http2")]
237 http2_initial_connection_window_size: None,
238 #[cfg(feature = "http2")]
239 http2_adaptive_window: false,
240 #[cfg(feature = "http2")]
241 http2_max_frame_size: None,
242 #[cfg(feature = "http2")]
243 http2_keep_alive_interval: None,
244 #[cfg(feature = "http2")]
245 http2_keep_alive_timeout: None,
246 #[cfg(feature = "http2")]
247 http2_keep_alive_while_idle: false,
248 local_address: None,
249 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
250 interface: None,
251 nodelay: true,
252 hickory_dns: cfg!(feature = "hickory-dns"),
253 #[cfg(feature = "cookies")]
254 cookie_store: None,
255 https_only: false,
256 dns_overrides: HashMap::new(),
257 #[cfg(feature = "http3")]
258 tls_enable_early_data: false,
259 #[cfg(feature = "http3")]
260 quic_max_idle_timeout: None,
261 #[cfg(feature = "http3")]
262 quic_stream_receive_window: None,
263 #[cfg(feature = "http3")]
264 quic_receive_window: None,
265 #[cfg(feature = "http3")]
266 quic_send_window: None,
267 dns_resolver: None,
268 },
269 }
270 }
271
272 pub fn build(self) -> crate::Result<Client> {
279 let config = self.config;
280
281 if let Some(err) = config.error {
282 return Err(err);
283 }
284
285 let mut proxies = config.proxies;
286 if config.auto_sys_proxy {
287 proxies.push(Proxy::system());
288 }
289 let proxies = Arc::new(proxies);
290
291 #[allow(unused)]
292 #[cfg(feature = "http3")]
293 let mut h3_connector = None;
294
295 let mut connector = {
296 #[cfg(feature = "__tls")]
297 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
298 headers.get(USER_AGENT).cloned()
299 }
300
301 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
302 false => Arc::new(GaiResolver::new()),
303 #[cfg(feature = "hickory-dns")]
304 true => Arc::new(HickoryDnsResolver::default()),
305 #[cfg(not(feature = "hickory-dns"))]
306 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
307 };
308 if let Some(dns_resolver) = config.dns_resolver {
309 resolver = dns_resolver;
310 }
311 if !config.dns_overrides.is_empty() {
312 resolver = Arc::new(DnsResolverWithOverrides::new(
313 resolver,
314 config.dns_overrides,
315 ));
316 }
317 let mut http = HttpConnector::new_with_resolver(DynResolver::new(resolver.clone()));
318 http.set_connect_timeout(config.connect_timeout);
319
320 #[cfg(all(feature = "http3", feature = "__rustls"))]
321 let build_h3_connector =
322 |resolver,
323 tls,
324 quic_max_idle_timeout: Option<Duration>,
325 quic_stream_receive_window,
326 quic_receive_window,
327 quic_send_window,
328 local_address,
329 http_version_pref: &HttpVersionPref| {
330 let mut transport_config = TransportConfig::default();
331
332 if let Some(max_idle_timeout) = quic_max_idle_timeout {
333 transport_config.max_idle_timeout(Some(
334 max_idle_timeout.try_into().map_err(error::builder)?,
335 ));
336 }
337
338 if let Some(stream_receive_window) = quic_stream_receive_window {
339 transport_config.stream_receive_window(stream_receive_window);
340 }
341
342 if let Some(receive_window) = quic_receive_window {
343 transport_config.receive_window(receive_window);
344 }
345
346 if let Some(send_window) = quic_send_window {
347 transport_config.send_window(send_window);
348 }
349
350 let res = H3Connector::new(
351 DynResolver::new(resolver),
352 tls,
353 local_address,
354 transport_config,
355 );
356
357 match res {
358 Ok(connector) => Ok(Some(connector)),
359 Err(err) => {
360 if let HttpVersionPref::Http3 = http_version_pref {
361 Err(error::builder(err))
362 } else {
363 Ok(None)
364 }
365 }
366 }
367 };
368
369 #[cfg(feature = "__tls")]
370 match config.tls {
371 #[cfg(feature = "default-tls")]
372 TlsBackend::Default => {
373 let mut tls = TlsConnector::builder();
374
375 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
376 {
377 match config.http_version_pref {
378 HttpVersionPref::Http1 => {
379 tls.request_alpns(&["http/1.1"]);
380 }
381 #[cfg(feature = "http2")]
382 HttpVersionPref::Http2 => {
383 tls.request_alpns(&["h2"]);
384 }
385 HttpVersionPref::All => {
386 tls.request_alpns(&["h2", "http/1.1"]);
387 }
388 }
389 }
390
391 #[cfg(feature = "native-tls")]
392 {
393 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
394 }
395
396 tls.danger_accept_invalid_certs(!config.certs_verification);
397
398 tls.use_sni(config.tls_sni);
399
400 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
401
402 for cert in config.root_certs {
403 cert.add_to_native_tls(&mut tls);
404 }
405
406 #[cfg(feature = "native-tls")]
407 {
408 if let Some(id) = config.identity {
409 id.add_to_native_tls(&mut tls)?;
410 }
411 }
412 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
413 {
414 if let Some(_id) = config.identity {
416 return Err(crate::error::builder("incompatible TLS identity type"));
417 }
418 }
419
420 if let Some(min_tls_version) = config.min_tls_version {
421 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
422 crate::error::builder("invalid minimum TLS version for backend")
426 })?;
427 tls.min_protocol_version(Some(protocol));
428 }
429
430 if let Some(max_tls_version) = config.max_tls_version {
431 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
432 crate::error::builder("invalid maximum TLS version for backend")
437 })?;
438 tls.max_protocol_version(Some(protocol));
439 }
440
441 Connector::new_default_tls(
442 http,
443 tls,
444 proxies.clone(),
445 user_agent(&config.headers),
446 config.local_address,
447 #[cfg(any(
448 target_os = "android",
449 target_os = "fuchsia",
450 target_os = "linux"
451 ))]
452 config.interface.as_deref(),
453 config.nodelay,
454 config.tls_info,
455 )?
456 }
457 #[cfg(feature = "native-tls")]
458 TlsBackend::BuiltNativeTls(conn) => Connector::from_built_default_tls(
459 http,
460 conn,
461 proxies.clone(),
462 user_agent(&config.headers),
463 config.local_address,
464 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
465 config.interface.as_deref(),
466 config.nodelay,
467 config.tls_info,
468 ),
469 #[cfg(feature = "__rustls")]
470 TlsBackend::BuiltRustls(conn) => {
471 #[cfg(feature = "http3")]
472 {
473 h3_connector = build_h3_connector(
474 resolver,
475 conn.clone(),
476 config.quic_max_idle_timeout,
477 config.quic_stream_receive_window,
478 config.quic_receive_window,
479 config.quic_send_window,
480 config.local_address,
481 &config.http_version_pref,
482 )?;
483 }
484
485 Connector::new_rustls_tls(
486 http,
487 conn,
488 proxies.clone(),
489 user_agent(&config.headers),
490 config.local_address,
491 #[cfg(any(
492 target_os = "android",
493 target_os = "fuchsia",
494 target_os = "linux"
495 ))]
496 config.interface.as_deref(),
497 config.nodelay,
498 config.tls_info,
499 )
500 }
501 #[cfg(feature = "__rustls")]
502 TlsBackend::Rustls => {
503 use crate::tls::NoVerifier;
504
505 let mut root_cert_store = rustls::RootCertStore::empty();
507 for cert in config.root_certs {
508 cert.add_to_rustls(&mut root_cert_store)?;
509 }
510
511 #[cfg(feature = "rustls-tls-webpki-roots")]
512 if config.tls_built_in_certs_webpki {
513 root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
514 }
515
516 #[cfg(feature = "rustls-tls-native-roots")]
517 if config.tls_built_in_certs_native {
518 let mut valid_count = 0;
519 let mut invalid_count = 0;
520 for cert in rustls_native_certs::load_native_certs()
521 .map_err(crate::error::builder)?
522 {
523 match root_cert_store.add(cert.into()) {
527 Ok(_) => valid_count += 1,
528 Err(err) => {
529 invalid_count += 1;
530 log::debug!("rustls failed to parse DER certificate: {err:?}");
531 }
532 }
533 }
534 if valid_count == 0 && invalid_count > 0 {
535 return Err(crate::error::builder(
536 "zero valid certificates found in native root store",
537 ));
538 }
539 }
540
541 let mut versions = rustls::ALL_VERSIONS.to_vec();
543
544 if let Some(min_tls_version) = config.min_tls_version {
545 versions.retain(|&supported_version| {
546 match tls::Version::from_rustls(supported_version.version) {
547 Some(version) => version >= min_tls_version,
548 None => true,
551 }
552 });
553 }
554
555 if let Some(max_tls_version) = config.max_tls_version {
556 versions.retain(|&supported_version| {
557 match tls::Version::from_rustls(supported_version.version) {
558 Some(version) => version <= max_tls_version,
559 None => false,
560 }
561 });
562 }
563
564 if versions.is_empty() {
565 return Err(crate::error::builder("empty supported tls versions"));
566 }
567
568 let provider = rustls::crypto::CryptoProvider::get_default()
571 .map(|arc| arc.clone())
572 .unwrap_or_else(|| {
573 #[cfg(not(feature = "__rustls-ring"))]
574 panic!("No provider set");
575
576 #[cfg(feature = "__rustls-ring")]
577 Arc::new(rustls::crypto::ring::default_provider())
578 });
579
580 let config_builder = rustls::ClientConfig::builder_with_provider(provider)
582 .with_protocol_versions(&versions)
583 .map_err(|_| crate::error::builder("invalid TLS versions"))?
584 .with_root_certificates(root_cert_store);
585
586 let mut tls = if let Some(id) = config.identity {
588 id.add_to_rustls(config_builder)?
589 } else {
590 config_builder.with_no_client_auth()
591 };
592
593 if !config.certs_verification {
595 tls.dangerous()
596 .set_certificate_verifier(Arc::new(NoVerifier));
597 }
598
599 tls.enable_sni = config.tls_sni;
600
601 match config.http_version_pref {
603 HttpVersionPref::Http1 => {
604 tls.alpn_protocols = vec!["http/1.1".into()];
605 }
606 #[cfg(feature = "http2")]
607 HttpVersionPref::Http2 => {
608 tls.alpn_protocols = vec!["h2".into()];
609 }
610 #[cfg(feature = "http3")]
611 HttpVersionPref::Http3 => {
612 tls.alpn_protocols = vec!["h3".into()];
613 }
614 HttpVersionPref::All => {
615 tls.alpn_protocols = vec![
616 #[cfg(feature = "http2")]
617 "h2".into(),
618 "http/1.1".into(),
619 ];
620 }
621 }
622
623 #[cfg(feature = "http3")]
624 {
625 tls.enable_early_data = config.tls_enable_early_data;
626
627 h3_connector = build_h3_connector(
628 resolver,
629 tls.clone(),
630 config.quic_max_idle_timeout,
631 config.quic_stream_receive_window,
632 config.quic_receive_window,
633 config.quic_send_window,
634 config.local_address,
635 &config.http_version_pref,
636 )?;
637 }
638
639 Connector::new_rustls_tls(
640 http,
641 tls,
642 proxies.clone(),
643 user_agent(&config.headers),
644 config.local_address,
645 #[cfg(any(
646 target_os = "android",
647 target_os = "fuchsia",
648 target_os = "linux"
649 ))]
650 config.interface.as_deref(),
651 config.nodelay,
652 config.tls_info,
653 )
654 }
655 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
656 TlsBackend::UnknownPreconfigured => {
657 return Err(crate::error::builder(
658 "Unknown TLS backend passed to `use_preconfigured_tls`",
659 ));
660 }
661 }
662
663 #[cfg(not(feature = "__tls"))]
664 Connector::new(
665 http,
666 proxies.clone(),
667 config.local_address,
668 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
669 config.interface.as_deref(),
670 config.nodelay,
671 )
672 };
673
674 connector.set_timeout(config.connect_timeout);
675 connector.set_verbose(config.connection_verbose);
676
677 let mut builder =
678 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
679 #[cfg(feature = "http2")]
680 {
681 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
682 builder.http2_only(true);
683 }
684
685 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
686 {
687 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
688 }
689 if let Some(http2_initial_connection_window_size) =
690 config.http2_initial_connection_window_size
691 {
692 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
693 }
694 if config.http2_adaptive_window {
695 builder.http2_adaptive_window(true);
696 }
697 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
698 builder.http2_max_frame_size(http2_max_frame_size);
699 }
700 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
701 builder.http2_keep_alive_interval(http2_keep_alive_interval);
702 }
703 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
704 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
705 }
706 if config.http2_keep_alive_while_idle {
707 builder.http2_keep_alive_while_idle(true);
708 }
709 }
710
711 #[cfg(not(target_arch = "wasm32"))]
712 builder.timer(hyper_util::rt::TokioTimer::new());
713 builder.pool_idle_timeout(config.pool_idle_timeout);
714 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
715 connector.set_keepalive(config.tcp_keepalive);
716
717 if config.http09_responses {
718 builder.http09_responses(true);
719 }
720
721 if config.http1_title_case_headers {
722 builder.http1_title_case_headers(true);
723 }
724
725 if config.http1_allow_obsolete_multiline_headers_in_responses {
726 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
727 }
728
729 if config.http1_ignore_invalid_headers_in_responses {
730 builder.http1_ignore_invalid_headers_in_responses(true);
731 }
732
733 if config.http1_allow_spaces_after_header_name_in_responses {
734 builder.http1_allow_spaces_after_header_name_in_responses(true);
735 }
736
737 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
738
739 Ok(Client {
740 inner: Arc::new(ClientRef {
741 accepts: config.accepts,
742 #[cfg(feature = "cookies")]
743 cookie_store: config.cookie_store,
744 #[cfg(feature = "http3")]
747 h3_client: match h3_connector {
748 Some(h3_connector) => {
749 Some(H3Client::new(h3_connector, config.pool_idle_timeout))
750 }
751 None => None,
752 },
753 hyper: builder.build(connector),
754 headers: config.headers,
755 redirect_policy: config.redirect_policy,
756 referer: config.referer,
757 read_timeout: config.read_timeout,
758 request_timeout: config.timeout,
759 proxies,
760 proxies_maybe_http_auth,
761 https_only: config.https_only,
762 }),
763 })
764 }
765
766 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
789 where
790 V: TryInto<HeaderValue>,
791 V::Error: Into<http::Error>,
792 {
793 match value.try_into() {
794 Ok(value) => {
795 self.config.headers.insert(USER_AGENT, value);
796 }
797 Err(e) => {
798 self.config.error = Some(crate::error::builder(e.into()));
799 }
800 };
801 self
802 }
803 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
827 for (key, value) in headers.iter() {
828 self.config.headers.insert(key, value.clone());
829 }
830 self
831 }
832
833 #[cfg(feature = "cookies")]
848 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
849 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
850 if enable {
851 self.cookie_provider(Arc::new(cookie::Jar::default()))
852 } else {
853 self.config.cookie_store = None;
854 self
855 }
856 }
857
858 #[cfg(feature = "cookies")]
872 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
873 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
874 mut self,
875 cookie_store: Arc<C>,
876 ) -> ClientBuilder {
877 self.config.cookie_store = Some(cookie_store as _);
878 self
879 }
880
881 #[cfg(feature = "gzip")]
898 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
899 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
900 self.config.accepts.gzip = enable;
901 self
902 }
903
904 #[cfg(feature = "brotli")]
921 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
922 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
923 self.config.accepts.brotli = enable;
924 self
925 }
926
927 #[cfg(feature = "zstd")]
944 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
945 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
946 self.config.accepts.zstd = enable;
947 self
948 }
949
950 #[cfg(feature = "deflate")]
967 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
968 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
969 self.config.accepts.deflate = enable;
970 self
971 }
972
973 pub fn no_gzip(self) -> ClientBuilder {
979 #[cfg(feature = "gzip")]
980 {
981 self.gzip(false)
982 }
983
984 #[cfg(not(feature = "gzip"))]
985 {
986 self
987 }
988 }
989
990 pub fn no_brotli(self) -> ClientBuilder {
996 #[cfg(feature = "brotli")]
997 {
998 self.brotli(false)
999 }
1000
1001 #[cfg(not(feature = "brotli"))]
1002 {
1003 self
1004 }
1005 }
1006
1007 pub fn no_zstd(self) -> ClientBuilder {
1013 #[cfg(feature = "zstd")]
1014 {
1015 self.zstd(false)
1016 }
1017
1018 #[cfg(not(feature = "zstd"))]
1019 {
1020 self
1021 }
1022 }
1023
1024 pub fn no_deflate(self) -> ClientBuilder {
1030 #[cfg(feature = "deflate")]
1031 {
1032 self.deflate(false)
1033 }
1034
1035 #[cfg(not(feature = "deflate"))]
1036 {
1037 self
1038 }
1039 }
1040
1041 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1047 self.config.redirect_policy = policy;
1048 self
1049 }
1050
1051 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1055 self.config.referer = enable;
1056 self
1057 }
1058
1059 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1067 self.config.proxies.push(proxy);
1068 self.config.auto_sys_proxy = false;
1069 self
1070 }
1071
1072 pub fn no_proxy(mut self) -> ClientBuilder {
1080 self.config.proxies.clear();
1081 self.config.auto_sys_proxy = false;
1082 self
1083 }
1084
1085 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1094 self.config.timeout = Some(timeout);
1095 self
1096 }
1097
1098 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1106 self.config.read_timeout = Some(timeout);
1107 self
1108 }
1109
1110 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1119 self.config.connect_timeout = Some(timeout);
1120 self
1121 }
1122
1123 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1130 self.config.connection_verbose = verbose;
1131 self
1132 }
1133
1134 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1142 where
1143 D: Into<Option<Duration>>,
1144 {
1145 self.config.pool_idle_timeout = val.into();
1146 self
1147 }
1148
1149 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1151 self.config.pool_max_idle_per_host = max;
1152 self
1153 }
1154
1155 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1157 self.config.http1_title_case_headers = true;
1158 self
1159 }
1160
1161 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1167 mut self,
1168 value: bool,
1169 ) -> ClientBuilder {
1170 self.config
1171 .http1_allow_obsolete_multiline_headers_in_responses = value;
1172 self
1173 }
1174
1175 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1177 self.config.http1_ignore_invalid_headers_in_responses = value;
1178 self
1179 }
1180
1181 pub fn http1_allow_spaces_after_header_name_in_responses(
1187 mut self,
1188 value: bool,
1189 ) -> ClientBuilder {
1190 self.config
1191 .http1_allow_spaces_after_header_name_in_responses = value;
1192 self
1193 }
1194
1195 pub fn http1_only(mut self) -> ClientBuilder {
1197 self.config.http_version_pref = HttpVersionPref::Http1;
1198 self
1199 }
1200
1201 pub fn http09_responses(mut self) -> ClientBuilder {
1203 self.config.http09_responses = true;
1204 self
1205 }
1206
1207 #[cfg(feature = "http2")]
1209 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1210 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1211 self.config.http_version_pref = HttpVersionPref::Http2;
1212 self
1213 }
1214
1215 #[cfg(feature = "http3")]
1217 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1218 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1219 self.config.http_version_pref = HttpVersionPref::Http3;
1220 self
1221 }
1222
1223 #[cfg(feature = "http2")]
1227 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1228 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1229 self.config.http2_initial_stream_window_size = sz.into();
1230 self
1231 }
1232
1233 #[cfg(feature = "http2")]
1237 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1238 pub fn http2_initial_connection_window_size(
1239 mut self,
1240 sz: impl Into<Option<u32>>,
1241 ) -> ClientBuilder {
1242 self.config.http2_initial_connection_window_size = sz.into();
1243 self
1244 }
1245
1246 #[cfg(feature = "http2")]
1251 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1252 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1253 self.config.http2_adaptive_window = enabled;
1254 self
1255 }
1256
1257 #[cfg(feature = "http2")]
1261 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1262 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1263 self.config.http2_max_frame_size = sz.into();
1264 self
1265 }
1266
1267 #[cfg(feature = "http2")]
1272 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1273 pub fn http2_keep_alive_interval(
1274 mut self,
1275 interval: impl Into<Option<Duration>>,
1276 ) -> ClientBuilder {
1277 self.config.http2_keep_alive_interval = interval.into();
1278 self
1279 }
1280
1281 #[cfg(feature = "http2")]
1287 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1288 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1289 self.config.http2_keep_alive_timeout = Some(timeout);
1290 self
1291 }
1292
1293 #[cfg(feature = "http2")]
1300 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1301 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1302 self.config.http2_keep_alive_while_idle = enabled;
1303 self
1304 }
1305
1306 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1312 self.config.nodelay = enabled;
1313 self
1314 }
1315
1316 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1330 where
1331 T: Into<Option<IpAddr>>,
1332 {
1333 self.config.local_address = addr.into();
1334 self
1335 }
1336
1337 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1350 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1351 self.config.interface = Some(interface.to_string());
1352 self
1353 }
1354
1355 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1359 where
1360 D: Into<Option<Duration>>,
1361 {
1362 self.config.tcp_keepalive = val.into();
1363 self
1364 }
1365
1366 #[cfg(feature = "__tls")]
1378 #[cfg_attr(
1379 docsrs,
1380 doc(cfg(any(
1381 feature = "default-tls",
1382 feature = "native-tls",
1383 feature = "rustls-tls"
1384 )))
1385 )]
1386 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1387 self.config.root_certs.push(cert);
1388 self
1389 }
1390
1391 #[cfg(feature = "__tls")]
1409 #[cfg_attr(
1410 docsrs,
1411 doc(cfg(any(
1412 feature = "default-tls",
1413 feature = "native-tls",
1414 feature = "rustls-tls"
1415 )))
1416 )]
1417 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1418 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1419
1420 #[cfg(feature = "rustls-tls-webpki-roots")]
1421 {
1422 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1423 }
1424
1425 #[cfg(feature = "rustls-tls-native-roots")]
1426 {
1427 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1428 }
1429
1430 self
1431 }
1432
1433 #[cfg(feature = "rustls-tls-webpki-roots")]
1437 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots")))]
1438 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1439 self.config.tls_built_in_certs_webpki = enabled;
1440 self
1441 }
1442
1443 #[cfg(feature = "rustls-tls-native-roots")]
1447 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots")))]
1448 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1449 self.config.tls_built_in_certs_native = enabled;
1450 self
1451 }
1452
1453 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1460 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1461 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1462 self.config.identity = Some(identity);
1463 self
1464 }
1465
1466 #[cfg(feature = "native-tls")]
1481 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1482 pub fn danger_accept_invalid_hostnames(
1483 mut self,
1484 accept_invalid_hostname: bool,
1485 ) -> ClientBuilder {
1486 self.config.hostname_verification = !accept_invalid_hostname;
1487 self
1488 }
1489
1490 #[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 danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1516 self.config.certs_verification = !accept_invalid_certs;
1517 self
1518 }
1519
1520 #[cfg(feature = "__tls")]
1529 #[cfg_attr(
1530 docsrs,
1531 doc(cfg(any(
1532 feature = "default-tls",
1533 feature = "native-tls",
1534 feature = "rustls-tls"
1535 )))
1536 )]
1537 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1538 self.config.tls_sni = tls_sni;
1539 self
1540 }
1541
1542 #[cfg(feature = "__tls")]
1558 #[cfg_attr(
1559 docsrs,
1560 doc(cfg(any(
1561 feature = "default-tls",
1562 feature = "native-tls",
1563 feature = "rustls-tls"
1564 )))
1565 )]
1566 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1567 self.config.min_tls_version = Some(version);
1568 self
1569 }
1570
1571 #[cfg(feature = "__tls")]
1590 #[cfg_attr(
1591 docsrs,
1592 doc(cfg(any(
1593 feature = "default-tls",
1594 feature = "native-tls",
1595 feature = "rustls-tls"
1596 )))
1597 )]
1598 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1599 self.config.max_tls_version = Some(version);
1600 self
1601 }
1602
1603 #[cfg(feature = "native-tls")]
1612 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1613 pub fn use_native_tls(mut self) -> ClientBuilder {
1614 self.config.tls = TlsBackend::Default;
1615 self
1616 }
1617
1618 #[cfg(feature = "__rustls")]
1627 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1628 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1629 self.config.tls = TlsBackend::Rustls;
1630 self
1631 }
1632
1633 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
1652 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1653 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
1654 let mut tls = Some(tls);
1655 #[cfg(feature = "native-tls")]
1656 {
1657 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
1658 let tls = conn.take().expect("is definitely Some");
1659 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
1660 self.config.tls = tls;
1661 return self;
1662 }
1663 }
1664 #[cfg(feature = "__rustls")]
1665 {
1666 if let Some(conn) =
1667 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
1668 {
1669 let tls = conn.take().expect("is definitely Some");
1670 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
1671 self.config.tls = tls;
1672 return self;
1673 }
1674 }
1675
1676 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
1678 self
1679 }
1680
1681 #[cfg(feature = "__tls")]
1688 #[cfg_attr(
1689 docsrs,
1690 doc(cfg(any(
1691 feature = "default-tls",
1692 feature = "native-tls",
1693 feature = "rustls-tls"
1694 )))
1695 )]
1696 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
1697 self.config.tls_info = tls_info;
1698 self
1699 }
1700
1701 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
1705 self.config.https_only = enabled;
1706 self
1707 }
1708
1709 #[cfg(feature = "hickory-dns")]
1718 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1719 #[deprecated(note = "use `hickory_dns` instead")]
1720 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
1721 self.config.hickory_dns = enable;
1722 self
1723 }
1724
1725 #[cfg(feature = "hickory-dns")]
1734 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1735 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
1736 self.config.hickory_dns = enable;
1737 self
1738 }
1739
1740 #[deprecated(note = "use `no_hickory_dns` instead")]
1746 pub fn no_trust_dns(self) -> ClientBuilder {
1747 #[cfg(feature = "hickory-dns")]
1748 {
1749 self.hickory_dns(false)
1750 }
1751
1752 #[cfg(not(feature = "hickory-dns"))]
1753 {
1754 self
1755 }
1756 }
1757
1758 pub fn no_hickory_dns(self) -> ClientBuilder {
1764 #[cfg(feature = "hickory-dns")]
1765 {
1766 self.hickory_dns(false)
1767 }
1768
1769 #[cfg(not(feature = "hickory-dns"))]
1770 {
1771 self
1772 }
1773 }
1774
1775 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
1784 self.resolve_to_addrs(domain, &[addr])
1785 }
1786
1787 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
1796 self.config
1797 .dns_overrides
1798 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
1799 self
1800 }
1801
1802 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
1808 self.config.dns_resolver = Some(resolver as _);
1809 self
1810 }
1811
1812 #[cfg(feature = "http3")]
1817 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1818 pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder {
1819 self.config.tls_enable_early_data = enabled;
1820 self
1821 }
1822
1823 #[cfg(feature = "http3")]
1829 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1830 pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
1831 self.config.quic_max_idle_timeout = Some(value);
1832 self
1833 }
1834
1835 #[cfg(feature = "http3")]
1846 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1847 pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder {
1848 self.config.quic_stream_receive_window = Some(value.try_into().unwrap());
1849 self
1850 }
1851
1852 #[cfg(feature = "http3")]
1863 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1864 pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder {
1865 self.config.quic_receive_window = Some(value.try_into().unwrap());
1866 self
1867 }
1868
1869 #[cfg(feature = "http3")]
1875 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1876 pub fn http3_send_window(mut self, value: u64) -> ClientBuilder {
1877 self.config.quic_send_window = Some(value);
1878 self
1879 }
1880}
1881
1882type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
1883
1884impl Default for Client {
1885 fn default() -> Self {
1886 Self::new()
1887 }
1888}
1889
1890impl Client {
1891 pub fn new() -> Client {
1901 ClientBuilder::new().build().expect("Client::new()")
1902 }
1903
1904 pub fn builder() -> ClientBuilder {
1908 ClientBuilder::new()
1909 }
1910
1911 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1917 self.request(Method::GET, url)
1918 }
1919
1920 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1926 self.request(Method::POST, url)
1927 }
1928
1929 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1935 self.request(Method::PUT, url)
1936 }
1937
1938 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1944 self.request(Method::PATCH, url)
1945 }
1946
1947 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1953 self.request(Method::DELETE, url)
1954 }
1955
1956 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1962 self.request(Method::HEAD, url)
1963 }
1964
1965 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
1974 let req = url.into_url().map(move |url| Request::new(method, url));
1975 RequestBuilder::new(self.clone(), req)
1976 }
1977
1978 pub fn execute(
1991 &self,
1992 request: Request,
1993 ) -> impl Future<Output = Result<Response, crate::Error>> {
1994 self.execute_request(request)
1995 }
1996
1997 pub(super) fn execute_request(&self, req: Request) -> Pending {
1998 let (method, url, mut headers, body, timeout, version) = req.pieces();
1999 if url.scheme() != "http" && url.scheme() != "https" {
2000 return Pending::new_err(error::url_bad_scheme(url));
2001 }
2002
2003 if self.inner.https_only && url.scheme() != "https" {
2005 return Pending::new_err(error::url_bad_scheme(url));
2006 }
2007
2008 for (key, value) in &self.inner.headers {
2011 if let Entry::Vacant(entry) = headers.entry(key) {
2012 entry.insert(value.clone());
2013 }
2014 }
2015
2016 #[cfg(feature = "cookies")]
2018 {
2019 if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
2020 if headers.get(crate::header::COOKIE).is_none() {
2021 add_cookie_header(&mut headers, &**cookie_store, &url);
2022 }
2023 }
2024 }
2025
2026 let accept_encoding = self.inner.accepts.as_str();
2027
2028 if let Some(accept_encoding) = accept_encoding {
2029 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2030 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2031 }
2032 }
2033
2034 let uri = match try_uri(&url) {
2035 Ok(uri) => uri,
2036 _ => return Pending::new_err(error::url_invalid_uri(url)),
2037 };
2038
2039 let (reusable, body) = match body {
2040 Some(body) => {
2041 let (reusable, body) = body.try_reuse();
2042 (Some(reusable), body)
2043 }
2044 None => (None, Body::empty()),
2045 };
2046
2047 self.proxy_auth(&uri, &mut headers);
2048
2049 let builder = hyper::Request::builder()
2050 .method(method.clone())
2051 .uri(uri)
2052 .version(version);
2053
2054 let in_flight = match version {
2055 #[cfg(feature = "http3")]
2056 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2057 let mut req = builder.body(body).expect("valid request parts");
2058 *req.headers_mut() = headers.clone();
2059 ResponseFuture::H3(self.inner.h3_client.as_ref().unwrap().request(req))
2060 }
2061 _ => {
2062 let mut req = builder.body(body).expect("valid request parts");
2063 *req.headers_mut() = headers.clone();
2064 ResponseFuture::Default(self.inner.hyper.request(req))
2065 }
2066 };
2067
2068 let total_timeout = timeout
2069 .or(self.inner.request_timeout)
2070 .map(tokio::time::sleep)
2071 .map(Box::pin);
2072
2073 let read_timeout_fut = self
2074 .inner
2075 .read_timeout
2076 .map(tokio::time::sleep)
2077 .map(Box::pin);
2078
2079 Pending {
2080 inner: PendingInner::Request(PendingRequest {
2081 method,
2082 url,
2083 headers,
2084 body: reusable,
2085
2086 urls: Vec::new(),
2087
2088 retry_count: 0,
2089
2090 client: self.inner.clone(),
2091
2092 in_flight,
2093 total_timeout,
2094 read_timeout_fut,
2095 read_timeout: self.inner.read_timeout,
2096 }),
2097 }
2098 }
2099
2100 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2101 if !self.inner.proxies_maybe_http_auth {
2102 return;
2103 }
2104
2105 if dst.scheme() != Some(&Scheme::HTTP) {
2109 return;
2110 }
2111
2112 if headers.contains_key(PROXY_AUTHORIZATION) {
2113 return;
2114 }
2115
2116 for proxy in self.inner.proxies.iter() {
2117 if proxy.is_match(dst) {
2118 if let Some(header) = proxy.http_basic_auth(dst) {
2119 headers.insert(PROXY_AUTHORIZATION, header);
2120 }
2121
2122 break;
2123 }
2124 }
2125 }
2126}
2127
2128impl fmt::Debug for Client {
2129 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2130 let mut builder = f.debug_struct("Client");
2131 self.inner.fmt_fields(&mut builder);
2132 builder.finish()
2133 }
2134}
2135
2136impl tower_service::Service<Request> for Client {
2137 type Response = Response;
2138 type Error = crate::Error;
2139 type Future = Pending;
2140
2141 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2142 Poll::Ready(Ok(()))
2143 }
2144
2145 fn call(&mut self, req: Request) -> Self::Future {
2146 self.execute_request(req)
2147 }
2148}
2149
2150impl tower_service::Service<Request> for &'_ Client {
2151 type Response = Response;
2152 type Error = crate::Error;
2153 type Future = Pending;
2154
2155 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2156 Poll::Ready(Ok(()))
2157 }
2158
2159 fn call(&mut self, req: Request) -> Self::Future {
2160 self.execute_request(req)
2161 }
2162}
2163
2164impl fmt::Debug for ClientBuilder {
2165 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2166 let mut builder = f.debug_struct("ClientBuilder");
2167 self.config.fmt_fields(&mut builder);
2168 builder.finish()
2169 }
2170}
2171
2172impl Config {
2173 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2174 #[cfg(feature = "cookies")]
2178 {
2179 if let Some(_) = self.cookie_store {
2180 f.field("cookie_store", &true);
2181 }
2182 }
2183
2184 f.field("accepts", &self.accepts);
2185
2186 if !self.proxies.is_empty() {
2187 f.field("proxies", &self.proxies);
2188 }
2189
2190 if !self.redirect_policy.is_default() {
2191 f.field("redirect_policy", &self.redirect_policy);
2192 }
2193
2194 if self.referer {
2195 f.field("referer", &true);
2196 }
2197
2198 f.field("default_headers", &self.headers);
2199
2200 if self.http1_title_case_headers {
2201 f.field("http1_title_case_headers", &true);
2202 }
2203
2204 if self.http1_allow_obsolete_multiline_headers_in_responses {
2205 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2206 }
2207
2208 if self.http1_ignore_invalid_headers_in_responses {
2209 f.field("http1_ignore_invalid_headers_in_responses", &true);
2210 }
2211
2212 if self.http1_allow_spaces_after_header_name_in_responses {
2213 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2214 }
2215
2216 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2217 f.field("http1_only", &true);
2218 }
2219
2220 #[cfg(feature = "http2")]
2221 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2222 f.field("http2_prior_knowledge", &true);
2223 }
2224
2225 if let Some(ref d) = self.connect_timeout {
2226 f.field("connect_timeout", d);
2227 }
2228
2229 if let Some(ref d) = self.timeout {
2230 f.field("timeout", d);
2231 }
2232
2233 if let Some(ref v) = self.local_address {
2234 f.field("local_address", v);
2235 }
2236
2237 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
2238 if let Some(ref v) = self.interface {
2239 f.field("interface", v);
2240 }
2241
2242 if self.nodelay {
2243 f.field("tcp_nodelay", &true);
2244 }
2245
2246 #[cfg(feature = "native-tls")]
2247 {
2248 if !self.hostname_verification {
2249 f.field("danger_accept_invalid_hostnames", &true);
2250 }
2251 }
2252
2253 #[cfg(feature = "__tls")]
2254 {
2255 if !self.certs_verification {
2256 f.field("danger_accept_invalid_certs", &true);
2257 }
2258
2259 if let Some(ref min_tls_version) = self.min_tls_version {
2260 f.field("min_tls_version", min_tls_version);
2261 }
2262
2263 if let Some(ref max_tls_version) = self.max_tls_version {
2264 f.field("max_tls_version", max_tls_version);
2265 }
2266
2267 f.field("tls_sni", &self.tls_sni);
2268
2269 f.field("tls_info", &self.tls_info);
2270 }
2271
2272 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2273 {
2274 f.field("tls_backend", &self.tls);
2275 }
2276
2277 if !self.dns_overrides.is_empty() {
2278 f.field("dns_overrides", &self.dns_overrides);
2279 }
2280
2281 #[cfg(feature = "http3")]
2282 {
2283 if self.tls_enable_early_data {
2284 f.field("tls_enable_early_data", &true);
2285 }
2286 }
2287 }
2288}
2289
2290struct ClientRef {
2291 accepts: Accepts,
2292 #[cfg(feature = "cookies")]
2293 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2294 headers: HeaderMap,
2295 hyper: HyperClient,
2296 #[cfg(feature = "http3")]
2297 h3_client: Option<H3Client>,
2298 redirect_policy: redirect::Policy,
2299 referer: bool,
2300 request_timeout: Option<Duration>,
2301 read_timeout: Option<Duration>,
2302 proxies: Arc<Vec<Proxy>>,
2303 proxies_maybe_http_auth: bool,
2304 https_only: bool,
2305}
2306
2307impl ClientRef {
2308 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2309 #[cfg(feature = "cookies")]
2313 {
2314 if let Some(_) = self.cookie_store {
2315 f.field("cookie_store", &true);
2316 }
2317 }
2318
2319 f.field("accepts", &self.accepts);
2320
2321 if !self.proxies.is_empty() {
2322 f.field("proxies", &self.proxies);
2323 }
2324
2325 if !self.redirect_policy.is_default() {
2326 f.field("redirect_policy", &self.redirect_policy);
2327 }
2328
2329 if self.referer {
2330 f.field("referer", &true);
2331 }
2332
2333 f.field("default_headers", &self.headers);
2334
2335 if let Some(ref d) = self.request_timeout {
2336 f.field("timeout", d);
2337 }
2338
2339 if let Some(ref d) = self.read_timeout {
2340 f.field("read_timeout", d);
2341 }
2342 }
2343}
2344
2345pin_project! {
2346 pub struct Pending {
2347 #[pin]
2348 inner: PendingInner,
2349 }
2350}
2351
2352enum PendingInner {
2353 Request(PendingRequest),
2354 Error(Option<crate::Error>),
2355}
2356
2357pin_project! {
2358 struct PendingRequest {
2359 method: Method,
2360 url: Url,
2361 headers: HeaderMap,
2362 body: Option<Option<Bytes>>,
2363
2364 urls: Vec<Url>,
2365
2366 retry_count: usize,
2367
2368 client: Arc<ClientRef>,
2369
2370 #[pin]
2371 in_flight: ResponseFuture,
2372 #[pin]
2373 total_timeout: Option<Pin<Box<Sleep>>>,
2374 #[pin]
2375 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2376 read_timeout: Option<Duration>,
2377 }
2378}
2379
2380enum ResponseFuture {
2381 Default(HyperResponseFuture),
2382 #[cfg(feature = "http3")]
2383 H3(H3ResponseFuture),
2384}
2385
2386impl PendingRequest {
2387 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2388 self.project().in_flight
2389 }
2390
2391 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2392 self.project().total_timeout
2393 }
2394
2395 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2396 self.project().read_timeout_fut
2397 }
2398
2399 fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
2400 self.project().urls
2401 }
2402
2403 fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
2404 self.project().headers
2405 }
2406
2407 #[cfg(any(feature = "http2", feature = "http3"))]
2408 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2409 use log::trace;
2410
2411 if !is_retryable_error(err) {
2412 return false;
2413 }
2414
2415 trace!("can retry {err:?}");
2416
2417 let body = match self.body {
2418 Some(Some(ref body)) => Body::reusable(body.clone()),
2419 Some(None) => {
2420 debug!("error was retryable, but body not reusable");
2421 return false;
2422 }
2423 None => Body::empty(),
2424 };
2425
2426 if self.retry_count >= 2 {
2427 trace!("retry count too high");
2428 return false;
2429 }
2430 self.retry_count += 1;
2431
2432 let uri = try_uri(&self.url).expect("URL was already validated as URI");
2434
2435 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
2436 #[cfg(feature = "http3")]
2437 ResponseFuture::H3(_) => {
2438 let mut req = hyper::Request::builder()
2439 .method(self.method.clone())
2440 .uri(uri)
2441 .body(body)
2442 .expect("valid request parts");
2443 *req.headers_mut() = self.headers.clone();
2444 ResponseFuture::H3(
2445 self.client
2446 .h3_client
2447 .as_ref()
2448 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2449 .request(req),
2450 )
2451 }
2452 _ => {
2453 let mut req = hyper::Request::builder()
2454 .method(self.method.clone())
2455 .uri(uri)
2456 .body(body)
2457 .expect("valid request parts");
2458 *req.headers_mut() = self.headers.clone();
2459 ResponseFuture::Default(self.client.hyper.request(req))
2460 }
2461 };
2462
2463 true
2464 }
2465}
2466
2467#[cfg(any(feature = "http2", feature = "http3"))]
2468fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
2469 let err = if let Some(err) = err.source() {
2471 err
2472 } else {
2473 return false;
2474 };
2475
2476 #[cfg(feature = "http3")]
2477 if let Some(cause) = err.source() {
2478 if let Some(err) = cause.downcast_ref::<h3::Error>() {
2479 debug!("determining if HTTP/3 error {err} can be retried");
2480 return err.to_string().as_str() == "timeout";
2482 }
2483 }
2484
2485 #[cfg(feature = "http2")]
2486 if let Some(cause) = err.source() {
2487 if let Some(err) = cause.downcast_ref::<h2::Error>() {
2488 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
2490 return true;
2491 }
2492
2493 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
2496 {
2497 return true;
2498 }
2499 }
2500 }
2501 false
2502}
2503
2504impl Pending {
2505 pub(super) fn new_err(err: crate::Error) -> Pending {
2506 Pending {
2507 inner: PendingInner::Error(Some(err)),
2508 }
2509 }
2510
2511 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2512 self.project().inner
2513 }
2514}
2515
2516impl Future for Pending {
2517 type Output = Result<Response, crate::Error>;
2518
2519 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2520 let inner = self.inner();
2521 match inner.get_mut() {
2522 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2523 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2524 .take()
2525 .expect("Pending error polled more than once"))),
2526 }
2527 }
2528}
2529
2530impl Future for PendingRequest {
2531 type Output = Result<Response, crate::Error>;
2532
2533 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2534 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
2535 if let Poll::Ready(()) = delay.poll(cx) {
2536 return Poll::Ready(Err(
2537 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2538 ));
2539 }
2540 }
2541
2542 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
2543 if let Poll::Ready(()) = delay.poll(cx) {
2544 return Poll::Ready(Err(
2545 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2546 ));
2547 }
2548 }
2549
2550 loop {
2551 let res = match self.as_mut().in_flight().get_mut() {
2552 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
2553 Poll::Ready(Err(e)) => {
2554 #[cfg(feature = "http2")]
2555 if self.as_mut().retry_error(&e) {
2556 continue;
2557 }
2558 return Poll::Ready(Err(
2559 crate::error::request(e).with_url(self.url.clone())
2560 ));
2561 }
2562 Poll::Ready(Ok(res)) => res.map(super::body::boxed),
2563 Poll::Pending => return Poll::Pending,
2564 },
2565 #[cfg(feature = "http3")]
2566 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
2567 Poll::Ready(Err(e)) => {
2568 if self.as_mut().retry_error(&e) {
2569 continue;
2570 }
2571 return Poll::Ready(Err(
2572 crate::error::request(e).with_url(self.url.clone())
2573 ));
2574 }
2575 Poll::Ready(Ok(res)) => res,
2576 Poll::Pending => return Poll::Pending,
2577 },
2578 };
2579
2580 #[cfg(feature = "cookies")]
2581 {
2582 if let Some(ref cookie_store) = self.client.cookie_store {
2583 let mut cookies =
2584 cookie::extract_response_cookie_headers(&res.headers()).peekable();
2585 if cookies.peek().is_some() {
2586 cookie_store.set_cookies(&mut cookies, &self.url);
2587 }
2588 }
2589 }
2590 let should_redirect = match res.status() {
2591 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
2592 self.body = None;
2593 for header in &[
2594 TRANSFER_ENCODING,
2595 CONTENT_ENCODING,
2596 CONTENT_TYPE,
2597 CONTENT_LENGTH,
2598 ] {
2599 self.headers.remove(header);
2600 }
2601
2602 match self.method {
2603 Method::GET | Method::HEAD => {}
2604 _ => {
2605 self.method = Method::GET;
2606 }
2607 }
2608 true
2609 }
2610 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
2611 match self.body {
2612 Some(Some(_)) | None => true,
2613 Some(None) => false,
2614 }
2615 }
2616 _ => false,
2617 };
2618 if should_redirect {
2619 let loc = res.headers().get(LOCATION).and_then(|val| {
2620 let loc = (|| -> Option<Url> {
2621 self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
2625 })();
2626
2627 let loc = loc.and_then(|url| {
2631 if try_uri(&url).is_ok() {
2632 Some(url)
2633 } else {
2634 None
2635 }
2636 });
2637
2638 if loc.is_none() {
2639 debug!("Location header had invalid URI: {val:?}");
2640 }
2641 loc
2642 });
2643 if let Some(loc) = loc {
2644 if self.client.referer {
2645 if let Some(referer) = make_referer(&loc, &self.url) {
2646 self.headers.insert(REFERER, referer);
2647 }
2648 }
2649 let url = self.url.clone();
2650 self.as_mut().urls().push(url);
2651 let action = self
2652 .client
2653 .redirect_policy
2654 .check(res.status(), &loc, &self.urls);
2655
2656 match action {
2657 redirect::ActionKind::Follow => {
2658 debug!("redirecting '{}' to '{}'", self.url, loc);
2659
2660 if loc.scheme() != "http" && loc.scheme() != "https" {
2661 return Poll::Ready(Err(error::url_bad_scheme(loc)));
2662 }
2663
2664 if self.client.https_only && loc.scheme() != "https" {
2665 return Poll::Ready(Err(error::redirect(
2666 error::url_bad_scheme(loc.clone()),
2667 loc,
2668 )));
2669 }
2670
2671 self.url = loc;
2672 let mut headers =
2673 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
2674
2675 remove_sensitive_headers(&mut headers, &self.url, &self.urls);
2676 let uri = try_uri(&self.url)?;
2677 let body = match self.body {
2678 Some(Some(ref body)) => Body::reusable(body.clone()),
2679 _ => Body::empty(),
2680 };
2681
2682 #[cfg(feature = "cookies")]
2684 {
2685 if let Some(ref cookie_store) = self.client.cookie_store {
2686 add_cookie_header(&mut headers, &**cookie_store, &self.url);
2687 }
2688 }
2689
2690 *self.as_mut().in_flight().get_mut() =
2691 match *self.as_mut().in_flight().as_ref() {
2692 #[cfg(feature = "http3")]
2693 ResponseFuture::H3(_) => {
2694 let mut req = hyper::Request::builder()
2695 .method(self.method.clone())
2696 .uri(uri.clone())
2697 .body(body)
2698 .expect("valid request parts");
2699 *req.headers_mut() = headers.clone();
2700 std::mem::swap(self.as_mut().headers(), &mut headers);
2701 ResponseFuture::H3(self.client.h3_client
2702 .as_ref()
2703 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2704 .request(req))
2705 }
2706 _ => {
2707 let mut req = hyper::Request::builder()
2708 .method(self.method.clone())
2709 .uri(uri.clone())
2710 .body(body)
2711 .expect("valid request parts");
2712 *req.headers_mut() = headers.clone();
2713 std::mem::swap(self.as_mut().headers(), &mut headers);
2714 ResponseFuture::Default(self.client.hyper.request(req))
2715 }
2716 };
2717
2718 continue;
2719 }
2720 redirect::ActionKind::Stop => {
2721 debug!("redirect policy disallowed redirection to '{loc}'");
2722 }
2723 redirect::ActionKind::Error(err) => {
2724 return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
2725 }
2726 }
2727 }
2728 }
2729
2730 let res = Response::new(
2731 res,
2732 self.url.clone(),
2733 self.client.accepts,
2734 self.total_timeout.take(),
2735 self.read_timeout,
2736 );
2737 return Poll::Ready(Ok(res));
2738 }
2739 }
2740}
2741
2742impl fmt::Debug for Pending {
2743 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2744 match self.inner {
2745 PendingInner::Request(ref req) => f
2746 .debug_struct("Pending")
2747 .field("method", &req.method)
2748 .field("url", &req.url)
2749 .finish(),
2750 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
2751 }
2752 }
2753}
2754
2755fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
2756 if next.scheme() == "http" && previous.scheme() == "https" {
2757 return None;
2758 }
2759
2760 let mut referer = previous.clone();
2761 let _ = referer.set_username("");
2762 let _ = referer.set_password(None);
2763 referer.set_fragment(None);
2764 referer.as_str().parse().ok()
2765}
2766
2767#[cfg(feature = "cookies")]
2768fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
2769 if let Some(header) = cookie_store.cookies(url) {
2770 headers.insert(crate::header::COOKIE, header);
2771 }
2772}
2773
2774#[cfg(test)]
2775mod tests {
2776 #![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
2777
2778 #[tokio::test]
2779 async fn execute_request_rejects_invalid_urls() {
2780 let url_str = "hxxps://www.rust-lang.org/";
2781 let url = url::Url::parse(url_str).unwrap();
2782 let result = crate::get(url.clone()).await;
2783
2784 assert!(result.is_err());
2785 let err = result.err().unwrap();
2786 assert!(err.is_builder());
2787 assert_eq!(url_str, err.url().unwrap().as_str());
2788 }
2789
2790 #[tokio::test]
2792 async fn execute_request_rejects_invalid_hostname() {
2793 let url_str = "https://{{hostname}}/";
2794 let url = url::Url::parse(url_str).unwrap();
2795 let result = crate::get(url.clone()).await;
2796
2797 assert!(result.is_err());
2798 let err = result.err().unwrap();
2799 assert!(err.is_builder());
2800 assert_eq!(url_str, err.url().unwrap().as_str());
2801 }
2802}