use std::{error::Error, io, ops::Deref, rc::Rc};
use serde_json::error::Error as JsonError;
#[cfg(feature = "openssl")]
use tls_openssl::ssl::{Error as SslError, HandshakeError};
use crate::error::ErrorDiagnostic;
use crate::http::error::{DecodeError, EncodeError, HttpError, PayloadError};
use crate::util::{Either, clone_io_error};
#[derive(thiserror::Error, Debug)]
pub enum JsonPayloadError {
#[error("Content type error")]
ContentType,
#[error("Json deserialize error")]
Deserialize(#[source] Option<JsonError>),
#[error("Error that occur during reading payload")]
Payload(
#[from]
#[source]
ClientPayloadError,
),
}
impl Clone for JsonPayloadError {
fn clone(&self) -> Self {
match self {
JsonPayloadError::ContentType => JsonPayloadError::ContentType,
JsonPayloadError::Deserialize(_) => JsonPayloadError::Deserialize(None),
JsonPayloadError::Payload(err) => JsonPayloadError::Payload(err.clone()),
}
}
}
impl From<JsonError> for JsonPayloadError {
fn from(err: JsonError) -> JsonPayloadError {
JsonPayloadError::Deserialize(Some(err))
}
}
impl From<PayloadError> for JsonPayloadError {
fn from(err: PayloadError) -> JsonPayloadError {
JsonPayloadError::Payload(ClientPayloadError(err))
}
}
impl ErrorDiagnostic for JsonPayloadError {
fn signature(&self) -> &'static str {
match self {
JsonPayloadError::ContentType => "ntex-client-JsonContentType",
JsonPayloadError::Deserialize(_) => "ntex-client-JsonDeserialize",
JsonPayloadError::Payload(_) => "ntex-client-JsonPayload",
}
}
}
#[derive(thiserror::Error, Clone, Debug)]
#[error("{0}")]
pub struct ClientPayloadError(
#[from]
#[source]
pub(crate) PayloadError,
);
impl Deref for ClientPayloadError {
type Target = PayloadError;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ErrorDiagnostic for ClientPayloadError {
fn signature(&self) -> &'static str {
"ntex-client-Payload"
}
}
#[derive(thiserror::Error, Copy, Clone, Debug)]
pub enum ClientBuilderError {
#[error("Cannot construct connector")]
ConnectorFailed,
}
#[derive(thiserror::Error, Debug)]
pub enum ConnectError {
#[error("SSL is not supported")]
SslIsNotSupported,
#[cfg(feature = "openssl")]
#[error("{0}")]
SslError(#[source] Rc<SslError>),
#[cfg(feature = "openssl")]
#[error("{0}")]
SslHandshakeError(String),
#[error("Failed resolving hostname: {0}")]
Resolver(
#[from]
#[source]
io::Error,
),
#[error("No dns records found for the input")]
NoRecords,
#[error("Timeout while establishing connection")]
Timeout,
#[error("Connector has been disconnected")]
Disconnected(#[source] Option<io::Error>),
#[error("Connector received `Connect` method with unresolved host")]
Unresolved,
}
impl ErrorDiagnostic for ConnectError {
fn signature(&self) -> &'static str {
match self {
ConnectError::SslIsNotSupported => "ntex-client-connect-SslIsNotSupported",
#[cfg(feature = "openssl")]
ConnectError::SslError(_) => "ntex-client-connect-SslError",
#[cfg(feature = "openssl")]
ConnectError::SslHandshakeError(_) => "ntex-client-connect-SslHandshakeError",
ConnectError::Resolver(..) => "ntex-client-connect-Resolver",
ConnectError::NoRecords => "ntex-client-connect-NoRecords",
ConnectError::Timeout => "ntex-client-connect-Timeout",
ConnectError::Disconnected(_) => "ntex-client-connect-Disconnected",
ConnectError::Unresolved => "ntex-client-connect-Unresolved",
}
}
}
impl Clone for ConnectError {
fn clone(&self) -> Self {
match self {
ConnectError::SslIsNotSupported => ConnectError::SslIsNotSupported,
#[cfg(feature = "openssl")]
ConnectError::SslError(e) => ConnectError::SslError(e.clone()),
#[cfg(feature = "openssl")]
ConnectError::SslHandshakeError(e) => {
ConnectError::SslHandshakeError(e.clone())
}
ConnectError::Resolver(e) => ConnectError::Resolver(clone_io_error(e)),
ConnectError::NoRecords => ConnectError::NoRecords,
ConnectError::Timeout => ConnectError::Timeout,
ConnectError::Disconnected(e) => {
if let Some(e) = e {
ConnectError::Disconnected(Some(clone_io_error(e)))
} else {
ConnectError::Disconnected(None)
}
}
ConnectError::Unresolved => ConnectError::Unresolved,
}
}
}
#[cfg(feature = "openssl")]
impl From<SslError> for ConnectError {
fn from(err: SslError) -> Self {
ConnectError::SslError(Rc::new(err))
}
}
impl From<crate::connect::ConnectError> for ConnectError {
fn from(err: crate::connect::ConnectError) -> ConnectError {
match err {
crate::connect::ConnectError::Resolver(e) => ConnectError::Resolver(e),
crate::connect::ConnectError::NoRecords => ConnectError::NoRecords,
crate::connect::ConnectError::InvalidInput => panic!(),
crate::connect::ConnectError::Unresolved => ConnectError::Unresolved,
crate::connect::ConnectError::Io(e) => ConnectError::Disconnected(Some(e)),
}
}
}
#[cfg(feature = "openssl")]
impl<T: std::fmt::Debug> From<HandshakeError<T>> for ConnectError {
fn from(err: HandshakeError<T>) -> ConnectError {
ConnectError::SslHandshakeError(format!("{err:?}"))
}
}
#[derive(Copy, Clone, Debug, thiserror::Error)]
pub enum InvalidUrl {
#[error("Missing url scheme")]
MissingScheme,
#[error("Unknown url scheme")]
UnknownScheme,
#[error("Missing host name")]
MissingHost,
#[error("Url parse error: {0}")]
Http(#[from] HttpError),
}
#[doc(hidden)]
#[deprecated(since = "3.2.0", note = "ClientError")]
pub type SendRequestError = ClientError;
#[derive(Debug, thiserror::Error)]
pub enum ClientError {
#[error("Invalid URL: {0}")]
Url(
#[from]
#[source]
InvalidUrl,
),
#[error("Failed to connect to host: {0}")]
Connect(
#[from]
#[source]
ConnectError,
),
#[error("Error sending request: {0}")]
Send(
#[from]
#[source]
io::Error,
),
#[error("Error during request encoding: {0}")]
Request(
#[from]
#[source]
EncodeError,
),
#[error("Error during response parsing: {0}")]
Response(
#[from]
#[source]
DecodeError,
),
#[error("{0}")]
Http(
#[from]
#[source]
HttpError,
),
#[error("Http2 error {0}")]
H2(
#[from]
#[source]
ntex_h2::OperationError,
),
#[error("Timeout while waiting for response")]
Timeout,
#[error("Tunnels are not supported for http2 connection")]
TunnelNotSupported,
#[error("Error sending request body {0}")]
Error(
#[from]
#[source]
Rc<dyn Error>,
),
}
impl Clone for ClientError {
fn clone(&self) -> ClientError {
match self {
ClientError::Url(err) => ClientError::Url(*err),
ClientError::Connect(err) => ClientError::Connect(err.clone()),
ClientError::Request(err) => ClientError::Request(err.clone()),
ClientError::Response(err) => ClientError::Response(*err),
ClientError::Http(err) => ClientError::Http(*err),
ClientError::H2(err) => ClientError::H2(err.clone()),
ClientError::Timeout => ClientError::Timeout,
ClientError::TunnelNotSupported => ClientError::TunnelNotSupported,
ClientError::Error(err) => ClientError::Error(err.clone()),
ClientError::Send(err) => ClientError::Send(crate::util::clone_io_error(err)),
}
}
}
impl From<Either<EncodeError, io::Error>> for ClientError {
fn from(err: Either<EncodeError, io::Error>) -> Self {
match err {
Either::Left(err) => ClientError::Request(err),
Either::Right(err) => ClientError::Send(err),
}
}
}
impl From<Either<DecodeError, io::Error>> for ClientError {
fn from(err: Either<DecodeError, io::Error>) -> Self {
match err {
Either::Left(err) => ClientError::Response(err),
Either::Right(err) => ClientError::Send(err),
}
}
}
impl ErrorDiagnostic for ClientError {
fn signature(&self) -> &'static str {
match self {
ClientError::Url(_) => "ntex-client-Url",
ClientError::Http(_) => "ntex-client-Http",
ClientError::Connect(err) => err.signature(),
ClientError::Send(err) => err.signature(),
ClientError::Request(_) => "ntex-client-Request",
ClientError::Response(_) => "ntex-client-Response",
ClientError::Timeout => "ntex-client-Timeout",
ClientError::TunnelNotSupported => "ntex-client-TunnelNotSupported",
ClientError::Error(_) => "ntex-client-SendBody",
ClientError::H2(err) => err.signature(),
}
}
}
impl From<ClientBuilderError> for io::Error {
fn from(err: ClientBuilderError) -> io::Error {
io::Error::other(err)
}
}