use std::{error, fmt, io}; /// The error type for HTTP/2 operations /// /// XXX does this sufficiently destinguish stream-level errors from connection-level errors? #[derive(Debug)] pub enum ConnectionError { /// An error caused by an action taken by the remote peer. /// /// This is either an error received by the peer or caused by an invalid /// action taken by the peer (i.e. a protocol error). Proto(Reason), /// An `io::Error` occurred while trying to read or write. Io(io::Error), /// An error resulting from an invalid action taken by the user of this /// library. User(User), // TODO: reserve additional variants } #[derive(Debug)] pub struct StreamError(Reason); impl StreamError { pub fn new(r: Reason) -> StreamError { StreamError(r) } pub fn reason(&self) -> Reason { self.0 } } impl From for StreamError { fn from(r: Reason) -> Self { StreamError(r) } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Reason { NoError, ProtocolError, InternalError, FlowControlError, SettingsTimeout, StreamClosed, FrameSizeError, RefusedStream, Cancel, CompressionError, ConnectError, EnhanceYourCalm, InadequateSecurity, Http11Required, Other(u32), // TODO: reserve additional variants } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum User { /// The specified stream ID is invalid. /// /// For example, using a stream ID reserved for a push promise from the /// client or using a non-zero stream ID for settings. InvalidStreamId, /// The stream ID is no longer accepting frames. InactiveStreamId, /// The stream is not currently expecting a frame of this type. UnexpectedFrameType, /// The connection or stream does not have a sufficient flow control window to /// transmit a Data frame to the remote. FlowControlViolation, /// The connection state is corrupt and the connection should be dropped. Corrupt, /// The stream state has been reset. StreamReset(Reason), /// The application attempted to initiate too many streams to remote. Rejected, // TODO: reserve additional variants } macro_rules! reason_desc { ($reason:expr) => (reason_desc!($reason, "")); ($reason:expr, $prefix:expr) => ({ use self::Reason::*; match $reason { NoError => concat!($prefix, "not a result of an error"), ProtocolError => concat!($prefix, "unspecific protocol error detected"), InternalError => concat!($prefix, "unexpected internal error encountered"), FlowControlError => concat!($prefix, "flow-control protocol violated"), SettingsTimeout => concat!($prefix, "settings ACK not received in timely manner"), StreamClosed => concat!($prefix, "received frame when stream half-closed"), FrameSizeError => concat!($prefix, "frame sent with invalid size"), RefusedStream => concat!($prefix, "refused stream before processing any application logic"), Cancel => concat!($prefix, "stream no longer needed"), CompressionError => concat!($prefix, "unable to maintain the header compression context"), ConnectError => concat!($prefix, "connection established in response to a CONNECT request was reset or abnormally closed"), EnhanceYourCalm => concat!($prefix, "detected excessive load generating behavior"), InadequateSecurity => concat!($prefix, "security properties do not meet minimum requirements"), Http11Required => concat!($prefix, "endpoint requires HTTP/1.1"), Other(_) => concat!($prefix, "other reason (ain't no tellin')"), } }); } macro_rules! user_desc { ($reason:expr) => (user_desc!($reason, "")); ($reason:expr, $prefix:expr) => ({ use self::User::*; match $reason { InvalidStreamId => concat!($prefix, "invalid stream ID"), InactiveStreamId => concat!($prefix, "inactive stream ID"), UnexpectedFrameType => concat!($prefix, "unexpected frame type"), FlowControlViolation => concat!($prefix, "flow control violation"), StreamReset(_) => concat!($prefix, "frame sent on reset stream"), Corrupt => concat!($prefix, "connection state corrupt"), Rejected => concat!($prefix, "stream would exceed remote max concurrency"), } }); } // ===== impl ConnectionError ===== impl From for ConnectionError { fn from(src: io::Error) -> ConnectionError { ConnectionError::Io(src) } } impl From for ConnectionError { fn from(src: Reason) -> ConnectionError { ConnectionError::Proto(src) } } impl From for ConnectionError { fn from(src: User) -> ConnectionError { ConnectionError::User(src) } } impl From for io::Error { fn from(src: ConnectionError) -> io::Error { io::Error::new(io::ErrorKind::Other, src) } } impl fmt::Display for ConnectionError { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { use self::ConnectionError::*; match *self { Proto(reason) => write!(fmt, "protocol error: {}", reason), Io(ref e) => fmt::Display::fmt(e, fmt), User(e) => write!(fmt, "user error: {}", e), } } } impl error::Error for ConnectionError { fn description(&self) -> &str { use self::ConnectionError::*; match *self { Io(ref e) => error::Error::description(e), Proto(reason) => reason_desc!(reason, "protocol error: "), User(user) => user_desc!(user, "user error: "), } } } // ===== impl Reason ===== impl Reason { pub fn description(&self) -> &str { reason_desc!(*self) } } impl From for Reason { fn from(src: u32) -> Reason { use self::Reason::*; match src { 0x0 => NoError, 0x1 => ProtocolError, 0x2 => InternalError, 0x3 => FlowControlError, 0x4 => SettingsTimeout, 0x5 => StreamClosed, 0x6 => FrameSizeError, 0x7 => RefusedStream, 0x8 => Cancel, 0x9 => CompressionError, 0xa => ConnectError, 0xb => EnhanceYourCalm, 0xc => InadequateSecurity, 0xd => Http11Required, _ => Other(src), } } } impl From for u32 { fn from(src: Reason) -> u32 { use self::Reason::*; match src { NoError => 0x0, ProtocolError => 0x1, InternalError => 0x2, FlowControlError => 0x3, SettingsTimeout => 0x4, StreamClosed => 0x5, FrameSizeError => 0x6, RefusedStream => 0x7, Cancel => 0x8, CompressionError => 0x9, ConnectError => 0xa, EnhanceYourCalm => 0xb, InadequateSecurity => 0xc, Http11Required => 0xd, Other(v) => v, } } } impl fmt::Display for Reason { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.description()) } } // ===== impl User ===== impl User { pub fn description(&self) -> &str { user_desc!(*self) } } impl fmt::Display for User { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.description()) } }