use std::{error::Error as StdError, fmt::Display};
use snafu::Snafu;
use crate::varint::VarInt;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Code(VarInt);
impl From<VarInt> for Code {
fn from(value: VarInt) -> Self {
Self(value)
}
}
impl From<Code> for VarInt {
fn from(value: Code) -> Self {
value.0
}
}
impl Code {
pub const fn into_inner(self) -> VarInt {
self.0
}
}
macro_rules! codes {
(
$(
$(#[$meta:meta])*
pub const $name:ident = $value:expr;
)*
) => {
impl Code {
$(
$(#[$meta])*
pub const $name: Self = Self(VarInt::from_u32($value));
)*
}
impl Display for Code {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
$(
Self::$name => write!(f, "{} (0x{:x})", stringify!($name), $value),
)*
_ => write!(f, "Code 0x{:x}", self.0),
}
}
}
};
}
codes! {
pub const H3_NO_ERROR = 0x0100;
pub const H3_GENERAL_PROTOCOL_ERROR = 0x0101;
pub const H3_INTERNAL_ERROR = 0x0102;
pub const H3_STREAM_CREATION_ERROR = 0x0103;
pub const H3_CLOSED_CRITICAL_STREAM = 0x0104;
pub const H3_FRAME_UNEXPECTED = 0x0105;
pub const H3_FRAME_ERROR = 0x0106;
pub const H3_EXCESSIVE_LOAD = 0x0107;
pub const H3_ID_ERROR = 0x0108;
pub const H3_SETTINGS_ERROR = 0x0109;
pub const H3_MISSING_SETTINGS = 0x010a;
pub const H3_REQUEST_REJECTED = 0x010b;
pub const H3_REQUEST_CANCELLED = 0x010c;
pub const H3_REQUEST_INCOMPLETE = 0x010d;
pub const H3_MESSAGE_ERROR = 0x010e;
pub const H3_CONNECT_ERROR = 0x010f;
pub const H3_VERSION_FALLBACK = 0x0110;
pub const QPACK_DECOMPRESSION_FAILED = 0x200;
pub const QPACK_ENCODER_STREAM_ERROR = 0x201;
pub const QPACK_DECODER_STREAM_ERROR = 0x202;
}
impl Code {
pub const fn new(code: VarInt) -> Self {
Self(code)
}
pub const fn value(&self) -> VarInt {
self.0
}
}
#[non_exhaustive]
#[allow(clippy::enum_variant_names)]
#[derive(Debug, Snafu, Clone, Copy)]
pub enum H3StreamCreationError {
#[snafu(display("control stream already exists"))]
DuplicateControlStream,
#[snafu(display("qpack encoder stream already exists"))]
DuplicateQpackEncoderStream,
#[snafu(display("qpack decoder stream already exists"))]
DuplicateQpackDecoderStream,
}
impl H3Error for H3StreamCreationError {
fn code(&self) -> Code {
Code::H3_STREAM_CREATION_ERROR
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}
#[non_exhaustive]
#[derive(Debug, Snafu)]
pub enum H3CriticalStreamClosed {
#[snafu(display("qpack encoder stream closed unexpectedly"))]
QPackEncoder,
#[snafu(display("qpack decoder stream closed unexpectedly"))]
QPackDecoder,
#[snafu(display("control stream closed unexpectedly"))]
Control,
}
impl H3Error for H3CriticalStreamClosed {
fn code(&self) -> Code {
Code::H3_CLOSED_CRITICAL_STREAM
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}
#[non_exhaustive]
#[derive(Debug, Snafu, Clone, Copy)]
pub enum H3FrameUnexpected {
#[snafu(display("received subsequent SETTINGS frame"))]
DuplicateSettings,
#[snafu(display("unexpected frame type on request stream"))]
UnexpectedFrameType,
#[snafu(display("unexpected frame during trailer reading"))]
UnexpectedFrameDuringTrailer,
}
impl H3Error for H3FrameUnexpected {
fn code(&self) -> Code {
Code::H3_FRAME_UNEXPECTED
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorScope {
Stream,
Connection,
}
pub trait H3Error: StdError {
fn code(&self) -> Code;
fn scope(&self) -> ErrorScope;
}
#[derive(Debug, Snafu, Clone, Copy)]
#[snafu(display("no error"))]
pub struct H3NoError;
impl H3Error for H3NoError {
fn code(&self) -> Code {
Code::H3_NO_ERROR
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}
#[non_exhaustive]
#[derive(Debug, Snafu, Clone, Copy)]
pub enum H3MessageError {
#[snafu(display("missing header section in HTTP message"))]
MissingHeaderSection,
#[snafu(display("unexpected headers frame in message body"))]
UnexpectedHeadersInBody,
}
impl H3Error for H3MessageError {
fn code(&self) -> Code {
Code::H3_MESSAGE_ERROR
}
fn scope(&self) -> ErrorScope {
ErrorScope::Stream
}
}
#[derive(Debug, Snafu, Clone, Copy)]
#[snafu(display("no SETTINGS frame at beginning of control stream"))]
pub struct H3MissingSettings;
impl H3Error for H3MissingSettings {
fn code(&self) -> Code {
Code::H3_MISSING_SETTINGS
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}
#[non_exhaustive]
#[derive(Debug, Snafu, Clone)]
#[snafu(module)]
pub enum H3GeneralProtocolError {
#[snafu(display("trailing payload in GOAWAY frame"))]
TrailingPayload,
#[snafu(display("protocol decode error"))]
Decode { source: crate::codec::DecodeError },
}
impl H3Error for H3GeneralProtocolError {
fn code(&self) -> Code {
Code::H3_GENERAL_PROTOCOL_ERROR
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}
#[non_exhaustive]
#[derive(Debug, Snafu)]
pub enum H3InternalError {
#[snafu(display("QPACK encoder encode failure"))]
QPackEncoderEncode {
source: crate::codec::EncodeStreamError,
},
#[snafu(display("missing server name (SNI) on incoming connection"))]
MissingServerName,
}
impl H3Error for H3InternalError {
fn code(&self) -> Code {
Code::H3_INTERNAL_ERROR
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}
#[derive(Debug, Snafu, Clone)]
#[snafu(display("frame decode error"))]
pub struct H3FrameDecodeError {
pub source: crate::codec::DecodeError,
}
impl H3Error for H3FrameDecodeError {
fn code(&self) -> Code {
Code::H3_FRAME_ERROR
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}
#[derive(Debug, Snafu)]
#[snafu(module)]
pub enum QpackDecompressionFailed {
#[snafu(display("QPACK decompression decode error"))]
Decode { source: crate::codec::DecodeError },
}
impl H3Error for QpackDecompressionFailed {
fn code(&self) -> Code {
Code::QPACK_DECOMPRESSION_FAILED
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}
#[derive(Debug, Snafu, Clone)]
#[snafu(display("field section size {actual} exceeds limit {limit}"))]
pub struct H3ExcessiveFieldSectionSize {
pub actual: u64,
pub limit: u64,
}
impl H3Error for H3ExcessiveFieldSectionSize {
fn code(&self) -> Code {
Code::H3_EXCESSIVE_LOAD
}
fn scope(&self) -> ErrorScope {
ErrorScope::Stream
}
}
#[non_exhaustive]
#[derive(Debug, Snafu, Clone, Copy)]
pub enum H3IdError {
#[snafu(display("push ID exceeds limit"))]
PushIdExceedsLimit,
#[snafu(display("GOAWAY stream ID ordering violation"))]
GoawayStreamIdOrdering,
}
impl H3Error for H3IdError {
fn code(&self) -> Code {
Code::H3_ID_ERROR
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}