diff options
Diffstat (limited to 'vendor/hyper-tls/src')
-rw-r--r-- | vendor/hyper-tls/src/client.rs | 183 | ||||
-rw-r--r-- | vendor/hyper-tls/src/lib.rs | 35 | ||||
-rw-r--r-- | vendor/hyper-tls/src/stream.rs | 111 |
3 files changed, 329 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 {} diff --git a/vendor/hyper-tls/src/lib.rs b/vendor/hyper-tls/src/lib.rs new file mode 100644 index 000000000..2245ee34c --- /dev/null +++ b/vendor/hyper-tls/src/lib.rs @@ -0,0 +1,35 @@ +//! # hyper-tls +//! +//! An HTTPS connector to be used with [hyper][]. +//! +//! [hyper]: https://hyper.rs +//! +//! ## Example +//! +//! ```no_run +//! use hyper_tls::HttpsConnector; +//! use hyper::Client; +//! +//! #[tokio::main(flavor = "current_thread")] +//! async fn main() -> Result<(), Box<dyn std::error::Error>>{ +//! let https = HttpsConnector::new(); +//! let client = Client::builder().build::<_, hyper::Body>(https); +//! +//! let res = client.get("https://hyper.rs".parse()?).await?; +//! assert_eq!(res.status(), 200); +//! Ok(()) +//! } +//! ``` +#![doc(html_root_url = "https://docs.rs/hyper-tls/0.5.0")] +#![cfg_attr(test, deny(warnings))] +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] + +#[doc(hidden)] +pub extern crate native_tls; + +pub use client::{HttpsConnecting, HttpsConnector}; +pub use stream::{MaybeHttpsStream, TlsStream}; + +mod client; +mod stream; diff --git a/vendor/hyper-tls/src/stream.rs b/vendor/hyper-tls/src/stream.rs new file mode 100644 index 000000000..487541003 --- /dev/null +++ b/vendor/hyper-tls/src/stream.rs @@ -0,0 +1,111 @@ +use std::fmt; +use std::io; +use std::io::IoSlice; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use hyper::client::connect::{Connected, Connection}; +use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +pub use tokio_native_tls::TlsStream; + +/// A stream that might be protected with TLS. +pub enum MaybeHttpsStream<T> { + /// A stream over plain text. + Http(T), + /// A stream protected with TLS. + Https(TlsStream<T>), +} + +// ===== impl MaybeHttpsStream ===== + +impl<T: fmt::Debug> fmt::Debug for MaybeHttpsStream<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + MaybeHttpsStream::Http(s) => f.debug_tuple("Http").field(s).finish(), + MaybeHttpsStream::Https(s) => f.debug_tuple("Https").field(s).finish(), + } + } +} + +impl<T> From<T> for MaybeHttpsStream<T> { + fn from(inner: T) -> Self { + MaybeHttpsStream::Http(inner) + } +} + +impl<T> From<TlsStream<T>> for MaybeHttpsStream<T> { + fn from(inner: TlsStream<T>) -> Self { + MaybeHttpsStream::Https(inner) + } +} + +impl<T: AsyncRead + AsyncWrite + Unpin> AsyncRead for MaybeHttpsStream<T> { + #[inline] + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context, + buf: &mut ReadBuf, + ) -> Poll<Result<(), io::Error>> { + match Pin::get_mut(self) { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_read(cx, buf), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_read(cx, buf), + } + } +} + +impl<T: AsyncWrite + AsyncRead + Unpin> AsyncWrite 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) { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_write(cx, buf), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_write(cx, buf), + } + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll<Result<usize, io::Error>> { + match Pin::get_mut(self) { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_write_vectored(cx, bufs), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_write_vectored(cx, bufs), + } + } + + fn is_write_vectored(&self) -> bool { + match self { + MaybeHttpsStream::Http(s) => s.is_write_vectored(), + MaybeHttpsStream::Https(s) => s.is_write_vectored(), + } + } + + #[inline] + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> { + match Pin::get_mut(self) { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_flush(cx), + MaybeHttpsStream::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) { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_shutdown(cx), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_shutdown(cx), + } + } +} + +impl<T: AsyncRead + AsyncWrite + Connection + Unpin> Connection for MaybeHttpsStream<T> { + fn connected(&self) -> Connected { + match self { + MaybeHttpsStream::Http(s) => s.connected(), + MaybeHttpsStream::Https(s) => s.get_ref().get_ref().get_ref().connected(), + } + } +} |