use crate::frame::Frame;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
#[allow(clippy::enum_variant_names)]
#[derive(Clone, Debug, Default, PartialEq, Eq, EnumIter)]
pub enum Error {
#[default]
NoError,
InternalError,
ConnectionRefused,
FlowControlError,
StreamLimitError,
StreamStateError,
FinalSizeError,
FrameEncodingError,
TransportParameterError,
ConnectionIdLimitError,
ProtocolViolation,
InvalidToken,
ApplicationError,
CryptoBufferExceeded,
KeyUpdateError,
AeadLimitReached,
NoViablePath,
CryptoError(u8),
MultipathProtocolViolation,
Done,
BufferTooShort,
UnknownVersion,
InvalidPacket,
InvalidState(String),
InvalidOperation(String),
InvalidConfig(String),
ExpiredToken,
CryptoFail,
TlsFail(String),
StreamStopped(u64),
StreamReset(u64),
IoError(String),
}
impl Error {
pub(crate) fn to_wire(&self) -> u64 {
match *self {
Error::NoError => 0x0,
Error::InternalError => 0x1,
Error::ConnectionRefused => 0x2,
Error::FlowControlError => 0x3,
Error::StreamLimitError => 0x4,
Error::StreamStateError => 0x5,
Error::FinalSizeError => 0x6,
Error::FrameEncodingError => 0x7,
Error::TransportParameterError => 0x8,
Error::ConnectionIdLimitError => 0x9,
Error::ProtocolViolation => 0x0a,
Error::InvalidToken => 0x0b,
Error::ApplicationError => 0x0c,
Error::CryptoBufferExceeded => 0x0d,
Error::KeyUpdateError => 0x0e,
Error::AeadLimitReached => 0x0f,
Error::NoViablePath => 0x10,
Error::CryptoError(v) => v as u64,
Error::MultipathProtocolViolation => 0x1001d76d3ded42f3,
_ => 0x0,
}
}
pub(crate) fn to_errno(&self) -> libc::ssize_t {
match self {
Error::NoError => 0,
Error::InternalError => -1,
Error::ConnectionRefused => -2,
Error::FlowControlError => -3,
Error::StreamLimitError => -4,
Error::StreamStateError => -5,
Error::FinalSizeError => -6,
Error::FrameEncodingError => -7,
Error::TransportParameterError => -8,
Error::ConnectionIdLimitError => -9,
Error::ProtocolViolation => -10,
Error::InvalidToken => -11,
Error::ApplicationError => -12,
Error::CryptoBufferExceeded => -13,
Error::KeyUpdateError => -14,
Error::AeadLimitReached => -15,
Error::NoViablePath => -16,
Error::CryptoError(_) => -17,
Error::MultipathProtocolViolation => -18,
Error::Done => -100,
Error::BufferTooShort => -101,
Error::UnknownVersion => -102,
Error::InvalidPacket => -103,
Error::InvalidState(_) => -104,
Error::InvalidOperation(_) => -105,
Error::InvalidConfig(_) => -106,
Error::ExpiredToken => -107,
Error::CryptoFail => -108,
Error::TlsFail(_) => -109,
Error::StreamStopped(_) => -110,
Error::StreamReset(_) => -111,
Error::IoError(_) => -112,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl std::convert::From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::IoError(format!("{}", err))
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct ConnectionError {
pub is_app: bool,
pub error_code: u64,
pub frame: Option<Frame>,
pub reason: Vec<u8>,
}
impl std::fmt::Debug for ConnectionError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "is_app={:?} ", self.is_app)?;
write!(f, "error_code={:?} ", self.error_code)?;
match std::str::from_utf8(&self.reason) {
Ok(v) => write!(f, "reason={:?}", v)?,
Err(_) => write!(f, "reason={:?}", self.reason)?,
};
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Error::IoError;
#[test]
fn error_to_wire() {
let mut found_internal_err = false;
for err in Error::iter() {
if err == Error::NoError {
assert!(err.to_wire() == 0);
continue;
}
if err == Error::Done {
found_internal_err = true;
}
if found_internal_err {
assert_eq!(err.to_wire(), 0);
continue;
}
if let Error::CryptoError(_) = err {
assert_eq!(err.to_wire(), 0);
} else {
assert!(err.to_wire() > 0);
}
}
}
#[test]
fn error_to_errno() {
for err in Error::iter() {
if err == Error::NoError {
assert_eq!(err.to_errno(), 0);
} else {
assert!(err.to_errno() < 0);
}
}
}
#[test]
fn io_error() {
use std::error::Error;
let e = std::io::Error::from(std::io::ErrorKind::UnexpectedEof);
let e = super::Error::from(e);
assert_eq!(format!("{}", e), "IoError(\"unexpected end of file\")");
assert!(e.source().is_none());
}
#[test]
fn connection_error() {
let e = ConnectionError {
is_app: false,
error_code: 0,
frame: None,
reason: vec![],
};
assert_eq!(format!("{:?}", e), "is_app=false error_code=0 reason=\"\"");
let e = ConnectionError {
is_app: true,
error_code: 1,
frame: None,
reason: vec![0x97, 0x61, 0x6C],
};
assert_eq!(
format!("{:?}", e),
"is_app=true error_code=1 reason=[151, 97, 108]"
);
}
}