From 1d2c3f3d5f965ab8792fc67b947a0f4f17730b11 Mon Sep 17 00:00:00 2001 From: Tiga Ultraman Date: Thu, 6 Jun 2024 12:09:44 +0800 Subject: [PATCH] add dns and tls error type Signed-off-by: Tiga Ultraman --- ylong_http_client/src/async_impl/client.rs | 10 +- .../src/async_impl/connector/mod.rs | 110 ++++++++++++------ ylong_http_client/src/async_impl/pool.rs | 19 +-- ylong_http_client/src/error.rs | 61 ++++++++++ .../src/util/config/connector.rs | 4 +- ylong_http_client/src/util/config/mod.rs | 4 +- 6 files changed, 149 insertions(+), 59 deletions(-) diff --git a/ylong_http_client/src/async_impl/client.rs b/ylong_http_client/src/async_impl/client.rs index 3b408dd..b894a32 100644 --- a/ylong_http_client/src/async_impl/client.rs +++ b/ylong_http_client/src/async_impl/client.rs @@ -24,7 +24,7 @@ use crate::error::HttpClientError; use crate::runtime::timeout; #[cfg(feature = "__c_openssl")] use crate::util::c_openssl::verify::PubKeyPins; -#[cfg(all(target_os = "linux", feature = "ylong_base"))] +#[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] use crate::util::config::FchownConfig; use crate::util::config::{ ClientConfig, ConnectorConfig, HttpConfig, HttpVersion, Proxy, Redirect, Timeout, @@ -259,7 +259,7 @@ pub struct ClientBuilder { /// Options and flags that is related to `Proxy`. proxies: Proxies, - #[cfg(all(target_os = "linux", feature = "ylong_base"))] + #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] /// Fchown configuration. fchown: Option, @@ -285,7 +285,7 @@ impl ClientBuilder { http: HttpConfig::default(), client: ClientConfig::default(), proxies: Proxies::default(), - #[cfg(all(target_os = "linux", feature = "ylong_base"))] + #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] fchown: None, interceptors: Arc::new(IdleInterceptor), #[cfg(feature = "__tls")] @@ -371,7 +371,7 @@ impl ClientBuilder { /// /// let builder = ClientBuilder::new().sockets_owner(1000, 1000); /// ``` - #[cfg(all(target_os = "linux", feature = "ylong_base"))] + #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] pub fn sockets_owner(mut self, uid: u32, gid: u32) -> Self { self.fchown = Some(FchownConfig::new(uid, gid)); self @@ -466,7 +466,7 @@ impl ClientBuilder { let config = ConnectorConfig { proxies: self.proxies, - #[cfg(all(target_os = "linux", feature = "ylong_base"))] + #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] fchown: self.fchown, #[cfg(feature = "__tls")] tls: tls_builder.build()?, diff --git a/ylong_http_client/src/async_impl/connector/mod.rs b/ylong_http_client/src/async_impl/connector/mod.rs index a5fd08b..83caf5e 100644 --- a/ylong_http_client/src/async_impl/connector/mod.rs +++ b/ylong_http_client/src/async_impl/connector/mod.rs @@ -16,8 +16,6 @@ mod stream; use core::future::Future; -use std::error::Error; -use std::io; /// Information of an IO. pub use stream::ConnInfo; @@ -25,17 +23,20 @@ use ylong_http::request::uri::Uri; use crate::runtime::{AsyncRead, AsyncWrite, TcpStream}; use crate::util::config::ConnectorConfig; +use crate::HttpClientError; /// `Connector` trait used by `async_impl::Client`. `Connector` provides /// asynchronous connection establishment interfaces. pub trait Connector { /// Streams that this connector produces. type Stream: AsyncRead + AsyncWrite + ConnInfo + Unpin + Sync + Send + 'static; - /// Possible errors that this connector may generate when attempting to - /// connect. - type Error: Into>; + /// Futures generated by this connector when attempting to create a stream. - type Future: Future> + Unpin + Sync + Send + 'static; + type Future: Future> + + Unpin + + Sync + + Send + + 'static; /// Attempts to establish a connection. fn connect(&self, uri: &Uri) -> Self::Future; @@ -56,12 +57,20 @@ impl HttpConnector { } } -async fn tcp_stream(addr: &str) -> io::Result { +async fn tcp_stream(addr: &str) -> Result { TcpStream::connect(addr) .await + .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e)) .and_then(|stream| match stream.set_nodelay(true) { Ok(()) => Ok(stream), - Err(e) => Err(e), + Err(e) => { + if let Some(code) = e.raw_os_error() { + if (0x2329..=0x26B2).contains(&code) { + return err_from_dns!(Connect, e); + } + } + err_from_io!(Connect, e) + } }) } @@ -69,7 +78,6 @@ async fn tcp_stream(addr: &str) -> io::Result { mod no_tls { use core::future::Future; use core::pin::Pin; - use std::io::Error; use ylong_http::request::uri::Uri; @@ -77,12 +85,12 @@ mod no_tls { use crate::async_impl::connector::stream::HttpStream; use crate::async_impl::interceptor::{ConnDetail, ConnProtocol}; use crate::runtime::TcpStream; + use crate::HttpClientError; impl Connector for HttpConnector { type Stream = HttpStream; - type Error = Error; type Future = - Pin> + Sync + Send>>; + Pin> + Sync + Send>>; fn connect(&self, uri: &Uri) -> Self::Future { // Checks if this uri need be proxied. @@ -94,8 +102,12 @@ mod no_tls { } Box::pin(async move { let stream = tcp_stream(&addr).await?; - let local = stream.local_addr()?; - let peer = stream.peer_addr()?; + let local = stream + .local_addr() + .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e))?; + let peer = stream + .peer_addr() + .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e))?; let detail = ConnDetail { protocol: ConnProtocol::Tcp, alpn: None, @@ -124,16 +136,15 @@ mod tls { use crate::async_impl::connector::stream::HttpStream; use crate::async_impl::interceptor::{ConnDetail, ConnProtocol}; use crate::async_impl::ssl_stream::{AsyncSslStream, MixStream}; - #[cfg(all(target_os = "linux", feature = "ylong_base"))] + #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] use crate::config::FchownConfig; use crate::runtime::{AsyncReadExt, AsyncWriteExt, TcpStream}; - use crate::TlsConfig; + use crate::{HttpClientError, TlsConfig}; impl Connector for HttpConnector { type Stream = HttpStream>; - type Error = Error; type Future = - Pin> + Sync + Send>>; + Pin> + Sync + Send>>; fn connect(&self, uri: &Uri) -> Self::Future { // Make sure all parts of uri is accurate. @@ -151,18 +162,26 @@ mod tls { .and_then(|v| v.to_string().ok()); is_proxy = true; } - #[cfg(all(target_os = "linux", feature = "ylong_base"))] + #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] let fchown = self.config.fchown.clone(); match *uri.scheme().unwrap() { Scheme::HTTP => Box::pin(async move { let stream = tcp_stream(&addr).await?; - #[cfg(all(target_os = "linux", feature = "ylong_base"))] + #[cfg(all( + target_os = "linux", + feature = "ylong_base", + feature = "__c_openssl" + ))] if let Some(fchown) = fchown { let _ = stream.fchown(fchown.uid, fchown.gid); } - let local = stream.local_addr()?; - let peer = stream.peer_addr()?; + let local = stream.local_addr().map_err(|e| { + HttpClientError::from_io_error(crate::ErrorKind::Connect, e) + })?; + let peer = stream.peer_addr().map_err(|e| { + HttpClientError::from_io_error(crate::ErrorKind::Connect, e) + })?; let detail = ConnDetail { protocol: ConnProtocol::Tcp, alpn: None, @@ -179,11 +198,19 @@ mod tls { let port = uri.port().unwrap().as_u16().unwrap(); let config = self.config.tls.clone(); Box::pin(async move { - #[cfg(all(target_os = "linux", feature = "ylong_base"))] + #[cfg(all( + target_os = "linux", + feature = "ylong_base", + feature = "__c_openssl" + ))] { https_connect(config, addr, is_proxy, auth, host, port, fchown).await } - #[cfg(not(all(target_os = "linux", feature = "ylong_base")))] + #[cfg(not(all( + target_os = "linux", + feature = "ylong_base", + feature = "__c_openssl" + )))] { https_connect(config, addr, is_proxy, auth, host, port).await } @@ -200,29 +227,43 @@ mod tls { auth: Option, host: String, port: u16, - #[cfg(all(target_os = "linux", feature = "ylong_base"))] fchown: Option, - ) -> Result>, Error> { + #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] + fchown: Option, + ) -> Result>, HttpClientError> { let mut tcp = tcp_stream(addr.as_str()).await?; - #[cfg(all(target_os = "linux", feature = "ylong_base"))] + #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] if let Some(fchown) = fchown { let _ = tcp.fchown(fchown.uid, fchown.gid); } - let local = tcp.local_addr()?; - let peer = tcp.peer_addr()?; + let local = tcp + .local_addr() + .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e))?; + let peer = tcp + .peer_addr() + .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e))?; if is_proxy { - tcp = tunnel(tcp, &host, port, auth).await?; + tcp = tunnel(tcp, &host, port, auth) + .await + .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e))?; }; let pinned_key = config.pinning_host_match(addr.as_str()); let mut stream = config .ssl_new(&host) .and_then(|ssl| AsyncSslStream::new(ssl.into_inner(), tcp, pinned_key)) - .map_err(|e| Error::new(ErrorKind::Other, e))?; + .map_err(|e| { + HttpClientError::from_tls_error( + crate::ErrorKind::Connect, + Error::new(ErrorKind::Other, e), + ) + })?; - Pin::new(&mut stream) - .connect() - .await - .map_err(|e| Error::new(ErrorKind::Other, e))?; + Pin::new(&mut stream).connect().await.map_err(|e| { + HttpClientError::from_tls_error( + crate::ErrorKind::Connect, + Error::new(ErrorKind::Other, e), + ) + })?; let alpn = stream.negotiated_alpn_protocol().map(Vec::from); let detail = ConnDetail { @@ -532,7 +573,6 @@ mod tls { Header: "Content-Length", "11", Body: body.as_str(), }, - Shutdown: std::net::Shutdown::Both, ); let handle = handles.pop().expect("No more handles !"); diff --git a/ylong_http_client/src/async_impl/pool.rs b/ylong_http_client/src/async_impl/pool.rs index 6889b02..58befb5 100644 --- a/ylong_http_client/src/async_impl/pool.rs +++ b/ylong_http_client/src/async_impl/pool.rs @@ -20,7 +20,7 @@ use ylong_http::request::uri::Uri; use crate::async_impl::connector::ConnInfo; use crate::async_impl::Connector; -use crate::error::{ErrorKind, HttpClientError}; +use crate::error::HttpClientError; use crate::runtime::{AsyncRead, AsyncWrite}; #[cfg(feature = "http2")] use crate::util::config::H2Config; @@ -118,12 +118,7 @@ impl Conns if let Some(conn) = self.exist_h1_conn() { return Ok(conn); } - let dispatcher = ConnDispatcher::http1( - connector - .connect(url) - .await - .map_err(|e| HttpClientError::from_error(ErrorKind::Connect, e))?, - ); + let dispatcher = ConnDispatcher::http1(connector.connect(url).await?); Ok(self.dispatch_h1_conn(dispatcher)) } @@ -145,10 +140,7 @@ impl Conns if let Some(conn) = Self::exist_h2_conn(&mut lock) { return Ok(conn); } - let stream = connector - .connect(url) - .await - .map_err(|e| HttpClientError::from_error(ErrorKind::Connect, e))?; + let stream = connector.connect(url).await?; let details = stream.conn_detail(); let tls = if let Some(scheme) = url.scheme() { *scheme == Scheme::HTTPS @@ -188,10 +180,7 @@ impl Conns return Ok(conn); } - let stream = connector - .connect(url) - .await - .map_err(|e| HttpClientError::from_error(ErrorKind::Connect, e))?; + let stream = connector.connect(url).await?; let details = stream.conn_detail(); let protocol = if let Some(bytes) = details.alpn() { diff --git a/ylong_http_client/src/error.rs b/ylong_http_client/src/error.rs index cf4dcd0..424c782 100644 --- a/ylong_http_client/src/error.rs +++ b/ylong_http_client/src/error.rs @@ -104,6 +104,32 @@ impl HttpClientError { _ => None, } } + + /// Check whether the cause of the error is dns error + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::HttpClientError; + /// + /// assert!(!HttpClientError::user_aborted().is_dns_error()) + /// ``` + pub fn is_dns_error(&self) -> bool { + matches!(self.cause, Cause::Dns(_)) + } + + /// Check whether the cause of the error is tls connection error + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::HttpClientError; + /// + /// assert!(!HttpClientError::user_aborted().is_tls_error()) + /// ``` + pub fn is_tls_error(&self) -> bool { + matches!(self.cause, Cause::Tls(_)) + } } impl HttpClientError { @@ -117,6 +143,17 @@ impl HttpClientError { } } + #[cfg(feature = "__c_openssl")] + pub(crate) fn from_tls_error(kind: ErrorKind, err: T) -> Self + where + T: Into>, + { + Self { + kind, + cause: Cause::Tls(err.into()), + } + } + pub(crate) fn from_str(kind: ErrorKind, msg: &'static str) -> Self { Self { kind, @@ -130,6 +167,13 @@ impl HttpClientError { cause: Cause::Io(err), } } + + pub(crate) fn from_dns_error(kind: ErrorKind, err: io::Error) -> Self { + Self { + kind, + cause: Cause::Dns(err), + } + } } impl Debug for HttpClientError { @@ -227,6 +271,9 @@ impl ErrorKind { pub(crate) enum Cause { NoReason, + Dns(io::Error), + #[cfg(feature = "__c_openssl")] + Tls(Box), Io(io::Error), Msg(&'static str), Other(Box), @@ -236,6 +283,9 @@ impl Debug for Cause { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::NoReason => write!(f, "No reason"), + Self::Dns(err) => Debug::fmt(err, f), + #[cfg(feature = "__c_openssl")] + Self::Tls(err) => Debug::fmt(err, f), Self::Io(err) => Debug::fmt(err, f), Self::Msg(msg) => write!(f, "{}", msg), Self::Other(err) => Debug::fmt(err, f), @@ -247,6 +297,9 @@ impl Display for Cause { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::NoReason => write!(f, "No reason"), + Self::Dns(err) => Display::fmt(err, f), + #[cfg(feature = "__c_openssl")] + Self::Tls(err) => Display::fmt(err, f), Self::Io(err) => Display::fmt(err, f), Self::Msg(msg) => write!(f, "{}", msg), Self::Other(err) => Display::fmt(err, f), @@ -270,6 +323,14 @@ macro_rules! err_from_io { }}; } +macro_rules! err_from_dns { + ($kind: ident, $err: expr) => {{ + use crate::error::{ErrorKind, HttpClientError}; + + Err(HttpClientError::from_dns_error(ErrorKind::$kind, $err)) + }}; +} + macro_rules! err_from_msg { ($kind: ident, $msg: literal) => {{ use crate::error::{ErrorKind, HttpClientError}; diff --git a/ylong_http_client/src/util/config/connector.rs b/ylong_http_client/src/util/config/connector.rs index 80de0ee..c61d49f 100644 --- a/ylong_http_client/src/util/config/connector.rs +++ b/ylong_http_client/src/util/config/connector.rs @@ -13,7 +13,7 @@ //! Connector configure module. - #[cfg(all(target_os = "linux", feature = "ylong_base"))] +#[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] use super::FchownConfig; use crate::util::proxy::Proxies; @@ -21,7 +21,7 @@ use crate::util::proxy::Proxies; pub(crate) struct ConnectorConfig { pub(crate) proxies: Proxies, - #[cfg(all(target_os = "linux", feature = "ylong_base"))] + #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] pub(crate) fchown: Option, #[cfg(feature = "__tls")] diff --git a/ylong_http_client/src/util/config/mod.rs b/ylong_http_client/src/util/config/mod.rs index 3e766d5..efa1091 100644 --- a/ylong_http_client/src/util/config/mod.rs +++ b/ylong_http_client/src/util/config/mod.rs @@ -30,7 +30,7 @@ pub(crate) use tls::{AlpnProtocol, AlpnProtocolList}; pub use tls::{CertVerifier, ServerCerts}; #[cfg(feature = "tls_rust_ssl")] pub use tls::{Certificate, PrivateKey, TlsConfig, TlsConfigBuilder, TlsFileType, TlsVersion}; - #[cfg(all(target_os = "linux", feature = "ylong_base"))] +#[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] mod fchown; - #[cfg(all(target_os = "linux", feature = "ylong_base"))] +#[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))] pub(crate) use fchown::FchownConfig; -- Gitee