From 01959b16a21b22b5df5f16569c2a8e8f92beecef Mon Sep 17 00:00:00 2001 From: mo khan Date: Thu, 10 Jul 2025 13:11:11 -0600 Subject: chore: vendor dependencies --- vendor/hyper-rustls/src/config.rs | 136 ++++++++ vendor/hyper-rustls/src/connector.rs | 296 ++++++++++++++++ vendor/hyper-rustls/src/connector/builder.rs | 500 +++++++++++++++++++++++++++ vendor/hyper-rustls/src/lib.rs | 76 ++++ vendor/hyper-rustls/src/stream.rs | 121 +++++++ 5 files changed, 1129 insertions(+) create mode 100644 vendor/hyper-rustls/src/config.rs create mode 100644 vendor/hyper-rustls/src/connector.rs create mode 100644 vendor/hyper-rustls/src/connector/builder.rs create mode 100644 vendor/hyper-rustls/src/lib.rs create mode 100644 vendor/hyper-rustls/src/stream.rs (limited to 'vendor/hyper-rustls/src') diff --git a/vendor/hyper-rustls/src/config.rs b/vendor/hyper-rustls/src/config.rs new file mode 100644 index 00000000..38382617 --- /dev/null +++ b/vendor/hyper-rustls/src/config.rs @@ -0,0 +1,136 @@ +#[cfg(feature = "rustls-native-certs")] +use std::io; + +#[cfg(any( + feature = "rustls-platform-verifier", + feature = "rustls-native-certs", + feature = "webpki-roots" +))] +use rustls::client::WantsClientCert; +use rustls::{ClientConfig, ConfigBuilder, WantsVerifier}; +#[cfg(feature = "rustls-native-certs")] +use rustls_native_certs::CertificateResult; +#[cfg(feature = "rustls-platform-verifier")] +use rustls_platform_verifier::BuilderVerifierExt; + +/// Methods for configuring roots +/// +/// This adds methods (gated by crate features) for easily configuring +/// TLS server roots a rustls ClientConfig will trust. +pub trait ConfigBuilderExt: sealed::Sealed { + /// Use the platform's native verifier to verify server certificates. + /// + /// See the documentation for [rustls-platform-verifier] for more details. + /// + /// # Panics + /// + /// Since 0.27.7, this method will panic if the platform verifier cannot be initialized. + /// Use `try_with_platform_verifier()` instead to handle errors gracefully. + /// + /// [rustls-platform-verifier]: https://docs.rs/rustls-platform-verifier + #[deprecated(since = "0.27.7", note = "use `try_with_platform_verifier` instead")] + #[cfg(feature = "rustls-platform-verifier")] + fn with_platform_verifier(self) -> ConfigBuilder; + + /// Use the platform's native verifier to verify server certificates. + /// + /// See the documentation for [rustls-platform-verifier] for more details. + /// + /// [rustls-platform-verifier]: https://docs.rs/rustls-platform-verifier + #[cfg(feature = "rustls-platform-verifier")] + fn try_with_platform_verifier( + self, + ) -> Result, rustls::Error>; + + /// This configures the platform's trusted certs, as implemented by + /// rustls-native-certs + /// + /// This will return an error if no valid certs were found. In that case, + /// it's recommended to use `with_webpki_roots`. + #[cfg(feature = "rustls-native-certs")] + fn with_native_roots(self) -> Result, io::Error>; + + /// This configures the webpki roots, which are Mozilla's set of + /// trusted roots as packaged by webpki-roots. + #[cfg(feature = "webpki-roots")] + fn with_webpki_roots(self) -> ConfigBuilder; +} + +impl ConfigBuilderExt for ConfigBuilder { + #[cfg(feature = "rustls-platform-verifier")] + fn with_platform_verifier(self) -> ConfigBuilder { + self.try_with_platform_verifier() + .expect("failure to initialize platform verifier") + } + + #[cfg(feature = "rustls-platform-verifier")] + fn try_with_platform_verifier( + self, + ) -> Result, rustls::Error> { + BuilderVerifierExt::with_platform_verifier(self) + } + + #[cfg(feature = "rustls-native-certs")] + #[cfg_attr(not(feature = "logging"), allow(unused_variables))] + fn with_native_roots(self) -> Result, io::Error> { + let mut roots = rustls::RootCertStore::empty(); + let mut valid_count = 0; + let mut invalid_count = 0; + + let CertificateResult { certs, errors, .. } = rustls_native_certs::load_native_certs(); + if !errors.is_empty() { + crate::log::warn!("native root CA certificate loading errors: {errors:?}"); + } + + if certs.is_empty() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("no native root CA certificates found (errors: {errors:?})"), + )); + } + + for cert in certs { + match roots.add(cert) { + Ok(_) => valid_count += 1, + Err(err) => { + crate::log::debug!("certificate parsing failed: {:?}", err); + invalid_count += 1 + } + } + } + + crate::log::debug!( + "with_native_roots processed {} valid and {} invalid certs", + valid_count, + invalid_count + ); + if roots.is_empty() { + crate::log::debug!("no valid native root CA certificates found"); + Err(io::Error::new( + io::ErrorKind::NotFound, + format!("no valid native root CA certificates found ({invalid_count} invalid)"), + ))? + } + + Ok(self.with_root_certificates(roots)) + } + + #[cfg(feature = "webpki-roots")] + fn with_webpki_roots(self) -> ConfigBuilder { + let mut roots = rustls::RootCertStore::empty(); + roots.extend( + webpki_roots::TLS_SERVER_ROOTS + .iter() + .cloned(), + ); + self.with_root_certificates(roots) + } +} + +mod sealed { + use super::*; + + pub trait Sealed {} + + impl Sealed for ConfigBuilder {} +} diff --git a/vendor/hyper-rustls/src/connector.rs b/vendor/hyper-rustls/src/connector.rs new file mode 100644 index 00000000..f8abe457 --- /dev/null +++ b/vendor/hyper-rustls/src/connector.rs @@ -0,0 +1,296 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::{fmt, io}; + +use http::Uri; +use hyper::rt; +use hyper_util::client::legacy::connect::Connection; +use hyper_util::rt::TokioIo; +use pki_types::ServerName; +use tokio_rustls::TlsConnector; +use tower_service::Service; + +use crate::stream::MaybeHttpsStream; + +pub(crate) mod builder; + +type BoxError = Box; + +/// A Connector for the `https` scheme. +#[derive(Clone)] +pub struct HttpsConnector { + force_https: bool, + http: T, + tls_config: Arc, + server_name_resolver: Arc, +} + +impl HttpsConnector { + /// Creates a [`crate::HttpsConnectorBuilder`] to configure a `HttpsConnector`. + /// + /// This is the same as [`crate::HttpsConnectorBuilder::new()`]. + pub fn builder() -> builder::ConnectorBuilder { + builder::ConnectorBuilder::new() + } + + /// Force the use of HTTPS when connecting. + /// + /// If a URL is not `https` when connecting, an error is returned. + pub fn enforce_https(&mut self) { + self.force_https = true; + } +} + +impl Service for HttpsConnector +where + T: Service, + T::Response: Connection + rt::Read + rt::Write + Send + Unpin + 'static, + T::Future: Send + 'static, + T::Error: Into, +{ + type Response = MaybeHttpsStream; + type Error = BoxError; + + #[allow(clippy::type_complexity)] + type Future = + Pin, BoxError>> + Send>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + match self.http.poll_ready(cx) { + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())), + Poll::Pending => Poll::Pending, + } + } + + fn call(&mut self, dst: Uri) -> Self::Future { + // dst.scheme() would need to derive Eq to be matchable; + // use an if cascade instead + match dst.scheme() { + Some(scheme) if scheme == &http::uri::Scheme::HTTP && !self.force_https => { + let future = self.http.call(dst); + return Box::pin(async move { + Ok(MaybeHttpsStream::Http(future.await.map_err(Into::into)?)) + }); + } + Some(scheme) if scheme != &http::uri::Scheme::HTTPS => { + let message = format!("unsupported scheme {scheme}"); + return Box::pin(async move { + Err(io::Error::new(io::ErrorKind::Other, message).into()) + }); + } + Some(_) => {} + None => { + return Box::pin(async move { + Err(io::Error::new(io::ErrorKind::Other, "missing scheme").into()) + }) + } + }; + + let cfg = self.tls_config.clone(); + let hostname = match self.server_name_resolver.resolve(&dst) { + Ok(hostname) => hostname, + Err(e) => { + return Box::pin(async move { Err(e) }); + } + }; + + let connecting_future = self.http.call(dst); + Box::pin(async move { + let tcp = connecting_future + .await + .map_err(Into::into)?; + Ok(MaybeHttpsStream::Https(TokioIo::new( + TlsConnector::from(cfg) + .connect(hostname, TokioIo::new(tcp)) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?, + ))) + }) + } +} + +impl From<(H, C)> for HttpsConnector +where + C: Into>, +{ + fn from((http, cfg): (H, C)) -> Self { + Self { + force_https: false, + http, + tls_config: cfg.into(), + server_name_resolver: Arc::new(DefaultServerNameResolver::default()), + } + } +} + +impl fmt::Debug for HttpsConnector { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("HttpsConnector") + .field("force_https", &self.force_https) + .finish() + } +} + +/// The default server name resolver, which uses the hostname in the URI. +#[derive(Default)] +pub struct DefaultServerNameResolver(()); + +impl ResolveServerName for DefaultServerNameResolver { + fn resolve( + &self, + uri: &Uri, + ) -> Result, Box> { + let mut hostname = uri.host().unwrap_or_default(); + + // Remove square brackets around IPv6 address. + if let Some(trimmed) = hostname + .strip_prefix('[') + .and_then(|h| h.strip_suffix(']')) + { + hostname = trimmed; + } + + ServerName::try_from(hostname.to_string()).map_err(|e| Box::new(e) as _) + } +} + +/// A server name resolver which always returns the same fixed name. +pub struct FixedServerNameResolver { + name: ServerName<'static>, +} + +impl FixedServerNameResolver { + /// Creates a new resolver returning the specified name. + pub fn new(name: ServerName<'static>) -> Self { + Self { name } + } +} + +impl ResolveServerName for FixedServerNameResolver { + fn resolve( + &self, + _: &Uri, + ) -> Result, Box> { + Ok(self.name.clone()) + } +} + +impl ResolveServerName for F +where + F: Fn(&Uri) -> Result, E>, + E: Into>, +{ + fn resolve( + &self, + uri: &Uri, + ) -> Result, Box> { + self(uri).map_err(Into::into) + } +} + +/// A trait implemented by types that can resolve a [`ServerName`] for a request. +pub trait ResolveServerName { + /// Maps a [`Uri`] into a [`ServerName`]. + fn resolve( + &self, + uri: &Uri, + ) -> Result, Box>; +} + +#[cfg(all( + test, + any(feature = "ring", feature = "aws-lc-rs"), + any( + feature = "rustls-native-certs", + feature = "webpki-roots", + feature = "rustls-platform-verifier", + ) +))] +mod tests { + use std::future::poll_fn; + + use http::Uri; + use hyper_util::rt::TokioIo; + use tokio::net::TcpStream; + use tower_service::Service; + + use super::*; + use crate::{ConfigBuilderExt, HttpsConnectorBuilder, MaybeHttpsStream}; + + #[tokio::test] + async fn connects_https() { + connect(Allow::Any, Scheme::Https) + .await + .unwrap(); + } + + #[tokio::test] + async fn connects_http() { + connect(Allow::Any, Scheme::Http) + .await + .unwrap(); + } + + #[tokio::test] + async fn connects_https_only() { + connect(Allow::Https, Scheme::Https) + .await + .unwrap(); + } + + #[tokio::test] + async fn enforces_https_only() { + let message = connect(Allow::Https, Scheme::Http) + .await + .unwrap_err() + .to_string(); + + assert_eq!(message, "unsupported scheme http"); + } + + async fn connect( + allow: Allow, + scheme: Scheme, + ) -> Result>, BoxError> { + let config_builder = rustls::ClientConfig::builder(); + cfg_if::cfg_if! { + if #[cfg(feature = "rustls-platform-verifier")] { + let config_builder = config_builder.try_with_platform_verifier()?; + } else if #[cfg(feature = "rustls-native-certs")] { + let config_builder = config_builder.with_native_roots().unwrap(); + } else if #[cfg(feature = "webpki-roots")] { + let config_builder = config_builder.with_webpki_roots(); + } + } + let config = config_builder.with_no_client_auth(); + + let builder = HttpsConnectorBuilder::new().with_tls_config(config); + let mut service = match allow { + Allow::Https => builder.https_only(), + Allow::Any => builder.https_or_http(), + } + .enable_http1() + .build(); + + poll_fn(|cx| service.poll_ready(cx)).await?; + service + .call(Uri::from_static(match scheme { + Scheme::Https => "https://google.com", + Scheme::Http => "http://google.com", + })) + .await + } + + enum Allow { + Https, + Any, + } + + enum Scheme { + Https, + Http, + } +} diff --git a/vendor/hyper-rustls/src/connector/builder.rs b/vendor/hyper-rustls/src/connector/builder.rs new file mode 100644 index 00000000..417d1303 --- /dev/null +++ b/vendor/hyper-rustls/src/connector/builder.rs @@ -0,0 +1,500 @@ +use std::sync::Arc; + +use hyper_util::client::legacy::connect::HttpConnector; +#[cfg(any( + feature = "rustls-native-certs", + feature = "rustls-platform-verifier", + feature = "webpki-roots" +))] +use rustls::crypto::CryptoProvider; +use rustls::ClientConfig; + +use super::{DefaultServerNameResolver, HttpsConnector, ResolveServerName}; +#[cfg(any( + feature = "rustls-native-certs", + feature = "webpki-roots", + feature = "rustls-platform-verifier" +))] +use crate::config::ConfigBuilderExt; +use pki_types::ServerName; + +/// A builder for an [`HttpsConnector`] +/// +/// This makes configuration flexible and explicit and ensures connector +/// features match crate features +/// +/// # Examples +/// +/// ``` +/// use hyper_rustls::HttpsConnectorBuilder; +/// +/// # #[cfg(all(feature = "webpki-roots", feature = "http1", feature="aws-lc-rs"))] +/// # { +/// # let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); +/// let https = HttpsConnectorBuilder::new() +/// .with_webpki_roots() +/// .https_only() +/// .enable_http1() +/// .build(); +/// # } +/// ``` +pub struct ConnectorBuilder(State); + +/// State of a builder that needs a TLS client config next +pub struct WantsTlsConfig(()); + +impl ConnectorBuilder { + /// Creates a new [`ConnectorBuilder`] + pub fn new() -> Self { + Self(WantsTlsConfig(())) + } + + /// Passes a rustls [`ClientConfig`] to configure the TLS connection + /// + /// The [`alpn_protocols`](ClientConfig::alpn_protocols) field is + /// required to be empty (or the function will panic) and will be + /// rewritten to match the enabled schemes (see + /// [`enable_http1`](ConnectorBuilder::enable_http1), + /// [`enable_http2`](ConnectorBuilder::enable_http2)) before the + /// connector is built. + pub fn with_tls_config(self, config: ClientConfig) -> ConnectorBuilder { + assert!( + config.alpn_protocols.is_empty(), + "ALPN protocols should not be pre-defined" + ); + ConnectorBuilder(WantsSchemes { tls_config: config }) + } + + /// Shorthand for using rustls' default crypto provider and other defaults, and + /// the platform verifier. + /// + /// See [`ConfigBuilderExt::with_platform_verifier()`]. + #[cfg(all( + any(feature = "ring", feature = "aws-lc-rs"), + feature = "rustls-platform-verifier" + ))] + pub fn with_platform_verifier(self) -> ConnectorBuilder { + self.try_with_platform_verifier() + .expect("failure to initialize platform verifier") + } + + /// Shorthand for using rustls' default crypto provider and other defaults, and + /// the platform verifier. + /// + /// See [`ConfigBuilderExt::with_platform_verifier()`]. + #[cfg(all( + any(feature = "ring", feature = "aws-lc-rs"), + feature = "rustls-platform-verifier" + ))] + pub fn try_with_platform_verifier( + self, + ) -> Result, rustls::Error> { + Ok(self.with_tls_config( + ClientConfig::builder() + .try_with_platform_verifier()? + .with_no_client_auth(), + )) + } + + /// Shorthand for using a custom [`CryptoProvider`] and the platform verifier. + /// + /// See [`ConfigBuilderExt::with_platform_verifier()`]. + #[cfg(feature = "rustls-platform-verifier")] + pub fn with_provider_and_platform_verifier( + self, + provider: impl Into>, + ) -> std::io::Result> { + Ok(self.with_tls_config( + ClientConfig::builder_with_provider(provider.into()) + .with_safe_default_protocol_versions() + .and_then(|builder| builder.try_with_platform_verifier()) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))? + .with_no_client_auth(), + )) + } + + /// Shorthand for using rustls' default crypto provider and safe defaults, with + /// native roots. + /// + /// See [`ConfigBuilderExt::with_native_roots`] + #[cfg(all( + any(feature = "ring", feature = "aws-lc-rs"), + feature = "rustls-native-certs" + ))] + pub fn with_native_roots(self) -> std::io::Result> { + Ok(self.with_tls_config( + ClientConfig::builder() + .with_native_roots()? + .with_no_client_auth(), + )) + } + + /// Shorthand for using a custom [`CryptoProvider`] and native roots + /// + /// See [`ConfigBuilderExt::with_native_roots`] + #[cfg(feature = "rustls-native-certs")] + pub fn with_provider_and_native_roots( + self, + provider: impl Into>, + ) -> std::io::Result> { + Ok(self.with_tls_config( + ClientConfig::builder_with_provider(provider.into()) + .with_safe_default_protocol_versions() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))? + .with_native_roots()? + .with_no_client_auth(), + )) + } + + /// Shorthand for using rustls' default crypto provider and its + /// safe defaults. + /// + /// See [`ConfigBuilderExt::with_webpki_roots`] + #[cfg(all(any(feature = "ring", feature = "aws-lc-rs"), feature = "webpki-roots"))] + pub fn with_webpki_roots(self) -> ConnectorBuilder { + self.with_tls_config( + ClientConfig::builder() + .with_webpki_roots() + .with_no_client_auth(), + ) + } + + /// Shorthand for using a custom [`CryptoProvider`], Rustls' safe default + /// protocol versions and Mozilla roots + /// + /// See [`ConfigBuilderExt::with_webpki_roots`] + #[cfg(feature = "webpki-roots")] + pub fn with_provider_and_webpki_roots( + self, + provider: impl Into>, + ) -> Result, rustls::Error> { + Ok(self.with_tls_config( + ClientConfig::builder_with_provider(provider.into()) + .with_safe_default_protocol_versions()? + .with_webpki_roots() + .with_no_client_auth(), + )) + } +} + +impl Default for ConnectorBuilder { + fn default() -> Self { + Self::new() + } +} + +/// State of a builder that needs schemes (https:// and http://) to be +/// configured next +pub struct WantsSchemes { + tls_config: ClientConfig, +} + +impl ConnectorBuilder { + /// Enforce the use of HTTPS when connecting + /// + /// Only URLs using the HTTPS scheme will be connectable. + pub fn https_only(self) -> ConnectorBuilder { + ConnectorBuilder(WantsProtocols1 { + tls_config: self.0.tls_config, + https_only: true, + server_name_resolver: None, + }) + } + + /// Allow both HTTPS and HTTP when connecting + /// + /// HTTPS URLs will be handled through rustls, + /// HTTP URLs will be handled by the lower-level connector. + pub fn https_or_http(self) -> ConnectorBuilder { + ConnectorBuilder(WantsProtocols1 { + tls_config: self.0.tls_config, + https_only: false, + server_name_resolver: None, + }) + } +} + +/// State of a builder that needs to have some protocols (HTTP1 or later) +/// enabled next +/// +/// No protocol has been enabled at this point. +pub struct WantsProtocols1 { + tls_config: ClientConfig, + https_only: bool, + server_name_resolver: Option>, +} + +impl WantsProtocols1 { + fn wrap_connector(self, conn: H) -> HttpsConnector { + HttpsConnector { + force_https: self.https_only, + http: conn, + tls_config: std::sync::Arc::new(self.tls_config), + server_name_resolver: self + .server_name_resolver + .unwrap_or_else(|| Arc::new(DefaultServerNameResolver::default())), + } + } + + fn build(self) -> HttpsConnector { + let mut http = HttpConnector::new(); + // HttpConnector won't enforce scheme, but HttpsConnector will + http.enforce_http(false); + self.wrap_connector(http) + } +} + +impl ConnectorBuilder { + /// Enable HTTP1 + /// + /// This needs to be called explicitly, no protocol is enabled by default + #[cfg(feature = "http1")] + pub fn enable_http1(self) -> ConnectorBuilder { + ConnectorBuilder(WantsProtocols2 { inner: self.0 }) + } + + /// Enable HTTP2 + /// + /// This needs to be called explicitly, no protocol is enabled by default + #[cfg(feature = "http2")] + pub fn enable_http2(mut self) -> ConnectorBuilder { + self.0.tls_config.alpn_protocols = vec![b"h2".to_vec()]; + ConnectorBuilder(WantsProtocols3 { + inner: self.0, + enable_http1: false, + }) + } + + /// Enable all HTTP versions built into this library (enabled with Cargo features) + /// + /// For now, this could enable both HTTP 1 and 2, depending on active features. + /// In the future, other supported versions will be enabled as well. + #[cfg(feature = "http2")] + pub fn enable_all_versions(mut self) -> ConnectorBuilder { + #[cfg(feature = "http1")] + let alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; + #[cfg(not(feature = "http1"))] + let alpn_protocols = vec![b"h2".to_vec()]; + + self.0.tls_config.alpn_protocols = alpn_protocols; + ConnectorBuilder(WantsProtocols3 { + inner: self.0, + enable_http1: cfg!(feature = "http1"), + }) + } + + /// Override server name for the TLS stack + /// + /// By default, for each connection hyper-rustls will extract host portion + /// of the destination URL and verify that server certificate contains + /// this value. + /// + /// If this method is called, hyper-rustls will instead use this resolver + /// to compute the value used to verify the server certificate. + pub fn with_server_name_resolver( + mut self, + resolver: impl ResolveServerName + 'static + Sync + Send, + ) -> Self { + self.0.server_name_resolver = Some(Arc::new(resolver)); + self + } + + /// Override server name for the TLS stack + /// + /// By default, for each connection hyper-rustls will extract host portion + /// of the destination URL and verify that server certificate contains + /// this value. + /// + /// If this method is called, hyper-rustls will instead verify that server + /// certificate contains `override_server_name`. Domain name included in + /// the URL will not affect certificate validation. + #[deprecated( + since = "0.27.1", + note = "use Self::with_server_name_resolver with FixedServerNameResolver instead" + )] + pub fn with_server_name(self, mut override_server_name: String) -> Self { + // remove square brackets around IPv6 address. + if let Some(trimmed) = override_server_name + .strip_prefix('[') + .and_then(|s| s.strip_suffix(']')) + { + override_server_name = trimmed.to_string(); + } + + self.with_server_name_resolver(move |_: &_| { + ServerName::try_from(override_server_name.clone()) + }) + } +} + +/// State of a builder with HTTP1 enabled, that may have some other +/// protocols (HTTP2 or later) enabled next +/// +/// At this point a connector can be built, see +/// [`build`](ConnectorBuilder::build) and +/// [`wrap_connector`](ConnectorBuilder::wrap_connector). +pub struct WantsProtocols2 { + inner: WantsProtocols1, +} + +impl ConnectorBuilder { + /// Enable HTTP2 + /// + /// This needs to be called explicitly, no protocol is enabled by default + #[cfg(feature = "http2")] + pub fn enable_http2(mut self) -> ConnectorBuilder { + self.0.inner.tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; + ConnectorBuilder(WantsProtocols3 { + inner: self.0.inner, + enable_http1: true, + }) + } + + /// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`] + pub fn build(self) -> HttpsConnector { + self.0.inner.build() + } + + /// This wraps an arbitrary low-level connector into an [`HttpsConnector`] + pub fn wrap_connector(self, conn: H) -> HttpsConnector { + // HTTP1-only, alpn_protocols stays empty + // HttpConnector doesn't have a way to say http1-only; + // its connection pool may still support HTTP2 + // though it won't be used + self.0.inner.wrap_connector(conn) + } +} + +/// State of a builder with HTTP2 (and possibly HTTP1) enabled +/// +/// At this point a connector can be built, see +/// [`build`](ConnectorBuilder::build) and +/// [`wrap_connector`](ConnectorBuilder::wrap_connector). +#[cfg(feature = "http2")] +pub struct WantsProtocols3 { + inner: WantsProtocols1, + // ALPN is built piecemeal without the need to read back this field + #[allow(dead_code)] + enable_http1: bool, +} + +#[cfg(feature = "http2")] +impl ConnectorBuilder { + /// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`] + pub fn build(self) -> HttpsConnector { + self.0.inner.build() + } + + /// This wraps an arbitrary low-level connector into an [`HttpsConnector`] + pub fn wrap_connector(self, conn: H) -> HttpsConnector { + // If HTTP1 is disabled, we can set http2_only + // on the Client (a higher-level object that uses the connector) + // client.http2_only(!self.0.enable_http1); + self.0.inner.wrap_connector(conn) + } +} + +#[cfg(test)] +mod tests { + // Typical usage + #[test] + #[cfg(all(feature = "webpki-roots", feature = "http1"))] + fn test_builder() { + ensure_global_state(); + let _connector = super::ConnectorBuilder::new() + .with_webpki_roots() + .https_only() + .enable_http1() + .build(); + } + + #[test] + #[cfg(feature = "http1")] + #[should_panic(expected = "ALPN protocols should not be pre-defined")] + fn test_reject_predefined_alpn() { + ensure_global_state(); + let roots = rustls::RootCertStore::empty(); + let mut config_with_alpn = rustls::ClientConfig::builder() + .with_root_certificates(roots) + .with_no_client_auth(); + config_with_alpn.alpn_protocols = vec![b"fancyprotocol".to_vec()]; + let _connector = super::ConnectorBuilder::new() + .with_tls_config(config_with_alpn) + .https_only() + .enable_http1() + .build(); + } + + #[test] + #[cfg(all(feature = "http1", feature = "http2"))] + fn test_alpn() { + ensure_global_state(); + let roots = rustls::RootCertStore::empty(); + let tls_config = rustls::ClientConfig::builder() + .with_root_certificates(roots) + .with_no_client_auth(); + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config.clone()) + .https_only() + .enable_http1() + .build(); + assert!(connector + .tls_config + .alpn_protocols + .is_empty()); + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config.clone()) + .https_only() + .enable_http2() + .build(); + assert_eq!(&connector.tls_config.alpn_protocols, &[b"h2".to_vec()]); + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config.clone()) + .https_only() + .enable_http1() + .enable_http2() + .build(); + assert_eq!( + &connector.tls_config.alpn_protocols, + &[b"h2".to_vec(), b"http/1.1".to_vec()] + ); + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config) + .https_only() + .enable_all_versions() + .build(); + assert_eq!( + &connector.tls_config.alpn_protocols, + &[b"h2".to_vec(), b"http/1.1".to_vec()] + ); + } + + #[test] + #[cfg(all(not(feature = "http1"), feature = "http2"))] + fn test_alpn_http2() { + let roots = rustls::RootCertStore::empty(); + let tls_config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(roots) + .with_no_client_auth(); + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config.clone()) + .https_only() + .enable_http2() + .build(); + assert_eq!(&connector.tls_config.alpn_protocols, &[b"h2".to_vec()]); + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config) + .https_only() + .enable_all_versions() + .build(); + assert_eq!(&connector.tls_config.alpn_protocols, &[b"h2".to_vec()]); + } + + fn ensure_global_state() { + #[cfg(feature = "ring")] + let _ = rustls::crypto::ring::default_provider().install_default(); + #[cfg(feature = "aws-lc-rs")] + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + } +} diff --git a/vendor/hyper-rustls/src/lib.rs b/vendor/hyper-rustls/src/lib.rs new file mode 100644 index 00000000..89e355ae --- /dev/null +++ b/vendor/hyper-rustls/src/lib.rs @@ -0,0 +1,76 @@ +//! # hyper-rustls +//! +//! A pure-Rust HTTPS connector for [hyper](https://hyper.rs), based on +//! [Rustls](https://github.com/rustls/rustls). +//! +//! ## Example client +//! +//! ```no_run +//! # #[cfg(all(feature = "rustls-native-certs", feature = "http1"))] +//! # fn main() { +//! use http::StatusCode; +//! use http_body_util::Empty; +//! use hyper::body::Bytes; +//! use hyper_util::client::legacy::Client; +//! use hyper_util::rt::TokioExecutor; +//! +//! let mut rt = tokio::runtime::Runtime::new().unwrap(); +//! let url = ("https://hyper.rs").parse().unwrap(); +//! let https = hyper_rustls::HttpsConnectorBuilder::new() +//! .with_native_roots() +//! .expect("no native root CA certificates found") +//! .https_only() +//! .enable_http1() +//! .build(); +//! +//! let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(https); +//! +//! let res = rt.block_on(client.get(url)).unwrap(); +//! assert_eq!(res.status(), StatusCode::OK); +//! # } +//! # #[cfg(not(all(feature = "rustls-native-certs", feature = "http1")))] +//! # fn main() {} +//! ``` + +#![warn(missing_docs, unreachable_pub, clippy::use_self)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +mod config; +mod connector; +mod stream; + +#[cfg(feature = "logging")] +mod log { + #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] + pub(crate) use log::debug; + #[cfg(feature = "rustls-native-certs")] + pub(crate) use log::warn; +} + +#[cfg(not(feature = "logging"))] +mod log { + #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] + macro_rules! debug ( ($($tt:tt)*) => {{}} ); + #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] + pub(crate) use debug; + #[cfg(feature = "rustls-native-certs")] + macro_rules! warn_ ( ($($tt:tt)*) => {{}} ); + #[cfg(feature = "rustls-native-certs")] + pub(crate) use warn_ as warn; +} + +pub use crate::config::ConfigBuilderExt; +pub use crate::connector::builder::ConnectorBuilder as HttpsConnectorBuilder; +pub use crate::connector::{ + DefaultServerNameResolver, FixedServerNameResolver, HttpsConnector, ResolveServerName, +}; +pub use crate::stream::MaybeHttpsStream; + +/// The various states of the [`HttpsConnectorBuilder`] +pub mod builderstates { + #[cfg(feature = "http2")] + pub use crate::connector::builder::WantsProtocols3; + pub use crate::connector::builder::{ + WantsProtocols1, WantsProtocols2, WantsSchemes, WantsTlsConfig, + }; +} diff --git a/vendor/hyper-rustls/src/stream.rs b/vendor/hyper-rustls/src/stream.rs new file mode 100644 index 00000000..f08e7b1b --- /dev/null +++ b/vendor/hyper-rustls/src/stream.rs @@ -0,0 +1,121 @@ +// Copied from hyperium/hyper-tls#62e3376/src/stream.rs +use std::fmt; +use std::io; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use hyper::rt; +use hyper_util::client::legacy::connect::{Connected, Connection}; + +use hyper_util::rt::TokioIo; +use tokio_rustls::client::TlsStream; + +/// A stream that might be protected with TLS. +#[allow(clippy::large_enum_variant)] +pub enum MaybeHttpsStream { + /// A stream over plain text. + Http(T), + /// A stream protected with TLS. + Https(TokioIo>>), +} + +impl Connection for MaybeHttpsStream { + fn connected(&self) -> Connected { + match self { + Self::Http(s) => s.connected(), + Self::Https(s) => { + let (tcp, tls) = s.inner().get_ref(); + if tls.alpn_protocol() == Some(b"h2") { + tcp.inner().connected().negotiated_h2() + } else { + tcp.inner().connected() + } + } + } + } +} + +impl fmt::Debug for MaybeHttpsStream { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Http(..) => f.pad("Http(..)"), + Self::Https(..) => f.pad("Https(..)"), + } + } +} + +impl From for MaybeHttpsStream { + fn from(inner: T) -> Self { + Self::Http(inner) + } +} + +impl From>> for MaybeHttpsStream { + fn from(inner: TlsStream>) -> Self { + Self::Https(TokioIo::new(inner)) + } +} + +impl rt::Read for MaybeHttpsStream { + #[inline] + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context, + buf: rt::ReadBufCursor<'_>, + ) -> Poll> { + match Pin::get_mut(self) { + Self::Http(s) => Pin::new(s).poll_read(cx, buf), + Self::Https(s) => Pin::new(s).poll_read(cx, buf), + } + } +} + +impl rt::Write for MaybeHttpsStream { + #[inline] + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match Pin::get_mut(self) { + Self::Http(s) => Pin::new(s).poll_write(cx, buf), + Self::Https(s) => Pin::new(s).poll_write(cx, buf), + } + } + + #[inline] + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match Pin::get_mut(self) { + Self::Http(s) => Pin::new(s).poll_flush(cx), + Self::Https(s) => Pin::new(s).poll_flush(cx), + } + } + + #[inline] + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match Pin::get_mut(self) { + Self::Http(s) => Pin::new(s).poll_shutdown(cx), + Self::Https(s) => Pin::new(s).poll_shutdown(cx), + } + } + + #[inline] + fn is_write_vectored(&self) -> bool { + match self { + Self::Http(s) => s.is_write_vectored(), + Self::Https(s) => s.is_write_vectored(), + } + } + + #[inline] + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[io::IoSlice<'_>], + ) -> Poll> { + match Pin::get_mut(self) { + Self::Http(s) => Pin::new(s).poll_write_vectored(cx, bufs), + Self::Https(s) => Pin::new(s).poll_write_vectored(cx, bufs), + } + } +} -- cgit v1.2.3