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#[derive(Debug)]
23pub struct Error {
24 kind: Kind,
25}
26
27#[derive(Debug)]
28#[allow(dead_code)]
29enum Kind {
30 Reset(StreamId, Reason, Initiator),
32
33 GoAway(Bytes, Reason, Initiator),
35
36 Reason(Reason),
38
39 User(UserError),
42
43 Io(io::Error),
45}
46
47impl Error {
50 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 pub fn is_io(&self) -> bool {
65 matches!(self.kind, Kind::Io(..))
66 }
67
68 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 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 pub fn is_go_away(&self) -> bool {
92 matches!(self.kind, Kind::GoAway(..))
93 }
94
95 pub fn is_reset(&self) -> bool {
97 matches!(self.kind, Kind::Reset(..))
98 }
99
100 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}