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 config_builder =
570 rustls::ClientConfig::builder_with_protocol_versions(&versions)
571 .with_root_certificates(root_cert_store);
572
573 let mut tls = if let Some(id) = config.identity {
575 id.add_to_rustls(config_builder)?
576 } else {
577 config_builder.with_no_client_auth()
578 };
579
580 if !config.certs_verification {
582 tls.dangerous()
583 .set_certificate_verifier(Arc::new(NoVerifier));
584 }
585
586 tls.enable_sni = config.tls_sni;
587
588 match config.http_version_pref {
590 HttpVersionPref::Http1 => {
591 tls.alpn_protocols = vec!["http/1.1".into()];
592 }
593 #[cfg(feature = "http2")]
594 HttpVersionPref::Http2 => {
595 tls.alpn_protocols = vec!["h2".into()];
596 }
597 #[cfg(feature = "http3")]
598 HttpVersionPref::Http3 => {
599 tls.alpn_protocols = vec!["h3".into()];
600 }
601 HttpVersionPref::All => {
602 tls.alpn_protocols = vec![
603 #[cfg(feature = "http2")]
604 "h2".into(),
605 "http/1.1".into(),
606 ];
607 }
608 }
609
610 #[cfg(feature = "http3")]
611 {
612 tls.enable_early_data = config.tls_enable_early_data;
613
614 h3_connector = build_h3_connector(
615 resolver,
616 tls.clone(),
617 config.quic_max_idle_timeout,
618 config.quic_stream_receive_window,
619 config.quic_receive_window,
620 config.quic_send_window,
621 config.local_address,
622 &config.http_version_pref,
623 )?;
624 }
625
626 Connector::new_rustls_tls(
627 http,
628 tls,
629 proxies.clone(),
630 user_agent(&config.headers),
631 config.local_address,
632 #[cfg(any(
633 target_os = "android",
634 target_os = "fuchsia",
635 target_os = "linux"
636 ))]
637 config.interface.as_deref(),
638 config.nodelay,
639 config.tls_info,
640 )
641 }
642 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
643 TlsBackend::UnknownPreconfigured => {
644 return Err(crate::error::builder(
645 "Unknown TLS backend passed to `use_preconfigured_tls`",
646 ));
647 }
648 }
649
650 #[cfg(not(feature = "__tls"))]
651 Connector::new(
652 http,
653 proxies.clone(),
654 config.local_address,
655 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
656 config.interface.as_deref(),
657 config.nodelay,
658 )
659 };
660
661 connector.set_timeout(config.connect_timeout);
662 connector.set_verbose(config.connection_verbose);
663
664 let mut builder =
665 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
666 #[cfg(feature = "http2")]
667 {
668 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
669 builder.http2_only(true);
670 }
671
672 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
673 {
674 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
675 }
676 if let Some(http2_initial_connection_window_size) =
677 config.http2_initial_connection_window_size
678 {
679 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
680 }
681 if config.http2_adaptive_window {
682 builder.http2_adaptive_window(true);
683 }
684 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
685 builder.http2_max_frame_size(http2_max_frame_size);
686 }
687 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
688 builder.http2_keep_alive_interval(http2_keep_alive_interval);
689 }
690 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
691 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
692 }
693 if config.http2_keep_alive_while_idle {
694 builder.http2_keep_alive_while_idle(true);
695 }
696 }
697
698 #[cfg(not(target_arch = "wasm32"))]
699 builder.timer(hyper_util::rt::TokioTimer::new());
700 builder.pool_idle_timeout(config.pool_idle_timeout);
701 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
702 connector.set_keepalive(config.tcp_keepalive);
703
704 if config.http09_responses {
705 builder.http09_responses(true);
706 }
707
708 if config.http1_title_case_headers {
709 builder.http1_title_case_headers(true);
710 }
711
712 if config.http1_allow_obsolete_multiline_headers_in_responses {
713 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
714 }
715
716 if config.http1_ignore_invalid_headers_in_responses {
717 builder.http1_ignore_invalid_headers_in_responses(true);
718 }
719
720 if config.http1_allow_spaces_after_header_name_in_responses {
721 builder.http1_allow_spaces_after_header_name_in_responses(true);
722 }
723
724 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
725
726 Ok(Client {
727 inner: Arc::new(ClientRef {
728 accepts: config.accepts,
729 #[cfg(feature = "cookies")]
730 cookie_store: config.cookie_store,
731 #[cfg(feature = "http3")]
734 h3_client: match h3_connector {
735 Some(h3_connector) => {
736 Some(H3Client::new(h3_connector, config.pool_idle_timeout))
737 }
738 None => None,
739 },
740 hyper: builder.build(connector),
741 headers: config.headers,
742 redirect_policy: config.redirect_policy,
743 referer: config.referer,
744 read_timeout: config.read_timeout,
745 request_timeout: config.timeout,
746 proxies,
747 proxies_maybe_http_auth,
748 https_only: config.https_only,
749 }),
750 })
751 }
752
753 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
776 where
777 V: TryInto<HeaderValue>,
778 V::Error: Into<http::Error>,
779 {
780 match value.try_into() {
781 Ok(value) => {
782 self.config.headers.insert(USER_AGENT, value);
783 }
784 Err(e) => {
785 self.config.error = Some(crate::error::builder(e.into()));
786 }
787 };
788 self
789 }
790 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
814 for (key, value) in headers.iter() {
815 self.config.headers.insert(key, value.clone());
816 }
817 self
818 }
819
820 #[cfg(feature = "cookies")]
835 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
836 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
837 if enable {
838 self.cookie_provider(Arc::new(cookie::Jar::default()))
839 } else {
840 self.config.cookie_store = None;
841 self
842 }
843 }
844
845 #[cfg(feature = "cookies")]
859 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
860 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
861 mut self,
862 cookie_store: Arc<C>,
863 ) -> ClientBuilder {
864 self.config.cookie_store = Some(cookie_store as _);
865 self
866 }
867
868 #[cfg(feature = "gzip")]
885 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
886 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
887 self.config.accepts.gzip = enable;
888 self
889 }
890
891 #[cfg(feature = "brotli")]
908 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
909 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
910 self.config.accepts.brotli = enable;
911 self
912 }
913
914 #[cfg(feature = "zstd")]
931 #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
932 pub fn zstd(mut self, enable: bool) -> ClientBuilder {
933 self.config.accepts.zstd = enable;
934 self
935 }
936
937 #[cfg(feature = "deflate")]
954 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
955 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
956 self.config.accepts.deflate = enable;
957 self
958 }
959
960 pub fn no_gzip(self) -> ClientBuilder {
966 #[cfg(feature = "gzip")]
967 {
968 self.gzip(false)
969 }
970
971 #[cfg(not(feature = "gzip"))]
972 {
973 self
974 }
975 }
976
977 pub fn no_brotli(self) -> ClientBuilder {
983 #[cfg(feature = "brotli")]
984 {
985 self.brotli(false)
986 }
987
988 #[cfg(not(feature = "brotli"))]
989 {
990 self
991 }
992 }
993
994 pub fn no_zstd(self) -> ClientBuilder {
1000 #[cfg(feature = "zstd")]
1001 {
1002 self.zstd(false)
1003 }
1004
1005 #[cfg(not(feature = "zstd"))]
1006 {
1007 self
1008 }
1009 }
1010
1011 pub fn no_deflate(self) -> ClientBuilder {
1017 #[cfg(feature = "deflate")]
1018 {
1019 self.deflate(false)
1020 }
1021
1022 #[cfg(not(feature = "deflate"))]
1023 {
1024 self
1025 }
1026 }
1027
1028 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
1034 self.config.redirect_policy = policy;
1035 self
1036 }
1037
1038 pub fn referer(mut self, enable: bool) -> ClientBuilder {
1042 self.config.referer = enable;
1043 self
1044 }
1045
1046 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
1054 self.config.proxies.push(proxy);
1055 self.config.auto_sys_proxy = false;
1056 self
1057 }
1058
1059 pub fn no_proxy(mut self) -> ClientBuilder {
1067 self.config.proxies.clear();
1068 self.config.auto_sys_proxy = false;
1069 self
1070 }
1071
1072 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1081 self.config.timeout = Some(timeout);
1082 self
1083 }
1084
1085 pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
1093 self.config.read_timeout = Some(timeout);
1094 self
1095 }
1096
1097 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1106 self.config.connect_timeout = Some(timeout);
1107 self
1108 }
1109
1110 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1117 self.config.connection_verbose = verbose;
1118 self
1119 }
1120
1121 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1129 where
1130 D: Into<Option<Duration>>,
1131 {
1132 self.config.pool_idle_timeout = val.into();
1133 self
1134 }
1135
1136 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1138 self.config.pool_max_idle_per_host = max;
1139 self
1140 }
1141
1142 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1144 self.config.http1_title_case_headers = true;
1145 self
1146 }
1147
1148 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1154 mut self,
1155 value: bool,
1156 ) -> ClientBuilder {
1157 self.config
1158 .http1_allow_obsolete_multiline_headers_in_responses = value;
1159 self
1160 }
1161
1162 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1164 self.config.http1_ignore_invalid_headers_in_responses = value;
1165 self
1166 }
1167
1168 pub fn http1_allow_spaces_after_header_name_in_responses(
1174 mut self,
1175 value: bool,
1176 ) -> ClientBuilder {
1177 self.config
1178 .http1_allow_spaces_after_header_name_in_responses = value;
1179 self
1180 }
1181
1182 pub fn http1_only(mut self) -> ClientBuilder {
1184 self.config.http_version_pref = HttpVersionPref::Http1;
1185 self
1186 }
1187
1188 pub fn http09_responses(mut self) -> ClientBuilder {
1190 self.config.http09_responses = true;
1191 self
1192 }
1193
1194 #[cfg(feature = "http2")]
1196 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1197 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1198 self.config.http_version_pref = HttpVersionPref::Http2;
1199 self
1200 }
1201
1202 #[cfg(feature = "http3")]
1204 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1205 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1206 self.config.http_version_pref = HttpVersionPref::Http3;
1207 self
1208 }
1209
1210 #[cfg(feature = "http2")]
1214 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1215 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1216 self.config.http2_initial_stream_window_size = sz.into();
1217 self
1218 }
1219
1220 #[cfg(feature = "http2")]
1224 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1225 pub fn http2_initial_connection_window_size(
1226 mut self,
1227 sz: impl Into<Option<u32>>,
1228 ) -> ClientBuilder {
1229 self.config.http2_initial_connection_window_size = sz.into();
1230 self
1231 }
1232
1233 #[cfg(feature = "http2")]
1238 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1239 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1240 self.config.http2_adaptive_window = enabled;
1241 self
1242 }
1243
1244 #[cfg(feature = "http2")]
1248 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1249 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1250 self.config.http2_max_frame_size = sz.into();
1251 self
1252 }
1253
1254 #[cfg(feature = "http2")]
1259 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1260 pub fn http2_keep_alive_interval(
1261 mut self,
1262 interval: impl Into<Option<Duration>>,
1263 ) -> ClientBuilder {
1264 self.config.http2_keep_alive_interval = interval.into();
1265 self
1266 }
1267
1268 #[cfg(feature = "http2")]
1274 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1275 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1276 self.config.http2_keep_alive_timeout = Some(timeout);
1277 self
1278 }
1279
1280 #[cfg(feature = "http2")]
1287 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
1288 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1289 self.config.http2_keep_alive_while_idle = enabled;
1290 self
1291 }
1292
1293 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1299 self.config.nodelay = enabled;
1300 self
1301 }
1302
1303 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1315 where
1316 T: Into<Option<IpAddr>>,
1317 {
1318 self.config.local_address = addr.into();
1319 self
1320 }
1321
1322 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1333 pub fn interface(mut self, interface: &str) -> ClientBuilder {
1334 self.config.interface = Some(interface.to_string());
1335 self
1336 }
1337
1338 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1342 where
1343 D: Into<Option<Duration>>,
1344 {
1345 self.config.tcp_keepalive = val.into();
1346 self
1347 }
1348
1349 #[cfg(feature = "__tls")]
1361 #[cfg_attr(
1362 docsrs,
1363 doc(cfg(any(
1364 feature = "default-tls",
1365 feature = "native-tls",
1366 feature = "rustls-tls"
1367 )))
1368 )]
1369 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1370 self.config.root_certs.push(cert);
1371 self
1372 }
1373
1374 #[cfg(feature = "__tls")]
1392 #[cfg_attr(
1393 docsrs,
1394 doc(cfg(any(
1395 feature = "default-tls",
1396 feature = "native-tls",
1397 feature = "rustls-tls"
1398 )))
1399 )]
1400 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1401 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1402
1403 #[cfg(feature = "rustls-tls-webpki-roots")]
1404 {
1405 self.config.tls_built_in_certs_webpki = tls_built_in_root_certs;
1406 }
1407
1408 #[cfg(feature = "rustls-tls-native-roots")]
1409 {
1410 self.config.tls_built_in_certs_native = tls_built_in_root_certs;
1411 }
1412
1413 self
1414 }
1415
1416 #[cfg(feature = "rustls-tls-webpki-roots")]
1420 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots")))]
1421 pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder {
1422 self.config.tls_built_in_certs_webpki = enabled;
1423 self
1424 }
1425
1426 #[cfg(feature = "rustls-tls-native-roots")]
1430 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots")))]
1431 pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder {
1432 self.config.tls_built_in_certs_native = enabled;
1433 self
1434 }
1435
1436 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1443 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1444 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1445 self.config.identity = Some(identity);
1446 self
1447 }
1448
1449 #[cfg(feature = "native-tls")]
1464 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1465 pub fn danger_accept_invalid_hostnames(
1466 mut self,
1467 accept_invalid_hostname: bool,
1468 ) -> ClientBuilder {
1469 self.config.hostname_verification = !accept_invalid_hostname;
1470 self
1471 }
1472
1473 #[cfg(feature = "__tls")]
1490 #[cfg_attr(
1491 docsrs,
1492 doc(cfg(any(
1493 feature = "default-tls",
1494 feature = "native-tls",
1495 feature = "rustls-tls"
1496 )))
1497 )]
1498 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1499 self.config.certs_verification = !accept_invalid_certs;
1500 self
1501 }
1502
1503 #[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_sni(mut self, tls_sni: bool) -> ClientBuilder {
1521 self.config.tls_sni = tls_sni;
1522 self
1523 }
1524
1525 #[cfg(feature = "__tls")]
1541 #[cfg_attr(
1542 docsrs,
1543 doc(cfg(any(
1544 feature = "default-tls",
1545 feature = "native-tls",
1546 feature = "rustls-tls"
1547 )))
1548 )]
1549 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1550 self.config.min_tls_version = Some(version);
1551 self
1552 }
1553
1554 #[cfg(feature = "__tls")]
1573 #[cfg_attr(
1574 docsrs,
1575 doc(cfg(any(
1576 feature = "default-tls",
1577 feature = "native-tls",
1578 feature = "rustls-tls"
1579 )))
1580 )]
1581 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1582 self.config.max_tls_version = Some(version);
1583 self
1584 }
1585
1586 #[cfg(feature = "native-tls")]
1595 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1596 pub fn use_native_tls(mut self) -> ClientBuilder {
1597 self.config.tls = TlsBackend::Default;
1598 self
1599 }
1600
1601 #[cfg(feature = "__rustls")]
1610 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1611 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1612 self.config.tls = TlsBackend::Rustls;
1613 self
1614 }
1615
1616 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
1635 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1636 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
1637 let mut tls = Some(tls);
1638 #[cfg(feature = "native-tls")]
1639 {
1640 if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::<Option<TlsConnector>>() {
1641 let tls = conn.take().expect("is definitely Some");
1642 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
1643 self.config.tls = tls;
1644 return self;
1645 }
1646 }
1647 #[cfg(feature = "__rustls")]
1648 {
1649 if let Some(conn) =
1650 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
1651 {
1652 let tls = conn.take().expect("is definitely Some");
1653 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
1654 self.config.tls = tls;
1655 return self;
1656 }
1657 }
1658
1659 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
1661 self
1662 }
1663
1664 #[cfg(feature = "__tls")]
1671 #[cfg_attr(
1672 docsrs,
1673 doc(cfg(any(
1674 feature = "default-tls",
1675 feature = "native-tls",
1676 feature = "rustls-tls"
1677 )))
1678 )]
1679 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
1680 self.config.tls_info = tls_info;
1681 self
1682 }
1683
1684 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
1688 self.config.https_only = enabled;
1689 self
1690 }
1691
1692 #[cfg(feature = "hickory-dns")]
1701 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1702 #[deprecated(note = "use `hickory_dns` instead")]
1703 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
1704 self.config.hickory_dns = enable;
1705 self
1706 }
1707
1708 #[cfg(feature = "hickory-dns")]
1717 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1718 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
1719 self.config.hickory_dns = enable;
1720 self
1721 }
1722
1723 #[deprecated(note = "use `no_hickory_dns` instead")]
1729 pub fn no_trust_dns(self) -> ClientBuilder {
1730 #[cfg(feature = "hickory-dns")]
1731 {
1732 self.hickory_dns(false)
1733 }
1734
1735 #[cfg(not(feature = "hickory-dns"))]
1736 {
1737 self
1738 }
1739 }
1740
1741 pub fn no_hickory_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 resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
1767 self.resolve_to_addrs(domain, &[addr])
1768 }
1769
1770 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
1779 self.config
1780 .dns_overrides
1781 .insert(domain.to_ascii_lowercase(), addrs.to_vec());
1782 self
1783 }
1784
1785 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
1791 self.config.dns_resolver = Some(resolver as _);
1792 self
1793 }
1794
1795 #[cfg(feature = "http3")]
1800 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1801 pub fn set_tls_enable_early_data(mut self, enabled: bool) -> ClientBuilder {
1802 self.config.tls_enable_early_data = enabled;
1803 self
1804 }
1805
1806 #[cfg(feature = "http3")]
1812 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1813 pub fn set_quic_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
1814 self.config.quic_max_idle_timeout = Some(value);
1815 self
1816 }
1817
1818 #[cfg(feature = "http3")]
1825 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1826 pub fn set_quic_stream_receive_window(mut self, value: VarInt) -> ClientBuilder {
1827 self.config.quic_stream_receive_window = Some(value);
1828 self
1829 }
1830
1831 #[cfg(feature = "http3")]
1838 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1839 pub fn set_quic_receive_window(mut self, value: VarInt) -> ClientBuilder {
1840 self.config.quic_receive_window = Some(value);
1841 self
1842 }
1843
1844 #[cfg(feature = "http3")]
1850 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1851 pub fn set_quic_send_window(mut self, value: u64) -> ClientBuilder {
1852 self.config.quic_send_window = Some(value);
1853 self
1854 }
1855}
1856
1857type HyperClient = hyper_util::client::legacy::Client<Connector, super::Body>;
1858
1859impl Default for Client {
1860 fn default() -> Self {
1861 Self::new()
1862 }
1863}
1864
1865impl Client {
1866 pub fn new() -> Client {
1876 ClientBuilder::new().build().expect("Client::new()")
1877 }
1878
1879 pub fn builder() -> ClientBuilder {
1883 ClientBuilder::new()
1884 }
1885
1886 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1892 self.request(Method::GET, url)
1893 }
1894
1895 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1901 self.request(Method::POST, url)
1902 }
1903
1904 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1910 self.request(Method::PUT, url)
1911 }
1912
1913 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1919 self.request(Method::PATCH, url)
1920 }
1921
1922 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1928 self.request(Method::DELETE, url)
1929 }
1930
1931 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1937 self.request(Method::HEAD, url)
1938 }
1939
1940 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
1949 let req = url.into_url().map(move |url| Request::new(method, url));
1950 RequestBuilder::new(self.clone(), req)
1951 }
1952
1953 pub fn execute(
1966 &self,
1967 request: Request,
1968 ) -> impl Future<Output = Result<Response, crate::Error>> {
1969 self.execute_request(request)
1970 }
1971
1972 pub(super) fn execute_request(&self, req: Request) -> Pending {
1973 let (method, url, mut headers, body, timeout, version) = req.pieces();
1974 if url.scheme() != "http" && url.scheme() != "https" {
1975 return Pending::new_err(error::url_bad_scheme(url));
1976 }
1977
1978 if self.inner.https_only && url.scheme() != "https" {
1980 return Pending::new_err(error::url_bad_scheme(url));
1981 }
1982
1983 for (key, value) in &self.inner.headers {
1986 if let Entry::Vacant(entry) = headers.entry(key) {
1987 entry.insert(value.clone());
1988 }
1989 }
1990
1991 #[cfg(feature = "cookies")]
1993 {
1994 if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
1995 if headers.get(crate::header::COOKIE).is_none() {
1996 add_cookie_header(&mut headers, &**cookie_store, &url);
1997 }
1998 }
1999 }
2000
2001 let accept_encoding = self.inner.accepts.as_str();
2002
2003 if let Some(accept_encoding) = accept_encoding {
2004 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
2005 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
2006 }
2007 }
2008
2009 let uri = match try_uri(&url) {
2010 Ok(uri) => uri,
2011 _ => return Pending::new_err(error::url_invalid_uri(url)),
2012 };
2013
2014 let (reusable, body) = match body {
2015 Some(body) => {
2016 let (reusable, body) = body.try_reuse();
2017 (Some(reusable), body)
2018 }
2019 None => (None, Body::empty()),
2020 };
2021
2022 self.proxy_auth(&uri, &mut headers);
2023
2024 let builder = hyper::Request::builder()
2025 .method(method.clone())
2026 .uri(uri)
2027 .version(version);
2028
2029 let in_flight = match version {
2030 #[cfg(feature = "http3")]
2031 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
2032 let mut req = builder.body(body).expect("valid request parts");
2033 *req.headers_mut() = headers.clone();
2034 ResponseFuture::H3(self.inner.h3_client.as_ref().unwrap().request(req))
2035 }
2036 _ => {
2037 let mut req = builder.body(body).expect("valid request parts");
2038 *req.headers_mut() = headers.clone();
2039 ResponseFuture::Default(self.inner.hyper.request(req))
2040 }
2041 };
2042
2043 let total_timeout = timeout
2044 .or(self.inner.request_timeout)
2045 .map(tokio::time::sleep)
2046 .map(Box::pin);
2047
2048 let read_timeout_fut = self
2049 .inner
2050 .read_timeout
2051 .map(tokio::time::sleep)
2052 .map(Box::pin);
2053
2054 Pending {
2055 inner: PendingInner::Request(PendingRequest {
2056 method,
2057 url,
2058 headers,
2059 body: reusable,
2060
2061 urls: Vec::new(),
2062
2063 retry_count: 0,
2064
2065 client: self.inner.clone(),
2066
2067 in_flight,
2068 total_timeout,
2069 read_timeout_fut,
2070 read_timeout: self.inner.read_timeout,
2071 }),
2072 }
2073 }
2074
2075 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
2076 if !self.inner.proxies_maybe_http_auth {
2077 return;
2078 }
2079
2080 if dst.scheme() != Some(&Scheme::HTTP) {
2084 return;
2085 }
2086
2087 if headers.contains_key(PROXY_AUTHORIZATION) {
2088 return;
2089 }
2090
2091 for proxy in self.inner.proxies.iter() {
2092 if proxy.is_match(dst) {
2093 if let Some(header) = proxy.http_basic_auth(dst) {
2094 headers.insert(PROXY_AUTHORIZATION, header);
2095 }
2096
2097 break;
2098 }
2099 }
2100 }
2101}
2102
2103impl fmt::Debug for Client {
2104 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2105 let mut builder = f.debug_struct("Client");
2106 self.inner.fmt_fields(&mut builder);
2107 builder.finish()
2108 }
2109}
2110
2111impl tower_service::Service<Request> for Client {
2112 type Response = Response;
2113 type Error = crate::Error;
2114 type Future = Pending;
2115
2116 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2117 Poll::Ready(Ok(()))
2118 }
2119
2120 fn call(&mut self, req: Request) -> Self::Future {
2121 self.execute_request(req)
2122 }
2123}
2124
2125impl tower_service::Service<Request> for &'_ Client {
2126 type Response = Response;
2127 type Error = crate::Error;
2128 type Future = Pending;
2129
2130 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2131 Poll::Ready(Ok(()))
2132 }
2133
2134 fn call(&mut self, req: Request) -> Self::Future {
2135 self.execute_request(req)
2136 }
2137}
2138
2139impl fmt::Debug for ClientBuilder {
2140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2141 let mut builder = f.debug_struct("ClientBuilder");
2142 self.config.fmt_fields(&mut builder);
2143 builder.finish()
2144 }
2145}
2146
2147impl Config {
2148 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2149 #[cfg(feature = "cookies")]
2153 {
2154 if let Some(_) = self.cookie_store {
2155 f.field("cookie_store", &true);
2156 }
2157 }
2158
2159 f.field("accepts", &self.accepts);
2160
2161 if !self.proxies.is_empty() {
2162 f.field("proxies", &self.proxies);
2163 }
2164
2165 if !self.redirect_policy.is_default() {
2166 f.field("redirect_policy", &self.redirect_policy);
2167 }
2168
2169 if self.referer {
2170 f.field("referer", &true);
2171 }
2172
2173 f.field("default_headers", &self.headers);
2174
2175 if self.http1_title_case_headers {
2176 f.field("http1_title_case_headers", &true);
2177 }
2178
2179 if self.http1_allow_obsolete_multiline_headers_in_responses {
2180 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2181 }
2182
2183 if self.http1_ignore_invalid_headers_in_responses {
2184 f.field("http1_ignore_invalid_headers_in_responses", &true);
2185 }
2186
2187 if self.http1_allow_spaces_after_header_name_in_responses {
2188 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2189 }
2190
2191 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2192 f.field("http1_only", &true);
2193 }
2194
2195 #[cfg(feature = "http2")]
2196 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2197 f.field("http2_prior_knowledge", &true);
2198 }
2199
2200 if let Some(ref d) = self.connect_timeout {
2201 f.field("connect_timeout", d);
2202 }
2203
2204 if let Some(ref d) = self.timeout {
2205 f.field("timeout", d);
2206 }
2207
2208 if let Some(ref v) = self.local_address {
2209 f.field("local_address", v);
2210 }
2211
2212 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
2213 if let Some(ref v) = self.interface {
2214 f.field("interface", v);
2215 }
2216
2217 if self.nodelay {
2218 f.field("tcp_nodelay", &true);
2219 }
2220
2221 #[cfg(feature = "native-tls")]
2222 {
2223 if !self.hostname_verification {
2224 f.field("danger_accept_invalid_hostnames", &true);
2225 }
2226 }
2227
2228 #[cfg(feature = "__tls")]
2229 {
2230 if !self.certs_verification {
2231 f.field("danger_accept_invalid_certs", &true);
2232 }
2233
2234 if let Some(ref min_tls_version) = self.min_tls_version {
2235 f.field("min_tls_version", min_tls_version);
2236 }
2237
2238 if let Some(ref max_tls_version) = self.max_tls_version {
2239 f.field("max_tls_version", max_tls_version);
2240 }
2241
2242 f.field("tls_sni", &self.tls_sni);
2243
2244 f.field("tls_info", &self.tls_info);
2245 }
2246
2247 #[cfg(all(feature = "default-tls", feature = "__rustls"))]
2248 {
2249 f.field("tls_backend", &self.tls);
2250 }
2251
2252 if !self.dns_overrides.is_empty() {
2253 f.field("dns_overrides", &self.dns_overrides);
2254 }
2255
2256 #[cfg(feature = "http3")]
2257 {
2258 if self.tls_enable_early_data {
2259 f.field("tls_enable_early_data", &true);
2260 }
2261 }
2262 }
2263}
2264
2265struct ClientRef {
2266 accepts: Accepts,
2267 #[cfg(feature = "cookies")]
2268 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2269 headers: HeaderMap,
2270 hyper: HyperClient,
2271 #[cfg(feature = "http3")]
2272 h3_client: Option<H3Client>,
2273 redirect_policy: redirect::Policy,
2274 referer: bool,
2275 request_timeout: Option<Duration>,
2276 read_timeout: Option<Duration>,
2277 proxies: Arc<Vec<Proxy>>,
2278 proxies_maybe_http_auth: bool,
2279 https_only: bool,
2280}
2281
2282impl ClientRef {
2283 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2284 #[cfg(feature = "cookies")]
2288 {
2289 if let Some(_) = self.cookie_store {
2290 f.field("cookie_store", &true);
2291 }
2292 }
2293
2294 f.field("accepts", &self.accepts);
2295
2296 if !self.proxies.is_empty() {
2297 f.field("proxies", &self.proxies);
2298 }
2299
2300 if !self.redirect_policy.is_default() {
2301 f.field("redirect_policy", &self.redirect_policy);
2302 }
2303
2304 if self.referer {
2305 f.field("referer", &true);
2306 }
2307
2308 f.field("default_headers", &self.headers);
2309
2310 if let Some(ref d) = self.request_timeout {
2311 f.field("timeout", d);
2312 }
2313
2314 if let Some(ref d) = self.read_timeout {
2315 f.field("read_timeout", d);
2316 }
2317 }
2318}
2319
2320pin_project! {
2321 pub struct Pending {
2322 #[pin]
2323 inner: PendingInner,
2324 }
2325}
2326
2327enum PendingInner {
2328 Request(PendingRequest),
2329 Error(Option<crate::Error>),
2330}
2331
2332pin_project! {
2333 struct PendingRequest {
2334 method: Method,
2335 url: Url,
2336 headers: HeaderMap,
2337 body: Option<Option<Bytes>>,
2338
2339 urls: Vec<Url>,
2340
2341 retry_count: usize,
2342
2343 client: Arc<ClientRef>,
2344
2345 #[pin]
2346 in_flight: ResponseFuture,
2347 #[pin]
2348 total_timeout: Option<Pin<Box<Sleep>>>,
2349 #[pin]
2350 read_timeout_fut: Option<Pin<Box<Sleep>>>,
2351 read_timeout: Option<Duration>,
2352 }
2353}
2354
2355enum ResponseFuture {
2356 Default(HyperResponseFuture),
2357 #[cfg(feature = "http3")]
2358 H3(H3ResponseFuture),
2359}
2360
2361impl PendingRequest {
2362 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2363 self.project().in_flight
2364 }
2365
2366 fn total_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2367 self.project().total_timeout
2368 }
2369
2370 fn read_timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2371 self.project().read_timeout_fut
2372 }
2373
2374 fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
2375 self.project().urls
2376 }
2377
2378 fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
2379 self.project().headers
2380 }
2381
2382 #[cfg(feature = "http2")]
2383 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2384 use log::trace;
2385
2386 if !is_retryable_error(err) {
2387 return false;
2388 }
2389
2390 trace!("can retry {err:?}");
2391
2392 let body = match self.body {
2393 Some(Some(ref body)) => Body::reusable(body.clone()),
2394 Some(None) => {
2395 debug!("error was retryable, but body not reusable");
2396 return false;
2397 }
2398 None => Body::empty(),
2399 };
2400
2401 if self.retry_count >= 2 {
2402 trace!("retry count too high");
2403 return false;
2404 }
2405 self.retry_count += 1;
2406
2407 let uri = try_uri(&self.url).expect("URL was already validated as URI");
2409
2410 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
2411 #[cfg(feature = "http3")]
2412 ResponseFuture::H3(_) => {
2413 let mut req = hyper::Request::builder()
2414 .method(self.method.clone())
2415 .uri(uri)
2416 .body(body)
2417 .expect("valid request parts");
2418 *req.headers_mut() = self.headers.clone();
2419 ResponseFuture::H3(
2420 self.client
2421 .h3_client
2422 .as_ref()
2423 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2424 .request(req),
2425 )
2426 }
2427 _ => {
2428 let mut req = hyper::Request::builder()
2429 .method(self.method.clone())
2430 .uri(uri)
2431 .body(body)
2432 .expect("valid request parts");
2433 *req.headers_mut() = self.headers.clone();
2434 ResponseFuture::Default(self.client.hyper.request(req))
2435 }
2436 };
2437
2438 true
2439 }
2440}
2441
2442#[cfg(feature = "http2")]
2443fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
2444 let err = if let Some(err) = err.source() {
2446 err
2447 } else {
2448 return false;
2449 };
2450
2451 #[cfg(feature = "http3")]
2452 if let Some(cause) = err.source() {
2453 if let Some(err) = cause.downcast_ref::<h3::Error>() {
2454 debug!("determining if HTTP/3 error {err} can be retried");
2455 return err.to_string().as_str() == "timeout";
2457 }
2458 }
2459
2460 if let Some(cause) = err.source() {
2461 if let Some(err) = cause.downcast_ref::<h2::Error>() {
2462 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
2464 return true;
2465 }
2466
2467 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
2470 {
2471 return true;
2472 }
2473 }
2474 }
2475 false
2476}
2477
2478impl Pending {
2479 pub(super) fn new_err(err: crate::Error) -> Pending {
2480 Pending {
2481 inner: PendingInner::Error(Some(err)),
2482 }
2483 }
2484
2485 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2486 self.project().inner
2487 }
2488}
2489
2490impl Future for Pending {
2491 type Output = Result<Response, crate::Error>;
2492
2493 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2494 let inner = self.inner();
2495 match inner.get_mut() {
2496 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2497 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2498 .take()
2499 .expect("Pending error polled more than once"))),
2500 }
2501 }
2502}
2503
2504impl Future for PendingRequest {
2505 type Output = Result<Response, crate::Error>;
2506
2507 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2508 if let Some(delay) = self.as_mut().total_timeout().as_mut().as_pin_mut() {
2509 if let Poll::Ready(()) = delay.poll(cx) {
2510 return Poll::Ready(Err(
2511 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2512 ));
2513 }
2514 }
2515
2516 if let Some(delay) = self.as_mut().read_timeout().as_mut().as_pin_mut() {
2517 if let Poll::Ready(()) = delay.poll(cx) {
2518 return Poll::Ready(Err(
2519 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2520 ));
2521 }
2522 }
2523
2524 loop {
2525 let res = match self.as_mut().in_flight().get_mut() {
2526 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
2527 Poll::Ready(Err(e)) => {
2528 #[cfg(feature = "http2")]
2529 if self.as_mut().retry_error(&e) {
2530 continue;
2531 }
2532 return Poll::Ready(Err(
2533 crate::error::request(e).with_url(self.url.clone())
2534 ));
2535 }
2536 Poll::Ready(Ok(res)) => res,
2537 Poll::Pending => return Poll::Pending,
2538 },
2539 #[cfg(feature = "http3")]
2540 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
2541 Poll::Ready(Err(e)) => {
2542 if self.as_mut().retry_error(&e) {
2543 continue;
2544 }
2545 return Poll::Ready(Err(
2546 crate::error::request(e).with_url(self.url.clone())
2547 ));
2548 }
2549 Poll::Ready(Ok(res)) => res,
2550 Poll::Pending => return Poll::Pending,
2551 },
2552 };
2553
2554 #[cfg(feature = "cookies")]
2555 {
2556 if let Some(ref cookie_store) = self.client.cookie_store {
2557 let mut cookies =
2558 cookie::extract_response_cookie_headers(&res.headers()).peekable();
2559 if cookies.peek().is_some() {
2560 cookie_store.set_cookies(&mut cookies, &self.url);
2561 }
2562 }
2563 }
2564 let should_redirect = match res.status() {
2565 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
2566 self.body = None;
2567 for header in &[
2568 TRANSFER_ENCODING,
2569 CONTENT_ENCODING,
2570 CONTENT_TYPE,
2571 CONTENT_LENGTH,
2572 ] {
2573 self.headers.remove(header);
2574 }
2575
2576 match self.method {
2577 Method::GET | Method::HEAD => {}
2578 _ => {
2579 self.method = Method::GET;
2580 }
2581 }
2582 true
2583 }
2584 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
2585 match self.body {
2586 Some(Some(_)) | None => true,
2587 Some(None) => false,
2588 }
2589 }
2590 _ => false,
2591 };
2592 if should_redirect {
2593 let loc = res.headers().get(LOCATION).and_then(|val| {
2594 let loc = (|| -> Option<Url> {
2595 self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
2599 })();
2600
2601 let loc = loc.and_then(|url| {
2605 if try_uri(&url).is_ok() {
2606 Some(url)
2607 } else {
2608 None
2609 }
2610 });
2611
2612 if loc.is_none() {
2613 debug!("Location header had invalid URI: {val:?}");
2614 }
2615 loc
2616 });
2617 if let Some(loc) = loc {
2618 if self.client.referer {
2619 if let Some(referer) = make_referer(&loc, &self.url) {
2620 self.headers.insert(REFERER, referer);
2621 }
2622 }
2623 let url = self.url.clone();
2624 self.as_mut().urls().push(url);
2625 let action = self
2626 .client
2627 .redirect_policy
2628 .check(res.status(), &loc, &self.urls);
2629
2630 match action {
2631 redirect::ActionKind::Follow => {
2632 debug!("redirecting '{}' to '{}'", self.url, loc);
2633
2634 if loc.scheme() != "http" && loc.scheme() != "https" {
2635 return Poll::Ready(Err(error::url_bad_scheme(loc)));
2636 }
2637
2638 if self.client.https_only && loc.scheme() != "https" {
2639 return Poll::Ready(Err(error::redirect(
2640 error::url_bad_scheme(loc.clone()),
2641 loc,
2642 )));
2643 }
2644
2645 self.url = loc;
2646 let mut headers =
2647 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
2648
2649 remove_sensitive_headers(&mut headers, &self.url, &self.urls);
2650 let uri = try_uri(&self.url)?;
2651 let body = match self.body {
2652 Some(Some(ref body)) => Body::reusable(body.clone()),
2653 _ => Body::empty(),
2654 };
2655
2656 #[cfg(feature = "cookies")]
2658 {
2659 if let Some(ref cookie_store) = self.client.cookie_store {
2660 add_cookie_header(&mut headers, &**cookie_store, &self.url);
2661 }
2662 }
2663
2664 *self.as_mut().in_flight().get_mut() =
2665 match *self.as_mut().in_flight().as_ref() {
2666 #[cfg(feature = "http3")]
2667 ResponseFuture::H3(_) => {
2668 let mut req = hyper::Request::builder()
2669 .method(self.method.clone())
2670 .uri(uri.clone())
2671 .body(body)
2672 .expect("valid request parts");
2673 *req.headers_mut() = headers.clone();
2674 std::mem::swap(self.as_mut().headers(), &mut headers);
2675 ResponseFuture::H3(self.client.h3_client
2676 .as_ref()
2677 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2678 .request(req))
2679 }
2680 _ => {
2681 let mut req = hyper::Request::builder()
2682 .method(self.method.clone())
2683 .uri(uri.clone())
2684 .body(body)
2685 .expect("valid request parts");
2686 *req.headers_mut() = headers.clone();
2687 std::mem::swap(self.as_mut().headers(), &mut headers);
2688 ResponseFuture::Default(self.client.hyper.request(req))
2689 }
2690 };
2691
2692 continue;
2693 }
2694 redirect::ActionKind::Stop => {
2695 debug!("redirect policy disallowed redirection to '{loc}'");
2696 }
2697 redirect::ActionKind::Error(err) => {
2698 return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
2699 }
2700 }
2701 }
2702 }
2703
2704 let res = Response::new(
2705 res,
2706 self.url.clone(),
2707 self.client.accepts,
2708 self.total_timeout.take(),
2709 self.read_timeout,
2710 );
2711 return Poll::Ready(Ok(res));
2712 }
2713 }
2714}
2715
2716impl fmt::Debug for Pending {
2717 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2718 match self.inner {
2719 PendingInner::Request(ref req) => f
2720 .debug_struct("Pending")
2721 .field("method", &req.method)
2722 .field("url", &req.url)
2723 .finish(),
2724 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
2725 }
2726 }
2727}
2728
2729fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
2730 if next.scheme() == "http" && previous.scheme() == "https" {
2731 return None;
2732 }
2733
2734 let mut referer = previous.clone();
2735 let _ = referer.set_username("");
2736 let _ = referer.set_password(None);
2737 referer.set_fragment(None);
2738 referer.as_str().parse().ok()
2739}
2740
2741#[cfg(feature = "cookies")]
2742fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
2743 if let Some(header) = cookie_store.cookies(url) {
2744 headers.insert(crate::header::COOKIE, header);
2745 }
2746}
2747
2748#[cfg(test)]
2749mod tests {
2750 #[tokio::test]
2751 async fn execute_request_rejects_invalid_urls() {
2752 let url_str = "hxxps://www.rust-lang.org/";
2753 let url = url::Url::parse(url_str).unwrap();
2754 let result = crate::get(url.clone()).await;
2755
2756 assert!(result.is_err());
2757 let err = result.err().unwrap();
2758 assert!(err.is_builder());
2759 assert_eq!(url_str, err.url().unwrap().as_str());
2760 }
2761
2762 #[tokio::test]
2764 async fn execute_request_rejects_invalid_hostname() {
2765 let url_str = "https://{{hostname}}/";
2766 let url = url::Url::parse(url_str).unwrap();
2767 let result = crate::get(url.clone()).await;
2768
2769 assert!(result.is_err());
2770 let err = result.err().unwrap();
2771 assert!(err.is_builder());
2772 assert_eq!(url_str, err.url().unwrap().as_str());
2773 }
2774}