use core::fmt;
use crate::settings::SettingError;
use crate::varint::VarInt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u64)]
pub enum ErrorCode {
NoError = 0x100,
GeneralProtocolError = 0x101,
InternalError = 0x102,
StreamCreationError = 0x103,
ClosedCriticalStream = 0x104,
FrameUnexpected = 0x105,
FrameError = 0x106,
ExcessiveLoad = 0x107,
IdError = 0x108,
SettingsError = 0x109,
MissingSettings = 0x10a,
RequestRejected = 0x10b,
RequestCancelled = 0x10c,
RequestIncomplete = 0x10d,
MessageError = 0x10e,
ConnectError = 0x10f,
VersionFallback = 0x110,
QpackDecompressionFailed = 0x200,
QpackEncoderStreamError = 0x201,
QpackDecoderStreamError = 0x202,
H3DatagramError = 0x33,
}
impl ErrorCode {
pub fn from_code(code: u64) -> Option<Self> {
match code {
0x100 => Some(Self::NoError),
0x101 => Some(Self::GeneralProtocolError),
0x102 => Some(Self::InternalError),
0x103 => Some(Self::StreamCreationError),
0x104 => Some(Self::ClosedCriticalStream),
0x105 => Some(Self::FrameUnexpected),
0x106 => Some(Self::FrameError),
0x107 => Some(Self::ExcessiveLoad),
0x108 => Some(Self::IdError),
0x109 => Some(Self::SettingsError),
0x10a => Some(Self::MissingSettings),
0x10b => Some(Self::RequestRejected),
0x10c => Some(Self::RequestCancelled),
0x10d => Some(Self::RequestIncomplete),
0x10e => Some(Self::MessageError),
0x10f => Some(Self::ConnectError),
0x110 => Some(Self::VersionFallback),
0x200 => Some(Self::QpackDecompressionFailed),
0x201 => Some(Self::QpackEncoderStreamError),
0x202 => Some(Self::QpackDecoderStreamError),
0x33 => Some(Self::H3DatagramError),
_ => None,
}
}
pub fn code(self) -> u64 {
self as u64
}
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoError => write!(f, "H3_NO_ERROR"),
Self::GeneralProtocolError => write!(f, "H3_GENERAL_PROTOCOL_ERROR"),
Self::InternalError => write!(f, "H3_INTERNAL_ERROR"),
Self::StreamCreationError => write!(f, "H3_STREAM_CREATION_ERROR"),
Self::ClosedCriticalStream => write!(f, "H3_CLOSED_CRITICAL_STREAM"),
Self::FrameUnexpected => write!(f, "H3_FRAME_UNEXPECTED"),
Self::FrameError => write!(f, "H3_FRAME_ERROR"),
Self::ExcessiveLoad => write!(f, "H3_EXCESSIVE_LOAD"),
Self::IdError => write!(f, "H3_ID_ERROR"),
Self::SettingsError => write!(f, "H3_SETTINGS_ERROR"),
Self::MissingSettings => write!(f, "H3_MISSING_SETTINGS"),
Self::RequestRejected => write!(f, "H3_REQUEST_REJECTED"),
Self::RequestCancelled => write!(f, "H3_REQUEST_CANCELLED"),
Self::RequestIncomplete => write!(f, "H3_REQUEST_INCOMPLETE"),
Self::MessageError => write!(f, "H3_MESSAGE_ERROR"),
Self::ConnectError => write!(f, "H3_CONNECT_ERROR"),
Self::VersionFallback => write!(f, "H3_VERSION_FALLBACK"),
Self::QpackDecompressionFailed => write!(f, "QPACK_DECOMPRESSION_FAILED"),
Self::QpackEncoderStreamError => write!(f, "QPACK_ENCODER_STREAM_ERROR"),
Self::QpackDecoderStreamError => write!(f, "QPACK_DECODER_STREAM_ERROR"),
Self::H3DatagramError => write!(f, "H3_DATAGRAM_ERROR"),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
ConnectionError(ErrorCode),
StreamError(ErrorCode),
BufferTooShort,
InvalidStreamId(u64),
StreamNotFound(u64),
StreamClosed(u64),
VarintDecode,
FrameDecode(FrameDecodeError),
Qpack(QpackError),
WtSessionDraining(u64),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ConnectionError(code) => write!(f, "connection error: {code}"),
Self::StreamError(code) => write!(f, "stream error: {code}"),
Self::BufferTooShort => write!(f, "buffer too short"),
Self::InvalidStreamId(id) => write!(f, "invalid stream id: {id}"),
Self::StreamNotFound(id) => write!(f, "stream not found: {id}"),
Self::StreamClosed(id) => write!(f, "stream closed: {id}"),
Self::VarintDecode => write!(f, "varint decode error"),
Self::FrameDecode(e) => write!(f, "frame decode error: {e}"),
Self::Qpack(e) => write!(f, "qpack error: {e}"),
Self::WtSessionDraining(id) => write!(f, "webtransport session draining: {id}"),
}
}
}
impl core::error::Error for Error {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::FrameDecode(e) => Some(e),
Self::Qpack(e) => Some(e),
_ => None,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameDecodeError {
BufferTooShort,
UnknownFrameType(u64),
InvalidLength,
InvalidSetting(SettingError),
Http2Frame(VarInt),
ServerPushNotSupported(VarInt),
}
impl fmt::Display for FrameDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BufferTooShort => write!(f, "buffer too short"),
Self::UnknownFrameType(t) => write!(f, "unknown frame type: {t:#x}"),
Self::InvalidLength => write!(f, "invalid frame length"),
Self::InvalidSetting(e) => write!(f, "invalid settings parameter: {e}"),
Self::Http2Frame(t) => write!(f, "http/2 frame not allowed: {:#x}", t.get()),
Self::ServerPushNotSupported(t) => {
write!(f, "server push not supported: {:#x}", t.get())
}
}
}
}
impl core::error::Error for FrameDecodeError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::InvalidSetting(e) => Some(e),
_ => None,
}
}
}
impl From<SettingError> for FrameDecodeError {
fn from(e: SettingError) -> Self {
Self::InvalidSetting(e)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QpackError {
BufferTooShort,
InvalidIndex(u64),
InvalidHuffman,
StringTooLong,
DecodeFailed,
DynamicTableDisabled,
CapacityExceeded,
}
impl fmt::Display for QpackError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BufferTooShort => write!(f, "buffer too short"),
Self::InvalidIndex(idx) => write!(f, "invalid index: {idx}"),
Self::InvalidHuffman => write!(f, "invalid huffman encoding"),
Self::StringTooLong => write!(f, "string too long"),
Self::DecodeFailed => write!(f, "decode failed"),
Self::DynamicTableDisabled => {
write!(f, "dynamic table disabled (max capacity is zero)")
}
Self::CapacityExceeded => write!(f, "capacity exceeds maximum table capacity"),
}
}
}
impl core::error::Error for QpackError {}
impl From<FrameDecodeError> for Error {
fn from(e: FrameDecodeError) -> Self {
Self::FrameDecode(e)
}
}
impl From<QpackError> for Error {
fn from(e: QpackError) -> Self {
Self::Qpack(e)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_code_from_code() {
assert_eq!(ErrorCode::from_code(0x100), Some(ErrorCode::NoError));
assert_eq!(
ErrorCode::from_code(0x101),
Some(ErrorCode::GeneralProtocolError)
);
assert_eq!(
ErrorCode::from_code(0x200),
Some(ErrorCode::QpackDecompressionFailed)
);
assert_eq!(
ErrorCode::from_code(0x10b),
Some(ErrorCode::RequestRejected)
);
assert_eq!(ErrorCode::from_code(0x999), None);
}
#[test]
fn test_error_code_display() {
assert_eq!(format!("{}", ErrorCode::NoError), "H3_NO_ERROR");
assert_eq!(format!("{}", ErrorCode::FrameError), "H3_FRAME_ERROR");
assert_eq!(
format!("{}", ErrorCode::RequestRejected),
"H3_REQUEST_REJECTED"
);
}
#[test]
fn test_error_display() {
let e = Error::ConnectionError(ErrorCode::FrameError);
assert_eq!(format!("{e}"), "connection error: H3_FRAME_ERROR");
let e = Error::BufferTooShort;
assert_eq!(format!("{e}"), "buffer too short");
}
}