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::ResponseFuture;
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 log::{debug, trace};
26
27use super::decoder::Accepts;
28use super::request::{Request, RequestBuilder};
29use super::response::Response;
30use super::Body;
31use crate::connect::{Connector, HttpConnector};
32#[cfg(feature = "cookies")]
33use crate::cookie;
34use crate::error;
35use crate::into_url::{expect_uri, try_uri};
36use crate::redirect::{self, remove_sensitive_headers};
37#[cfg(feature = "__tls")]
38use crate::tls::{self, TlsBackend};
39#[cfg(feature = "__tls")]
40use crate::Certificate;
41#[cfg(any(feature = "native-tls", feature = "__rustls"))]
42use crate::Identity;
43use crate::{IntoUrl, Method, Proxy, StatusCode, Url};
44
45#[derive(Clone)]
59pub struct Client {
60 inner: Arc<ClientRef>,
61}
62
63#[must_use]
65pub struct ClientBuilder {
66 config: Config,
67}
68
69enum HttpVersionPref {
70 Http1,
71 Http2,
72 All,
73}
74
75struct Config {
76 accepts: Accepts,
78 headers: HeaderMap,
79 #[cfg(feature = "native-tls")]
80 hostname_verification: bool,
81 #[cfg(feature = "__tls")]
82 certs_verification: bool,
83 connect_timeout: Option<Duration>,
84 connection_verbose: bool,
85 pool_idle_timeout: Option<Duration>,
86 pool_max_idle_per_host: usize,
87 tcp_keepalive: Option<Duration>,
88 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
89 identity: Option<Identity>,
90 proxies: Vec<Proxy>,
91 auto_sys_proxy: bool,
92 redirect_policy: redirect::Policy,
93 referer: bool,
94 timeout: Option<Duration>,
95 #[cfg(feature = "__tls")]
96 root_certs: Vec<Certificate>,
97 #[cfg(feature = "__tls")]
98 tls_built_in_root_certs: bool,
99 #[cfg(feature = "__tls")]
100 min_tls_version: Option<tls::Version>,
101 #[cfg(feature = "__tls")]
102 max_tls_version: Option<tls::Version>,
103 #[cfg(feature = "__tls")]
104 tls: TlsBackend,
105 http_version_pref: HttpVersionPref,
106 http09_responses: bool,
107 http1_title_case_headers: bool,
108 http1_allow_obsolete_multiline_headers_in_responses: bool,
109 http2_initial_stream_window_size: Option<u32>,
110 http2_initial_connection_window_size: Option<u32>,
111 http2_adaptive_window: bool,
112 http2_max_frame_size: Option<u32>,
113 http2_keep_alive_interval: Option<Duration>,
114 http2_keep_alive_timeout: Option<Duration>,
115 http2_keep_alive_while_idle: bool,
116 local_address: Option<IpAddr>,
117 nodelay: bool,
118 #[cfg(feature = "cookies")]
119 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
120 trust_dns: bool,
121 error: Option<crate::Error>,
122 https_only: bool,
123 dns_overrides: HashMap<String, Vec<SocketAddr>>,
124}
125
126impl Default for ClientBuilder {
127 fn default() -> Self {
128 Self::new()
129 }
130}
131
132impl ClientBuilder {
133 pub fn new() -> ClientBuilder {
137 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
138 headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
139
140 ClientBuilder {
141 config: Config {
142 error: None,
143 accepts: Accepts::default(),
144 headers,
145 #[cfg(feature = "native-tls")]
146 hostname_verification: true,
147 #[cfg(feature = "__tls")]
148 certs_verification: true,
149 connect_timeout: None,
150 connection_verbose: false,
151 pool_idle_timeout: Some(Duration::from_secs(90)),
152 pool_max_idle_per_host: std::usize::MAX,
153 tcp_keepalive: None, proxies: Vec::new(),
157 auto_sys_proxy: true,
158 redirect_policy: redirect::Policy::default(),
159 referer: true,
160 timeout: None,
161 #[cfg(feature = "__tls")]
162 root_certs: Vec::new(),
163 #[cfg(feature = "__tls")]
164 tls_built_in_root_certs: true,
165 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
166 identity: None,
167 #[cfg(feature = "__tls")]
168 min_tls_version: None,
169 #[cfg(feature = "__tls")]
170 max_tls_version: None,
171 #[cfg(feature = "__tls")]
172 tls: TlsBackend::default(),
173 http_version_pref: HttpVersionPref::All,
174 http09_responses: false,
175 http1_title_case_headers: false,
176 http1_allow_obsolete_multiline_headers_in_responses: false,
177 http2_initial_stream_window_size: None,
178 http2_initial_connection_window_size: None,
179 http2_adaptive_window: false,
180 http2_max_frame_size: None,
181 http2_keep_alive_interval: None,
182 http2_keep_alive_timeout: None,
183 http2_keep_alive_while_idle: false,
184 local_address: None,
185 nodelay: true,
186 trust_dns: cfg!(feature = "trust-dns"),
187 #[cfg(feature = "cookies")]
188 cookie_store: None,
189 https_only: false,
190 dns_overrides: HashMap::new(),
191 },
192 }
193 }
194
195 pub fn build(self) -> crate::Result<Client> {
202 let config = self.config;
203
204 if let Some(err) = config.error {
205 return Err(err);
206 }
207
208 let mut proxies = config.proxies;
209 if config.auto_sys_proxy {
210 proxies.push(Proxy::system());
211 }
212 let proxies = Arc::new(proxies);
213
214 let mut connector = {
215 #[cfg(feature = "__tls")]
216 fn user_agent(headers: &HeaderMap) -> Option<HeaderValue> {
217 headers.get(USER_AGENT).cloned()
218 }
219
220 let http = match config.trust_dns {
221 false => {
222 if config.dns_overrides.is_empty() {
223 HttpConnector::new_gai()
224 } else {
225 HttpConnector::new_gai_with_overrides(config.dns_overrides)
226 }
227 }
228 #[cfg(feature = "trust-dns")]
229 true => {
230 if config.dns_overrides.is_empty() {
231 HttpConnector::new_trust_dns()?
232 } else {
233 HttpConnector::new_trust_dns_with_overrides(config.dns_overrides)?
234 }
235 }
236 #[cfg(not(feature = "trust-dns"))]
237 true => unreachable!("trust-dns shouldn't be enabled unless the feature is"),
238 };
239
240 #[cfg(feature = "__tls")]
241 match config.tls {
242 #[cfg(feature = "default-tls")]
243 TlsBackend::Default => {
244 let mut tls = TlsConnector::builder();
245
246 #[cfg(feature = "native-tls-alpn")]
247 {
248 match config.http_version_pref {
249 HttpVersionPref::Http1 => {
250 tls.request_alpns(&["http/1.1"]);
251 }
252 HttpVersionPref::Http2 => {
253 tls.request_alpns(&["h2"]);
254 }
255 HttpVersionPref::All => {
256 tls.request_alpns(&["h2", "http/1.1"]);
257 }
258 }
259 }
260
261 #[cfg(feature = "native-tls")]
262 {
263 tls.danger_accept_invalid_hostnames(!config.hostname_verification);
264 }
265
266 tls.danger_accept_invalid_certs(!config.certs_verification);
267
268 tls.disable_built_in_roots(!config.tls_built_in_root_certs);
269
270 for cert in config.root_certs {
271 cert.add_to_native_tls(&mut tls);
272 }
273
274 #[cfg(feature = "native-tls")]
275 {
276 if let Some(id) = config.identity {
277 id.add_to_native_tls(&mut tls)?;
278 }
279 }
280
281 if let Some(min_tls_version) = config.min_tls_version {
282 let protocol = min_tls_version.to_native_tls().ok_or_else(|| {
283 crate::error::builder("invalid minimum TLS version for backend")
287 })?;
288 tls.min_protocol_version(Some(protocol));
289 }
290
291 if let Some(max_tls_version) = config.max_tls_version {
292 let protocol = max_tls_version.to_native_tls().ok_or_else(|| {
293 crate::error::builder("invalid maximum TLS version for backend")
298 })?;
299 tls.max_protocol_version(Some(protocol));
300 }
301
302 Connector::new_default_tls(
303 http,
304 tls,
305 proxies.clone(),
306 user_agent(&config.headers),
307 config.local_address,
308 config.nodelay,
309 )?
310 }
311 #[cfg(feature = "native-tls")]
312 TlsBackend::BuiltNativeTls(conn) => Connector::from_built_default_tls(
313 http,
314 conn,
315 proxies.clone(),
316 user_agent(&config.headers),
317 config.local_address,
318 config.nodelay,
319 ),
320 #[cfg(feature = "__rustls")]
321 TlsBackend::BuiltRustls(conn) => Connector::new_rustls_tls(
322 http,
323 conn,
324 proxies.clone(),
325 user_agent(&config.headers),
326 config.local_address,
327 config.nodelay,
328 ),
329 #[cfg(feature = "__rustls")]
330 TlsBackend::Rustls => {
331 use crate::tls::NoVerifier;
332
333 let mut root_cert_store = rustls::RootCertStore::empty();
335 for cert in config.root_certs {
336 cert.add_to_rustls(&mut root_cert_store)?;
337 }
338
339 #[cfg(feature = "rustls-tls-webpki-roots")]
340 if config.tls_built_in_root_certs {
341 use rustls::OwnedTrustAnchor;
342
343 let trust_anchors =
344 webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|trust_anchor| {
345 OwnedTrustAnchor::from_subject_spki_name_constraints(
346 trust_anchor.subject,
347 trust_anchor.spki,
348 trust_anchor.name_constraints,
349 )
350 });
351
352 root_cert_store.add_server_trust_anchors(trust_anchors);
353 }
354
355 #[cfg(feature = "rustls-tls-native-roots")]
356 if config.tls_built_in_root_certs {
357 let mut valid_count = 0;
358 let mut invalid_count = 0;
359 for cert in rustls_native_certs::load_native_certs()
360 .map_err(crate::error::builder)?
361 {
362 let cert = rustls::Certificate(cert.0);
363 match root_cert_store.add(&cert) {
367 Ok(_) => valid_count += 1,
368 Err(err) => {
369 invalid_count += 1;
370 log::warn!(
371 "rustls failed to parse DER certificate {:?} {:?}",
372 &err,
373 &cert
374 );
375 }
376 }
377 }
378 if valid_count == 0 && invalid_count > 0 {
379 return Err(crate::error::builder(
380 "zero valid certificates found in native root store",
381 ));
382 }
383 }
384
385 let mut versions = rustls::ALL_VERSIONS.to_vec();
387
388 if let Some(min_tls_version) = config.min_tls_version {
389 versions.retain(|&supported_version| {
390 match tls::Version::from_rustls(supported_version.version) {
391 Some(version) => version >= min_tls_version,
392 None => true,
395 }
396 });
397 }
398
399 if let Some(max_tls_version) = config.max_tls_version {
400 versions.retain(|&supported_version| {
401 match tls::Version::from_rustls(supported_version.version) {
402 Some(version) => version <= max_tls_version,
403 None => false,
404 }
405 });
406 }
407
408 let config_builder = rustls::ClientConfig::builder()
410 .with_safe_default_cipher_suites()
411 .with_safe_default_kx_groups()
412 .with_protocol_versions(&versions)
413 .map_err(crate::error::builder)?
414 .with_root_certificates(root_cert_store);
415
416 let mut tls = if let Some(id) = config.identity {
418 id.add_to_rustls(config_builder)?
419 } else {
420 config_builder.with_no_client_auth()
421 };
422
423 if !config.certs_verification {
425 tls.dangerous()
426 .set_certificate_verifier(Arc::new(NoVerifier));
427 }
428
429 match config.http_version_pref {
431 HttpVersionPref::Http1 => {
432 tls.alpn_protocols = vec!["http/1.1".into()];
433 }
434 HttpVersionPref::Http2 => {
435 tls.alpn_protocols = vec!["h2".into()];
436 }
437 HttpVersionPref::All => {
438 tls.alpn_protocols = vec!["h2".into(), "http/1.1".into()];
439 }
440 }
441
442 Connector::new_rustls_tls(
443 http,
444 tls,
445 proxies.clone(),
446 user_agent(&config.headers),
447 config.local_address,
448 config.nodelay,
449 )
450 }
451 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
452 TlsBackend::UnknownPreconfigured => {
453 return Err(crate::error::builder(
454 "Unknown TLS backend passed to `use_preconfigured_tls`",
455 ));
456 }
457 }
458
459 #[cfg(not(feature = "__tls"))]
460 Connector::new(http, proxies.clone(), config.local_address, config.nodelay)
461 };
462
463 connector.set_timeout(config.connect_timeout);
464 connector.set_verbose(config.connection_verbose);
465
466 let mut builder = hyper::Client::builder();
467 if matches!(config.http_version_pref, HttpVersionPref::Http2) {
468 builder.http2_only(true);
469 }
470
471 if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size {
472 builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
473 }
474 if let Some(http2_initial_connection_window_size) =
475 config.http2_initial_connection_window_size
476 {
477 builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
478 }
479 if config.http2_adaptive_window {
480 builder.http2_adaptive_window(true);
481 }
482 if let Some(http2_max_frame_size) = config.http2_max_frame_size {
483 builder.http2_max_frame_size(http2_max_frame_size);
484 }
485 if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
486 builder.http2_keep_alive_interval(http2_keep_alive_interval);
487 }
488 if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
489 builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
490 }
491 if config.http2_keep_alive_while_idle {
492 builder.http2_keep_alive_while_idle(true);
493 }
494
495 builder.pool_idle_timeout(config.pool_idle_timeout);
496 builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
497 connector.set_keepalive(config.tcp_keepalive);
498
499 if config.http09_responses {
500 builder.http09_responses(true);
501 }
502
503 if config.http1_title_case_headers {
504 builder.http1_title_case_headers(true);
505 }
506
507 if config.http1_allow_obsolete_multiline_headers_in_responses {
508 builder.http1_allow_obsolete_multiline_headers_in_responses(true);
509 }
510
511 let hyper_client = builder.build(connector);
512
513 let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
514
515 Ok(Client {
516 inner: Arc::new(ClientRef {
517 accepts: config.accepts,
518 #[cfg(feature = "cookies")]
519 cookie_store: config.cookie_store,
520 hyper: hyper_client,
521 headers: config.headers,
522 redirect_policy: config.redirect_policy,
523 referer: config.referer,
524 request_timeout: config.timeout,
525 proxies,
526 proxies_maybe_http_auth,
527 https_only: config.https_only,
528 }),
529 })
530 }
531
532 pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
555 where
556 V: TryInto<HeaderValue>,
557 V::Error: Into<http::Error>,
558 {
559 match value.try_into() {
560 Ok(value) => {
561 self.config.headers.insert(USER_AGENT, value);
562 }
563 Err(e) => {
564 self.config.error = Some(crate::error::builder(e.into()));
565 }
566 };
567 self
568 }
569 pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
614 for (key, value) in headers.iter() {
615 self.config.headers.insert(key, value.clone());
616 }
617 self
618 }
619
620 #[cfg(feature = "cookies")]
631 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
632 pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
633 if enable {
634 self.cookie_provider(Arc::new(cookie::Jar::default()))
635 } else {
636 self.config.cookie_store = None;
637 self
638 }
639 }
640
641 #[cfg(feature = "cookies")]
652 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
653 pub fn cookie_provider<C: cookie::CookieStore + 'static>(
654 mut self,
655 cookie_store: Arc<C>,
656 ) -> ClientBuilder {
657 self.config.cookie_store = Some(cookie_store as _);
658 self
659 }
660
661 #[cfg(feature = "gzip")]
678 #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
679 pub fn gzip(mut self, enable: bool) -> ClientBuilder {
680 self.config.accepts.gzip = enable;
681 self
682 }
683
684 #[cfg(feature = "brotli")]
701 #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
702 pub fn brotli(mut self, enable: bool) -> ClientBuilder {
703 self.config.accepts.brotli = enable;
704 self
705 }
706
707 #[cfg(feature = "deflate")]
724 #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
725 pub fn deflate(mut self, enable: bool) -> ClientBuilder {
726 self.config.accepts.deflate = enable;
727 self
728 }
729
730 pub fn no_gzip(self) -> ClientBuilder {
736 #[cfg(feature = "gzip")]
737 {
738 self.gzip(false)
739 }
740
741 #[cfg(not(feature = "gzip"))]
742 {
743 self
744 }
745 }
746
747 pub fn no_brotli(self) -> ClientBuilder {
753 #[cfg(feature = "brotli")]
754 {
755 self.brotli(false)
756 }
757
758 #[cfg(not(feature = "brotli"))]
759 {
760 self
761 }
762 }
763
764 pub fn no_deflate(self) -> ClientBuilder {
770 #[cfg(feature = "deflate")]
771 {
772 self.deflate(false)
773 }
774
775 #[cfg(not(feature = "deflate"))]
776 {
777 self
778 }
779 }
780
781 pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
787 self.config.redirect_policy = policy;
788 self
789 }
790
791 pub fn referer(mut self, enable: bool) -> ClientBuilder {
795 self.config.referer = enable;
796 self
797 }
798
799 pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
807 self.config.proxies.push(proxy);
808 self.config.auto_sys_proxy = false;
809 self
810 }
811
812 pub fn no_proxy(mut self) -> ClientBuilder {
816 self.config.proxies.clear();
817 self.config.auto_sys_proxy = false;
818 self
819 }
820
821 pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
830 self.config.timeout = Some(timeout);
831 self
832 }
833
834 pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
843 self.config.connect_timeout = Some(timeout);
844 self
845 }
846
847 pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
854 self.config.connection_verbose = verbose;
855 self
856 }
857
858 pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
866 where
867 D: Into<Option<Duration>>,
868 {
869 self.config.pool_idle_timeout = val.into();
870 self
871 }
872
873 pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
875 self.config.pool_max_idle_per_host = max;
876 self
877 }
878
879 pub fn http1_title_case_headers(mut self) -> ClientBuilder {
881 self.config.http1_title_case_headers = true;
882 self
883 }
884
885 pub fn http1_allow_obsolete_multiline_headers_in_responses(
891 mut self,
892 value: bool,
893 ) -> ClientBuilder {
894 self.config
895 .http1_allow_obsolete_multiline_headers_in_responses = value;
896 self
897 }
898
899 pub fn http1_only(mut self) -> ClientBuilder {
901 self.config.http_version_pref = HttpVersionPref::Http1;
902 self
903 }
904
905 pub fn http09_responses(mut self) -> ClientBuilder {
907 self.config.http09_responses = true;
908 self
909 }
910
911 pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
913 self.config.http_version_pref = HttpVersionPref::Http2;
914 self
915 }
916
917 pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
921 self.config.http2_initial_stream_window_size = sz.into();
922 self
923 }
924
925 pub fn http2_initial_connection_window_size(
929 mut self,
930 sz: impl Into<Option<u32>>,
931 ) -> ClientBuilder {
932 self.config.http2_initial_connection_window_size = sz.into();
933 self
934 }
935
936 pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
941 self.config.http2_adaptive_window = enabled;
942 self
943 }
944
945 pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
949 self.config.http2_max_frame_size = sz.into();
950 self
951 }
952
953 pub fn http2_keep_alive_interval(
958 mut self,
959 interval: impl Into<Option<Duration>>,
960 ) -> ClientBuilder {
961 self.config.http2_keep_alive_interval = interval.into();
962 self
963 }
964
965 pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
971 self.config.http2_keep_alive_timeout = Some(timeout);
972 self
973 }
974
975 pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
982 self.config.http2_keep_alive_while_idle = enabled;
983 self
984 }
985
986 pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
992 self.config.nodelay = enabled;
993 self
994 }
995
996 pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
1008 where
1009 T: Into<Option<IpAddr>>,
1010 {
1011 self.config.local_address = addr.into();
1012 self
1013 }
1014
1015 pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
1019 where
1020 D: Into<Option<Duration>>,
1021 {
1022 self.config.tcp_keepalive = val.into();
1023 self
1024 }
1025
1026 #[cfg(feature = "__tls")]
1038 #[cfg_attr(
1039 docsrs,
1040 doc(cfg(any(
1041 feature = "default-tls",
1042 feature = "native-tls",
1043 feature = "rustls-tls"
1044 )))
1045 )]
1046 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
1047 self.config.root_certs.push(cert);
1048 self
1049 }
1050
1051 #[cfg(feature = "__tls")]
1060 #[cfg_attr(
1061 docsrs,
1062 doc(cfg(any(
1063 feature = "default-tls",
1064 feature = "native-tls",
1065 feature = "rustls-tls"
1066 )))
1067 )]
1068 pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder {
1069 self.config.tls_built_in_root_certs = tls_built_in_root_certs;
1070 self
1071 }
1072
1073 #[cfg(any(feature = "native-tls", feature = "__rustls"))]
1080 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1081 pub fn identity(mut self, identity: Identity) -> ClientBuilder {
1082 self.config.identity = Some(identity);
1083 self
1084 }
1085
1086 #[cfg(feature = "native-tls")]
1101 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1102 pub fn danger_accept_invalid_hostnames(
1103 mut self,
1104 accept_invalid_hostname: bool,
1105 ) -> ClientBuilder {
1106 self.config.hostname_verification = !accept_invalid_hostname;
1107 self
1108 }
1109
1110 #[cfg(feature = "__tls")]
1127 #[cfg_attr(
1128 docsrs,
1129 doc(cfg(any(
1130 feature = "default-tls",
1131 feature = "native-tls",
1132 feature = "rustls-tls"
1133 )))
1134 )]
1135 pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
1136 self.config.certs_verification = !accept_invalid_certs;
1137 self
1138 }
1139
1140 #[cfg(feature = "__tls")]
1156 #[cfg_attr(
1157 docsrs,
1158 doc(cfg(any(
1159 feature = "default-tls",
1160 feature = "native-tls",
1161 feature = "rustls-tls"
1162 )))
1163 )]
1164 pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1165 self.config.min_tls_version = Some(version);
1166 self
1167 }
1168
1169 #[cfg(feature = "__tls")]
1185 #[cfg_attr(
1186 docsrs,
1187 doc(cfg(any(
1188 feature = "default-tls",
1189 feature = "native-tls",
1190 feature = "rustls-tls"
1191 )))
1192 )]
1193 pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder {
1194 self.config.max_tls_version = Some(version);
1195 self
1196 }
1197
1198 #[cfg(feature = "native-tls")]
1207 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
1208 pub fn use_native_tls(mut self) -> ClientBuilder {
1209 self.config.tls = TlsBackend::Default;
1210 self
1211 }
1212
1213 #[cfg(feature = "__rustls")]
1222 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
1223 pub fn use_rustls_tls(mut self) -> ClientBuilder {
1224 self.config.tls = TlsBackend::Rustls;
1225 self
1226 }
1227
1228 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
1247 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))]
1248 pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder {
1249 let mut tls = Some(tls);
1250 #[cfg(feature = "native-tls")]
1251 {
1252 if let Some(conn) =
1253 (&mut tls as &mut dyn Any).downcast_mut::<Option<native_tls_crate::TlsConnector>>()
1254 {
1255 let tls = conn.take().expect("is definitely Some");
1256 let tls = crate::tls::TlsBackend::BuiltNativeTls(tls);
1257 self.config.tls = tls;
1258 return self;
1259 }
1260 }
1261 #[cfg(feature = "__rustls")]
1262 {
1263 if let Some(conn) =
1264 (&mut tls as &mut dyn Any).downcast_mut::<Option<rustls::ClientConfig>>()
1265 {
1266 let tls = conn.take().expect("is definitely Some");
1267 let tls = crate::tls::TlsBackend::BuiltRustls(tls);
1268 self.config.tls = tls;
1269 return self;
1270 }
1271 }
1272
1273 self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured;
1275 self
1276 }
1277
1278 #[cfg(feature = "trust-dns")]
1286 #[cfg_attr(docsrs, doc(cfg(feature = "trust-dns")))]
1287 pub fn trust_dns(mut self, enable: bool) -> ClientBuilder {
1288 self.config.trust_dns = enable;
1289 self
1290 }
1291
1292 pub fn no_trust_dns(self) -> ClientBuilder {
1298 #[cfg(feature = "trust-dns")]
1299 {
1300 self.trust_dns(false)
1301 }
1302
1303 #[cfg(not(feature = "trust-dns"))]
1304 {
1305 self
1306 }
1307 }
1308
1309 pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
1313 self.config.https_only = enabled;
1314 self
1315 }
1316
1317 pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
1326 self.resolve_to_addrs(domain, &[addr])
1327 }
1328
1329 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder {
1338 self.config
1339 .dns_overrides
1340 .insert(domain.to_string(), addrs.to_vec());
1341 self
1342 }
1343}
1344
1345type HyperClient = hyper::Client<Connector, super::body::ImplStream>;
1346
1347impl Default for Client {
1348 fn default() -> Self {
1349 Self::new()
1350 }
1351}
1352
1353impl Client {
1354 pub fn new() -> Client {
1364 ClientBuilder::new().build().expect("Client::new()")
1365 }
1366
1367 pub fn builder() -> ClientBuilder {
1371 ClientBuilder::new()
1372 }
1373
1374 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1380 self.request(Method::GET, url)
1381 }
1382
1383 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1389 self.request(Method::POST, url)
1390 }
1391
1392 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1398 self.request(Method::PUT, url)
1399 }
1400
1401 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1407 self.request(Method::PATCH, url)
1408 }
1409
1410 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1416 self.request(Method::DELETE, url)
1417 }
1418
1419 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
1425 self.request(Method::HEAD, url)
1426 }
1427
1428 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
1437 let req = url.into_url().map(move |url| Request::new(method, url));
1438 RequestBuilder::new(self.clone(), req)
1439 }
1440
1441 pub fn execute(
1454 &self,
1455 request: Request,
1456 ) -> impl Future<Output = Result<Response, crate::Error>> {
1457 self.execute_request(request)
1458 }
1459
1460 pub(super) fn execute_request(&self, req: Request) -> Pending {
1461 let (method, url, mut headers, body, timeout, version) = req.pieces();
1462 if url.scheme() != "http" && url.scheme() != "https" {
1463 return Pending::new_err(error::url_bad_scheme(url));
1464 }
1465
1466 if self.inner.https_only && url.scheme() != "https" {
1468 return Pending::new_err(error::url_bad_scheme(url));
1469 }
1470
1471 for (key, value) in &self.inner.headers {
1474 if let Entry::Vacant(entry) = headers.entry(key) {
1475 entry.insert(value.clone());
1476 }
1477 }
1478
1479 #[cfg(feature = "cookies")]
1481 {
1482 if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
1483 if headers.get(crate::header::COOKIE).is_none() {
1484 add_cookie_header(&mut headers, &**cookie_store, &url);
1485 }
1486 }
1487 }
1488
1489 let accept_encoding = self.inner.accepts.as_str();
1490
1491 if let Some(accept_encoding) = accept_encoding {
1492 if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
1493 headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(accept_encoding));
1494 }
1495 }
1496
1497 let uri = expect_uri(&url);
1498
1499 let (reusable, body) = match body {
1500 Some(body) => {
1501 let (reusable, body) = body.try_reuse();
1502 (Some(reusable), body)
1503 }
1504 None => (None, Body::empty()),
1505 };
1506
1507 self.proxy_auth(&uri, &mut headers);
1508
1509 let mut req = hyper::Request::builder()
1510 .method(method.clone())
1511 .uri(uri)
1512 .version(version)
1513 .body(body.into_stream())
1514 .expect("valid request parts");
1515
1516 let timeout = timeout
1517 .or(self.inner.request_timeout)
1518 .map(tokio::time::sleep)
1519 .map(Box::pin);
1520
1521 *req.headers_mut() = headers.clone();
1522
1523 let in_flight = self.inner.hyper.request(req);
1524
1525 Pending {
1526 inner: PendingInner::Request(PendingRequest {
1527 method,
1528 url,
1529 headers,
1530 body: reusable,
1531
1532 urls: Vec::new(),
1533
1534 retry_count: 0,
1535
1536 client: self.inner.clone(),
1537
1538 in_flight,
1539 timeout,
1540 }),
1541 }
1542 }
1543
1544 fn proxy_auth(&self, dst: &Uri, headers: &mut HeaderMap) {
1545 if !self.inner.proxies_maybe_http_auth {
1546 return;
1547 }
1548
1549 if dst.scheme() != Some(&Scheme::HTTP) {
1553 return;
1554 }
1555
1556 if headers.contains_key(PROXY_AUTHORIZATION) {
1557 return;
1558 }
1559
1560 for proxy in self.inner.proxies.iter() {
1561 if proxy.is_match(dst) {
1562 if let Some(header) = proxy.http_basic_auth(dst) {
1563 headers.insert(PROXY_AUTHORIZATION, header);
1564 }
1565
1566 break;
1567 }
1568 }
1569 }
1570}
1571
1572impl fmt::Debug for Client {
1573 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1574 let mut builder = f.debug_struct("Client");
1575 self.inner.fmt_fields(&mut builder);
1576 builder.finish()
1577 }
1578}
1579
1580impl tower_service::Service<Request> for Client {
1581 type Response = Response;
1582 type Error = crate::Error;
1583 type Future = Pending;
1584
1585 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1586 Poll::Ready(Ok(()))
1587 }
1588
1589 fn call(&mut self, req: Request) -> Self::Future {
1590 self.execute_request(req)
1591 }
1592}
1593
1594impl tower_service::Service<Request> for &'_ Client {
1595 type Response = Response;
1596 type Error = crate::Error;
1597 type Future = Pending;
1598
1599 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1600 Poll::Ready(Ok(()))
1601 }
1602
1603 fn call(&mut self, req: Request) -> Self::Future {
1604 self.execute_request(req)
1605 }
1606}
1607
1608impl fmt::Debug for ClientBuilder {
1609 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1610 let mut builder = f.debug_struct("ClientBuilder");
1611 self.config.fmt_fields(&mut builder);
1612 builder.finish()
1613 }
1614}
1615
1616impl Config {
1617 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
1618 #[cfg(feature = "cookies")]
1622 {
1623 if let Some(_) = self.cookie_store {
1624 f.field("cookie_store", &true);
1625 }
1626 }
1627
1628 f.field("accepts", &self.accepts);
1629
1630 if !self.proxies.is_empty() {
1631 f.field("proxies", &self.proxies);
1632 }
1633
1634 if !self.redirect_policy.is_default() {
1635 f.field("redirect_policy", &self.redirect_policy);
1636 }
1637
1638 if self.referer {
1639 f.field("referer", &true);
1640 }
1641
1642 f.field("default_headers", &self.headers);
1643
1644 if self.http1_title_case_headers {
1645 f.field("http1_title_case_headers", &true);
1646 }
1647
1648 if self.http1_allow_obsolete_multiline_headers_in_responses {
1649 f.field("http1_allow_obsolete_multiline_headers_in_responses", &true);
1650 }
1651
1652 if matches!(self.http_version_pref, HttpVersionPref::Http1) {
1653 f.field("http1_only", &true);
1654 }
1655
1656 if matches!(self.http_version_pref, HttpVersionPref::Http2) {
1657 f.field("http2_prior_knowledge", &true);
1658 }
1659
1660 if let Some(ref d) = self.connect_timeout {
1661 f.field("connect_timeout", d);
1662 }
1663
1664 if let Some(ref d) = self.timeout {
1665 f.field("timeout", d);
1666 }
1667
1668 if let Some(ref v) = self.local_address {
1669 f.field("local_address", v);
1670 }
1671
1672 if self.nodelay {
1673 f.field("tcp_nodelay", &true);
1674 }
1675
1676 #[cfg(feature = "native-tls")]
1677 {
1678 if !self.hostname_verification {
1679 f.field("danger_accept_invalid_hostnames", &true);
1680 }
1681 }
1682
1683 #[cfg(feature = "__tls")]
1684 {
1685 if !self.certs_verification {
1686 f.field("danger_accept_invalid_certs", &true);
1687 }
1688
1689 if let Some(ref min_tls_version) = self.min_tls_version {
1690 f.field("min_tls_version", min_tls_version);
1691 }
1692
1693 if let Some(ref max_tls_version) = self.max_tls_version {
1694 f.field("max_tls_version", max_tls_version);
1695 }
1696 }
1697
1698 #[cfg(all(feature = "native-tls-crate", feature = "__rustls"))]
1699 {
1700 f.field("tls_backend", &self.tls);
1701 }
1702
1703 if !self.dns_overrides.is_empty() {
1704 f.field("dns_overrides", &self.dns_overrides);
1705 }
1706 }
1707}
1708
1709struct ClientRef {
1710 accepts: Accepts,
1711 #[cfg(feature = "cookies")]
1712 cookie_store: Option<Arc<dyn cookie::CookieStore>>,
1713 headers: HeaderMap,
1714 hyper: HyperClient,
1715 redirect_policy: redirect::Policy,
1716 referer: bool,
1717 request_timeout: Option<Duration>,
1718 proxies: Arc<Vec<Proxy>>,
1719 proxies_maybe_http_auth: bool,
1720 https_only: bool,
1721}
1722
1723impl ClientRef {
1724 fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
1725 #[cfg(feature = "cookies")]
1729 {
1730 if let Some(_) = self.cookie_store {
1731 f.field("cookie_store", &true);
1732 }
1733 }
1734
1735 f.field("accepts", &self.accepts);
1736
1737 if !self.proxies.is_empty() {
1738 f.field("proxies", &self.proxies);
1739 }
1740
1741 if !self.redirect_policy.is_default() {
1742 f.field("redirect_policy", &self.redirect_policy);
1743 }
1744
1745 if self.referer {
1746 f.field("referer", &true);
1747 }
1748
1749 f.field("default_headers", &self.headers);
1750
1751 if let Some(ref d) = self.request_timeout {
1752 f.field("timeout", d);
1753 }
1754 }
1755}
1756
1757pin_project! {
1758 pub struct Pending {
1759 #[pin]
1760 inner: PendingInner,
1761 }
1762}
1763
1764enum PendingInner {
1765 Request(PendingRequest),
1766 Error(Option<crate::Error>),
1767}
1768
1769pin_project! {
1770 struct PendingRequest {
1771 method: Method,
1772 url: Url,
1773 headers: HeaderMap,
1774 body: Option<Option<Bytes>>,
1775
1776 urls: Vec<Url>,
1777
1778 retry_count: usize,
1779
1780 client: Arc<ClientRef>,
1781
1782 #[pin]
1783 in_flight: ResponseFuture,
1784 #[pin]
1785 timeout: Option<Pin<Box<Sleep>>>,
1786 }
1787}
1788
1789impl PendingRequest {
1790 fn in_flight(self: Pin<&mut Self>) -> Pin<&mut ResponseFuture> {
1791 self.project().in_flight
1792 }
1793
1794 fn timeout(self: Pin<&mut Self>) -> Pin<&mut Option<Pin<Box<Sleep>>>> {
1795 self.project().timeout
1796 }
1797
1798 fn urls(self: Pin<&mut Self>) -> &mut Vec<Url> {
1799 self.project().urls
1800 }
1801
1802 fn headers(self: Pin<&mut Self>) -> &mut HeaderMap {
1803 self.project().headers
1804 }
1805
1806 fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
1807 if !is_retryable_error(err) {
1808 return false;
1809 }
1810
1811 trace!("can retry {:?}", err);
1812
1813 let body = match self.body {
1814 Some(Some(ref body)) => Body::reusable(body.clone()),
1815 Some(None) => {
1816 debug!("error was retryable, but body not reusable");
1817 return false;
1818 }
1819 None => Body::empty(),
1820 };
1821
1822 if self.retry_count >= 2 {
1823 trace!("retry count too high");
1824 return false;
1825 }
1826 self.retry_count += 1;
1827
1828 let uri = expect_uri(&self.url);
1829 let mut req = hyper::Request::builder()
1830 .method(self.method.clone())
1831 .uri(uri)
1832 .body(body.into_stream())
1833 .expect("valid request parts");
1834
1835 *req.headers_mut() = self.headers.clone();
1836
1837 *self.as_mut().in_flight().get_mut() = self.client.hyper.request(req);
1838
1839 true
1840 }
1841}
1842
1843fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
1844 if let Some(cause) = err.source() {
1845 if let Some(err) = cause.downcast_ref::<h2::Error>() {
1846 return err.is_go_away()
1848 && err.is_remote()
1849 && err.reason() == Some(h2::Reason::NO_ERROR);
1850 }
1851 }
1852 false
1853}
1854
1855impl Pending {
1856 pub(super) fn new_err(err: crate::Error) -> Pending {
1857 Pending {
1858 inner: PendingInner::Error(Some(err)),
1859 }
1860 }
1861
1862 fn inner(self: Pin<&mut Self>) -> Pin<&mut PendingInner> {
1863 self.project().inner
1864 }
1865}
1866
1867impl Future for Pending {
1868 type Output = Result<Response, crate::Error>;
1869
1870 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1871 let inner = self.inner();
1872 match inner.get_mut() {
1873 PendingInner::Request(ref mut req) => Pin::new(req).poll(cx),
1874 PendingInner::Error(ref mut err) => Poll::Ready(Err(err
1875 .take()
1876 .expect("Pending error polled more than once"))),
1877 }
1878 }
1879}
1880
1881impl Future for PendingRequest {
1882 type Output = Result<Response, crate::Error>;
1883
1884 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1885 if let Some(delay) = self.as_mut().timeout().as_mut().as_pin_mut() {
1886 if let Poll::Ready(()) = delay.poll(cx) {
1887 return Poll::Ready(Err(
1888 crate::error::request(crate::error::TimedOut).with_url(self.url.clone())
1889 ));
1890 }
1891 }
1892
1893 loop {
1894 let res = match self.as_mut().in_flight().as_mut().poll(cx) {
1895 Poll::Ready(Err(e)) => {
1896 if self.as_mut().retry_error(&e) {
1897 continue;
1898 }
1899 return Poll::Ready(Err(crate::error::request(e).with_url(self.url.clone())));
1900 }
1901 Poll::Ready(Ok(res)) => res,
1902 Poll::Pending => return Poll::Pending,
1903 };
1904
1905 #[cfg(feature = "cookies")]
1906 {
1907 if let Some(ref cookie_store) = self.client.cookie_store {
1908 let mut cookies =
1909 cookie::extract_response_cookie_headers(&res.headers()).peekable();
1910 if cookies.peek().is_some() {
1911 cookie_store.set_cookies(&mut cookies, &self.url);
1912 }
1913 }
1914 }
1915 let should_redirect = match res.status() {
1916 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
1917 self.body = None;
1918 for header in &[
1919 TRANSFER_ENCODING,
1920 CONTENT_ENCODING,
1921 CONTENT_TYPE,
1922 CONTENT_LENGTH,
1923 ] {
1924 self.headers.remove(header);
1925 }
1926
1927 match self.method {
1928 Method::GET | Method::HEAD => {}
1929 _ => {
1930 self.method = Method::GET;
1931 }
1932 }
1933 true
1934 }
1935 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
1936 match self.body {
1937 Some(Some(_)) | None => true,
1938 Some(None) => false,
1939 }
1940 }
1941 _ => false,
1942 };
1943 if should_redirect {
1944 let loc = res.headers().get(LOCATION).and_then(|val| {
1945 let loc = (|| -> Option<Url> {
1946 self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
1950 })();
1951
1952 let loc = loc.and_then(|url| {
1956 if try_uri(&url).is_some() {
1957 Some(url)
1958 } else {
1959 None
1960 }
1961 });
1962
1963 if loc.is_none() {
1964 debug!("Location header had invalid URI: {:?}", val);
1965 }
1966 loc
1967 });
1968 if let Some(loc) = loc {
1969 if self.client.referer {
1970 if let Some(referer) = make_referer(&loc, &self.url) {
1971 self.headers.insert(REFERER, referer);
1972 }
1973 }
1974 let url = self.url.clone();
1975 self.as_mut().urls().push(url);
1976 let action = self
1977 .client
1978 .redirect_policy
1979 .check(res.status(), &loc, &self.urls);
1980
1981 match action {
1982 redirect::ActionKind::Follow => {
1983 debug!("redirecting '{}' to '{}'", self.url, loc);
1984
1985 if self.client.https_only && loc.scheme() != "https" {
1986 return Poll::Ready(Err(error::redirect(
1987 error::url_bad_scheme(loc.clone()),
1988 loc,
1989 )));
1990 }
1991
1992 self.url = loc;
1993 let mut headers =
1994 std::mem::replace(self.as_mut().headers(), HeaderMap::new());
1995
1996 remove_sensitive_headers(&mut headers, &self.url, &self.urls);
1997 let uri = expect_uri(&self.url);
1998 let body = match self.body {
1999 Some(Some(ref body)) => Body::reusable(body.clone()),
2000 _ => Body::empty(),
2001 };
2002 let mut req = hyper::Request::builder()
2003 .method(self.method.clone())
2004 .uri(uri.clone())
2005 .body(body.into_stream())
2006 .expect("valid request parts");
2007
2008 #[cfg(feature = "cookies")]
2010 {
2011 if let Some(ref cookie_store) = self.client.cookie_store {
2012 add_cookie_header(&mut headers, &**cookie_store, &self.url);
2013 }
2014 }
2015
2016 *req.headers_mut() = headers.clone();
2017 std::mem::swap(self.as_mut().headers(), &mut headers);
2018 *self.as_mut().in_flight().get_mut() = self.client.hyper.request(req);
2019 continue;
2020 }
2021 redirect::ActionKind::Stop => {
2022 debug!("redirect policy disallowed redirection to '{}'", loc);
2023 }
2024 redirect::ActionKind::Error(err) => {
2025 return Poll::Ready(Err(crate::error::redirect(err, self.url.clone())));
2026 }
2027 }
2028 }
2029 }
2030
2031 let res = Response::new(
2032 res,
2033 self.url.clone(),
2034 self.client.accepts,
2035 self.timeout.take(),
2036 );
2037 return Poll::Ready(Ok(res));
2038 }
2039 }
2040}
2041
2042impl fmt::Debug for Pending {
2043 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2044 match self.inner {
2045 PendingInner::Request(ref req) => f
2046 .debug_struct("Pending")
2047 .field("method", &req.method)
2048 .field("url", &req.url)
2049 .finish(),
2050 PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(),
2051 }
2052 }
2053}
2054
2055fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
2056 if next.scheme() == "http" && previous.scheme() == "https" {
2057 return None;
2058 }
2059
2060 let mut referer = previous.clone();
2061 let _ = referer.set_username("");
2062 let _ = referer.set_password(None);
2063 referer.set_fragment(None);
2064 referer.as_str().parse().ok()
2065}
2066
2067#[cfg(feature = "cookies")]
2068fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
2069 if let Some(header) = cookie_store.cookies(url) {
2070 headers.insert(crate::header::COOKIE, header);
2071 }
2072}
2073
2074#[cfg(test)]
2075mod tests {
2076 #[tokio::test]
2077 async fn execute_request_rejects_invald_urls() {
2078 let url_str = "hxxps://www.rust-lang.org/";
2079 let url = url::Url::parse(url_str).unwrap();
2080 let result = crate::get(url.clone()).await;
2081
2082 assert!(result.is_err());
2083 let err = result.err().unwrap();
2084 assert!(err.is_builder());
2085 assert_eq!(url_str, err.url().unwrap().as_str());
2086 }
2087}