ntex_h2/
error.rs

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    /// An invalid stream identifier was provided
19    #[error("An invalid stream identifier was provided: {0}")]
20    InvalidStreamId(&'static str),
21    #[error("Unexpected setting ack received")]
22    UnexpectedSettingsAck,
23    /// Missing pseudo header
24    #[error("Missing pseudo header {0:?}")]
25    MissingPseudo(&'static str),
26    /// Missing pseudo header
27    #[error("Unexpected pseudo header {0:?}")]
28    UnexpectedPseudo(&'static str),
29    /// Window update value is zero
30    #[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    /// Keep-alive timeout
39    #[error("Keep-alive timeout")]
40    KeepaliveTimeout,
41    /// Read timeout
42    #[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/// Operation errors
147#[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    /// Cannot process operation for idle stream
156    #[error("Cannot process operation for idle stream")]
157    Idle,
158
159    /// Cannot process operation for stream in payload state
160    #[error("Cannot process operation for stream in payload state")]
161    Payload,
162
163    /// Stream is closed
164    #[error("Stream is closed {0:?}")]
165    Closed(Option<Reason>),
166
167    /// Stream has been reset from the peer
168    #[error("Stream has been reset from the peer with {0}")]
169    RemoteReset(Reason),
170
171    /// Stream has been reset from local side
172    #[error("Stream has been reset from local side with {0}")]
173    LocalReset(Reason),
174
175    /// The stream ID space is overflowed
176    ///
177    /// A new connection is needed.
178    #[error("The stream ID space is overflowed")]
179    OverflowedStreamId,
180
181    /// Disconnecting
182    #[error("Connection is disconnecting")]
183    Disconnecting,
184
185    /// Disconnected
186    #[error("Connection is closed")]
187    Disconnected,
188}