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())
 | |
|     }
 | |
| }
 |