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::client::{HttpConnector, ResponseFuture as HyperResponseFuture};
17#[cfg(feature = "native-tls-crate")]
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, trace};
50#[cfg(feature = "http3")]
51use quinn::TransportConfig;
52#[cfg(feature = "http3")]
53use quinn::VarInt;
54
55#[derive(Clone)]
69pub struct Client {
70 inner: Arc<ClientRef>,
71}
72
73#[must_use]
75pub struct ClientBuilder {
76 config: Config,
77}
78
79enum HttpVersionPref {
80 Http1,
81 Http2,
82 #[cfg(feature = "http3")]
83 Http3,
84 All,
85}
86
87struct Config {
88 accepts: Accepts,
90 headers: HeaderMap,
91 #[cfg(feature = "native-tls")]
92 hostname_verification: bool,
93 #[cfg(feature = "__tls")]
94 certs_verification: bool,
95 #[cfg(feature = "__tls")]
96 tls_sni: bool,
97 connect_timeout: Option<Duration>,
98 connection_verbose: bool,
99 pool_idle_timeout: Option<Duration>,
100 pool_max_idle_per_host: usize,
101 tcp_keepalive: Option<Duration>,
102 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
103 identity: Option<Identity>,
104 proxies: Vec<Proxy>,
105 auto_sys_proxy: bool,
106 redirect_policy: redirect::Policy,
107 referer: bool,
108 timeout: Option<Duration>,
109 #[cfg(feature = "__tls")]
110 root_certs: Vec<Certificate>,
111 #[cfg(feature = "__tls")]
112 tls_built_in_root_certs: bool,
113 #[cfg(feature = "__tls")]
114 min_tls_version: Option<tls::Version>,
115 #[cfg(feature = "__tls")]
116 max_tls_version: Option<tls::Version>,
117 #[cfg(feature = "__tls")]
118 tls_info: bool,
119 #[cfg(feature = "__tls")]
120 tls: TlsBackend,
121 http_version_pref: HttpVersionPref,
122 http09_responses: bool,
123 http1_title_case_headers: bool,
124 http1_allow_obsolete_multiline_headers_in_responses: bool,
125 http1_ignore_invalid_headers_in_responses: bool,
126 http1_allow_spaces_after_header_name_in_responses: bool,
127 http2_initial_stream_window_size: Option<u32>,
128 http2_initial_connection_window_size: Option<u32>,
129 http2_adaptive_window: bool,
130 http2_max_frame_size: Option<u32>,
131 http2_keep_alive_interval: Option<Duration>,
132 http2_keep_alive_timeout: Option<Duration>,
133 http2_keep_alive_while_idle: bool,
134 local_address: Option<IpAddr>,
135 nodelay: bool,
136 #[cfg(feature = "cookies")]
137 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
138 hickory_dns: bool,
139 error: Option<crate::Error>,
140 https_only: bool,
141 #[cfg(feature = "http3")]
142 tls_enable_early_data: bool,
143 #[cfg(feature = "http3")]
144 quic_max_idle_timeout: Option<Duration>,
145 #[cfg(feature = "http3")]
146 quic_stream_receive_window: Option<VarInt>,
147 #[cfg(feature = "http3")]
148 quic_receive_window: Option<VarInt>,
149 #[cfg(feature = "http3")]
150 quic_send_window: Option<u64>,
151 dns_overrides: HashMap<String, Vec<SocketAddr>>,
152 dns_resolver: Option<Arc<dyn Resolve>>,
153}
154
155impl Default for ClientBuilder {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161impl ClientBuilder {
162 pub fn new() -> ClientBuilder {
166 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
167 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
168
169 ClientBuilder {
170 config: Config {
171 error: None,
172 accepts: Accepts::default(),
173 headers,
174 #[cfg(feature = "native-tls")]
175 hostname_verification: true,
176 #[cfg(feature = "__tls")]
177 certs_verification: true,
178 #[cfg(feature = "__tls")]
179 tls_sni: true,
180 connect_timeout: None,
181 connection_verbose: false,
182 pool_idle_timeout: Some(Duration::from_secs(90)),
183 pool_max_idle_per_host: std::usize::MAX,
184 tcp_keepalive: None, proxies: Vec::new(),
188 auto_sys_proxy: true,
189 redirect_policy: redirect::Policy::default(),
190 referer: true,
191 timeout: None,
192 #[cfg(feature = "__tls")]
193 root_certs: Vec::new(),
194 #[cfg(feature = "__tls")]
195 tls_built_in_root_certs: true,
196 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
197 identity: None,
198 #[cfg(feature = "__tls")]
199 min_tls_version: None,
200 #[cfg(feature = "__tls")]
201 max_tls_version: None,
202 #[cfg(feature = "__tls")]
203 tls_info: false,
204 #[cfg(feature = "__tls")]
205 tls: TlsBackend::default(),
206 http_version_pref: HttpVersionPref::All,
207 http09_responses: false,
208 http1_title_case_headers: false,
209 http1_allow_obsolete_multiline_headers_in_responses: false,
210 http1_ignore_invalid_headers_in_responses: false,
211 http1_allow_spaces_after_header_name_in_responses: false,
212 http2_initial_stream_window_size: None,
213 http2_initial_connection_window_size: None,
214 http2_adaptive_window: false,
215 http2_max_frame_size: None,
216 http2_keep_alive_interval: None,
217 http2_keep_alive_timeout: None,
218 http2_keep_alive_while_idle: false,
219 local_address: None,
220 nodelay: true,
221 hickory_dns: cfg!(feature = "hickory-dns"),
222 #[cfg(feature = "cookies")]
223 cookie_store: None,
224 https_only: false,
225 dns_overrides: HashMap::new(),
226 #[cfg(feature = "http3")]
227 tls_enable_early_data: false,
228 #[cfg(feature = "http3")]
229 quic_max_idle_timeout: None,
230 #[cfg(feature = "http3")]
231 quic_stream_receive_window: None,
232 #[cfg(feature = "http3")]
233 quic_receive_window: None,
234 #[cfg(feature = "http3")]
235 quic_send_window: None,
236 dns_resolver: None,
237 },
238 }
239 }
240
241 pub fn build(self) -> crate::Result<Client> {
248 let config = self.config;
249
250 if let Some(err) = config.error {
251 return Err(err);
252 }
253
254 let mut proxies = config.proxies;
255 if config.auto_sys_proxy {
256 proxies.push(Proxy::system());
257 }
258 let proxies = Arc::new(proxies);
259
260 #[allow(unused)]
261 #[cfg(feature = "http3")]
262 let mut h3_connector = None;
263
264 let mut connector = {
265 #[cfg(feature = "__tls")]
266 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
267 headers.get(USER_AGENT).cloned()
268 }
269
270 let mut resolver: Arc<dyn Resolve> = match config.hickory_dns {
271 false => Arc::new(GaiResolver::new()),
272 #[cfg(feature = "hickory-dns")]
273 true => Arc::new(HickoryDnsResolver::default()),
274 #[cfg(not(feature = "hickory-dns"))]
275 true => unreachable!("hickory-dns shouldn't be enabled unless the feature is"),
276 };
277 if let Some(dns_resolver) = config.dns_resolver {
278 resolver = dns_resolver;
279 }
280 if !config.dns_overrides.is_empty() {
281 resolver = Arc::new(DnsResolverWithOverrides::new(
282 resolver,
283 config.dns_overrides,
284 ));
285 }
286 let mut http = HttpConnector::new_with_resolver(DynResolver::new(resolver.clone()));
287 http.set_connect_timeout(config.connect_timeout);
288
289 #[cfg(all(feature = "http3", feature = "__rustls"))]
290 let build_h3_connector =
291 |resolver,
292 tls,
293 quic_max_idle_timeout: Option<Duration>,
294 quic_stream_receive_window,
295 quic_receive_window,
296 quic_send_window,
297 local_address,
298 http_version_pref: &HttpVersionPref| {
299 let mut transport_config = TransportConfig::default();
300
301 if let Some(max_idle_timeout) = quic_max_idle_timeout {
302 transport_config.max_idle_timeout(Some(
303 max_idle_timeout.try_into().map_err(error::builder)?,
304 ));
305 }
306
307 if let Some(stream_receive_window) = quic_stream_receive_window {
308 transport_config.stream_receive_window(stream_receive_window);
309 }
310
311 if let Some(receive_window) = quic_receive_window {
312 transport_config.receive_window(receive_window);
313 }
314
315 if let Some(send_window) = quic_send_window {
316 transport_config.send_window(send_window);
317 }
318
319 let res = H3Connector::new(
320 DynResolver::new(resolver),
321 tls,
322 local_address,
323 transport_config,
324 );
325
326 match res {
327 Ok(connector) => Ok(Some(connector)),
328 Err(err) => {
329 if let HttpVersionPref::Http3 = http_version_pref {
330 Err(error::builder(err))
331 } else {
332 Ok(None)
333 }
334 }
335 }
336 };
337
338 #[cfg(feature = "__tls")]
339 match config.tls {
340 #[cfg(feature = "default-tls")]
341 TlsBackend::Default => {
342 let mut tls = TlsConnector::builder();
343
344 #[cfg(all(feature = "native-tls-alpn", not(feature = "http3")))]
345 {
346 match config.http_version_pref {
347 HttpVersionPref::Http1 => {
348 tls.request_alpns(&["http/1.1"]);
349 }
350 HttpVersionPref::Http2 => {
351 tls.request_alpns(&["h2"]);
352 }
353 HttpVersionPref::All => {
354 tls.request_alpns(&["h2", "http/1.1"]);
355 }
356 }
357 }
358
359 #[cfg(feature = "native-tls")]
360 {
361 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
362 }
363
364 tls.danger_accept_invalid_certs(!config.certs_verification);
365
366 tls.use_sni(config.tls_sni);
367
368 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
369
370 for cert in config.root_certs {
371 cert.add_to_native_tls(&mut tls);
372 }
373
374 #[cfg(feature = "native-tls")]
375 {
376 if let Some(id) = config.identity {
377 id.add_to_native_tls(&mut tls)?;
378 }
379 }
380 #[cfg(all(feature = "__rustls", not(feature = "native-tls")))]
381 {
382 if let Some(_id) = config.identity {
384 return Err(crate::error::builder("incompatible TLS identity type"));
385 }
386 }
387
388 if let Some(min_tls_version) = config.min_tls_version {
389 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
390 crate::error::builder("invalid minimum TLS version for backend")
394 })?;
395 tls.min_protocol_version(Some(protocol));
396 }
397
398 if let Some(max_tls_version) = config.max_tls_version {
399 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
400 crate::error::builder("invalid maximum TLS version for backend")
405 })?;
406 tls.max_protocol_version(Some(protocol));
407 }
408
409 Connector::new_default_tls(
410 http,
411 tls,
412 proxies.clone(),
413 user_agent(&config.headers),
414 config.local_address,
415 config.nodelay,
416 config.tls_info,
417 )?
418 }
419 #[cfg(feature = "native-tls")]
420 TlsBackend::BuiltNativeTls(conn) => Connector::from_built_default_tls(
421 http,
422 conn,
423 proxies.clone(),
424 user_agent(&config.headers),
425 config.local_address,
426 config.nodelay,
427 config.tls_info,
428 ),
429 #[cfg(feature = "__rustls")]
430 TlsBackend::BuiltRustls(conn) => {
431 #[cfg(feature = "http3")]
432 {
433 h3_connector = build_h3_connector(
434 resolver,
435 conn.clone(),
436 config.quic_max_idle_timeout,
437 config.quic_stream_receive_window,
438 config.quic_receive_window,
439 config.quic_send_window,
440 config.local_address,
441 &config.http_version_pref,
442 )?;
443 }
444
445 Connector::new_rustls_tls(
446 http,
447 conn,
448 proxies.clone(),
449 user_agent(&config.headers),
450 config.local_address,
451 config.nodelay,
452 config.tls_info,
453 )
454 }
455 #[cfg(feature = "__rustls")]
456 TlsBackend::Rustls => {
457 use crate::tls::NoVerifier;
458
459 let mut root_cert_store = rustls::RootCertStore::empty();
461 for cert in config.root_certs {
462 cert.add_to_rustls(&mut root_cert_store)?;
463 }
464
465 #[cfg(feature = "rustls-tls-webpki-roots")]
466 if config.tls_built_in_root_certs {
467 use rustls::OwnedTrustAnchor;
468
469 let trust_anchors =
470 webpki_roots::TLS_SERVER_ROOTS.iter().map(|trust_anchor| {
471 OwnedTrustAnchor::from_subject_spki_name_constraints(
472 trust_anchor.subject,
473 trust_anchor.spki,
474 trust_anchor.name_constraints,
475 )
476 });
477
478 root_cert_store.add_trust_anchors(trust_anchors);
479 }
480
481 #[cfg(feature = "rustls-tls-native-roots")]
482 if config.tls_built_in_root_certs {
483 let mut valid_count = 0;
484 let mut invalid_count = 0;
485 for cert in rustls_native_certs::load_native_certs()
486 .map_err(crate::error::builder)?
487 {
488 let cert = rustls::Certificate(cert.0);
489 match root_cert_store.add(&cert) {
493 Ok(_) => valid_count += 1,
494 Err(err) => {
495 invalid_count += 1;
496 log::warn!(
497 "rustls failed to parse DER certificate {err:?} {cert:?}"
498 );
499 }
500 }
501 }
502 if valid_count == 0 && invalid_count > 0 {
503 return Err(crate::error::builder(
504 "zero valid certificates found in native root store",
505 ));
506 }
507 }
508
509 let mut versions = rustls::ALL_VERSIONS.to_vec();
511
512 if let Some(min_tls_version) = config.min_tls_version {
513 versions.retain(|&supported_version| {
514 match tls::Version::from_rustls(supported_version.version) {
515 Some(version) => version >= min_tls_version,
516 None => true,
519 }
520 });
521 }
522
523 if let Some(max_tls_version) = config.max_tls_version {
524 versions.retain(|&supported_version| {
525 match tls::Version::from_rustls(supported_version.version) {
526 Some(version) => version <= max_tls_version,
527 None => false,
528 }
529 });
530 }
531
532 let config_builder = rustls::ClientConfig::builder()
534 .with_safe_default_cipher_suites()
535 .with_safe_default_kx_groups()
536 .with_protocol_versions(&versions)
537 .map_err(crate::error::builder)?
538 .with_root_certificates(root_cert_store);
539
540 let mut tls = if let Some(id) = config.identity {
542 id.add_to_rustls(config_builder)?
543 } else {
544 config_builder.with_no_client_auth()
545 };
546
547 if !config.certs_verification {
549 tls.dangerous()
550 .set_certificate_verifier(Arc::new(NoVerifier));
551 }
552
553 tls.enable_sni = config.tls_sni;
554
555 match config.http_version_pref {
557 HttpVersionPref::Http1 => {
558 tls.alpn_protocols = vec!["http/1.1".into()];
559 }
560 HttpVersionPref::Http2 => {
561 tls.alpn_protocols = vec!["h2".into()];
562 }
563 #[cfg(feature = "http3")]
564 HttpVersionPref::Http3 => {
565 tls.alpn_protocols = vec!["h3".into()];
566 }
567 HttpVersionPref::All => {
568 tls.alpn_protocols = vec!["h2".into(), "http/1.1".into()];
569 }
570 }
571
572 #[cfg(feature = "http3")]
573 {
574 tls.enable_early_data = config.tls_enable_early_data;
575
576 h3_connector = build_h3_connector(
577 resolver,
578 tls.clone(),
579 config.quic_max_idle_timeout,
580 config.quic_stream_receive_window,
581 config.quic_receive_window,
582 config.quic_send_window,
583 config.local_address,
584 &config.http_version_pref,
585 )?;
586 }
587
588 Connector::new_rustls_tls(
589 http,
590 tls,
591 proxies.clone(),
592 user_agent(&config.headers),
593 config.local_address,
594 config.nodelay,
595 config.tls_info,
596 )
597 }
598 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
599 TlsBackend::UnknownPreconfigured => {
600 return Err(crate::error::builder(
601 "Unknown TLS backend passed to `use_preconfigured_tls`",
602 ));
603 }
604 }
605
606 #[cfg(not(feature = "__tls"))]
607 Connector::new(http, proxies.clone(), config.local_address, config.nodelay)
608 };
609
610 connector.set_timeout(config.connect_timeout);
611 connector.set_verbose(config.connection_verbose);
612
613 let mut builder = hyper::Client::builder();
614 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
615 builder.http2_only(true);
616 }
617
618 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size {
619 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
620 }
621 if let Some(http2_initial_connection_window_size) =
622 config.http2_initial_connection_window_size
623 {
624 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
625 }
626 if config.http2_adaptive_window {
627 builder.http2_adaptive_window(true);
628 }
629 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
630 builder.http2_max_frame_size(http2_max_frame_size);
631 }
632 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
633 builder.http2_keep_alive_interval(http2_keep_alive_interval);
634 }
635 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
636 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
637 }
638 if config.http2_keep_alive_while_idle {
639 builder.http2_keep_alive_while_idle(true);
640 }
641
642 builder.pool_idle_timeout(config.pool_idle_timeout);
643 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
644 connector.set_keepalive(config.tcp_keepalive);
645
646 if config.http09_responses {
647 builder.http09_responses(true);
648 }
649
650 if config.http1_title_case_headers {
651 builder.http1_title_case_headers(true);
652 }
653
654 if config.http1_allow_obsolete_multiline_headers_in_responses {
655 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
656 }
657
658 if config.http1_ignore_invalid_headers_in_responses {
659 builder.http1_ignore_invalid_headers_in_responses(true);
660 }
661
662 if config.http1_allow_spaces_after_header_name_in_responses {
663 builder.http1_allow_spaces_after_header_name_in_responses(true);
664 }
665
666 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
667
668 Ok(Client {
669 inner: Arc::new(ClientRef {
670 accepts: config.accepts,
671 #[cfg(feature = "cookies")]
672 cookie_store: config.cookie_store,
673 #[cfg(feature = "http3")]
676 h3_client: match h3_connector {
677 Some(h3_connector) => {
678 Some(H3Client::new(h3_connector, config.pool_idle_timeout))
679 }
680 None => None,
681 },
682 hyper: builder.build(connector),
683 headers: config.headers,
684 redirect_policy: config.redirect_policy,
685 referer: config.referer,
686 request_timeout: config.timeout,
687 proxies,
688 proxies_maybe_http_auth,
689 https_only: config.https_only,
690 }),
691 })
692 }
693
694 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
717 where
718 V: TryInto<HeaderValue>,
719 V::Error: Into<http::Error>,
720 {
721 match value.try_into() {
722 Ok(value) => {
723 self.config.headers.insert(USER_AGENT, value);
724 }
725 Err(e) => {
726 self.config.error = Some(crate::error::builder(e.into()));
727 }
728 };
729 self
730 }
731 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
776 for (key, value) in headers.iter() {
777 self.config.headers.insert(key, value.clone());
778 }
779 self
780 }
781
782 #[cfg(feature = "cookies")]
797 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
798 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
799 if enable {
800 self.cookie_provider(Arc::new(cookie::Jar::default()))
801 } else {
802 self.config.cookie_store = None;
803 self
804 }
805 }
806
807 #[cfg(feature = "cookies")]
821 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
822 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
823 mut self,
824 cookie_store: Arc<C>,
825 ) -> ClientBuilder {
826 self.config.cookie_store = Some(cookie_store as _);
827 self
828 }
829
830 #[cfg(feature = "gzip")]
847 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
848 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
849 self.config.accepts.gzip = enable;
850 self
851 }
852
853 #[cfg(feature = "brotli")]
870 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
871 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
872 self.config.accepts.brotli = enable;
873 self
874 }
875
876 #[cfg(feature = "deflate")]
893 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
894 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
895 self.config.accepts.deflate = enable;
896 self
897 }
898
899 pub fn no_gzip(self) -> ClientBuilder {
905 #[cfg(feature = "gzip")]
906 {
907 self.gzip(false)
908 }
909
910 #[cfg(not(feature = "gzip"))]
911 {
912 self
913 }
914 }
915
916 pub fn no_brotli(self) -> ClientBuilder {
922 #[cfg(feature = "brotli")]
923 {
924 self.brotli(false)
925 }
926
927 #[cfg(not(feature = "brotli"))]
928 {
929 self
930 }
931 }
932
933 pub fn no_deflate(self) -> ClientBuilder {
939 #[cfg(feature = "deflate")]
940 {
941 self.deflate(false)
942 }
943
944 #[cfg(not(feature = "deflate"))]
945 {
946 self
947 }
948 }
949
950 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
956 self.config.redirect_policy = policy;
957 self
958 }
959
960 pub fn referer(mut self, enable: bool) -> ClientBuilder {
964 self.config.referer = enable;
965 self
966 }
967
968 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
976 self.config.proxies.push(proxy);
977 self.config.auto_sys_proxy = false;
978 self
979 }
980
981 pub fn no_proxy(mut self) -> ClientBuilder {
989 self.config.proxies.clear();
990 self.config.auto_sys_proxy = false;
991 self
992 }
993
994 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
1003 self.config.timeout = Some(timeout);
1004 self
1005 }
1006
1007 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1016 self.config.connect_timeout = Some(timeout);
1017 self
1018 }
1019
1020 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
1027 self.config.connection_verbose = verbose;
1028 self
1029 }
1030
1031 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
1039 where
1040 D: Into<Option<Duration>>,
1041 {
1042 self.config.pool_idle_timeout = val.into();
1043 self
1044 }
1045
1046 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
1048 self.config.pool_max_idle_per_host = max;
1049 self
1050 }
1051
1052 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
1054 self.config.http1_title_case_headers = true;
1055 self
1056 }
1057
1058 pub fn http1_allow_obsolete_multiline_headers_in_responses(
1064 mut self,
1065 value: bool,
1066 ) -> ClientBuilder {
1067 self.config
1068 .http1_allow_obsolete_multiline_headers_in_responses = value;
1069 self
1070 }
1071
1072 pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder {
1074 self.config.http1_ignore_invalid_headers_in_responses = value;
1075 self
1076 }
1077
1078 pub fn http1_allow_spaces_after_header_name_in_responses(
1084 mut self,
1085 value: bool,
1086 ) -> ClientBuilder {
1087 self.config
1088 .http1_allow_spaces_after_header_name_in_responses = value;
1089 self
1090 }
1091
1092 pub fn http1_only(mut self) -> ClientBuilder {
1094 self.config.http_version_pref = HttpVersionPref::Http1;
1095 self
1096 }
1097
1098 pub fn http09_responses(mut self) -> ClientBuilder {
1100 self.config.http09_responses = true;
1101 self
1102 }
1103
1104 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
1106 self.config.http_version_pref = HttpVersionPref::Http2;
1107 self
1108 }
1109
1110 #[cfg(feature = "http3")]
1112 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1113 pub fn http3_prior_knowledge(mut self) -> ClientBuilder {
1114 self.config.http_version_pref = HttpVersionPref::Http3;
1115 self
1116 }
1117
1118 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1122 self.config.http2_initial_stream_window_size = sz.into();
1123 self
1124 }
1125
1126 pub fn http2_initial_connection_window_size(
1130 mut self,
1131 sz: impl Into<Option<u32>>,
1132 ) -> ClientBuilder {
1133 self.config.http2_initial_connection_window_size = sz.into();
1134 self
1135 }
1136
1137 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
1142 self.config.http2_adaptive_window = enabled;
1143 self
1144 }
1145
1146 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
1150 self.config.http2_max_frame_size = sz.into();
1151 self
1152 }
1153
1154 pub fn http2_keep_alive_interval(
1159 mut self,
1160 interval: impl Into<Option<Duration>>,
1161 ) -> ClientBuilder {
1162 self.config.http2_keep_alive_interval = interval.into();
1163 self
1164 }
1165
1166 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
1172 self.config.http2_keep_alive_timeout = Some(timeout);
1173 self
1174 }
1175
1176 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
1183 self.config.http2_keep_alive_while_idle = enabled;
1184 self
1185 }
1186
1187 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
1193 self.config.nodelay = enabled;
1194 self
1195 }
1196
1197 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1209 where
1210 T: Into<Option<IpAddr>>,
1211 {
1212 self.config.local_address = addr.into();
1213 self
1214 }
1215
1216 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1220 where
1221 D: Into<Option<Duration>>,
1222 {
1223 self.config.tcp_keepalive = val.into();
1224 self
1225 }
1226
1227 #[cfg(feature = "__tls")]
1239 #[cfg_attr(
1240 docsrs,
1241 doc(cfg(any(
1242 feature = "default-tls",
1243 feature = "native-tls",
1244 feature = "rustls-tls"
1245 )))
1246 )]
1247 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1248 self.config.root_certs.push(cert);
1249 self
1250 }
1251
1252 #[cfg(feature = "__tls")]
1261 #[cfg_attr(
1262 docsrs,
1263 doc(cfg(any(
1264 feature = "default-tls",
1265 feature = "native-tls",
1266 feature = "rustls-tls"
1267 )))
1268 )]
1269 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1270 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1271 self
1272 }
1273
1274 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1281 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1282 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1283 self.config.identity = Some(identity);
1284 self
1285 }
1286
1287 #[cfg(feature = "native-tls")]
1302 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1303 pub fn danger_accept_invalid_hostnames(
1304 mut self,
1305 accept_invalid_hostname: bool,
1306 ) -> ClientBuilder {
1307 self.config.hostname_verification = !accept_invalid_hostname;
1308 self
1309 }
1310
1311 #[cfg(feature = "__tls")]
1328 #[cfg_attr(
1329 docsrs,
1330 doc(cfg(any(
1331 feature = "default-tls",
1332 feature = "native-tls",
1333 feature = "rustls-tls"
1334 )))
1335 )]
1336 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1337 self.config.certs_verification = !accept_invalid_certs;
1338 self
1339 }
1340
1341 #[cfg(feature = "__tls")]
1350 #[cfg_attr(
1351 docsrs,
1352 doc(cfg(any(
1353 feature = "default-tls",
1354 feature = "native-tls",
1355 feature = "rustls-tls"
1356 )))
1357 )]
1358 pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
1359 self.config.tls_sni = tls_sni;
1360 self
1361 }
1362
1363 #[cfg(feature = "__tls")]
1379 #[cfg_attr(
1380 docsrs,
1381 doc(cfg(any(
1382 feature = "default-tls",
1383 feature = "native-tls",
1384 feature = "rustls-tls"
1385 )))
1386 )]
1387 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1388 self.config.min_tls_version = Some(version);
1389 self
1390 }
1391
1392 #[cfg(feature = "__tls")]
1408 #[cfg_attr(
1409 docsrs,
1410 doc(cfg(any(
1411 feature = "default-tls",
1412 feature = "native-tls",
1413 feature = "rustls-tls"
1414 )))
1415 )]
1416 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1417 self.config.max_tls_version = Some(version);
1418 self
1419 }
1420
1421 #[cfg(feature = "native-tls")]
1430 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1431 pub fn use_native_tls(mut self) -> ClientBuilder {
1432 self.config.tls = TlsBackend::Default;
1433 self
1434 }
1435
1436 #[cfg(feature = "__rustls")]
1445 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1446 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1447 self.config.tls = TlsBackend::Rustls;
1448 self
1449 }
1450
1451 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
1470 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1471 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
1472 let mut tls = Some(tls);
1473 #[cfg(feature = "native-tls")]
1474 {
1475 if let Some(conn) =
1476 (&mut tls as &mut dyn Any).downcast_mut::<Option<native_tls_crate::TlsConnector>>()
1477 {
1478 let tls = conn.take().expect("is definitely Some");
1479 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
1480 self.config.tls = tls;
1481 return self;
1482 }
1483 }
1484 #[cfg(feature = "__rustls")]
1485 {
1486 if let Some(conn) =
1487 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
1488 {
1489 let tls = conn.take().expect("is definitely Some");
1490 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
1491 self.config.tls = tls;
1492 return self;
1493 }
1494 }
1495
1496 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
1498 self
1499 }
1500
1501 #[cfg(feature = "__tls")]
1508 #[cfg_attr(
1509 docsrs,
1510 doc(cfg(any(
1511 feature = "default-tls",
1512 feature = "native-tls",
1513 feature = "rustls-tls"
1514 )))
1515 )]
1516 pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
1517 self.config.tls_info = tls_info;
1518 self
1519 }
1520
1521 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
1525 self.config.https_only = enabled;
1526 self
1527 }
1528
1529 #[cfg(feature = "hickory-dns")]
1538 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1539 #[deprecated(note = "use `hickory_dns` instead")]
1540 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
1541 self.config.hickory_dns = enable;
1542 self
1543 }
1544
1545 #[cfg(feature = "hickory-dns")]
1554 #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
1555 pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder {
1556 self.config.hickory_dns = enable;
1557 self
1558 }
1559
1560 #[deprecated(note = "use `no_hickory_dns` instead")]
1566 pub fn no_trust_dns(self) -> ClientBuilder {
1567 #[cfg(feature = "hickory-dns")]
1568 {
1569 self.hickory_dns(false)
1570 }
1571
1572 #[cfg(not(feature = "hickory-dns"))]
1573 {
1574 self
1575 }
1576 }
1577
1578 pub fn no_hickory_dns(self) -> ClientBuilder {
1584 #[cfg(feature = "hickory-dns")]
1585 {
1586 self.hickory_dns(false)
1587 }
1588
1589 #[cfg(not(feature = "hickory-dns"))]
1590 {
1591 self
1592 }
1593 }
1594
1595 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
1604 self.resolve_to_addrs(domain, &[addr])
1605 }
1606
1607 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
1616 self.config
1617 .dns_overrides
1618 .insert(domain.to_string(), addrs.to_vec());
1619 self
1620 }
1621
1622 pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> ClientBuilder {
1628 self.config.dns_resolver = Some(resolver as _);
1629 self
1630 }
1631
1632 #[cfg(feature = "http3")]
1637 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1638 pub fn set_tls_enable_early_data(mut self, enabled: bool) -> ClientBuilder {
1639 self.config.tls_enable_early_data = enabled;
1640 self
1641 }
1642
1643 #[cfg(feature = "http3")]
1649 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1650 pub fn set_quic_max_idle_timeout(mut self, value: Duration) -> ClientBuilder {
1651 self.config.quic_max_idle_timeout = Some(value);
1652 self
1653 }
1654
1655 #[cfg(feature = "http3")]
1662 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1663 pub fn set_quic_stream_receive_window(mut self, value: VarInt) -> ClientBuilder {
1664 self.config.quic_stream_receive_window = Some(value);
1665 self
1666 }
1667
1668 #[cfg(feature = "http3")]
1675 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1676 pub fn set_quic_receive_window(mut self, value: VarInt) -> ClientBuilder {
1677 self.config.quic_receive_window = Some(value);
1678 self
1679 }
1680
1681 #[cfg(feature = "http3")]
1687 #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))]
1688 pub fn set_quic_send_window(mut self, value: u64) -> ClientBuilder {
1689 self.config.quic_send_window = Some(value);
1690 self
1691 }
1692}
1693
1694type HyperClient = hyper::Client<Connector, super::body::ImplStream>;
1695
1696impl Default for Client {
1697 fn default() -> Self {
1698 Self::new()
1699 }
1700}
1701
1702impl Client {
1703 pub fn new() -> Client {
1713 ClientBuilder::new().build().expect("Client::new()")
1714 }
1715
1716 pub fn builder() -> ClientBuilder {
1720 ClientBuilder::new()
1721 }
1722
1723 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1729 self.request(Method::GET, url)
1730 }
1731
1732 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1738 self.request(Method::POST, url)
1739 }
1740
1741 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1747 self.request(Method::PUT, url)
1748 }
1749
1750 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1756 self.request(Method::PATCH, url)
1757 }
1758
1759 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1765 self.request(Method::DELETE, url)
1766 }
1767
1768 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1774 self.request(Method::HEAD, url)
1775 }
1776
1777 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
1786 let req = url.into_url().map(move |url| Request::new(method, url));
1787 RequestBuilder::new(self.clone(), req)
1788 }
1789
1790 pub fn execute(
1803 &self,
1804 request: Request,
1805 ) -> impl Future<Output = Result<Response, crate::Error>> {
1806 self.execute_request(request)
1807 }
1808
1809 pub(super) fn execute_request(&self, req: Request) -> Pending {
1810 let (method, url, mut headers, body, timeout, version) = req.pieces();
1811 if url.scheme() != "http" && url.scheme() != "https" {
1812 return Pending::new_err(error::url_bad_scheme(url));
1813 }
1814
1815 if self.inner.https_only && url.scheme() != "https" {
1817 return Pending::new_err(error::url_bad_scheme(url));
1818 }
1819
1820 for (key, value) in &self.inner.headers {
1823 if let Entry::Vacant(entry) = headers.entry(key) {
1824 entry.insert(value.clone());
1825 }
1826 }
1827
1828 #[cfg(feature = "cookies")]
1830 {
1831 if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
1832 if headers.get(crate::header::COOKIE).is_none() {
1833 add_cookie_header(&mut headers, &**cookie_store, &url);
1834 }
1835 }
1836 }
1837
1838 let accept_encoding = self.inner.accepts.as_str();
1839
1840 if let Some(accept_encoding) = accept_encoding {
1841 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
1842 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
1843 }
1844 }
1845
1846 let uri = match try_uri(&url) {
1847 Ok(uri) => uri,
1848 _ => return Pending::new_err(error::url_invalid_uri(url)),
1849 };
1850
1851 let (reusable, body) = match body {
1852 Some(body) => {
1853 let (reusable, body) = body.try_reuse();
1854 (Some(reusable), body)
1855 }
1856 None => (None, Body::empty()),
1857 };
1858
1859 self.proxy_auth(&uri, &mut headers);
1860
1861 let builder = hyper::Request::builder()
1862 .method(method.clone())
1863 .uri(uri)
1864 .version(version);
1865
1866 let in_flight = match version {
1867 #[cfg(feature = "http3")]
1868 http::Version::HTTP_3 if self.inner.h3_client.is_some() => {
1869 let mut req = builder.body(body).expect("valid request parts");
1870 *req.headers_mut() = headers.clone();
1871 ResponseFuture::H3(self.inner.h3_client.as_ref().unwrap().request(req))
1872 }
1873 _ => {
1874 let mut req = builder
1875 .body(body.into_stream())
1876 .expect("valid request parts");
1877 *req.headers_mut() = headers.clone();
1878 ResponseFuture::Default(self.inner.hyper.request(req))
1879 }
1880 };
1881
1882 let timeout = timeout
1883 .or(self.inner.request_timeout)
1884 .map(tokio::time::sleep)
1885 .map(Box::pin);
1886
1887 Pending {
1888 inner: PendingInner::Request(PendingRequest {
1889 method,
1890 url,
1891 headers,
1892 body: reusable,
1893
1894 urls: Vec::new(),
1895
1896 retry_count: 0,
1897
1898 client: self.inner.clone(),
1899
1900 in_flight,
1901 timeout,
1902 }),
1903 }
1904 }
1905
1906 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
1907 if !self.inner.proxies_maybe_http_auth {
1908 return;
1909 }
1910
1911 if dst.scheme() != Some(&Scheme::HTTP) {
1915 return;
1916 }
1917
1918 if headers.contains_key(PROXY_AUTHORIZATION) {
1919 return;
1920 }
1921
1922 for proxy in self.inner.proxies.iter() {
1923 if proxy.is_match(dst) {
1924 if let Some(header) = proxy.http_basic_auth(dst) {
1925 headers.insert(PROXY_AUTHORIZATION, header);
1926 }
1927
1928 break;
1929 }
1930 }
1931 }
1932}
1933
1934impl fmt::Debug for Client {
1935 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1936 let mut builder = f.debug_struct("Client");
1937 self.inner.fmt_fields(&mut builder);
1938 builder.finish()
1939 }
1940}
1941
1942impl tower_service::Service<Request> for Client {
1943 type Response = Response;
1944 type Error = crate::Error;
1945 type Future = Pending;
1946
1947 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1948 Poll::Ready(Ok(()))
1949 }
1950
1951 fn call(&mut self, req: Request) -> Self::Future {
1952 self.execute_request(req)
1953 }
1954}
1955
1956impl tower_service::Service<Request> for &'_ Client {
1957 type Response = Response;
1958 type Error = crate::Error;
1959 type Future = Pending;
1960
1961 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1962 Poll::Ready(Ok(()))
1963 }
1964
1965 fn call(&mut self, req: Request) -> Self::Future {
1966 self.execute_request(req)
1967 }
1968}
1969
1970impl fmt::Debug for ClientBuilder {
1971 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1972 let mut builder = f.debug_struct("ClientBuilder");
1973 self.config.fmt_fields(&mut builder);
1974 builder.finish()
1975 }
1976}
1977
1978impl Config {
1979 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
1980 #[cfg(feature = "cookies")]
1984 {
1985 if let Some(_) = self.cookie_store {
1986 f.field("cookie_store", &true);
1987 }
1988 }
1989
1990 f.field("accepts", &self.accepts);
1991
1992 if !self.proxies.is_empty() {
1993 f.field("proxies", &self.proxies);
1994 }
1995
1996 if !self.redirect_policy.is_default() {
1997 f.field("redirect_policy", &self.redirect_policy);
1998 }
1999
2000 if self.referer {
2001 f.field("referer", &true);
2002 }
2003
2004 f.field("default_headers", &self.headers);
2005
2006 if self.http1_title_case_headers {
2007 f.field("http1_title_case_headers", &true);
2008 }
2009
2010 if self.http1_allow_obsolete_multiline_headers_in_responses {
2011 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
2012 }
2013
2014 if self.http1_ignore_invalid_headers_in_responses {
2015 f.field("http1_ignore_invalid_headers_in_responses", &true);
2016 }
2017
2018 if self.http1_allow_spaces_after_header_name_in_responses {
2019 f.field("http1_allow_spaces_after_header_name_in_responses", &true);
2020 }
2021
2022 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
2023 f.field("http1_only", &true);
2024 }
2025
2026 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
2027 f.field("http2_prior_knowledge", &true);
2028 }
2029
2030 if let Some(ref d) = self.connect_timeout {
2031 f.field("connect_timeout", d);
2032 }
2033
2034 if let Some(ref d) = self.timeout {
2035 f.field("timeout", d);
2036 }
2037
2038 if let Some(ref v) = self.local_address {
2039 f.field("local_address", v);
2040 }
2041
2042 if self.nodelay {
2043 f.field("tcp_nodelay", &true);
2044 }
2045
2046 #[cfg(feature = "native-tls")]
2047 {
2048 if !self.hostname_verification {
2049 f.field("danger_accept_invalid_hostnames", &true);
2050 }
2051 }
2052
2053 #[cfg(feature = "__tls")]
2054 {
2055 if !self.certs_verification {
2056 f.field("danger_accept_invalid_certs", &true);
2057 }
2058
2059 if let Some(ref min_tls_version) = self.min_tls_version {
2060 f.field("min_tls_version", min_tls_version);
2061 }
2062
2063 if let Some(ref max_tls_version) = self.max_tls_version {
2064 f.field("max_tls_version", max_tls_version);
2065 }
2066
2067 f.field("tls_sni", &self.tls_sni);
2068
2069 f.field("tls_info", &self.tls_info);
2070 }
2071
2072 #[cfg(all(feature = "native-tls-crate", feature = "__rustls"))]
2073 {
2074 f.field("tls_backend", &self.tls);
2075 }
2076
2077 if !self.dns_overrides.is_empty() {
2078 f.field("dns_overrides", &self.dns_overrides);
2079 }
2080
2081 #[cfg(feature = "http3")]
2082 {
2083 if self.tls_enable_early_data {
2084 f.field("tls_enable_early_data", &true);
2085 }
2086 }
2087 }
2088}
2089
2090struct ClientRef {
2091 accepts: Accepts,
2092 #[cfg(feature = "cookies")]
2093 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
2094 headers: HeaderMap,
2095 hyper: HyperClient,
2096 #[cfg(feature = "http3")]
2097 h3_client: Option<H3Client>,
2098 redirect_policy: redirect::Policy,
2099 referer: bool,
2100 request_timeout: Option<Duration>,
2101 proxies: Arc<Vec<Proxy>>,
2102 proxies_maybe_http_auth: bool,
2103 https_only: bool,
2104}
2105
2106impl ClientRef {
2107 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
2108 #[cfg(feature = "cookies")]
2112 {
2113 if let Some(_) = self.cookie_store {
2114 f.field("cookie_store", &true);
2115 }
2116 }
2117
2118 f.field("accepts", &self.accepts);
2119
2120 if !self.proxies.is_empty() {
2121 f.field("proxies", &self.proxies);
2122 }
2123
2124 if !self.redirect_policy.is_default() {
2125 f.field("redirect_policy", &self.redirect_policy);
2126 }
2127
2128 if self.referer {
2129 f.field("referer", &true);
2130 }
2131
2132 f.field("default_headers", &self.headers);
2133
2134 if let Some(ref d) = self.request_timeout {
2135 f.field("timeout", d);
2136 }
2137 }
2138}
2139
2140pin_project! {
2141 pub struct Pending {
2142 #[pin]
2143 inner: PendingInner,
2144 }
2145}
2146
2147enum PendingInner {
2148 Request(PendingRequest),
2149 Error(Option<crate::Error>),
2150}
2151
2152pin_project! {
2153 struct PendingRequest {
2154 method: Method,
2155 url: Url,
2156 headers: HeaderMap,
2157 body: Option<Option<Bytes>>,
2158
2159 urls: Vec<Url>,
2160
2161 retry_count: usize,
2162
2163 client: Arc<ClientRef>,
2164
2165 #[pin]
2166 in_flight: ResponseFuture,
2167 #[pin]
2168 timeout: Option<Pin<Box<Sleep>>>,
2169 }
2170}
2171
2172enum ResponseFuture {
2173 Default(HyperResponseFuture),
2174 #[cfg(feature = "http3")]
2175 H3(H3ResponseFuture),
2176}
2177
2178impl PendingRequest {
2179 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
2180 self.project().in_flight
2181 }
2182
2183 fn timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
2184 self.project().timeout
2185 }
2186
2187 fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
2188 self.project().urls
2189 }
2190
2191 fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
2192 self.project().headers
2193 }
2194
2195 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2196 if !is_retryable_error(err) {
2197 return false;
2198 }
2199
2200 trace!("can retry {err:?}");
2201
2202 let body = match self.body {
2203 Some(Some(ref body)) => Body::reusable(body.clone()),
2204 Some(None) => {
2205 debug!("error was retryable, but body not reusable");
2206 return false;
2207 }
2208 None => Body::empty(),
2209 };
2210
2211 if self.retry_count >= 2 {
2212 trace!("retry count too high");
2213 return false;
2214 }
2215 self.retry_count += 1;
2216
2217 let uri = try_uri(&self.url).expect("URL was already validated as URI");
2219
2220 *self.as_mut().in_flight().get_mut() = match *self.as_mut().in_flight().as_ref() {
2221 #[cfg(feature = "http3")]
2222 ResponseFuture::H3(_) => {
2223 let mut req = hyper::Request::builder()
2224 .method(self.method.clone())
2225 .uri(uri)
2226 .body(body)
2227 .expect("valid request parts");
2228 *req.headers_mut() = self.headers.clone();
2229 ResponseFuture::H3(
2230 self.client
2231 .h3_client
2232 .as_ref()
2233 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2234 .request(req),
2235 )
2236 }
2237 _ => {
2238 let mut req = hyper::Request::builder()
2239 .method(self.method.clone())
2240 .uri(uri)
2241 .body(body.into_stream())
2242 .expect("valid request parts");
2243 *req.headers_mut() = self.headers.clone();
2244 ResponseFuture::Default(self.client.hyper.request(req))
2245 }
2246 };
2247
2248 true
2249 }
2250}
2251
2252fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
2253 #[cfg(feature = "http3")]
2254 if let Some(cause) = err.source() {
2255 if let Some(err) = cause.downcast_ref::<h3::Error>() {
2256 debug!("determining if HTTP/3 error {err} can be retried");
2257 return err.to_string().as_str() == "timeout";
2259 }
2260 }
2261
2262 if let Some(cause) = err.source() {
2263 if let Some(err) = cause.downcast_ref::<h2::Error>() {
2264 if err.is_go_away() && err.is_remote() && err.reason() == Some(h2::Reason::NO_ERROR) {
2266 return true;
2267 }
2268
2269 if err.is_reset() && err.is_remote() && err.reason() == Some(h2::Reason::REFUSED_STREAM)
2272 {
2273 return true;
2274 }
2275 }
2276 }
2277 false
2278}
2279
2280impl Pending {
2281 pub(super) fn new_err(err: crate::Error) -> Pending {
2282 Pending {
2283 inner: PendingInner::Error(Some(err)),
2284 }
2285 }
2286
2287 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
2288 self.project().inner
2289 }
2290}
2291
2292impl Future for Pending {
2293 type Output = Result<Response, crate::Error>;
2294
2295 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2296 let inner = self.inner();
2297 match inner.get_mut() {
2298 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
2299 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
2300 .take()
2301 .expect("Pending error polled more than once"))),
2302 }
2303 }
2304}
2305
2306impl Future for PendingRequest {
2307 type Output = Result<Response, crate::Error>;
2308
2309 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2310 if let Some(delay) = self.as_mut().timeout().as_mut().as_pin_mut() {
2311 if let Poll::Ready(()) = delay.poll(cx) {
2312 return Poll::Ready(Err(
2313 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
2314 ));
2315 }
2316 }
2317
2318 loop {
2319 let res = match self.as_mut().in_flight().get_mut() {
2320 ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
2321 Poll::Ready(Err(e)) => {
2322 if self.as_mut().retry_error(&e) {
2323 continue;
2324 }
2325 return Poll::Ready(Err(
2326 crate::error::request(e).with_url(self.url.clone())
2327 ));
2328 }
2329 Poll::Ready(Ok(res)) => res,
2330 Poll::Pending => return Poll::Pending,
2331 },
2332 #[cfg(feature = "http3")]
2333 ResponseFuture::H3(r) => match Pin::new(r).poll(cx) {
2334 Poll::Ready(Err(e)) => {
2335 if self.as_mut().retry_error(&e) {
2336 continue;
2337 }
2338 return Poll::Ready(Err(
2339 crate::error::request(e).with_url(self.url.clone())
2340 ));
2341 }
2342 Poll::Ready(Ok(res)) => res,
2343 Poll::Pending => return Poll::Pending,
2344 },
2345 };
2346
2347 #[cfg(feature = "cookies")]
2348 {
2349 if let Some(ref cookie_store) = self.client.cookie_store {
2350 let mut cookies =
2351 cookie::extract_response_cookie_headers(&res.headers()).peekable();
2352 if cookies.peek().is_some() {
2353 cookie_store.set_cookies(&mut cookies, &self.url);
2354 }
2355 }
2356 }
2357 let should_redirect = match res.status() {
2358 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
2359 self.body = None;
2360 for header in &[
2361 TRANSFER_ENCODING,
2362 CONTENT_ENCODING,
2363 CONTENT_TYPE,
2364 CONTENT_LENGTH,
2365 ] {
2366 self.headers.remove(header);
2367 }
2368
2369 match self.method {
2370 Method::GET | Method::HEAD => {}
2371 _ => {
2372 self.method = Method::GET;
2373 }
2374 }
2375 true
2376 }
2377 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
2378 match self.body {
2379 Some(Some(_)) | None => true,
2380 Some(None) => false,
2381 }
2382 }
2383 _ => false,
2384 };
2385 if should_redirect {
2386 let loc = res.headers().get(LOCATION).and_then(|val| {
2387 let loc = (|| -> Option<Url> {
2388 self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
2392 })();
2393
2394 let loc = loc.and_then(|url| {
2398 if try_uri(&url).is_ok() {
2399 Some(url)
2400 } else {
2401 None
2402 }
2403 });
2404
2405 if loc.is_none() {
2406 debug!("Location header had invalid URI: {val:?}");
2407 }
2408 loc
2409 });
2410 if let Some(loc) = loc {
2411 if self.client.referer {
2412 if let Some(referer) = make_referer(&loc, &self.url) {
2413 self.headers.insert(REFERER, referer);
2414 }
2415 }
2416 let url = self.url.clone();
2417 self.as_mut().urls().push(url);
2418 let action = self
2419 .client
2420 .redirect_policy
2421 .check(res.status(), &loc, &self.urls);
2422
2423 match action {
2424 redirect::ActionKind::Follow => {
2425 debug!("redirecting '{}' to '{}'", self.url, loc);
2426
2427 if loc.scheme() != "http" && loc.scheme() != "https" {
2428 return Poll::Ready(Err(error::url_bad_scheme(loc)));
2429 }
2430
2431 if self.client.https_only && loc.scheme() != "https" {
2432 return Poll::Ready(Err(error::redirect(
2433 error::url_bad_scheme(loc.clone()),
2434 loc,
2435 )));
2436 }
2437
2438 self.url = loc;
2439 let mut headers =
2440 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
2441
2442 remove_sensitive_headers(&mut headers, &self.url, &self.urls);
2443 let uri = try_uri(&self.url)?;
2444 let body = match self.body {
2445 Some(Some(ref body)) => Body::reusable(body.clone()),
2446 _ => Body::empty(),
2447 };
2448
2449 #[cfg(feature = "cookies")]
2451 {
2452 if let Some(ref cookie_store) = self.client.cookie_store {
2453 add_cookie_header(&mut headers, &**cookie_store, &self.url);
2454 }
2455 }
2456
2457 *self.as_mut().in_flight().get_mut() =
2458 match *self.as_mut().in_flight().as_ref() {
2459 #[cfg(feature = "http3")]
2460 ResponseFuture::H3(_) => {
2461 let mut req = hyper::Request::builder()
2462 .method(self.method.clone())
2463 .uri(uri.clone())
2464 .body(body)
2465 .expect("valid request parts");
2466 *req.headers_mut() = headers.clone();
2467 std::mem::swap(self.as_mut().headers(), &mut headers);
2468 ResponseFuture::H3(self.client.h3_client
2469 .as_ref()
2470 .expect("H3 client must exists, otherwise we can't have a h3 request here")
2471 .request(req))
2472 }
2473 _ => {
2474 let mut req = hyper::Request::builder()
2475 .method(self.method.clone())
2476 .uri(uri.clone())
2477 .body(body.into_stream())
2478 .expect("valid request parts");
2479 *req.headers_mut() = headers.clone();
2480 std::mem::swap(self.as_mut().headers(), &mut headers);
2481 ResponseFuture::Default(self.client.hyper.request(req))
2482 }
2483 };
2484
2485 continue;
2486 }
2487 redirect::ActionKind::Stop => {
2488 debug!("redirect policy disallowed redirection to '{loc}'");
2489 }
2490 redirect::ActionKind::Error(err) => {
2491 return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
2492 }
2493 }
2494 }
2495 }
2496
2497 let res = Response::new(
2498 res,
2499 self.url.clone(),
2500 self.client.accepts,
2501 self.timeout.take(),
2502 );
2503 return Poll::Ready(Ok(res));
2504 }
2505 }
2506}
2507
2508impl fmt::Debug for Pending {
2509 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2510 match self.inner {
2511 PendingInner::Request(ref req) => f
2512 .debug_struct("Pending")
2513 .field("method", &req.method)
2514 .field("url", &req.url)
2515 .finish(),
2516 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
2517 }
2518 }
2519}
2520
2521fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
2522 if next.scheme() == "http" && previous.scheme() == "https" {
2523 return None;
2524 }
2525
2526 let mut referer = previous.clone();
2527 let _ = referer.set_username("");
2528 let _ = referer.set_password(None);
2529 referer.set_fragment(None);
2530 referer.as_str().parse().ok()
2531}
2532
2533#[cfg(feature = "cookies")]
2534fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
2535 if let Some(header) = cookie_store.cookies(url) {
2536 headers.insert(crate::header::COOKIE, header);
2537 }
2538}
2539
2540#[cfg(test)]
2541mod tests {
2542 #[tokio::test]
2543 async fn execute_request_rejects_invalid_urls() {
2544 let url_str = "hxxps://www.rust-lang.org/";
2545 let url = url::Url::parse(url_str).unwrap();
2546 let result = crate::get(url.clone()).await;
2547
2548 assert!(result.is_err());
2549 let err = result.err().unwrap();
2550 assert!(err.is_builder());
2551 assert_eq!(url_str, err.url().unwrap().as_str());
2552 }
2553
2554 #[tokio::test]
2556 async fn execute_request_rejects_invalid_hostname() {
2557 let url_str = "https://{{hostname}}/";
2558 let url = url::Url::parse(url_str).unwrap();
2559 let result = crate::get(url.clone()).await;
2560
2561 assert!(result.is_err());
2562 let err = result.err().unwrap();
2563 assert!(err.is_builder());
2564 assert_eq!(url_str, err.url().unwrap().as_str());
2565 }
2566}