monoio_http/h2/
error.rs

1use std::{error, fmt, io};
2
3use bytes::Bytes;
4
5pub use crate::h2::frame::Reason;
6use crate::h2::{
7    codec::{SendError, UserError},
8    frame::StreamId,
9    proto::{self, Initiator},
10};
11
12/// Represents HTTP/2 operation errors.
13///
14/// `Error` covers error cases raised by protocol errors caused by the
15/// peer, I/O (transport) errors, and errors caused by the user of the library.
16///
17/// If the error was caused by the remote peer, then it will contain a
18/// [`Reason`] which can be obtained with the [`reason`] function.
19///
20/// [`Reason`]: struct.Reason.html
21/// [`reason`]: #method.reason
22#[derive(Debug)]
23pub struct Error {
24    kind: Kind,
25}
26
27#[derive(Debug)]
28#[allow(dead_code)]
29enum Kind {
30    /// A RST_STREAM frame was received or sent.
31    Reset(StreamId, Reason, Initiator),
32
33    /// A GO_AWAY frame was received or sent.
34    GoAway(Bytes, Reason, Initiator),
35
36    /// The user created an error from a bare Reason.
37    Reason(Reason),
38
39    /// An error resulting from an invalid action taken by the user of this
40    /// library.
41    User(UserError),
42
43    /// An `io::Error` occurred while trying to read or write.
44    Io(io::Error),
45}
46
47// ===== impl Error =====
48
49impl Error {
50    /// If the error was caused by the remote peer, the error reason.
51    ///
52    /// This is either an error received by the peer or caused by an invalid
53    /// action taken by the peer (i.e. a protocol error).
54    pub fn reason(&self) -> Option<Reason> {
55        match self.kind {
56            Kind::Reset(_, reason, _) | Kind::GoAway(_, reason, _) | Kind::Reason(reason) => {
57                Some(reason)
58            }
59            _ => None,
60        }
61    }
62
63    /// Returns true if the error is an io::Error
64    pub fn is_io(&self) -> bool {
65        matches!(self.kind, Kind::Io(..))
66    }
67
68    /// Returns the error if the error is an io::Error
69    pub fn get_io(&self) -> Option<&io::Error> {
70        match self.kind {
71            Kind::Io(ref e) => Some(e),
72            _ => None,
73        }
74    }
75
76    /// Returns the error if the error is an io::Error
77    pub fn into_io(self) -> Option<io::Error> {
78        match self.kind {
79            Kind::Io(e) => Some(e),
80            _ => None,
81        }
82    }
83
84    pub(crate) fn from_io(err: io::Error) -> Self {
85        Error {
86            kind: Kind::Io(err),
87        }
88    }
89
90    /// Returns true if the error is from a `GOAWAY`.
91    pub fn is_go_away(&self) -> bool {
92        matches!(self.kind, Kind::GoAway(..))
93    }
94
95    /// Returns true if the error is from a `RST_STREAM`.
96    pub fn is_reset(&self) -> bool {
97        matches!(self.kind, Kind::Reset(..))
98    }
99
100    /// Returns true if the error was received in a frame from the remote.
101    ///
102    /// Such as from a received `RST_STREAM` or `GOAWAY` frame.
103    pub fn is_remote(&self) -> bool {
104        matches!(
105            self.kind,
106            Kind::GoAway(_, _, Initiator::Remote) | Kind::Reset(_, _, Initiator::Remote)
107        )
108    }
109}
110
111impl From<proto::Error> for Error {
112    fn from(src: proto::Error) -> Error {
113        use crate::h2::proto::Error::*;
114
115        Error {
116            kind: match src {
117                Reset(stream_id, reason, initiator) => Kind::Reset(stream_id, reason, initiator),
118                GoAway(debug_data, reason, initiator) => {
119                    Kind::GoAway(debug_data, reason, initiator)
120                }
121                Io(kind, inner) => {
122                    Kind::Io(inner.map_or_else(|| kind.into(), |inner| io::Error::new(kind, inner)))
123                }
124            },
125        }
126    }
127}
128
129impl From<Reason> for Error {
130    fn from(src: Reason) -> Error {
131        Error {
132            kind: Kind::Reason(src),
133        }
134    }
135}
136
137impl From<SendError> for Error {
138    fn from(src: SendError) -> Error {
139        match src {
140            SendError::User(e) => e.into(),
141            SendError::Connection(e) => e.into(),
142        }
143    }
144}
145
146impl From<UserError> for Error {
147    fn from(src: UserError) -> Error {
148        Error {
149            kind: Kind::User(src),
150        }
151    }
152}
153
154impl fmt::Display for Error {
155    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
156        let debug_data = match self.kind {
157            Kind::Reset(_, reason, Initiator::User) => {
158                return write!(fmt, "stream error sent by user: {}", reason)
159            }
160            Kind::Reset(_, reason, Initiator::Library) => {
161                return write!(fmt, "stream error detected: {}", reason)
162            }
163            Kind::Reset(_, reason, Initiator::Remote) => {
164                return write!(fmt, "stream error received: {}", reason)
165            }
166            Kind::GoAway(ref debug_data, reason, Initiator::User) => {
167                write!(fmt, "connection error sent by user: {}", reason)?;
168                debug_data
169            }
170            Kind::GoAway(ref debug_data, reason, Initiator::Library) => {
171                write!(fmt, "connection error detected: {}", reason)?;
172                debug_data
173            }
174            Kind::GoAway(ref debug_data, reason, Initiator::Remote) => {
175                write!(fmt, "connection error received: {}", reason)?;
176                debug_data
177            }
178            Kind::Reason(reason) => return write!(fmt, "protocol error: {}", reason),
179            Kind::User(ref e) => return write!(fmt, "user error: {}", e),
180            Kind::Io(ref e) => return e.fmt(fmt),
181        };
182
183        if !debug_data.is_empty() {
184            write!(fmt, " ({:?})", debug_data)?;
185        }
186
187        Ok(())
188    }
189}
190
191impl error::Error for Error {}
192
193#[cfg(test)]
194mod tests {
195    use super::Error;
196    use crate::h2::Reason;
197
198    #[test]
199    fn error_from_reason() {
200        let err = Error::from(Reason::HTTP_1_1_REQUIRED);
201        assert_eq!(err.reason(), Some(Reason::HTTP_1_1_REQUIRED));
202    }
203}