use bytes::Bytes;
use std::{fmt, io, net::SocketAddr};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum EndpointError {
#[error("Certificate could not be generated for config")]
Certificate(CertificateError),
#[error(transparent)]
IoError(#[from] io::Error),
}
#[derive(Error, Debug)]
pub enum CertificateError {
#[error("Rcgen internal error generating certificate")]
Rcgen(#[from] rcgen::RcgenError),
#[error("Certificate or name validation error")]
WebPki(#[from] webpki::Error),
#[error("Rustls internal error")]
Rustls(#[from] rustls::Error),
}
#[derive(Clone, Debug, Error, PartialEq, Eq)]
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, {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(_)
| quinn::ConnectError::NoDefaultClientConfig
| quinn::ConnectError::UnsupportedVersion => {
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, Eq)]
#[error(transparent)]
pub struct InternalConfigError(quinn::ConnectError);
#[derive(Clone, Debug, PartialEq, Eq)]
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, "we closed the connection"),
Self::Application { error_code, reason } => ("remote application", error_code, reason),
Self::Transport { error_code, reason } => ("transport layer", error_code, reason),
};
write!(
f,
"{} closed the connection (error code: {}, reason: {})",
closed_by,
error_code,
String::from_utf8_lossy(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, Eq)]
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("The serialized message is too long ({0} bytes, max: 4 GiB)")]
MessageTooLong(usize),
#[error("Failed to serialize message")]
Serialization(#[from] bincode::Error),
#[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<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::ConnectionLost(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("Message received from peer has an empty payload")]
EmptyMsgPayload,
#[error("Received too few bytes for message")]
NotEnoughBytes,
#[error("Failed to deserialize message")]
Serialization(#[from] bincode::Error),
#[error("Invalid message type flag found in message header: {0}")]
InvalidMsgTypeFlag(u8),
#[error("Type of message received is unexpected: {0}")]
UnexpectedMsgReceived(String),
#[error("Connection was lost when trying to receive a message")]
ConnectionLost(#[from] ConnectionError),
#[error("Error reading to end of stream")]
ReadToEndError(#[from] quinn::ReadToEndError),
#[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<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::ConnectionLost(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::NotEnoughBytes,
quinn::ReadExactError::ReadError(error) => error.into(),
}
}
}
#[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>);