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