use super::wire_msg::WireMsg;
use crate::config::ConfigError;
#[cfg(feature = "igd")]
use crate::igd::IgdError;
use bytes::Bytes;
use std::{fmt, io, net::SocketAddr};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum EndpointError {
#[error("There was a problem with the provided configuration")]
Config(#[from] ConfigError),
#[error("Failed to bind UDP socket")]
Socket(#[source] io::Error),
#[error("Failed to query our public address from peer {peer}")]
EndpointEcho {
peer: SocketAddr,
#[source]
error: RpcError,
},
#[cfg(feature = "igd")]
#[error(transparent)]
Upnp(#[from] UpnpError),
#[error("Failed to verify our public address with peer {peer}")]
EndpointVerification {
peer: SocketAddr,
#[source]
error: RpcError,
},
#[error("Our determined public address ({public_addr}) is not reachable")]
Unreachable {
public_addr: SocketAddr,
},
}
impl From<quinn::EndpointError> for EndpointError {
fn from(error: quinn::EndpointError) -> Self {
match error {
quinn::EndpointError::Socket(error) => Self::Socket(error),
}
}
}
#[cfg(feature = "igd")]
impl From<IgdError> for EndpointError {
fn from(error: IgdError) -> Self {
Self::Upnp(UpnpError(error))
}
}
#[derive(Debug, Error)]
pub enum ClientEndpointError {
#[error("There was a problem with the provided configuration")]
Config(#[from] ConfigError),
#[error("Failed to bind UDP socket")]
Socket(#[source] io::Error),
}
impl From<quinn::EndpointError> for ClientEndpointError {
fn from(error: quinn::EndpointError) -> Self {
match error {
quinn::EndpointError::Socket(error) => Self::Socket(error),
}
}
}
#[derive(Clone, Debug, Error, PartialEq)]
pub enum ConnectionError {
#[error("The endpoint has been stopped")]
Stopped,
#[error("The number of active connections on the local endpoint is at the limit")]
TooManyConnections,
#[error("Invalid remote address: {0}")]
InvalidAddress(SocketAddr),
#[error("BUG: internal configuration error")]
InternalConfigError(#[source] InternalConfigError),
#[error("{}", quinn::ConnectionError::VersionMismatch)]
VersionMismatch,
#[error("{0}")]
TransportError(#[source] quinn_proto::TransportError),
#[error("{}", quinn::ConnectionError::Reset)]
Reset,
#[error("{}", quinn::ConnectionError::TimedOut)]
TimedOut,
#[error("The connection was closed by {0}")]
Closed(Close),
}
impl From<quinn::ConnectError> for ConnectionError {
fn from(error: quinn::ConnectError) -> Self {
match error {
quinn::ConnectError::EndpointStopping => Self::Stopped,
quinn::ConnectError::TooManyConnections => Self::TooManyConnections,
quinn::ConnectError::InvalidRemoteAddress(addr) => Self::InvalidAddress(addr),
quinn::ConnectError::InvalidDnsName(_) => {
Self::InternalConfigError(InternalConfigError(error))
}
quinn::ConnectError::Config(_) => {
Self::InternalConfigError(InternalConfigError(error))
}
}
}
}
impl From<quinn::ConnectionError> for ConnectionError {
fn from(error: quinn::ConnectionError) -> Self {
match error {
quinn::ConnectionError::LocallyClosed => Self::Closed(Close::Local),
quinn::ConnectionError::ApplicationClosed(close) => Self::Closed(Close::Application {
error_code: close.error_code.into_inner(),
reason: close.reason,
}),
quinn::ConnectionError::ConnectionClosed(close) => Self::Closed(Close::Transport {
error_code: TransportErrorCode(close.error_code),
reason: close.reason,
}),
quinn::ConnectionError::VersionMismatch => Self::VersionMismatch,
quinn::ConnectionError::TransportError(error) => Self::TransportError(error),
quinn::ConnectionError::Reset => Self::Reset,
quinn::ConnectionError::TimedOut => Self::TimedOut,
}
}
}
#[derive(Clone, Debug, Error, PartialEq)]
#[error(transparent)]
pub struct InternalConfigError(quinn::ConnectError);
#[derive(Clone, Debug, PartialEq)]
pub enum Close {
Local,
Application {
error_code: u64,
reason: Bytes,
},
Transport {
error_code: TransportErrorCode,
reason: Bytes,
},
}
impl fmt::Display for Close {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (closed_by, error_code, reason): (_, &dyn fmt::Display, _) = match self {
Self::Local => return write!(f, "us"),
Self::Application { error_code, reason } => {
("application", error_code, String::from_utf8_lossy(reason))
}
Self::Transport { error_code, reason } => {
("transport", error_code, String::from_utf8_lossy(reason))
}
};
write!(
f,
"{} (error code: {}, reason: {})",
closed_by, error_code, reason
)
}
}
impl From<quinn::ApplicationClose> for Close {
fn from(close: quinn::ApplicationClose) -> Self {
Self::Application {
error_code: close.error_code.into_inner(),
reason: close.reason,
}
}
}
impl From<quinn::ConnectionClose> for Close {
fn from(close: quinn::ConnectionClose) -> Self {
Self::Transport {
error_code: TransportErrorCode(close.error_code),
reason: close.reason,
}
}
}
#[derive(Clone, PartialEq)]
pub struct TransportErrorCode(quinn_proto::TransportErrorCode);
impl fmt::Debug for TransportErrorCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
impl fmt::Display for TransportErrorCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Error)]
pub enum RpcError {
#[error("We did not receive a response within the expected time")]
TimedOut,
#[error("Failed to send the request")]
Send(#[from] SendError),
#[error("Failed to receive the response")]
Recv(#[from] RecvError),
}
impl From<ConnectionError> for RpcError {
fn from(error: ConnectionError) -> Self {
Self::Send(error.into())
}
}
impl From<quinn::ConnectionError> for RpcError {
fn from(error: quinn::ConnectionError) -> Self {
Self::Send(error.into())
}
}
impl From<tokio::time::error::Elapsed> for RpcError {
fn from(_: tokio::time::error::Elapsed) -> Self {
Self::TimedOut
}
}
#[derive(Debug, Error)]
pub enum SendError {
#[error("Failed to serialize message")]
Serialization(#[from] SerializationError),
#[error("Connection was lost when trying to send a message")]
ConnectionLost(#[from] ConnectionError),
#[error("Stream was lost when trying to send a message")]
StreamLost(#[source] StreamError),
}
impl From<bincode::Error> for SendError {
fn from(error: bincode::Error) -> Self {
Self::Serialization(SerializationError(error))
}
}
impl From<quinn::ConnectionError> for SendError {
fn from(error: quinn::ConnectionError) -> Self {
Self::ConnectionLost(error.into())
}
}
impl From<quinn::WriteError> for SendError {
fn from(error: quinn::WriteError) -> Self {
match error {
quinn::WriteError::Stopped(code) => Self::StreamLost(StreamError::Stopped(code.into())),
quinn::WriteError::ConnectionClosed(error) => Self::ConnectionLost(error.into()),
quinn::WriteError::UnknownStream => Self::StreamLost(StreamError::Gone),
quinn::WriteError::ZeroRttRejected => Self::StreamLost(StreamError::Unsupported(
UnsupportedStreamOperation(error.into()),
)),
}
}
}
#[derive(Debug, Error)]
pub enum RecvError {
#[error("Failed to deserialize message")]
Serialization(#[from] SerializationError),
#[error("Connection was lost when trying to receive a message")]
ConnectionLost(#[from] ConnectionError),
#[error("Stream was lost when trying to receive a message")]
StreamLost(#[source] StreamError),
}
impl From<quinn::ConnectionError> for RecvError {
fn from(error: quinn::ConnectionError) -> Self {
Self::ConnectionLost(error.into())
}
}
impl From<bincode::Error> for RecvError {
fn from(error: bincode::Error) -> Self {
Self::Serialization(SerializationError(error))
}
}
impl From<quinn::ReadError> for RecvError {
fn from(error: quinn::ReadError) -> Self {
use quinn::ReadError;
match error {
ReadError::Reset(code) => Self::StreamLost(StreamError::Stopped(code.into())),
ReadError::ConnectionClosed(error) => Self::ConnectionLost(error.into()),
ReadError::UnknownStream => Self::StreamLost(StreamError::Gone),
ReadError::IllegalOrderedRead | ReadError::ZeroRttRejected => Self::StreamLost(
StreamError::Unsupported(UnsupportedStreamOperation(error.into())),
),
}
}
}
impl From<quinn::ReadExactError> for RecvError {
fn from(error: quinn::ReadExactError) -> Self {
match error {
quinn::ReadExactError::FinishedEarly => Self::Serialization(SerializationError::new(
"Received too few bytes for message",
)),
quinn::ReadExactError::ReadError(error) => error.into(),
}
}
}
#[derive(Debug, Error)]
#[error(transparent)]
pub struct SerializationError(bincode::Error);
impl SerializationError {
pub(crate) fn new(message: impl ToString) -> Self {
Self(bincode::ErrorKind::Custom(message.to_string()).into())
}
pub(crate) fn unexpected(actual: Option<WireMsg>) -> Self {
if let Some(actual) = actual {
Self::new(format!(
"The message received was not the expected one: {}",
actual
))
} else {
Self::new("Unexpected end of stream")
}
}
}
#[derive(Debug, Error)]
pub enum StreamError {
#[error("The peer abandoned the stream (error code: {0})")]
Stopped(u64),
#[error("The stream was already stopped, finished, or reset")]
Gone,
#[error("An error was caused by an unsupported operation")]
Unsupported(#[source] UnsupportedStreamOperation),
}
#[derive(Debug, Error)]
#[error(transparent)]
pub struct UnsupportedStreamOperation(Box<dyn std::error::Error + Send + Sync>);
#[cfg(feature = "igd")]
#[derive(Debug, Error)]
#[error("Failed to establish UPnP port forwarding")]
pub struct UpnpError(#[source] IgdError);