Skip to main content

h2ts_client/
errors.rs

1//! HTTP/2 error codes (RFC 7540 §7) and the error type used across the crate —
2//! the Rust analogue of `errors.ts`.
3
4use core::fmt;
5
6/// An HTTP/2 error code (RFC 7540 §7). The discriminant is the wire value.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[repr(u32)]
9pub enum ErrorCode {
10    NoError = 0x0,
11    ProtocolError = 0x1,
12    InternalError = 0x2,
13    FlowControlError = 0x3,
14    SettingsTimeout = 0x4,
15    StreamClosed = 0x5,
16    FrameSizeError = 0x6,
17    RefusedStream = 0x7,
18    Cancel = 0x8,
19    CompressionError = 0x9,
20    ConnectError = 0xa,
21    EnhanceYourCalm = 0xb,
22    InadequateSecurity = 0xc,
23    Http11Required = 0xd,
24}
25
26impl ErrorCode {
27    /// The wire value (RFC 7540 §7).
28    pub fn value(self) -> u32 {
29        self as u32
30    }
31
32    /// The code for a wire value, or `None` if unknown.
33    pub fn from_value(v: u32) -> Option<Self> {
34        use ErrorCode::*;
35        Some(match v {
36            0x0 => NoError,
37            0x1 => ProtocolError,
38            0x2 => InternalError,
39            0x3 => FlowControlError,
40            0x4 => SettingsTimeout,
41            0x5 => StreamClosed,
42            0x6 => FrameSizeError,
43            0x7 => RefusedStream,
44            0x8 => Cancel,
45            0x9 => CompressionError,
46            0xa => ConnectError,
47            0xb => EnhanceYourCalm,
48            0xc => InadequateSecurity,
49            0xd => Http11Required,
50            _ => return None,
51        })
52    }
53
54    /// The RFC name, e.g. `"PROTOCOL_ERROR"`.
55    pub fn name(self) -> &'static str {
56        use ErrorCode::*;
57        match self {
58            NoError => "NO_ERROR",
59            ProtocolError => "PROTOCOL_ERROR",
60            InternalError => "INTERNAL_ERROR",
61            FlowControlError => "FLOW_CONTROL_ERROR",
62            SettingsTimeout => "SETTINGS_TIMEOUT",
63            StreamClosed => "STREAM_CLOSED",
64            FrameSizeError => "FRAME_SIZE_ERROR",
65            RefusedStream => "REFUSED_STREAM",
66            Cancel => "CANCEL",
67            CompressionError => "COMPRESSION_ERROR",
68            ConnectError => "CONNECT_ERROR",
69            EnhanceYourCalm => "ENHANCE_YOUR_CALM",
70            InadequateSecurity => "INADEQUATE_SECURITY",
71            Http11Required => "HTTP_1_1_REQUIRED",
72        }
73    }
74}
75
76/// An HTTP/2-level error. When `stream_id` is set the error is stream-scoped
77/// (RST_STREAM); otherwise it is a connection error (GOAWAY).
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct H2Error {
80    pub code: ErrorCode,
81    pub message: Option<String>,
82    pub stream_id: Option<u32>,
83}
84
85impl H2Error {
86    /// A connection-level error.
87    pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
88        Self {
89            code,
90            message: Some(message.into()),
91            stream_id: None,
92        }
93    }
94
95    /// An error carrying only a code (no message).
96    pub fn code(code: ErrorCode) -> Self {
97        Self {
98            code,
99            message: None,
100            stream_id: None,
101        }
102    }
103
104    /// A stream-scoped error.
105    pub fn stream(code: ErrorCode, message: impl Into<String>, stream_id: u32) -> Self {
106        Self {
107            code,
108            message: Some(message.into()),
109            stream_id: Some(stream_id),
110        }
111    }
112}
113
114impl fmt::Display for H2Error {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        match &self.message {
117            Some(m) => write!(f, "{}: {m}", self.code.name()),
118            None => f.write_str(self.code.name()),
119        }
120    }
121}
122
123impl std::error::Error for H2Error {}