use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum H2ErrorCode {
NoError = 0x0,
ProtocolError = 0x1,
InternalError = 0x2,
FlowControlError = 0x3,
SettingsTimeout = 0x4,
StreamClosed = 0x5,
FrameSizeError = 0x6,
RefusedStream = 0x7,
Cancel = 0x8,
CompressionError = 0x9,
ConnectError = 0xa,
EnhanceYourCalm = 0xb,
InadequateSecurity = 0xc,
Http1_1Required = 0xd,
}
impl H2ErrorCode {
pub(crate) fn reason(self) -> &'static str {
match self {
Self::NoError => "Graceful shutdown or no error to signal.",
Self::ProtocolError => "Peer violated protocol requirements.",
Self::InternalError => "An internal error in the HTTP stack.",
Self::FlowControlError => "Peer violated flow-control limits.",
Self::SettingsTimeout => "Settings frame was not acknowledged in a timely manner.",
Self::StreamClosed => "A frame was received on a closed stream.",
Self::FrameSizeError => "A frame of an incorrect size was received.",
Self::RefusedStream => "The stream was refused before any application processing.",
Self::Cancel => "The stream was cancelled.",
Self::CompressionError => "HPACK compression state could not be maintained.",
Self::ConnectError => {
"TCP connection for a CONNECT request was reset or abnormally closed."
}
Self::EnhanceYourCalm => "Peer is generating excessive load.",
Self::InadequateSecurity => "Negotiated TLS parameters are unacceptable.",
Self::Http1_1Required => "Request must be retried over HTTP/1.1.",
}
}
}
impl fmt::Display for H2ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str((*self).reason())
}
}
impl std::error::Error for H2ErrorCode {}
impl From<u32> for H2ErrorCode {
fn from(value: u32) -> Self {
match value {
0x1 => Self::ProtocolError,
0x2 => Self::InternalError,
0x3 => Self::FlowControlError,
0x4 => Self::SettingsTimeout,
0x5 => Self::StreamClosed,
0x6 => Self::FrameSizeError,
0x7 => Self::RefusedStream,
0x8 => Self::Cancel,
0x9 => Self::CompressionError,
0xa => Self::ConnectError,
0xb => Self::EnhanceYourCalm,
0xc => Self::InadequateSecurity,
0xd => Self::Http1_1Required,
_ => Self::NoError,
}
}
}
impl From<H2ErrorCode> for u32 {
fn from(code: H2ErrorCode) -> u32 {
code as u32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_codes_roundtrip() {
for code in [
H2ErrorCode::NoError,
H2ErrorCode::ProtocolError,
H2ErrorCode::InternalError,
H2ErrorCode::FlowControlError,
H2ErrorCode::SettingsTimeout,
H2ErrorCode::StreamClosed,
H2ErrorCode::FrameSizeError,
H2ErrorCode::RefusedStream,
H2ErrorCode::Cancel,
H2ErrorCode::CompressionError,
H2ErrorCode::ConnectError,
H2ErrorCode::EnhanceYourCalm,
H2ErrorCode::InadequateSecurity,
H2ErrorCode::Http1_1Required,
] {
let wire: u32 = code.into();
assert_eq!(
H2ErrorCode::from(wire),
code,
"roundtrip failed for {code:?}"
);
}
}
#[test]
fn unknown_codes_decode_as_no_error() {
assert_eq!(H2ErrorCode::from(0xdead_beef), H2ErrorCode::NoError);
assert_eq!(H2ErrorCode::from(0xe), H2ErrorCode::NoError);
assert_eq!(H2ErrorCode::from(u32::MAX), H2ErrorCode::NoError);
}
}