diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index bc0e518dd..0719ba504 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -17,7 +17,9 @@ use super::Body; use crate::async_impl::h3_client::connect::H3Connector; #[cfg(feature = "http3")] use crate::async_impl::h3_client::{H3Client, H3ResponseFuture}; -use crate::connect::{Conn, Connector, ConnectorBuilder, ConnectorLayerBuilder, ConnectorService}; +use crate::connect::{ + BoxedConnectorLayer, BoxedConnectorService, Conn, Connector, ConnectorBuilder, +}; #[cfg(feature = "cookies")] use crate::cookie; #[cfg(feature = "hickory-dns")] @@ -52,7 +54,8 @@ use quinn::TransportConfig; #[cfg(feature = "http3")] use quinn::VarInt; use tokio::time::Sleep; -use tower::{layer::util::Stack, Layer, Service, ServiceBuilder}; +use tower::util::BoxCloneSyncServiceLayer; +use tower::{Layer, Service}; type HyperResponseFuture = hyper_util::client::legacy::ResponseFuture; @@ -76,10 +79,8 @@ pub struct Client { /// A `ClientBuilder` can be used to create a `Client` with custom configuration. #[must_use] -pub struct ClientBuilder { +pub struct ClientBuilder { config: Config, - // separated out to simplify casting between generic types while copying config - connector_layers: ConnectorLayerBuilder, } enum HttpVersionPref { @@ -132,6 +133,7 @@ struct Config { tls_info: bool, #[cfg(feature = "__tls")] tls: TlsBackend, + connector_layers: Vec, http_version_pref: HttpVersionPref, http09_responses: bool, http1_title_case_headers: bool, @@ -177,13 +179,13 @@ struct Config { dns_resolver: Option>, } -impl Default for ClientBuilder { +impl Default for ClientBuilder { fn default() -> Self { Self::new() } } -impl ClientBuilder { +impl ClientBuilder { /// Constructs a new `ClientBuilder`. /// /// This is the same as `Client::builder()`. @@ -235,6 +237,7 @@ impl ClientBuilder { tls_info: false, #[cfg(feature = "__tls")] tls: TlsBackend::default(), + connector_layers: Vec::new(), http_version_pref: HttpVersionPref::All, http09_responses: false, http1_title_case_headers: false, @@ -278,21 +281,11 @@ impl ClientBuilder { quic_send_window: None, dns_resolver: None, }, - connector_layers: ConnectorLayerBuilder { - builder: ServiceBuilder::new(), - has_custom_layers: false, - }, } } } -impl ClientBuilder -where - ConnectorLayers: Layer, - ConnectorLayers::Service: - Service + Clone + Send + Sync + 'static, - <>::Service as Service>::Future: Send + 'static, -{ +impl ClientBuilder { /// Returns a `Client` that uses this `ClientBuilder` configuration. /// /// # Errors @@ -815,7 +808,7 @@ where } None => None, }, - hyper: builder.build(connector_builder.build(self.connector_layers)), + hyper: builder.build(connector_builder.build(config.connector_layers)), headers: config.headers, redirect_policy: config.redirect_policy, referer: config.referer, @@ -850,7 +843,7 @@ where /// # Ok(()) /// # } /// ``` - pub fn user_agent(mut self, value: V) -> ClientBuilder + pub fn user_agent(mut self, value: V) -> ClientBuilder where V: TryInto, V::Error: Into, @@ -888,7 +881,7 @@ where /// # Ok(()) /// # } /// ``` - pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder { + pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder { for (key, value) in headers.iter() { self.config.headers.insert(key, value.clone()); } @@ -911,7 +904,7 @@ where /// This requires the optional `cookies` feature to be enabled. #[cfg(feature = "cookies")] #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))] - pub fn cookie_store(mut self, enable: bool) -> ClientBuilder { + pub fn cookie_store(mut self, enable: bool) -> ClientBuilder { if enable { self.cookie_provider(Arc::new(cookie::Jar::default())) } else { @@ -938,7 +931,7 @@ where pub fn cookie_provider( mut self, cookie_store: Arc, - ) -> ClientBuilder { + ) -> ClientBuilder { self.config.cookie_store = Some(cookie_store as _); self } @@ -961,7 +954,7 @@ where /// This requires the optional `gzip` feature to be enabled #[cfg(feature = "gzip")] #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))] - pub fn gzip(mut self, enable: bool) -> ClientBuilder { + pub fn gzip(mut self, enable: bool) -> ClientBuilder { self.config.accepts.gzip = enable; self } @@ -984,7 +977,7 @@ where /// This requires the optional `brotli` feature to be enabled #[cfg(feature = "brotli")] #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))] - pub fn brotli(mut self, enable: bool) -> ClientBuilder { + pub fn brotli(mut self, enable: bool) -> ClientBuilder { self.config.accepts.brotli = enable; self } @@ -1007,7 +1000,7 @@ where /// This requires the optional `zstd` feature to be enabled #[cfg(feature = "zstd")] #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))] - pub fn zstd(mut self, enable: bool) -> ClientBuilder { + pub fn zstd(mut self, enable: bool) -> ClientBuilder { self.config.accepts.zstd = enable; self } @@ -1030,7 +1023,7 @@ where /// This requires the optional `deflate` feature to be enabled #[cfg(feature = "deflate")] #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))] - pub fn deflate(mut self, enable: bool) -> ClientBuilder { + pub fn deflate(mut self, enable: bool) -> ClientBuilder { self.config.accepts.deflate = enable; self } @@ -1040,7 +1033,7 @@ where /// This method exists even if the optional `gzip` feature is not enabled. /// This can be used to ensure a `Client` doesn't use gzip decompression /// even if another dependency were to enable the optional `gzip` feature. - pub fn no_gzip(self) -> ClientBuilder { + pub fn no_gzip(self) -> ClientBuilder { #[cfg(feature = "gzip")] { self.gzip(false) @@ -1057,7 +1050,7 @@ where /// This method exists even if the optional `brotli` feature is not enabled. /// This can be used to ensure a `Client` doesn't use brotli decompression /// even if another dependency were to enable the optional `brotli` feature. - pub fn no_brotli(self) -> ClientBuilder { + pub fn no_brotli(self) -> ClientBuilder { #[cfg(feature = "brotli")] { self.brotli(false) @@ -1074,7 +1067,7 @@ where /// This method exists even if the optional `zstd` feature is not enabled. /// This can be used to ensure a `Client` doesn't use zstd decompression /// even if another dependency were to enable the optional `zstd` feature. - pub fn no_zstd(self) -> ClientBuilder { + pub fn no_zstd(self) -> ClientBuilder { #[cfg(feature = "zstd")] { self.zstd(false) @@ -1091,7 +1084,7 @@ where /// This method exists even if the optional `deflate` feature is not enabled. /// This can be used to ensure a `Client` doesn't use deflate decompression /// even if another dependency were to enable the optional `deflate` feature. - pub fn no_deflate(self) -> ClientBuilder { + pub fn no_deflate(self) -> ClientBuilder { #[cfg(feature = "deflate")] { self.deflate(false) @@ -1108,7 +1101,7 @@ where /// Set a `RedirectPolicy` for this client. /// /// Default will follow redirects up to a maximum of 10. - pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder { + pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder { self.config.redirect_policy = policy; self } @@ -1116,7 +1109,7 @@ where /// Enable or disable automatic setting of the `Referer` header. /// /// Default is `true`. - pub fn referer(mut self, enable: bool) -> ClientBuilder { + pub fn referer(mut self, enable: bool) -> ClientBuilder { self.config.referer = enable; self } @@ -1128,7 +1121,7 @@ where /// # Note /// /// Adding a proxy will disable the automatic usage of the "system" proxy. - pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder { + pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder { self.config.proxies.push(proxy); self.config.auto_sys_proxy = false; self @@ -1141,7 +1134,7 @@ where /// on all desired proxies instead. /// /// This also disables the automatic usage of the "system" proxy. - pub fn no_proxy(mut self) -> ClientBuilder { + pub fn no_proxy(mut self) -> ClientBuilder { self.config.proxies.clear(); self.config.auto_sys_proxy = false; self @@ -1155,7 +1148,7 @@ where /// response body has finished. Also considered a total deadline. /// /// Default is no timeout. - pub fn timeout(mut self, timeout: Duration) -> ClientBuilder { + pub fn timeout(mut self, timeout: Duration) -> ClientBuilder { self.config.timeout = Some(timeout); self } @@ -1167,7 +1160,7 @@ where /// connections when the size isn't known beforehand. /// /// Default is no timeout. - pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder { + pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder { self.config.read_timeout = Some(timeout); self } @@ -1180,7 +1173,7 @@ where /// /// This **requires** the futures be executed in a tokio runtime with /// a tokio timer enabled. - pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder { + pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder { self.config.connect_timeout = Some(timeout); self } @@ -1191,7 +1184,7 @@ where /// for read and write operations on connections. /// /// [log]: https://crates.io/crates/log - pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder { + pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder { self.config.connection_verbose = verbose; self } @@ -1203,7 +1196,7 @@ where /// Pass `None` to disable timeout. /// /// Default is 90 seconds. - pub fn pool_idle_timeout(mut self, val: D) -> ClientBuilder + pub fn pool_idle_timeout(mut self, val: D) -> ClientBuilder where D: Into>, { @@ -1212,13 +1205,13 @@ where } /// Sets the maximum idle connection per host allowed in the pool. - pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder { + pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder { self.config.pool_max_idle_per_host = max; self } /// Send headers as title case instead of lowercase. - pub fn http1_title_case_headers(mut self) -> ClientBuilder { + pub fn http1_title_case_headers(mut self) -> ClientBuilder { self.config.http1_title_case_headers = true; self } @@ -1231,17 +1224,14 @@ where pub fn http1_allow_obsolete_multiline_headers_in_responses( mut self, value: bool, - ) -> ClientBuilder { + ) -> ClientBuilder { self.config .http1_allow_obsolete_multiline_headers_in_responses = value; self } /// Sets whether invalid header lines should be silently ignored in HTTP/1 responses. - pub fn http1_ignore_invalid_headers_in_responses( - mut self, - value: bool, - ) -> ClientBuilder { + pub fn http1_ignore_invalid_headers_in_responses(mut self, value: bool) -> ClientBuilder { self.config.http1_ignore_invalid_headers_in_responses = value; self } @@ -1254,20 +1244,20 @@ where pub fn http1_allow_spaces_after_header_name_in_responses( mut self, value: bool, - ) -> ClientBuilder { + ) -> ClientBuilder { self.config .http1_allow_spaces_after_header_name_in_responses = value; self } /// Only use HTTP/1. - pub fn http1_only(mut self) -> ClientBuilder { + pub fn http1_only(mut self) -> ClientBuilder { self.config.http_version_pref = HttpVersionPref::Http1; self } /// Allow HTTP/0.9 responses - pub fn http09_responses(mut self) -> ClientBuilder { + pub fn http09_responses(mut self) -> ClientBuilder { self.config.http09_responses = true; self } @@ -1275,7 +1265,7 @@ where /// Only use HTTP/2. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_prior_knowledge(mut self) -> ClientBuilder { + pub fn http2_prior_knowledge(mut self) -> ClientBuilder { self.config.http_version_pref = HttpVersionPref::Http2; self } @@ -1283,7 +1273,7 @@ where /// Only use HTTP/3. #[cfg(feature = "http3")] #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))] - pub fn http3_prior_knowledge(mut self) -> ClientBuilder { + pub fn http3_prior_knowledge(mut self) -> ClientBuilder { self.config.http_version_pref = HttpVersionPref::Http3; self } @@ -1293,10 +1283,7 @@ where /// Default is currently 65,535 but may change internally to optimize for common uses. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_initial_stream_window_size( - mut self, - sz: impl Into>, - ) -> ClientBuilder { + pub fn http2_initial_stream_window_size(mut self, sz: impl Into>) -> ClientBuilder { self.config.http2_initial_stream_window_size = sz.into(); self } @@ -1309,7 +1296,7 @@ where pub fn http2_initial_connection_window_size( mut self, sz: impl Into>, - ) -> ClientBuilder { + ) -> ClientBuilder { self.config.http2_initial_connection_window_size = sz.into(); self } @@ -1320,7 +1307,7 @@ where /// `http2_initial_connection_window_size`. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder { + pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder { self.config.http2_adaptive_window = enabled; self } @@ -1330,10 +1317,7 @@ where /// Default is currently 16,384 but may change internally to optimize for common uses. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_max_frame_size( - mut self, - sz: impl Into>, - ) -> ClientBuilder { + pub fn http2_max_frame_size(mut self, sz: impl Into>) -> ClientBuilder { self.config.http2_max_frame_size = sz.into(); self } @@ -1343,10 +1327,7 @@ where /// Default is currently 16KB, but can change. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_max_header_list_size( - mut self, - max_header_size_bytes: u32, - ) -> ClientBuilder { + pub fn http2_max_header_list_size(mut self, max_header_size_bytes: u32) -> ClientBuilder { self.config.http2_max_header_list_size = Some(max_header_size_bytes); self } @@ -1360,7 +1341,7 @@ where pub fn http2_keep_alive_interval( mut self, interval: impl Into>, - ) -> ClientBuilder { + ) -> ClientBuilder { self.config.http2_keep_alive_interval = interval.into(); self } @@ -1372,7 +1353,7 @@ where /// Default is currently disabled. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder { + pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder { self.config.http2_keep_alive_timeout = Some(timeout); self } @@ -1385,7 +1366,7 @@ where /// Default is `false`. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder { + pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder { self.config.http2_keep_alive_while_idle = enabled; self } @@ -1395,7 +1376,7 @@ where /// Set whether sockets have `TCP_NODELAY` enabled. /// /// Default is `true`. - pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder { + pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder { self.config.nodelay = enabled; self } @@ -1413,7 +1394,7 @@ where /// .local_address(local_addr) /// .build().unwrap(); /// ``` - pub fn local_address(mut self, addr: T) -> ClientBuilder + pub fn local_address(mut self, addr: T) -> ClientBuilder where T: Into>, { @@ -1434,7 +1415,7 @@ where /// .build().unwrap(); /// ``` #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] - pub fn interface(mut self, interface: &str) -> ClientBuilder { + pub fn interface(mut self, interface: &str) -> ClientBuilder { self.config.interface = Some(interface.to_string()); self } @@ -1442,7 +1423,7 @@ where /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration. /// /// If `None`, the option will not be set. - pub fn tcp_keepalive(mut self, val: D) -> ClientBuilder + pub fn tcp_keepalive(mut self, val: D) -> ClientBuilder where D: Into>, { @@ -1470,7 +1451,7 @@ where feature = "rustls-tls" ))) )] - pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder { + pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder { self.config.root_certs.push(cert); self } @@ -1483,7 +1464,7 @@ where /// This requires the `rustls-tls(-...)` Cargo feature enabled. #[cfg(feature = "__rustls")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))] - pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder { + pub fn add_crl(mut self, crl: CertificateRevocationList) -> ClientBuilder { self.config.crls.push(crl); self } @@ -1499,7 +1480,7 @@ where pub fn add_crls( mut self, crls: impl IntoIterator, - ) -> ClientBuilder { + ) -> ClientBuilder { self.config.crls.extend(crls); self } @@ -1530,10 +1511,7 @@ where feature = "rustls-tls" ))) )] - pub fn tls_built_in_root_certs( - mut self, - tls_built_in_root_certs: bool, - ) -> ClientBuilder { + pub fn tls_built_in_root_certs(mut self, tls_built_in_root_certs: bool) -> ClientBuilder { self.config.tls_built_in_root_certs = tls_built_in_root_certs; #[cfg(feature = "rustls-tls-webpki-roots-no-provider")] @@ -1554,7 +1532,7 @@ where /// If the feature is enabled, this value is `true` by default. #[cfg(feature = "rustls-tls-webpki-roots-no-provider")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))] - pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder { + pub fn tls_built_in_webpki_certs(mut self, enabled: bool) -> ClientBuilder { self.config.tls_built_in_certs_webpki = enabled; self } @@ -1564,7 +1542,7 @@ where /// If the feature is enabled, this value is `true` by default. #[cfg(feature = "rustls-tls-native-roots-no-provider")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))] - pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder { + pub fn tls_built_in_native_certs(mut self, enabled: bool) -> ClientBuilder { self.config.tls_built_in_certs_native = enabled; self } @@ -1577,7 +1555,7 @@ where /// enabled. #[cfg(any(feature = "native-tls", feature = "__rustls"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))] - pub fn identity(mut self, identity: Identity) -> ClientBuilder { + pub fn identity(mut self, identity: Identity) -> ClientBuilder { self.config.identity = Some(identity); self } @@ -1609,7 +1587,7 @@ where pub fn danger_accept_invalid_hostnames( mut self, accept_invalid_hostname: bool, - ) -> ClientBuilder { + ) -> ClientBuilder { self.config.hostname_verification = !accept_invalid_hostname; self } @@ -1639,10 +1617,7 @@ where feature = "rustls-tls" ))) )] - pub fn danger_accept_invalid_certs( - mut self, - accept_invalid_certs: bool, - ) -> ClientBuilder { + pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder { self.config.certs_verification = !accept_invalid_certs; self } @@ -1664,7 +1639,7 @@ where feature = "rustls-tls" ))) )] - pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder { + pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder { self.config.tls_sni = tls_sni; self } @@ -1693,7 +1668,7 @@ where feature = "rustls-tls" ))) )] - pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder { + pub fn min_tls_version(mut self, version: tls::Version) -> ClientBuilder { self.config.min_tls_version = Some(version); self } @@ -1725,7 +1700,7 @@ where feature = "rustls-tls" ))) )] - pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder { + pub fn max_tls_version(mut self, version: tls::Version) -> ClientBuilder { self.config.max_tls_version = Some(version); self } @@ -1740,7 +1715,7 @@ where /// This requires the optional `native-tls` feature to be enabled. #[cfg(feature = "native-tls")] #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))] - pub fn use_native_tls(mut self) -> ClientBuilder { + pub fn use_native_tls(mut self) -> ClientBuilder { self.config.tls = TlsBackend::Default; self } @@ -1755,7 +1730,7 @@ where /// This requires the optional `rustls-tls(-...)` feature to be enabled. #[cfg(feature = "__rustls")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))] - pub fn use_rustls_tls(mut self) -> ClientBuilder { + pub fn use_rustls_tls(mut self) -> ClientBuilder { self.config.tls = TlsBackend::Rustls; self } @@ -1780,7 +1755,7 @@ where /// `rustls-tls(-...)` to be enabled. #[cfg(any(feature = "native-tls", feature = "__rustls",))] #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))] - pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder { + pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder { let mut tls = Some(tls); #[cfg(feature = "native-tls")] { @@ -1823,7 +1798,7 @@ where feature = "rustls-tls" ))) )] - pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder { + pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder { self.config.tls_info = tls_info; self } @@ -1831,7 +1806,7 @@ where /// Restrict the Client to be used with HTTPS only requests. /// /// Defaults to false. - pub fn https_only(mut self, enabled: bool) -> ClientBuilder { + pub fn https_only(mut self, enabled: bool) -> ClientBuilder { self.config.https_only = enabled; self } @@ -1840,7 +1815,7 @@ where #[cfg(feature = "hickory-dns")] #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))] #[deprecated(note = "use `hickory_dns` instead")] - pub fn trust_dns(mut self, enable: bool) -> ClientBuilder { + pub fn trust_dns(mut self, enable: bool) -> ClientBuilder { self.config.hickory_dns = enable; self } @@ -1860,14 +1835,14 @@ where /// that the default resolver does #[cfg(feature = "hickory-dns")] #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))] - pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder { + pub fn hickory_dns(mut self, enable: bool) -> ClientBuilder { self.config.hickory_dns = enable; self } #[doc(hidden)] #[deprecated(note = "use `no_hickory_dns` instead")] - pub fn no_trust_dns(self) -> ClientBuilder { + pub fn no_trust_dns(self) -> ClientBuilder { self.no_hickory_dns() } @@ -1876,7 +1851,7 @@ where /// This method exists even if the optional `hickory-dns` feature is not enabled. /// This can be used to ensure a `Client` doesn't use the hickory-dns async resolver /// even if another dependency were to enable the optional `hickory-dns` feature. - pub fn no_hickory_dns(self) -> ClientBuilder { + pub fn no_hickory_dns(self) -> ClientBuilder { #[cfg(feature = "hickory-dns")] { self.hickory_dns(false) @@ -1892,7 +1867,7 @@ where /// /// Set the port to `0` to use the conventional port for the given scheme (e.g. 80 for http). /// Ports in the URL itself will always be used instead of the port in the overridden addr. - pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder { + pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder { self.resolve_to_addrs(domain, &[addr]) } @@ -1900,11 +1875,7 @@ where /// /// Set the port to `0` to use the conventional port for the given scheme (e.g. 80 for http). /// Ports in the URL itself will always be used instead of the port in the overridden addr. - pub fn resolve_to_addrs( - mut self, - domain: &str, - addrs: &[SocketAddr], - ) -> ClientBuilder { + pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder { self.config .dns_overrides .insert(domain.to_ascii_lowercase(), addrs.to_vec()); @@ -1916,10 +1887,7 @@ where /// Pass an `Arc` wrapping a trait object implementing `Resolve`. /// Overrides for specific names passed to `resolve` and `resolve_to_addrs` will /// still be applied on top of this resolver. - pub fn dns_resolver( - mut self, - resolver: Arc, - ) -> ClientBuilder { + pub fn dns_resolver(mut self, resolver: Arc) -> ClientBuilder { self.config.dns_resolver = Some(resolver as _); self } @@ -1930,7 +1898,7 @@ where /// The default is false. #[cfg(feature = "http3")] #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))] - pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder { + pub fn tls_early_data(mut self, enabled: bool) -> ClientBuilder { self.config.tls_enable_early_data = enabled; self } @@ -1942,7 +1910,7 @@ where /// [`TransportConfig`]: https://docs.rs/quinn/latest/quinn/struct.TransportConfig.html #[cfg(feature = "http3")] #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))] - pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder { + pub fn http3_max_idle_timeout(mut self, value: Duration) -> ClientBuilder { self.config.quic_max_idle_timeout = Some(value); self } @@ -1959,7 +1927,7 @@ where /// Panics if the value is over 2^62. #[cfg(feature = "http3")] #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))] - pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder { + pub fn http3_stream_receive_window(mut self, value: u64) -> ClientBuilder { self.config.quic_stream_receive_window = Some(value.try_into().unwrap()); self } @@ -1976,7 +1944,7 @@ where /// Panics if the value is over 2^62. #[cfg(feature = "http3")] #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))] - pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder { + pub fn http3_conn_receive_window(mut self, value: u64) -> ClientBuilder { self.config.quic_receive_window = Some(value.try_into().unwrap()); self } @@ -1988,49 +1956,45 @@ where /// [`TransportConfig`]: https://docs.rs/quinn/latest/quinn/struct.TransportConfig.html #[cfg(feature = "http3")] #[cfg_attr(docsrs, doc(cfg(all(reqwest_unstable, feature = "http3",))))] - pub fn http3_send_window(mut self, value: u64) -> ClientBuilder { + pub fn http3_send_window(mut self, value: u64) -> ClientBuilder { self.config.quic_send_window = Some(value); self } /// Adds a new Tower [`Layer`](https://docs.rs/tower/latest/tower/trait.Layer.html) to the /// base connector [`Service`](https://docs.rs/tower/latest/tower/trait.Service.html) which - /// is responsible for connection establishment. + /// is responsible for connection establishment.a /// /// Each subsequent invocation of this function will wrap previous layers. /// /// If configured, the `connect_timeout` will be the outermost layer. /// - /// Simple example: + /// Example usage: /// ``` /// use std::time::Duration; /// /// # #[cfg(not(feature = "rustls-tls-no-provider"))] /// let client = reqwest::Client::builder() - /// // resolved to outermost layer, so before the semaphore permit is attempted - /// .connect_timeout(Duration::from_millis(100)) - /// // underneath the concurrency check, so only after a semaphore permit is acquired + /// // resolved to outermost layer, meaning while we are waiting on concurrency limit + /// .connect_timeout(Duration::from_millis(200)) + /// // underneath the concurrency check, so only after concurrency limit lets us through /// .connector_layer(tower::timeout::TimeoutLayer::new(Duration::from_millis(50))) /// .connector_layer(tower::limit::concurrency::ConcurrencyLimitLayer::new(2)) /// .build() /// .unwrap(); /// ``` /// - /// For a more complex example involving a custom layer, see `examples/connect_via_lower_priority_tokio_runtime.rs`. - pub fn connector_layer(self, layer: L) -> ClientBuilder> + pub fn connector_layer(mut self, layer: L) -> ClientBuilder where - S: Send + Sync + Clone + 'static, - L: Layer + Send + Sync + Clone + 'static, + L: Layer + Clone + Send + Sync + 'static, + L::Service: Service + Clone + Send + Sync + 'static, + >::Future: Send + 'static, { - let connector_layers = ConnectorLayerBuilder { - builder: self.connector_layers.builder.layer(layer), - has_custom_layers: true, - }; + let layer = BoxCloneSyncServiceLayer::new(layer); - ClientBuilder::> { - config: self.config, - connector_layers, - } + self.config.connector_layers.push(layer); + + self } } @@ -2059,7 +2023,7 @@ impl Client { /// Creates a `ClientBuilder` to configure a `Client`. /// /// This is the same as `ClientBuilder::new()`. - pub fn builder() -> ClientBuilder { + pub fn builder() -> ClientBuilder { ClientBuilder::new() } @@ -2316,7 +2280,7 @@ impl tower_service::Service for &'_ Client { } } -impl fmt::Debug for ClientBuilder { +impl fmt::Debug for ClientBuilder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut builder = f.debug_struct("ClientBuilder"); self.config.fmt_fields(&mut builder); diff --git a/src/blocking/client.rs b/src/blocking/client.rs index 5279d47f3..4ac51602b 100644 --- a/src/blocking/client.rs +++ b/src/blocking/client.rs @@ -13,15 +13,14 @@ use http::header::HeaderValue; use http::Uri; use log::{error, trace}; use tokio::sync::{mpsc, oneshot}; -use tower::layer::util::Stack; use tower::Layer; use tower::Service; use super::request::{Request, RequestBuilder}; use super::response::Response; use super::wait; +use crate::connect::BoxedConnectorService; use crate::connect::Conn; -use crate::connect::ConnectorService; use crate::dns::Resolve; use crate::error::BoxError; #[cfg(feature = "__tls")] @@ -76,18 +75,18 @@ pub struct Client { /// # } /// ``` #[must_use] -pub struct ClientBuilder { - inner: async_impl::ClientBuilder, +pub struct ClientBuilder { + inner: async_impl::ClientBuilder, timeout: Timeout, } -impl Default for ClientBuilder { +impl Default for ClientBuilder { fn default() -> Self { Self::new() } } -impl ClientBuilder { +impl ClientBuilder { /// Constructs a new `ClientBuilder`. /// /// This is the same as `Client::builder()`. @@ -99,13 +98,7 @@ impl ClientBuilder { } } -impl ClientBuilder -where - ConnectorLayers: Layer + Send + Sync + 'static, - ConnectorLayers::Service: - Service + Clone + Send + Sync + 'static, - <>::Service as Service>::Future: Send + 'static, -{ +impl ClientBuilder { /// Returns a `Client` that uses this `ClientBuilder` configuration. /// /// # Errors @@ -143,7 +136,7 @@ where /// # Ok(()) /// # } /// ``` - pub fn user_agent(self, value: V) -> ClientBuilder + pub fn user_agent(self, value: V) -> ClientBuilder where V: TryInto, V::Error: Into, @@ -175,7 +168,7 @@ where /// # Ok(()) /// # } /// ``` - pub fn default_headers(self, headers: header::HeaderMap) -> ClientBuilder { + pub fn default_headers(self, headers: header::HeaderMap) -> ClientBuilder { self.with_inner(move |inner| inner.default_headers(headers)) } @@ -191,7 +184,7 @@ where /// This requires the optional `cookies` feature to be enabled. #[cfg(feature = "cookies")] #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))] - pub fn cookie_store(self, enable: bool) -> ClientBuilder { + pub fn cookie_store(self, enable: bool) -> ClientBuilder { self.with_inner(|inner| inner.cookie_store(enable)) } @@ -210,7 +203,7 @@ where pub fn cookie_provider( self, cookie_store: Arc, - ) -> ClientBuilder { + ) -> ClientBuilder { self.with_inner(|inner| inner.cookie_provider(cookie_store)) } @@ -232,7 +225,7 @@ where /// This requires the optional `gzip` feature to be enabled #[cfg(feature = "gzip")] #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))] - pub fn gzip(self, enable: bool) -> ClientBuilder { + pub fn gzip(self, enable: bool) -> ClientBuilder { self.with_inner(|inner| inner.gzip(enable)) } @@ -254,7 +247,7 @@ where /// This requires the optional `brotli` feature to be enabled #[cfg(feature = "brotli")] #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))] - pub fn brotli(self, enable: bool) -> ClientBuilder { + pub fn brotli(self, enable: bool) -> ClientBuilder { self.with_inner(|inner| inner.brotli(enable)) } @@ -276,7 +269,7 @@ where /// This requires the optional `zstd` feature to be enabled #[cfg(feature = "zstd")] #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))] - pub fn zstd(self, enable: bool) -> ClientBuilder { + pub fn zstd(self, enable: bool) -> ClientBuilder { self.with_inner(|inner| inner.zstd(enable)) } @@ -298,7 +291,7 @@ where /// This requires the optional `deflate` feature to be enabled #[cfg(feature = "deflate")] #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))] - pub fn deflate(self, enable: bool) -> ClientBuilder { + pub fn deflate(self, enable: bool) -> ClientBuilder { self.with_inner(|inner| inner.deflate(enable)) } @@ -307,7 +300,7 @@ where /// This method exists even if the optional `gzip` feature is not enabled. /// This can be used to ensure a `Client` doesn't use gzip decompression /// even if another dependency were to enable the optional `gzip` feature. - pub fn no_gzip(self) -> ClientBuilder { + pub fn no_gzip(self) -> ClientBuilder { self.with_inner(|inner| inner.no_gzip()) } @@ -316,7 +309,7 @@ where /// This method exists even if the optional `brotli` feature is not enabled. /// This can be used to ensure a `Client` doesn't use brotli decompression /// even if another dependency were to enable the optional `brotli` feature. - pub fn no_brotli(self) -> ClientBuilder { + pub fn no_brotli(self) -> ClientBuilder { self.with_inner(|inner| inner.no_brotli()) } @@ -325,7 +318,7 @@ where /// This method exists even if the optional `zstd` feature is not enabled. /// This can be used to ensure a `Client` doesn't use zstd decompression /// even if another dependency were to enable the optional `zstd` feature. - pub fn no_zstd(self) -> ClientBuilder { + pub fn no_zstd(self) -> ClientBuilder { self.with_inner(|inner| inner.no_zstd()) } @@ -334,7 +327,7 @@ where /// This method exists even if the optional `deflate` feature is not enabled. /// This can be used to ensure a `Client` doesn't use deflate decompression /// even if another dependency were to enable the optional `deflate` feature. - pub fn no_deflate(self) -> ClientBuilder { + pub fn no_deflate(self) -> ClientBuilder { self.with_inner(|inner| inner.no_deflate()) } @@ -343,14 +336,14 @@ where /// Set a `redirect::Policy` for this client. /// /// Default will follow redirects up to a maximum of 10. - pub fn redirect(self, policy: redirect::Policy) -> ClientBuilder { + pub fn redirect(self, policy: redirect::Policy) -> ClientBuilder { self.with_inner(move |inner| inner.redirect(policy)) } /// Enable or disable automatic setting of the `Referer` header. /// /// Default is `true`. - pub fn referer(self, enable: bool) -> ClientBuilder { + pub fn referer(self, enable: bool) -> ClientBuilder { self.with_inner(|inner| inner.referer(enable)) } @@ -361,7 +354,7 @@ where /// # Note /// /// Adding a proxy will disable the automatic usage of the "system" proxy. - pub fn proxy(self, proxy: Proxy) -> ClientBuilder { + pub fn proxy(self, proxy: Proxy) -> ClientBuilder { self.with_inner(move |inner| inner.proxy(proxy)) } @@ -372,7 +365,7 @@ where /// on all desired proxies instead. /// /// This also disables the automatic usage of the "system" proxy. - pub fn no_proxy(self) -> ClientBuilder { + pub fn no_proxy(self) -> ClientBuilder { self.with_inner(move |inner| inner.no_proxy()) } @@ -383,7 +376,7 @@ where /// Default is 30 seconds. /// /// Pass `None` to disable timeout. - pub fn timeout(mut self, timeout: T) -> ClientBuilder + pub fn timeout(mut self, timeout: T) -> ClientBuilder where T: Into>, { @@ -394,7 +387,7 @@ where /// Set a timeout for only the connect phase of a `Client`. /// /// Default is `None`. - pub fn connect_timeout(self, timeout: T) -> ClientBuilder + pub fn connect_timeout(self, timeout: T) -> ClientBuilder where T: Into>, { @@ -412,7 +405,7 @@ where /// for read and write operations on connections. /// /// [log]: https://crates.io/crates/log - pub fn connection_verbose(self, verbose: bool) -> ClientBuilder { + pub fn connection_verbose(self, verbose: bool) -> ClientBuilder { self.with_inner(move |inner| inner.connection_verbose(verbose)) } @@ -423,7 +416,7 @@ where /// Pass `None` to disable timeout. /// /// Default is 90 seconds. - pub fn pool_idle_timeout(self, val: D) -> ClientBuilder + pub fn pool_idle_timeout(self, val: D) -> ClientBuilder where D: Into>, { @@ -431,12 +424,12 @@ where } /// Sets the maximum idle connection per host allowed in the pool. - pub fn pool_max_idle_per_host(self, max: usize) -> ClientBuilder { + pub fn pool_max_idle_per_host(self, max: usize) -> ClientBuilder { self.with_inner(move |inner| inner.pool_max_idle_per_host(max)) } /// Send headers as title case instead of lowercase. - pub fn http1_title_case_headers(self) -> ClientBuilder { + pub fn http1_title_case_headers(self) -> ClientBuilder { self.with_inner(|inner| inner.http1_title_case_headers()) } @@ -445,18 +438,12 @@ where /// /// Newline codepoints (`\r` and `\n`) will be transformed to spaces when /// parsing. - pub fn http1_allow_obsolete_multiline_headers_in_responses( - self, - value: bool, - ) -> ClientBuilder { + pub fn http1_allow_obsolete_multiline_headers_in_responses(self, value: bool) -> ClientBuilder { self.with_inner(|inner| inner.http1_allow_obsolete_multiline_headers_in_responses(value)) } /// Sets whether invalid header lines should be silently ignored in HTTP/1 responses. - pub fn http1_ignore_invalid_headers_in_responses( - self, - value: bool, - ) -> ClientBuilder { + pub fn http1_ignore_invalid_headers_in_responses(self, value: bool) -> ClientBuilder { self.with_inner(|inner| inner.http1_ignore_invalid_headers_in_responses(value)) } @@ -465,27 +452,24 @@ where /// /// Newline codepoints (\r and \n) will be transformed to spaces when /// parsing. - pub fn http1_allow_spaces_after_header_name_in_responses( - self, - value: bool, - ) -> ClientBuilder { + pub fn http1_allow_spaces_after_header_name_in_responses(self, value: bool) -> ClientBuilder { self.with_inner(|inner| inner.http1_allow_spaces_after_header_name_in_responses(value)) } /// Only use HTTP/1. - pub fn http1_only(self) -> ClientBuilder { + pub fn http1_only(self) -> ClientBuilder { self.with_inner(|inner| inner.http1_only()) } /// Allow HTTP/0.9 responses - pub fn http09_responses(self) -> ClientBuilder { + pub fn http09_responses(self) -> ClientBuilder { self.with_inner(|inner| inner.http09_responses()) } /// Only use HTTP/2. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_prior_knowledge(self) -> ClientBuilder { + pub fn http2_prior_knowledge(self) -> ClientBuilder { self.with_inner(|inner| inner.http2_prior_knowledge()) } @@ -494,10 +478,7 @@ where /// Default is currently 65,535 but may change internally to optimize for common uses. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_initial_stream_window_size( - self, - sz: impl Into>, - ) -> ClientBuilder { + pub fn http2_initial_stream_window_size(self, sz: impl Into>) -> ClientBuilder { self.with_inner(|inner| inner.http2_initial_stream_window_size(sz)) } @@ -506,10 +487,7 @@ where /// Default is currently 65,535 but may change internally to optimize for common uses. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_initial_connection_window_size( - self, - sz: impl Into>, - ) -> ClientBuilder { + pub fn http2_initial_connection_window_size(self, sz: impl Into>) -> ClientBuilder { self.with_inner(|inner| inner.http2_initial_connection_window_size(sz)) } @@ -519,7 +497,7 @@ where /// `http2_initial_connection_window_size`. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_adaptive_window(self, enabled: bool) -> ClientBuilder { + pub fn http2_adaptive_window(self, enabled: bool) -> ClientBuilder { self.with_inner(|inner| inner.http2_adaptive_window(enabled)) } @@ -528,10 +506,7 @@ where /// Default is currently 16,384 but may change internally to optimize for common uses. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_max_frame_size( - self, - sz: impl Into>, - ) -> ClientBuilder { + pub fn http2_max_frame_size(self, sz: impl Into>) -> ClientBuilder { self.with_inner(|inner| inner.http2_max_frame_size(sz)) } @@ -540,10 +515,7 @@ where /// Default is currently 16KB, but can change. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] - pub fn http2_max_header_list_size( - self, - max_header_size_bytes: u32, - ) -> ClientBuilder { + pub fn http2_max_header_list_size(self, max_header_size_bytes: u32) -> ClientBuilder { self.with_inner(|inner| inner.http2_max_header_list_size(max_header_size_bytes)) } @@ -551,7 +523,7 @@ where /// enabled. #[cfg(feature = "http3")] #[cfg_attr(docsrs, doc(cfg(feature = "http3")))] - pub fn http3_prior_knowledge(self) -> ClientBuilder { + pub fn http3_prior_knowledge(self) -> ClientBuilder { self.with_inner(|inner| inner.http3_prior_knowledge()) } @@ -560,7 +532,7 @@ where /// Set whether sockets have `TCP_NODELAY` enabled. /// /// Default is `true`. - pub fn tcp_nodelay(self, enabled: bool) -> ClientBuilder { + pub fn tcp_nodelay(self, enabled: bool) -> ClientBuilder { self.with_inner(move |inner| inner.tcp_nodelay(enabled)) } @@ -575,7 +547,7 @@ where /// .local_address(local_addr) /// .build().unwrap(); /// ``` - pub fn local_address(self, addr: T) -> ClientBuilder + pub fn local_address(self, addr: T) -> ClientBuilder where T: Into>, { @@ -593,14 +565,14 @@ where /// .build().unwrap(); /// ``` #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] - pub fn interface(self, interface: &str) -> ClientBuilder { + pub fn interface(self, interface: &str) -> ClientBuilder { self.with_inner(move |inner| inner.interface(interface)) } /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration. /// /// If `None`, the option will not be set. - pub fn tcp_keepalive(self, val: D) -> ClientBuilder + pub fn tcp_keepalive(self, val: D) -> ClientBuilder where D: Into>, { @@ -649,7 +621,7 @@ where feature = "rustls-tls" ))) )] - pub fn add_root_certificate(self, cert: Certificate) -> ClientBuilder { + pub fn add_root_certificate(self, cert: Certificate) -> ClientBuilder { self.with_inner(move |inner| inner.add_root_certificate(cert)) } @@ -661,7 +633,7 @@ where /// This requires the `rustls-tls(-...)` Cargo feature enabled. #[cfg(feature = "__rustls")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))] - pub fn add_crl(self, crl: CertificateRevocationList) -> ClientBuilder { + pub fn add_crl(self, crl: CertificateRevocationList) -> ClientBuilder { self.with_inner(move |inner| inner.add_crl(crl)) } @@ -676,7 +648,7 @@ where pub fn add_crls( self, crls: impl IntoIterator, - ) -> ClientBuilder { + ) -> ClientBuilder { self.with_inner(move |inner| inner.add_crls(crls)) } @@ -697,10 +669,7 @@ where feature = "rustls-tls" ))) )] - pub fn tls_built_in_root_certs( - self, - tls_built_in_root_certs: bool, - ) -> ClientBuilder { + pub fn tls_built_in_root_certs(self, tls_built_in_root_certs: bool) -> ClientBuilder { self.with_inner(move |inner| inner.tls_built_in_root_certs(tls_built_in_root_certs)) } @@ -709,7 +678,7 @@ where /// If the feature is enabled, this value is `true` by default. #[cfg(feature = "rustls-tls-webpki-roots-no-provider")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-webpki-roots-no-provider")))] - pub fn tls_built_in_webpki_certs(self, enabled: bool) -> ClientBuilder { + pub fn tls_built_in_webpki_certs(self, enabled: bool) -> ClientBuilder { self.with_inner(move |inner| inner.tls_built_in_webpki_certs(enabled)) } @@ -718,7 +687,7 @@ where /// If the feature is enabled, this value is `true` by default. #[cfg(feature = "rustls-tls-native-roots-no-provider")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls-native-roots-no-provider")))] - pub fn tls_built_in_native_certs(self, enabled: bool) -> ClientBuilder { + pub fn tls_built_in_native_certs(self, enabled: bool) -> ClientBuilder { self.with_inner(move |inner| inner.tls_built_in_native_certs(enabled)) } @@ -730,7 +699,7 @@ where /// enabled. #[cfg(any(feature = "native-tls", feature = "__rustls"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))] - pub fn identity(self, identity: Identity) -> ClientBuilder { + pub fn identity(self, identity: Identity) -> ClientBuilder { self.with_inner(move |inner| inner.identity(identity)) } @@ -758,10 +727,7 @@ where feature = "rustls-tls" ))) )] - pub fn danger_accept_invalid_hostnames( - self, - accept_invalid_hostname: bool, - ) -> ClientBuilder { + pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder { self.with_inner(|inner| inner.danger_accept_invalid_hostnames(accept_invalid_hostname)) } @@ -785,10 +751,7 @@ where feature = "rustls-tls" ))) )] - pub fn danger_accept_invalid_certs( - self, - accept_invalid_certs: bool, - ) -> ClientBuilder { + pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder { self.with_inner(|inner| inner.danger_accept_invalid_certs(accept_invalid_certs)) } @@ -804,7 +767,7 @@ where feature = "rustls-tls" ))) )] - pub fn tls_sni(self, tls_sni: bool) -> ClientBuilder { + pub fn tls_sni(self, tls_sni: bool) -> ClientBuilder { self.with_inner(|inner| inner.tls_sni(tls_sni)) } @@ -832,7 +795,7 @@ where feature = "rustls-tls" ))) )] - pub fn min_tls_version(self, version: tls::Version) -> ClientBuilder { + pub fn min_tls_version(self, version: tls::Version) -> ClientBuilder { self.with_inner(|inner| inner.min_tls_version(version)) } @@ -860,7 +823,7 @@ where feature = "rustls-tls" ))) )] - pub fn max_tls_version(self, version: tls::Version) -> ClientBuilder { + pub fn max_tls_version(self, version: tls::Version) -> ClientBuilder { self.with_inner(|inner| inner.max_tls_version(version)) } @@ -874,7 +837,7 @@ where /// This requires the optional `native-tls` feature to be enabled. #[cfg(feature = "native-tls")] #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))] - pub fn use_native_tls(self) -> ClientBuilder { + pub fn use_native_tls(self) -> ClientBuilder { self.with_inner(move |inner| inner.use_native_tls()) } @@ -888,7 +851,7 @@ where /// This requires the optional `rustls-tls(-...)` feature to be enabled. #[cfg(feature = "__rustls")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))] - pub fn use_rustls_tls(self) -> ClientBuilder { + pub fn use_rustls_tls(self) -> ClientBuilder { self.with_inner(move |inner| inner.use_rustls_tls()) } @@ -907,7 +870,7 @@ where feature = "rustls-tls" ))) )] - pub fn tls_info(self, tls_info: bool) -> ClientBuilder { + pub fn tls_info(self, tls_info: bool) -> ClientBuilder { self.with_inner(|inner| inner.tls_info(tls_info)) } @@ -931,7 +894,7 @@ where /// `rustls-tls(-...)` to be enabled. #[cfg(any(feature = "native-tls", feature = "__rustls",))] #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))] - pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder { + pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder { self.with_inner(move |inner| inner.use_preconfigured_tls(tls)) } @@ -945,7 +908,7 @@ where #[cfg(feature = "hickory-dns")] #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))] #[deprecated(note = "use `hickory_dns` instead", since = "0.12.0")] - pub fn trust_dns(self, enable: bool) -> ClientBuilder { + pub fn trust_dns(self, enable: bool) -> ClientBuilder { self.with_inner(|inner| inner.hickory_dns(enable)) } @@ -958,7 +921,7 @@ where /// This requires the optional `hickory-dns` feature to be enabled #[cfg(feature = "hickory-dns")] #[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))] - pub fn hickory_dns(self, enable: bool) -> ClientBuilder { + pub fn hickory_dns(self, enable: bool) -> ClientBuilder { self.with_inner(|inner| inner.hickory_dns(enable)) } @@ -968,7 +931,7 @@ where /// This can be used to ensure a `Client` doesn't use the hickory-dns async resolver /// even if another dependency were to enable the optional `hickory-dns` feature. #[deprecated(note = "use `no_hickory_dns` instead", since = "0.12.0")] - pub fn no_trust_dns(self) -> ClientBuilder { + pub fn no_trust_dns(self) -> ClientBuilder { self.with_inner(|inner| inner.no_hickory_dns()) } @@ -977,14 +940,14 @@ where /// This method exists even if the optional `hickory-dns` feature is not enabled. /// This can be used to ensure a `Client` doesn't use the hickory-dns async resolver /// even if another dependency were to enable the optional `hickory-dns` feature. - pub fn no_hickory_dns(self) -> ClientBuilder { + pub fn no_hickory_dns(self) -> ClientBuilder { self.with_inner(|inner| inner.no_hickory_dns()) } /// Restrict the Client to be used with HTTPS only requests. /// /// Defaults to false. - pub fn https_only(self, enabled: bool) -> ClientBuilder { + pub fn https_only(self, enabled: bool) -> ClientBuilder { self.with_inner(|inner| inner.https_only(enabled)) } @@ -992,7 +955,7 @@ where /// /// Set the port to `0` to use the conventional port for the given scheme (e.g. 80 for http). /// Ports in the URL itself will always be used instead of the port in the overridden addr. - pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder { + pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder { self.resolve_to_addrs(domain, &[addr]) } @@ -1000,11 +963,7 @@ where /// /// Set the port to `0` to use the conventional port for the given scheme (e.g. 80 for http). /// Ports in the URL itself will always be used instead of the port in the overridden addr. - pub fn resolve_to_addrs( - self, - domain: &str, - addrs: &[SocketAddr], - ) -> ClientBuilder { + pub fn resolve_to_addrs(self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder { self.with_inner(|inner| inner.resolve_to_addrs(domain, addrs)) } @@ -1013,10 +972,7 @@ where /// Pass an `Arc` wrapping a trait object implementing `Resolve`. /// Overrides for specific names passed to `resolve` and `resolve_to_addrs` will /// still be applied on top of this resolver. - pub fn dns_resolver( - self, - resolver: Arc, - ) -> ClientBuilder { + pub fn dns_resolver(self, resolver: Arc) -> ClientBuilder { self.with_inner(|inner| inner.dns_resolver(resolver)) } @@ -1026,50 +982,41 @@ where /// /// Each subsequent invocation of this function will wrap previous layers. /// - /// Simple example: + /// Example usage: /// ``` /// use std::time::Duration; /// /// let client = reqwest::blocking::Client::builder() - /// // resolved to outermost layer, so before the semaphore permit is attempted - /// .connect_timeout(Duration::from_millis(100)) - /// // underneath the concurrency check, so only after a semaphore permit is acquired + /// // resolved to outermost layer, meaning while we are waiting on concurrency limit + /// .connect_timeout(Duration::from_millis(200)) + /// // underneath the concurrency check, so only after concurrency limit lets us through /// .connector_layer(tower::timeout::TimeoutLayer::new(Duration::from_millis(50))) /// .connector_layer(tower::limit::concurrency::ConcurrencyLimitLayer::new(2)) /// .build() /// .unwrap(); /// ``` - pub fn connector_layer(self, layer: L) -> ClientBuilder> + pub fn connector_layer(self, layer: L) -> ClientBuilder where - S: Send + Sync + Clone + 'static, - L: Layer + Send + Sync + Clone + 'static, + L: Layer + Clone + Send + Sync + 'static, + L::Service: Service + Clone + Send + Sync + 'static, + >::Future: Send + 'static, { - // skipping using `with_inner` here because we need to cast the generic type - let inner = self.inner.connector_layer(layer); - - ClientBuilder::> { - inner, - timeout: self.timeout, - } + self.with_inner(|inner| inner.connector_layer(layer)) } // private - fn with_inner(mut self, func: F) -> ClientBuilder + fn with_inner(mut self, func: F) -> ClientBuilder where - F: FnOnce( - async_impl::ClientBuilder, - ) -> async_impl::ClientBuilder, + F: FnOnce(async_impl::ClientBuilder) -> async_impl::ClientBuilder, { self.inner = func(self.inner); self } } -impl From> - for ClientBuilder -{ - fn from(builder: async_impl::ClientBuilder) -> Self { +impl From for ClientBuilder { + fn from(builder: async_impl::ClientBuilder) -> Self { Self { inner: builder, timeout: Timeout::default(), @@ -1103,7 +1050,7 @@ impl Client { /// Creates a `ClientBuilder` to configure a `Client`. /// /// This is the same as `ClientBuilder::new()`. - pub fn builder() -> ClientBuilder { + pub fn builder() -> ClientBuilder { ClientBuilder::new() } @@ -1201,7 +1148,7 @@ impl fmt::Debug for Client { } } -impl fmt::Debug for ClientBuilder { +impl fmt::Debug for ClientBuilder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.inner.fmt(f) } @@ -1238,14 +1185,7 @@ impl Drop for InnerClientHandle { } impl ClientHandle { - fn new(builder: ClientBuilder) -> crate::Result - where - ConnectorLayers: Layer + Send + Sync + 'static, - ConnectorLayers::Service: - Service + Clone + Send + Sync + 'static, - <>::Service as Service>::Future: - Send + 'static, - { + fn new(builder: ClientBuilder) -> crate::Result { let timeout = builder.timeout; let builder = builder.inner; let (tx, rx) = mpsc::unbounded_channel::<(async_impl::Request, OneshotResponse)>(); diff --git a/src/connect.rs b/src/connect.rs index e1fc599b1..c7898acb9 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -8,7 +8,8 @@ use hyper_util::client::legacy::connect::{Connected, Connection}; use hyper_util::rt::TokioIo; #[cfg(feature = "default-tls")] use native_tls_crate::{TlsConnector, TlsConnectorBuilder}; -use tower::{timeout::TimeoutLayer, util::BoxCloneSyncService, Layer, ServiceBuilder}; +use tower::util::BoxCloneSyncServiceLayer; +use tower::{timeout::TimeoutLayer, util::BoxCloneSyncService, ServiceBuilder}; use tower_service::Service; use pin_project_lite::pin_project; @@ -59,14 +60,10 @@ impl Service for Connector { } } -pub(crate) struct ConnectorLayerBuilder { - pub(crate) builder: ServiceBuilder, - // It's not trivial to identify whether the builder stack is `Stack` or not - // so we simply add a boolean flag to track. - // - // Knowing allows us reduce indirection in certain cases. - pub(crate) has_custom_layers: bool, -} +pub(crate) type BoxedConnectorService = BoxCloneSyncService; + +pub(crate) type BoxedConnectorLayer = + BoxCloneSyncServiceLayer; pub(crate) struct ConnectorBuilder { inner: Inner, @@ -82,17 +79,8 @@ pub(crate) struct ConnectorBuilder { } impl ConnectorBuilder { - pub(crate) fn build( - self, - layer: ConnectorLayerBuilder, - ) -> Connector - where - ConnectorLayers: Layer, - ConnectorLayers::Service: - Service + Clone + Send + Sync + 'static, - <>::Service as Service>::Future: - Send + 'static, - { + pub(crate) fn build(self, layers: Vec) -> Connector +where { // construct the inner tower service let mut base_service = ConnectorService { inner: self.inner, @@ -107,38 +95,48 @@ impl ConnectorBuilder { simple_timeout: None, }; - // no user-provider layers so we can throw away our generic input layer stack - // and compose with named layers only - if !layer.has_custom_layers { - // if we know we have no other layers, we can embed the timeout directly inside - // our base service call which saves us a Box::pin + if layers.is_empty() { + // we have no user-provided layers, only use concrete types base_service.simple_timeout = self.timeout; return Connector::Simple(base_service); } - // we have user-provided generic layer stack - let service = layer.builder.service(base_service); - - if let Some(timeout) = self.timeout { - // add in named timeout layer on the outside of the stack - let service = ServiceBuilder::new() - .layer(TimeoutLayer::new(timeout)) - .service(service); - let service = ServiceBuilder::new() - .map_err(|error: BoxError| cast_to_internal_error(error)) - .service(service); - let service = BoxCloneSyncService::new(service); - return Connector::WithLayers(service); + // otherwise we have user provided layers + // so we need type erasure all the way through + + let mut service = BoxCloneSyncService::new(base_service); + + for layer in layers { + service = ServiceBuilder::new().layer(layer).service(service); } - // no named timeout layer but we still map errors since - // we might have user-provided timeout layer - let service = ServiceBuilder::new().service(service); - let service = ServiceBuilder::new() - .map_err(|error: BoxError| cast_to_internal_error(error)) - .service(service); - let service = BoxCloneSyncService::new(service); - return Connector::WithLayers(service); + // now we handle the concrete stuff - any `connect_timeout`, + // plus a final map_err layer we can use to cast default tower layer + // errors to internal errors + match self.timeout { + Some(timeout) => { + let service = ServiceBuilder::new() + .layer(TimeoutLayer::new(timeout)) + .service(service); + let service = ServiceBuilder::new() + .map_err(|error: BoxError| cast_to_internal_error(error)) + .service(service); + let service = BoxCloneSyncService::new(service); + + Connector::WithLayers(service) + } + None => { + // no timeout, but still map err + // no named timeout layer but we still map errors since + // we might have user-provided timeout layer + let service = ServiceBuilder::new().service(service); + let service = ServiceBuilder::new() + .map_err(|error: BoxError| cast_to_internal_error(error)) + .service(service); + let service = BoxCloneSyncService::new(service); + Connector::WithLayers(service) + } + } } #[cfg(not(feature = "__tls"))] @@ -298,14 +296,9 @@ impl ConnectorBuilder { } } -// Struct is public because we can't have private trait in public bounds -// until we are on MSRV 1.74 when private-in-public was switched -// from error to lint - https://github.com/rust-lang/rfcs/pull/2145 -// but no internal details are exposed. We don't expose debug -// for similar reasons. #[allow(missing_debug_implementations)] #[derive(Clone)] -pub struct ConnectorService { +pub(crate) struct ConnectorService { inner: Inner, proxies: Arc>, verbose: verbose::Wrapper, @@ -757,16 +750,16 @@ impl AsyncConnWithInfo for T {} type BoxConn = Box; pin_project! { - // Struct is public because we can't have private trait in public bounds - // until we are on MSRV 1.74 when private-in-public was switched - // from error to lint - https://github.com/rust-lang/rfcs/pull/2145 - // but no internal details are exposed. We don't expose debug - // for similar reasons. - /// Note: the `is_proxy` member means *is plain text HTTP proxy*. /// This tells hyper whether the URI should be written in /// * origin-form (`GET /just/a/path HTTP/1.1`), when `is_proxy == false`, or /// * absolute-form (`GET http://foo.bar/and/a/path HTTP/1.1`), otherwise. + + // Currently Conn is public but has no implementation details exposed. + // We need this because we support pre-1.74 rust versions where `private-in-public` + // is a hard error rather than lint warning. + // Eventually we probably will want to expose some elements of the connection stream + // for layer handling on the tower backswing. #[allow(missing_debug_implementations)] pub struct Conn { #[pin]