260 lines
7.4 KiB
Rust
260 lines
7.4 KiB
Rust
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<Reason> 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<io::Error> for ConnectionError {
|
|
fn from(src: io::Error) -> ConnectionError {
|
|
ConnectionError::Io(src)
|
|
}
|
|
}
|
|
|
|
impl From<Reason> for ConnectionError {
|
|
fn from(src: Reason) -> ConnectionError {
|
|
ConnectionError::Proto(src)
|
|
}
|
|
}
|
|
|
|
impl From<User> for ConnectionError {
|
|
fn from(src: User) -> ConnectionError {
|
|
ConnectionError::User(src)
|
|
}
|
|
}
|
|
|
|
impl From<ConnectionError> 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<u32> 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<Reason> 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())
|
|
}
|
|
}
|