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