Skip to main content

ntex_h2/
error.rs

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