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) => GoAway::new(Reason::PROTOCOL_ERROR)
57 .set_data(format!("Missing pseudo header {:?}", s)),
58 ConnectionError::UnexpectedPseudo(s) => GoAway::new(Reason::PROTOCOL_ERROR)
59 .set_data(format!("Unexpected pseudo header {:?}", s)),
60 ConnectionError::UnknownStream(_) => {
61 GoAway::new(Reason::PROTOCOL_ERROR).set_data("Unknown stream")
62 }
63 ConnectionError::InvalidStreamId(_) => GoAway::new(Reason::PROTOCOL_ERROR)
64 .set_data("An invalid stream identifier was provided"),
65 ConnectionError::StreamClosed(s, _) => {
66 GoAway::new(Reason::STREAM_CLOSED).set_data(format!("{:?} is closed", s))
67 }
68 ConnectionError::UnexpectedSettingsAck => {
69 GoAway::new(Reason::PROTOCOL_ERROR).set_data("Received unexpected settings ack")
70 }
71 ConnectionError::ZeroWindowUpdateValue => GoAway::new(Reason::PROTOCOL_ERROR)
72 .set_data("Zero value for window update frame is not allowed"),
73 ConnectionError::WindowValueOverflow => GoAway::new(Reason::FLOW_CONTROL_ERROR)
74 .set_data("Updated value for window is overflowed"),
75 ConnectionError::ConcurrencyOverflow => GoAway::new(Reason::FLOW_CONTROL_ERROR)
76 .set_data("Max concurrent streams count achieved"),
77 ConnectionError::StreamResetsLimit => GoAway::new(Reason::FLOW_CONTROL_ERROR)
78 .set_data("Stream rapid reset count achieved"),
79 ConnectionError::KeepaliveTimeout => {
80 GoAway::new(Reason::NO_ERROR).set_data("Keep-alive timeout")
81 }
82 ConnectionError::ReadTimeout => {
83 GoAway::new(Reason::NO_ERROR).set_data("Frame read timeout")
84 }
85 }
86 }
87}
88
89#[derive(Debug, Clone, thiserror::Error)]
90#[error("Stream error: {kind:?}")]
91pub(crate) struct StreamErrorInner {
92 kind: StreamError,
93 stream: StreamRef,
94}
95
96impl StreamErrorInner {
97 pub(crate) fn new(stream: StreamRef, kind: StreamError) -> Self {
98 Self { kind, stream }
99 }
100
101 pub(crate) fn into_inner(self) -> (StreamRef, StreamError) {
102 (self.stream, self.kind)
103 }
104}
105
106#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
107pub enum StreamError {
108 #[error("Stream in idle state: {0}")]
109 Idle(&'static str),
110 #[error("Stream is closed")]
111 Closed,
112 #[error("Window value is overflowed")]
113 WindowOverflowed,
114 #[error("Zero value for window")]
115 WindowZeroUpdateValue,
116 #[error("Trailers headers without end of stream flags")]
117 TrailersWithoutEos,
118 #[error("Invalid content length")]
119 InvalidContentLength,
120 #[error("Payload length does not match content-length header")]
121 WrongPayloadLength,
122 #[error("Non-empty payload for HEAD response")]
123 NonEmptyPayload,
124 #[error("Stream has been reset with {0}")]
125 Reset(Reason),
126}
127
128impl StreamError {
129 #[inline]
130 pub(crate) fn reason(&self) -> Reason {
131 match self {
132 StreamError::Idle(_) => Reason::PROTOCOL_ERROR,
133 StreamError::Closed => Reason::STREAM_CLOSED,
134 StreamError::WindowOverflowed => Reason::FLOW_CONTROL_ERROR,
135 StreamError::WindowZeroUpdateValue => Reason::PROTOCOL_ERROR,
136 StreamError::TrailersWithoutEos => Reason::PROTOCOL_ERROR,
137 StreamError::InvalidContentLength => Reason::PROTOCOL_ERROR,
138 StreamError::WrongPayloadLength => Reason::PROTOCOL_ERROR,
139 StreamError::NonEmptyPayload => Reason::PROTOCOL_ERROR,
140 StreamError::Reset(r) => *r,
141 }
142 }
143}
144
145#[derive(Debug, Clone, thiserror::Error)]
147pub enum OperationError {
148 #[error("{0:?}")]
149 Stream(#[from] StreamError),
150
151 #[error("{0}")]
152 Connection(#[from] ConnectionError),
153
154 #[error("Cannot process operation for idle stream")]
156 Idle,
157
158 #[error("Cannot process operation for stream in payload state")]
160 Payload,
161
162 #[error("Stream is closed {0:?}")]
164 Closed(Option<Reason>),
165
166 #[error("Stream has been reset from the peer with {0}")]
168 RemoteReset(Reason),
169
170 #[error("Stream has been reset from local side with {0}")]
172 LocalReset(Reason),
173
174 #[error("The stream ID space is overflowed")]
178 OverflowedStreamId,
179
180 #[error("Connection is disconnecting")]
182 Disconnecting,
183
184 #[error("Connection is closed")]
186 Disconnected,
187}