diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
commit | d1b2d29528b7794b41e66fc2136e395a02f8529b (patch) | |
tree | a4a17504b260206dec3cf55b2dca82929a348ac2 /vendor/hyper-tls/src/client.rs | |
parent | Releasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip |
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/hyper-tls/src/client.rs')
-rw-r--r-- | vendor/hyper-tls/src/client.rs | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/vendor/hyper-tls/src/client.rs b/vendor/hyper-tls/src/client.rs new file mode 100644 index 000000000..3b8339bba --- /dev/null +++ b/vendor/hyper-tls/src/client.rs @@ -0,0 +1,183 @@ +use std::fmt; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use hyper::{client::connect::HttpConnector, service::Service, Uri}; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_native_tls::TlsConnector; + +use crate::stream::MaybeHttpsStream; + +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: TlsConnector, +} + +impl HttpsConnector<HttpConnector> { + /// Construct a new HttpsConnector. + /// + /// This uses hyper's default `HttpConnector`, and default `TlsConnector`. + /// If you wish to use something besides the defaults, use `From::from`. + /// + /// # Note + /// + /// By default this connector will use plain HTTP if the URL provded uses + /// the HTTP scheme (eg: http://example.com/). + /// + /// If you would like to force the use of HTTPS then call https_only(true) + /// on the returned connector. + /// + /// # Panics + /// + /// This will panic if the underlying TLS context could not be created. + /// + /// To handle that error yourself, you can use the `HttpsConnector::from` + /// constructor after trying to make a `TlsConnector`. + pub fn new() -> Self { + native_tls::TlsConnector::new() + .map(|tls| HttpsConnector::new_(tls.into())) + .unwrap_or_else(|e| panic!("HttpsConnector::new() failure: {}", e)) + } + + fn new_(tls: TlsConnector) -> Self { + let mut http = HttpConnector::new(); + http.enforce_http(false); + HttpsConnector::from((http, tls)) + } +} + +impl<T: Default> Default for HttpsConnector<T> { + fn default() -> Self { + Self::new_with_connector(Default::default()) + } +} + +impl<T> HttpsConnector<T> { + /// Force the use of HTTPS when connecting. + /// + /// If a URL is not `https` when connecting, an error is returned. + pub fn https_only(&mut self, enable: bool) { + self.force_https = enable; + } + + /// With connector constructor + /// + pub fn new_with_connector(http: T) -> Self { + native_tls::TlsConnector::new() + .map(|tls| HttpsConnector::from((http, tls.into()))) + .unwrap_or_else(|e| { + panic!( + "HttpsConnector::new_with_connector(<connector>) failure: {}", + e + ) + }) + } +} + +impl<T> From<(T, TlsConnector)> for HttpsConnector<T> { + fn from(args: (T, TlsConnector)) -> HttpsConnector<T> { + HttpsConnector { + force_https: false, + http: args.0, + tls: args.1, + } + } +} + +impl<T: fmt::Debug> fmt::Debug for HttpsConnector<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("HttpsConnector") + .field("force_https", &self.force_https) + .field("http", &self.http) + .finish() + } +} + +impl<T> Service<Uri> for HttpsConnector<T> +where + T: Service<Uri>, + T::Response: AsyncRead + AsyncWrite + Send + Unpin, + T::Future: Send + 'static, + T::Error: Into<BoxError>, +{ + type Response = MaybeHttpsStream<T::Response>; + type Error = BoxError; + type Future = HttpsConnecting<T::Response>; + + 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 { + let is_https = dst.scheme_str() == Some("https"); + // Early abort if HTTPS is forced but can't be used + if !is_https && self.force_https { + return err(ForceHttpsButUriNotHttps.into()); + } + + let host = dst + .host() + .unwrap_or("") + .trim_matches(|c| c == '[' || c == ']') + .to_owned(); + let connecting = self.http.call(dst); + let tls = self.tls.clone(); + let fut = async move { + let tcp = connecting.await.map_err(Into::into)?; + let maybe = if is_https { + let tls = tls.connect(&host, tcp).await?; + MaybeHttpsStream::Https(tls) + } else { + MaybeHttpsStream::Http(tcp) + }; + Ok(maybe) + }; + HttpsConnecting(Box::pin(fut)) + } +} + +fn err<T>(e: BoxError) -> HttpsConnecting<T> { + HttpsConnecting(Box::pin(async { Err(e) })) +} + +type BoxedFut<T> = Pin<Box<dyn Future<Output = Result<MaybeHttpsStream<T>, BoxError>> + Send>>; + +/// A Future representing work to connect to a URL, and a TLS handshake. +pub struct HttpsConnecting<T>(BoxedFut<T>); + +impl<T: AsyncRead + AsyncWrite + Unpin> Future for HttpsConnecting<T> { + type Output = Result<MaybeHttpsStream<T>, BoxError>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + Pin::new(&mut self.0).poll(cx) + } +} + +impl<T> fmt::Debug for HttpsConnecting<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("HttpsConnecting") + } +} + +// ===== Custom Errors ===== + +#[derive(Debug)] +struct ForceHttpsButUriNotHttps; + +impl fmt::Display for ForceHttpsButUriNotHttps { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("https required but URI was not https") + } +} + +impl std::error::Error for ForceHttpsButUriNotHttps {} |