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