chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

136
vendor/hyper-rustls/src/config.rs vendored Normal file
View File

@@ -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<ClientConfig, WantsClientCert>;
/// 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<ConfigBuilder<ClientConfig, WantsClientCert>, 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<ConfigBuilder<ClientConfig, WantsClientCert>, 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<ClientConfig, WantsClientCert>;
}
impl ConfigBuilderExt for ConfigBuilder<ClientConfig, WantsVerifier> {
#[cfg(feature = "rustls-platform-verifier")]
fn with_platform_verifier(self) -> ConfigBuilder<ClientConfig, WantsClientCert> {
self.try_with_platform_verifier()
.expect("failure to initialize platform verifier")
}
#[cfg(feature = "rustls-platform-verifier")]
fn try_with_platform_verifier(
self,
) -> Result<ConfigBuilder<ClientConfig, WantsClientCert>, 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<ConfigBuilder<ClientConfig, WantsClientCert>, 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<ClientConfig, WantsClientCert> {
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<ClientConfig, WantsVerifier> {}
}

296
vendor/hyper-rustls/src/connector.rs vendored Normal file
View File

@@ -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<dyn std::error::Error + Send + Sync>;
/// A Connector for the `https` scheme.
#[derive(Clone)]
pub struct HttpsConnector<T> {
force_https: bool,
http: T,
tls_config: Arc<rustls::ClientConfig>,
server_name_resolver: Arc<dyn ResolveServerName + Sync + Send>,
}
impl<T> HttpsConnector<T> {
/// Creates a [`crate::HttpsConnectorBuilder`] to configure a `HttpsConnector`.
///
/// This is the same as [`crate::HttpsConnectorBuilder::new()`].
pub fn builder() -> builder::ConnectorBuilder<builder::WantsTlsConfig> {
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<T> Service<Uri> for HttpsConnector<T>
where
T: Service<Uri>,
T::Response: Connection + rt::Read + rt::Write + Send + Unpin + 'static,
T::Future: Send + 'static,
T::Error: Into<BoxError>,
{
type Response = MaybeHttpsStream<T::Response>;
type Error = BoxError;
#[allow(clippy::type_complexity)]
type Future =
Pin<Box<dyn Future<Output = Result<MaybeHttpsStream<T::Response>, BoxError>> + Send>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
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<H, C> From<(H, C)> for HttpsConnector<H>
where
C: Into<Arc<rustls::ClientConfig>>,
{
fn from((http, cfg): (H, C)) -> Self {
Self {
force_https: false,
http,
tls_config: cfg.into(),
server_name_resolver: Arc::new(DefaultServerNameResolver::default()),
}
}
}
impl<T> fmt::Debug for HttpsConnector<T> {
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<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>> {
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<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>> {
Ok(self.name.clone())
}
}
impl<F, E> ResolveServerName for F
where
F: Fn(&Uri) -> Result<ServerName<'static>, E>,
E: Into<Box<dyn std::error::Error + Sync + Send>>,
{
fn resolve(
&self,
uri: &Uri,
) -> Result<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>> {
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<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>>;
}
#[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<MaybeHttpsStream<TokioIo<TcpStream>>, 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,
}
}

View File

@@ -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);
/// State of a builder that needs a TLS client config next
pub struct WantsTlsConfig(());
impl ConnectorBuilder<WantsTlsConfig> {
/// 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<WantsSchemes> {
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<WantsSchemes> {
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<ConnectorBuilder<WantsSchemes>, 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<Arc<CryptoProvider>>,
) -> std::io::Result<ConnectorBuilder<WantsSchemes>> {
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<ConnectorBuilder<WantsSchemes>> {
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<Arc<CryptoProvider>>,
) -> std::io::Result<ConnectorBuilder<WantsSchemes>> {
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<WantsSchemes> {
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<Arc<CryptoProvider>>,
) -> Result<ConnectorBuilder<WantsSchemes>, 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<WantsTlsConfig> {
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<WantsSchemes> {
/// Enforce the use of HTTPS when connecting
///
/// Only URLs using the HTTPS scheme will be connectable.
pub fn https_only(self) -> ConnectorBuilder<WantsProtocols1> {
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<WantsProtocols1> {
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<Arc<dyn ResolveServerName + Sync + Send>>,
}
impl WantsProtocols1 {
fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {
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<HttpConnector> {
let mut http = HttpConnector::new();
// HttpConnector won't enforce scheme, but HttpsConnector will
http.enforce_http(false);
self.wrap_connector(http)
}
}
impl ConnectorBuilder<WantsProtocols1> {
/// Enable HTTP1
///
/// This needs to be called explicitly, no protocol is enabled by default
#[cfg(feature = "http1")]
pub fn enable_http1(self) -> ConnectorBuilder<WantsProtocols2> {
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<WantsProtocols3> {
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<WantsProtocols3> {
#[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<WantsProtocols2>::build) and
/// [`wrap_connector`](ConnectorBuilder<WantsProtocols2>::wrap_connector).
pub struct WantsProtocols2 {
inner: WantsProtocols1,
}
impl ConnectorBuilder<WantsProtocols2> {
/// Enable HTTP2
///
/// This needs to be called explicitly, no protocol is enabled by default
#[cfg(feature = "http2")]
pub fn enable_http2(mut self) -> ConnectorBuilder<WantsProtocols3> {
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<HttpConnector> {
self.0.inner.build()
}
/// This wraps an arbitrary low-level connector into an [`HttpsConnector`]
pub fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {
// 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<WantsProtocols3>::build) and
/// [`wrap_connector`](ConnectorBuilder<WantsProtocols3>::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<WantsProtocols3> {
/// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`]
pub fn build(self) -> HttpsConnector<HttpConnector> {
self.0.inner.build()
}
/// This wraps an arbitrary low-level connector into an [`HttpsConnector`]
pub fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {
// 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();
}
}

76
vendor/hyper-rustls/src/lib.rs vendored Normal file
View File

@@ -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<Bytes>> = 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,
};
}

121
vendor/hyper-rustls/src/stream.rs vendored Normal file
View File

@@ -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<T> {
/// A stream over plain text.
Http(T),
/// A stream protected with TLS.
Https(TokioIo<TlsStream<TokioIo<T>>>),
}
impl<T: rt::Read + rt::Write + Connection + Unpin> Connection for MaybeHttpsStream<T> {
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<T: fmt::Debug> fmt::Debug for MaybeHttpsStream<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Http(..) => f.pad("Http(..)"),
Self::Https(..) => f.pad("Https(..)"),
}
}
}
impl<T> From<T> for MaybeHttpsStream<T> {
fn from(inner: T) -> Self {
Self::Http(inner)
}
}
impl<T> From<TlsStream<TokioIo<T>>> for MaybeHttpsStream<T> {
fn from(inner: TlsStream<TokioIo<T>>) -> Self {
Self::Https(TokioIo::new(inner))
}
}
impl<T: rt::Read + rt::Write + Unpin> rt::Read for MaybeHttpsStream<T> {
#[inline]
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context,
buf: rt::ReadBufCursor<'_>,
) -> Poll<Result<(), io::Error>> {
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<T: rt::Write + rt::Read + Unpin> rt::Write for MaybeHttpsStream<T> {
#[inline]
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
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<Result<(), io::Error>> {
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<Result<(), io::Error>> {
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<Result<usize, io::Error>> {
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),
}
}
}