1 use crate::codec::{SendError, UserError};
2 use crate::frame::StreamId;
3 use crate::proto::{self, Initiator};
4 
5 use bytes::Bytes;
6 use std::{error, fmt, io};
7 
8 pub 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)]
21 pub struct Error {
22     kind: Kind,
23 }
24 
25 #[derive(Debug)]
26 enum 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 
47 impl 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).
reason(&self) -> Option<Reason>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
is_io(&self) -> bool62     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
get_io(&self) -> Option<&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
into_io(self) -> Option<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 
from_io(err: io::Error) -> Self82     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`.
is_go_away(&self) -> bool89     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`.
is_reset(&self) -> bool94     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.
is_remote(&self) -> bool101     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.
is_library(&self) -> bool111     pub fn is_library(&self) -> bool {
112         matches!(
113             self.kind,
114             Kind::GoAway(_, _, Initiator::Library) | Kind::Reset(_, _, Initiator::Library)
115         )
116     }
117 }
118 
119 impl From<proto::Error> for Error {
from(src: proto::Error) -> Error120     fn from(src: proto::Error) -> Error {
121         use crate::proto::Error::*;
122 
123         Error {
124             kind: match src {
125                 Reset(stream_id, reason, initiator) => Kind::Reset(stream_id, reason, initiator),
126                 GoAway(debug_data, reason, initiator) => {
127                     Kind::GoAway(debug_data, reason, initiator)
128                 }
129                 Io(kind, inner) => {
130                     Kind::Io(inner.map_or_else(|| kind.into(), |inner| io::Error::new(kind, inner)))
131                 }
132             },
133         }
134     }
135 }
136 
137 impl From<Reason> for Error {
from(src: Reason) -> Error138     fn from(src: Reason) -> Error {
139         Error {
140             kind: Kind::Reason(src),
141         }
142     }
143 }
144 
145 impl From<SendError> for Error {
from(src: SendError) -> Error146     fn from(src: SendError) -> Error {
147         match src {
148             SendError::User(e) => e.into(),
149             SendError::Connection(e) => e.into(),
150         }
151     }
152 }
153 
154 impl From<UserError> for Error {
from(src: UserError) -> Error155     fn from(src: UserError) -> Error {
156         Error {
157             kind: Kind::User(src),
158         }
159     }
160 }
161 
162 impl fmt::Display for Error {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result163     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 
199 impl error::Error for Error {}
200 
201 #[cfg(test)]
202 mod tests {
203     use super::Error;
204     use crate::Reason;
205 
206     #[test]
error_from_reason()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