diff --git a/src/client.rs b/src/client.rs index 1ad6555..c921995 100644 --- a/src/client.rs +++ b/src/client.rs @@ -7,10 +7,9 @@ use hyper::{client::connect::HttpConnector, service::Service, Uri}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_tls::TlsConnector; +use crate::error::HttpsConnectorError; use crate::stream::MaybeHttpsStream; -type BoxError = Box; - /// A Connector for the `https` scheme. #[derive(Clone)] pub struct HttpsConnector { @@ -85,16 +84,16 @@ where T: Service, T::Response: AsyncRead + AsyncWrite + Send + Unpin, T::Future: Send + 'static, - T::Error: Into, + T::Error: Send + Sync + 'static, { type Response = MaybeHttpsStream; - type Error = BoxError; - type Future = HttpsConnecting; + type Error = HttpsConnectorError; + type Future = HttpsConnecting; 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::Ready(Err(e)) => Poll::Ready(Err(HttpsConnectorError::HttpConnector(e))), Poll::Pending => Poll::Pending, } } @@ -103,20 +102,21 @@ where 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()); + return err(HttpsConnectorError::ForceHttpsButUriNotHttps); } - let host = dst.host() - .unwrap_or("") - .to_owned(); + let host = dst.host().unwrap_or("").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 tcp = connecting + .await + .map_err(HttpsConnectorError::HttpConnector)?; let maybe = if is_https { let tls = tls .connect(&host, tcp) - .await?; + .await + .map_err(|e| HttpsConnectorError::NativeTls(e))?; MaybeHttpsStream::Https(tls) } else { MaybeHttpsStream::Http(tcp) @@ -127,39 +127,25 @@ where } } -fn err(e: BoxError) -> HttpsConnecting { +fn err(e: E) -> HttpsConnecting { HttpsConnecting(Box::pin(async { Err(e) })) } -type BoxedFut = - Pin, BoxError>> + Send>>; +type BoxedFut = Pin, E>> + Send>>; /// A Future representing work to connect to a URL, and a TLS handshake. -pub struct HttpsConnecting(BoxedFut); +pub struct HttpsConnecting(BoxedFut); -impl Future for HttpsConnecting { - type Output = Result, BoxError>; +impl Future for HttpsConnecting { + type Output = Result, E>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.0).poll(cx) } } -impl fmt::Debug for HttpsConnecting { +impl fmt::Debug for HttpsConnecting { 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/src/error.rs b/src/error.rs new file mode 100644 index 0000000..0f9666c --- /dev/null +++ b/src/error.rs @@ -0,0 +1,55 @@ +/// HttpsConnectorError represents a HttpsConnector error. +pub enum HttpsConnectorError { + /// An https:// URI was provided when the force_https option was on. + ForceHttpsButUriNotHttps, + /// Underlying HttpConnector failed when setting up an HTTP connection. + HttpConnector(E), + /// `native_tls` failed when setting up a TLS connection. + NativeTls(native_tls::Error), + + #[doc(hidden)] + __Nonexhaustive, +} + +impl std::fmt::Debug for HttpsConnectorError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + HttpsConnectorError::ForceHttpsButUriNotHttps => { + f.write_str("HttpsConnectorError::ForceHttpsButUriNotHttps") + } + HttpsConnectorError::HttpConnector(err) => f + .debug_tuple("HttpsConnectorError::HttpConnector") + .field(err) + .finish(), + HttpsConnectorError::NativeTls(err) => f + .debug_tuple("HttpsConnectorError::NativeTls") + .field(err) + .finish(), + HttpsConnectorError::__Nonexhaustive => unimplemented!(), + } + } +} + +impl std::fmt::Display for HttpsConnectorError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + HttpsConnectorError::ForceHttpsButUriNotHttps => { + write!(f, "https required but URI was not https") + } + HttpsConnectorError::HttpConnector(err) => write!(f, "http connector error: {}", err), + HttpsConnectorError::NativeTls(err) => write!(f, "native tls error: {}", err), + HttpsConnectorError::__Nonexhaustive => unimplemented!(), + } + } +} + +impl std::error::Error for HttpsConnectorError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + HttpsConnectorError::ForceHttpsButUriNotHttps => None, + HttpsConnectorError::HttpConnector(err) => Some(err), + HttpsConnectorError::NativeTls(err) => Some(err), + HttpsConnectorError::__Nonexhaustive => unimplemented!(), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 94ab3d3..44fc7b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,9 @@ pub extern crate native_tls; pub use client::{HttpsConnecting, HttpsConnector}; +pub use error::HttpsConnectorError; pub use stream::{MaybeHttpsStream, TlsStream}; mod client; +mod error; mod stream;