trillium_http/h2/
error.rs1use std::fmt;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum H2ErrorCode {
11 NoError = 0x0,
13
14 ProtocolError = 0x1,
16
17 InternalError = 0x2,
19
20 FlowControlError = 0x3,
22
23 SettingsTimeout = 0x4,
25
26 StreamClosed = 0x5,
28
29 FrameSizeError = 0x6,
31
32 RefusedStream = 0x7,
34
35 Cancel = 0x8,
37
38 CompressionError = 0x9,
40
41 ConnectError = 0xa,
43
44 EnhanceYourCalm = 0xb,
46
47 InadequateSecurity = 0xc,
49
50 Http1_1Required = 0xd,
52}
53
54impl H2ErrorCode {
55 pub(crate) fn reason(self) -> &'static str {
57 match self {
58 Self::NoError => "Graceful shutdown or no error to signal.",
59 Self::ProtocolError => "Peer violated protocol requirements.",
60 Self::InternalError => "An internal error in the HTTP stack.",
61 Self::FlowControlError => "Peer violated flow-control limits.",
62 Self::SettingsTimeout => "Settings frame was not acknowledged in a timely manner.",
63 Self::StreamClosed => "A frame was received on a closed stream.",
64 Self::FrameSizeError => "A frame of an incorrect size was received.",
65 Self::RefusedStream => "The stream was refused before any application processing.",
66 Self::Cancel => "The stream was cancelled.",
67 Self::CompressionError => "HPACK compression state could not be maintained.",
68 Self::ConnectError => {
69 "TCP connection for a CONNECT request was reset or abnormally closed."
70 }
71 Self::EnhanceYourCalm => "Peer is generating excessive load.",
72 Self::InadequateSecurity => "Negotiated TLS parameters are unacceptable.",
73 Self::Http1_1Required => "Request must be retried over HTTP/1.1.",
74 }
75 }
76}
77
78impl fmt::Display for H2ErrorCode {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 f.write_str((*self).reason())
81 }
82}
83
84impl std::error::Error for H2ErrorCode {}
85
86impl From<u32> for H2ErrorCode {
87 fn from(value: u32) -> Self {
89 match value {
90 0x1 => Self::ProtocolError,
91 0x2 => Self::InternalError,
92 0x3 => Self::FlowControlError,
93 0x4 => Self::SettingsTimeout,
94 0x5 => Self::StreamClosed,
95 0x6 => Self::FrameSizeError,
96 0x7 => Self::RefusedStream,
97 0x8 => Self::Cancel,
98 0x9 => Self::CompressionError,
99 0xa => Self::ConnectError,
100 0xb => Self::EnhanceYourCalm,
101 0xc => Self::InadequateSecurity,
102 0xd => Self::Http1_1Required,
103 _ => Self::NoError,
104 }
105 }
106}
107
108impl From<H2ErrorCode> for u32 {
109 fn from(code: H2ErrorCode) -> u32 {
110 code as u32
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn known_codes_roundtrip() {
120 for code in [
121 H2ErrorCode::NoError,
122 H2ErrorCode::ProtocolError,
123 H2ErrorCode::InternalError,
124 H2ErrorCode::FlowControlError,
125 H2ErrorCode::SettingsTimeout,
126 H2ErrorCode::StreamClosed,
127 H2ErrorCode::FrameSizeError,
128 H2ErrorCode::RefusedStream,
129 H2ErrorCode::Cancel,
130 H2ErrorCode::CompressionError,
131 H2ErrorCode::ConnectError,
132 H2ErrorCode::EnhanceYourCalm,
133 H2ErrorCode::InadequateSecurity,
134 H2ErrorCode::Http1_1Required,
135 ] {
136 let wire: u32 = code.into();
137 assert_eq!(
138 H2ErrorCode::from(wire),
139 code,
140 "roundtrip failed for {code:?}"
141 );
142 }
143 }
144
145 #[test]
146 fn unknown_codes_decode_as_no_error() {
147 assert_eq!(H2ErrorCode::from(0xdead_beef), H2ErrorCode::NoError);
148 assert_eq!(H2ErrorCode::from(0xe), H2ErrorCode::NoError);
149 assert_eq!(H2ErrorCode::from(u32::MAX), H2ErrorCode::NoError);
150 }
151}