1pub use crate::codec::EncoderError;
2
3use crate::frame::{self, GoAway, Reason, StreamId};
4use crate::stream::StreamRef;
5
6#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
7pub enum ConnectionError {
8 #[error("Go away: {0}")]
9 GoAway(Reason),
10 #[error("Unknown stream id in {0} frame")]
11 UnknownStream(&'static str),
12 #[error("Encoder error: {0}")]
13 Encoder(#[from] EncoderError),
14 #[error("Decoder error: {0}")]
15 Decoder(#[from] frame::FrameError),
16 #[error("{0:?} is closed, {1}")]
17 StreamClosed(StreamId, &'static str),
18 #[error("An invalid stream identifier was provided: {0}")]
20 InvalidStreamId(&'static str),
21 #[error("Unexpected setting ack received")]
22 UnexpectedSettingsAck,
23 #[error("Missing pseudo header {0:?}")]
25 MissingPseudo(&'static str),
26 #[error("Unexpected pseudo header {0:?}")]
28 UnexpectedPseudo(&'static str),
29 #[error("Window update value is zero")]
31 ZeroWindowUpdateValue,
32 #[error("Window value is overflowed")]
33 WindowValueOverflow,
34 #[error("Max concurrent streams count achieved")]
35 ConcurrencyOverflow,
36 #[error("Stream rapid reset count achieved")]
37 StreamResetsLimit,
38 #[error("Keep-alive timeout")]
40 KeepaliveTimeout,
41 #[error("Read timeout")]
43 ReadTimeout,
44}
45
46impl ConnectionError {
47 pub fn to_goaway(&self) -> GoAway {
48 match self {
49 ConnectionError::GoAway(reason) => GoAway::new(*reason),
50 ConnectionError::Encoder(_) => {
51 GoAway::new(Reason::PROTOCOL_ERROR).set_data("Error during frame encoding")
52 }
53 ConnectionError::Decoder(_) => {
54 GoAway::new(Reason::PROTOCOL_ERROR).set_data("Error during frame decoding")
55 }
56 ConnectionError::MissingPseudo(s) => {
57 GoAway::new(Reason::PROTOCOL_ERROR).set_data(format!("Missing pseudo header {s:?}"))
58 }
59 ConnectionError::UnexpectedPseudo(s) => GoAway::new(Reason::PROTOCOL_ERROR)
60 .set_data(format!("Unexpected pseudo header {s:?}")),
61 ConnectionError::UnknownStream(_) => {
62 GoAway::new(Reason::PROTOCOL_ERROR).set_data("Unknown stream")
63 }
64 ConnectionError::InvalidStreamId(_) => GoAway::new(Reason::PROTOCOL_ERROR)
65 .set_data("An invalid stream identifier was provided"),
66 ConnectionError::StreamClosed(s, _) => {
67 GoAway::new(Reason::STREAM_CLOSED).set_data(format!("{s:?} is closed"))
68 }
69 ConnectionError::UnexpectedSettingsAck => {
70 GoAway::new(Reason::PROTOCOL_ERROR).set_data("Received unexpected settings ack")
71 }
72 ConnectionError::ZeroWindowUpdateValue => GoAway::new(Reason::PROTOCOL_ERROR)
73 .set_data("Zero value for window update frame is not allowed"),
74 ConnectionError::WindowValueOverflow => GoAway::new(Reason::FLOW_CONTROL_ERROR)
75 .set_data("Updated value for window is overflowed"),
76 ConnectionError::ConcurrencyOverflow => GoAway::new(Reason::FLOW_CONTROL_ERROR)
77 .set_data("Max concurrent streams count achieved"),
78 ConnectionError::StreamResetsLimit => GoAway::new(Reason::FLOW_CONTROL_ERROR)
79 .set_data("Stream rapid reset count achieved"),
80 ConnectionError::KeepaliveTimeout => {
81 GoAway::new(Reason::NO_ERROR).set_data("Keep-alive timeout")
82 }
83 ConnectionError::ReadTimeout => {
84 GoAway::new(Reason::NO_ERROR).set_data("Frame read timeout")
85 }
86 }
87 }
88}
89
90#[derive(Debug, Clone, thiserror::Error)]
91#[error("Stream error: {kind:?}")]
92pub(crate) struct StreamErrorInner {
93 kind: StreamError,
94 stream: StreamRef,
95}
96
97impl StreamErrorInner {
98 pub(crate) fn new(stream: StreamRef, kind: StreamError) -> Self {
99 Self { kind, stream }
100 }
101
102 pub(crate) fn into_inner(self) -> (StreamRef, StreamError) {
103 (self.stream, self.kind)
104 }
105}
106
107#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
108pub enum StreamError {
109 #[error("Stream in idle state: {0}")]
110 Idle(&'static str),
111 #[error("Stream is closed")]
112 Closed,
113 #[error("Window value is overflowed")]
114 WindowOverflowed,
115 #[error("Zero value for window")]
116 WindowZeroUpdateValue,
117 #[error("Trailers headers without end of stream flags")]
118 TrailersWithoutEos,
119 #[error("Invalid content length")]
120 InvalidContentLength,
121 #[error("Payload length does not match content-length header")]
122 WrongPayloadLength,
123 #[error("Non-empty payload for HEAD response")]
124 NonEmptyPayload,
125 #[error("Stream has been reset with {0}")]
126 Reset(Reason),
127}
128
129impl StreamError {
130 #[inline]
131 pub(crate) fn reason(&self) -> Reason {
132 match self {
133 StreamError::Idle(_) => Reason::PROTOCOL_ERROR,
134 StreamError::Closed => Reason::STREAM_CLOSED,
135 StreamError::WindowOverflowed => Reason::FLOW_CONTROL_ERROR,
136 StreamError::WindowZeroUpdateValue => Reason::PROTOCOL_ERROR,
137 StreamError::TrailersWithoutEos => Reason::PROTOCOL_ERROR,
138 StreamError::InvalidContentLength => Reason::PROTOCOL_ERROR,
139 StreamError::WrongPayloadLength => Reason::PROTOCOL_ERROR,
140 StreamError::NonEmptyPayload => Reason::PROTOCOL_ERROR,
141 StreamError::Reset(r) => *r,
142 }
143 }
144}
145
146#[derive(Debug, Clone, thiserror::Error)]
148pub enum OperationError {
149 #[error("{0:?}")]
150 Stream(#[from] StreamError),
151
152 #[error("{0}")]
153 Connection(#[from] ConnectionError),
154
155 #[error("Cannot process operation for idle stream")]
157 Idle,
158
159 #[error("Cannot process operation for stream in payload state")]
161 Payload,
162
163 #[error("Stream is closed {0:?}")]
165 Closed(Option<Reason>),
166
167 #[error("Stream has been reset from the peer with {0}")]
169 RemoteReset(Reason),
170
171 #[error("Stream has been reset from local side with {0}")]
173 LocalReset(Reason),
174
175 #[error("The stream ID space is overflowed")]
179 OverflowedStreamId,
180
181 #[error("Connection is disconnecting")]
183 Disconnecting,
184
185 #[error("Connection is closed")]
187 Disconnected,
188}