diff options
Diffstat (limited to 'third_party/rust/h2/src/error.rs')
-rw-r--r-- | third_party/rust/h2/src/error.rs | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/third_party/rust/h2/src/error.rs b/third_party/rust/h2/src/error.rs new file mode 100644 index 0000000000..eb2b2acbc4 --- /dev/null +++ b/third_party/rust/h2/src/error.rs @@ -0,0 +1,210 @@ +use crate::codec::{SendError, UserError}; +use crate::frame::StreamId; +use crate::proto::{self, Initiator}; + +use bytes::Bytes; +use std::{error, fmt, io}; + +pub use crate::frame::Reason; + +/// Represents HTTP/2 operation errors. +/// +/// `Error` covers error cases raised by protocol errors caused by the +/// peer, I/O (transport) errors, and errors caused by the user of the library. +/// +/// If the error was caused by the remote peer, then it will contain a +/// [`Reason`] which can be obtained with the [`reason`] function. +/// +/// [`Reason`]: struct.Reason.html +/// [`reason`]: #method.reason +#[derive(Debug)] +pub struct Error { + kind: Kind, +} + +#[derive(Debug)] +enum Kind { + /// A RST_STREAM frame was received or sent. + Reset(StreamId, Reason, Initiator), + + /// A GO_AWAY frame was received or sent. + GoAway(Bytes, Reason, Initiator), + + /// The user created an error from a bare Reason. + Reason(Reason), + + /// An error resulting from an invalid action taken by the user of this + /// library. + User(UserError), + + /// An `io::Error` occurred while trying to read or write. + Io(io::Error), +} + +// ===== impl Error ===== + +impl Error { + /// If the error was caused by the remote peer, the error reason. + /// + /// This is either an error received by the peer or caused by an invalid + /// action taken by the peer (i.e. a protocol error). + pub fn reason(&self) -> Option<Reason> { + match self.kind { + Kind::Reset(_, reason, _) | Kind::GoAway(_, reason, _) | Kind::Reason(reason) => { + Some(reason) + } + _ => None, + } + } + + /// Returns true if the error is an io::Error + pub fn is_io(&self) -> bool { + matches!(self.kind, Kind::Io(..)) + } + + /// Returns the error if the error is an io::Error + pub fn get_io(&self) -> Option<&io::Error> { + match self.kind { + Kind::Io(ref e) => Some(e), + _ => None, + } + } + + /// Returns the error if the error is an io::Error + pub fn into_io(self) -> Option<io::Error> { + match self.kind { + Kind::Io(e) => Some(e), + _ => None, + } + } + + pub(crate) fn from_io(err: io::Error) -> Self { + Error { + kind: Kind::Io(err), + } + } + + /// Returns true if the error is from a `GOAWAY`. + pub fn is_go_away(&self) -> bool { + matches!(self.kind, Kind::GoAway(..)) + } + + /// Returns true if the error is from a `RST_STREAM`. + pub fn is_reset(&self) -> bool { + matches!(self.kind, Kind::Reset(..)) + } + + /// Returns true if the error was received in a frame from the remote. + /// + /// Such as from a received `RST_STREAM` or `GOAWAY` frame. + pub fn is_remote(&self) -> bool { + matches!( + self.kind, + Kind::GoAway(_, _, Initiator::Remote) | Kind::Reset(_, _, Initiator::Remote) + ) + } + + /// Returns true if the error was created by `h2`. + /// + /// Such as noticing some protocol error and sending a GOAWAY or RST_STREAM. + pub fn is_library(&self) -> bool { + matches!( + self.kind, + Kind::GoAway(_, _, Initiator::Library) | Kind::Reset(_, _, Initiator::Library) + ) + } +} + +impl From<proto::Error> for Error { + fn from(src: proto::Error) -> Error { + use crate::proto::Error::*; + + Error { + kind: match src { + Reset(stream_id, reason, initiator) => Kind::Reset(stream_id, reason, initiator), + GoAway(debug_data, reason, initiator) => { + Kind::GoAway(debug_data, reason, initiator) + } + Io(kind, inner) => { + Kind::Io(inner.map_or_else(|| kind.into(), |inner| io::Error::new(kind, inner))) + } + }, + } + } +} + +impl From<Reason> for Error { + fn from(src: Reason) -> Error { + Error { + kind: Kind::Reason(src), + } + } +} + +impl From<SendError> for Error { + fn from(src: SendError) -> Error { + match src { + SendError::User(e) => e.into(), + SendError::Connection(e) => e.into(), + } + } +} + +impl From<UserError> for Error { + fn from(src: UserError) -> Error { + Error { + kind: Kind::User(src), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let debug_data = match self.kind { + Kind::Reset(_, reason, Initiator::User) => { + return write!(fmt, "stream error sent by user: {}", reason) + } + Kind::Reset(_, reason, Initiator::Library) => { + return write!(fmt, "stream error detected: {}", reason) + } + Kind::Reset(_, reason, Initiator::Remote) => { + return write!(fmt, "stream error received: {}", reason) + } + Kind::GoAway(ref debug_data, reason, Initiator::User) => { + write!(fmt, "connection error sent by user: {}", reason)?; + debug_data + } + Kind::GoAway(ref debug_data, reason, Initiator::Library) => { + write!(fmt, "connection error detected: {}", reason)?; + debug_data + } + Kind::GoAway(ref debug_data, reason, Initiator::Remote) => { + write!(fmt, "connection error received: {}", reason)?; + debug_data + } + Kind::Reason(reason) => return write!(fmt, "protocol error: {}", reason), + Kind::User(ref e) => return write!(fmt, "user error: {}", e), + Kind::Io(ref e) => return e.fmt(fmt), + }; + + if !debug_data.is_empty() { + write!(fmt, " ({:?})", debug_data)?; + } + + Ok(()) + } +} + +impl error::Error for Error {} + +#[cfg(test)] +mod tests { + use super::Error; + use crate::Reason; + + #[test] + fn error_from_reason() { + let err = Error::from(Reason::HTTP_1_1_REQUIRED); + assert_eq!(err.reason(), Some(Reason::HTTP_1_1_REQUIRED)); + } +} |