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 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 46 impl 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). reason(&self) -> Option<Reason>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 the true if the error is an io::Error is_io(&self) -> bool61 pub fn is_io(&self) -> bool { 62 match self.kind { 63 Kind::Io(_) => true, 64 _ => false, 65 } 66 } 67 68 /// Returns the error if the error is an io::Error get_io(&self) -> Option<&io::Error>69 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 /// Returns the error if the error is an io::Error into_io(self) -> Option<io::Error>77 pub fn into_io(self) -> Option<io::Error> { 78 match self.kind { 79 Kind::Io(e) => Some(e), 80 _ => None, 81 } 82 } 83 from_io(err: io::Error) -> Self84 pub(crate) fn from_io(err: io::Error) -> Self { 85 Error { 86 kind: Kind::Io(err), 87 } 88 } 89 } 90 91 impl From<proto::Error> for Error { from(src: proto::Error) -> Error92 fn from(src: proto::Error) -> Error { 93 use crate::proto::Error::*; 94 95 Error { 96 kind: match src { 97 Reset(stream_id, reason, initiator) => Kind::Reset(stream_id, reason, initiator), 98 GoAway(debug_data, reason, initiator) => { 99 Kind::GoAway(debug_data, reason, initiator) 100 } 101 Io(kind, inner) => { 102 Kind::Io(inner.map_or_else(|| kind.into(), |inner| io::Error::new(kind, inner))) 103 } 104 }, 105 } 106 } 107 } 108 109 impl From<Reason> for Error { from(src: Reason) -> Error110 fn from(src: Reason) -> Error { 111 Error { 112 kind: Kind::Reason(src), 113 } 114 } 115 } 116 117 impl From<SendError> for Error { from(src: SendError) -> Error118 fn from(src: SendError) -> Error { 119 match src { 120 SendError::User(e) => e.into(), 121 SendError::Connection(e) => e.into(), 122 } 123 } 124 } 125 126 impl From<UserError> for Error { from(src: UserError) -> Error127 fn from(src: UserError) -> Error { 128 Error { 129 kind: Kind::User(src), 130 } 131 } 132 } 133 134 impl fmt::Display for Error { fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result135 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 136 let debug_data = match self.kind { 137 Kind::Reset(_, reason, Initiator::User) => { 138 return write!(fmt, "stream error sent by user: {}", reason) 139 } 140 Kind::Reset(_, reason, Initiator::Library) => { 141 return write!(fmt, "stream error detected: {}", reason) 142 } 143 Kind::Reset(_, reason, Initiator::Remote) => { 144 return write!(fmt, "stream error received: {}", reason) 145 } 146 Kind::GoAway(ref debug_data, reason, Initiator::User) => { 147 write!(fmt, "connection error sent by user: {}", reason)?; 148 debug_data 149 } 150 Kind::GoAway(ref debug_data, reason, Initiator::Library) => { 151 write!(fmt, "connection error detected: {}", reason)?; 152 debug_data 153 } 154 Kind::GoAway(ref debug_data, reason, Initiator::Remote) => { 155 write!(fmt, "connection error received: {}", reason)?; 156 debug_data 157 } 158 Kind::Reason(reason) => return write!(fmt, "protocol error: {}", reason), 159 Kind::User(ref e) => return write!(fmt, "user error: {}", e), 160 Kind::Io(ref e) => return e.fmt(fmt), 161 }; 162 163 if !debug_data.is_empty() { 164 write!(fmt, " ({:?})", debug_data)?; 165 } 166 167 Ok(()) 168 } 169 } 170 171 impl error::Error for Error {} 172 173 #[cfg(test)] 174 mod tests { 175 use super::Error; 176 use crate::Reason; 177 178 #[test] error_from_reason()179 fn error_from_reason() { 180 let err = Error::from(Reason::HTTP_1_1_REQUIRED); 181 assert_eq!(err.reason(), Some(Reason::HTTP_1_1_REQUIRED)); 182 } 183 } 184