Refactor errors (#46)
This patch does a bunch of refactoring, mostly around error types, but it also
paves the way to allow `Codec` to be used standalone.
* `Codec` (and `FramedRead` / `FramedWrite`) is broken out into a codec module.
* An h2-codec crate is created that re-exports the frame and codec modules.
* New error types are introduced in the internals:
  * `RecvError` represents errors caused by trying to receive a frame.
  * `SendError` represents errors caused by trying to send a frame.
  * `UserError` is an enum of potential errors caused by invalid usage
    by the user of the lib.
  * `ProtoError` is either a `Reason` or an `io::Error`. However it doesn't
    specify connection or stream level.
  * `h2::Error` is an opaque error type and is the only error type exposed
    by the public API (used to be `ConnectionError`).
There are misc code changes to enable this as well. The biggest is a new "sink"
API for `Codec`. It provides buffer which queues up a frame followed by flush
which writes everything that is queued. This departs from the `Sink` trait in
order to provide more accurate error values. For example, buffer can never fail
(but it will panic if `poll_ready` is not called first).
			
			
This commit is contained in:
		| @@ -1,5 +1,4 @@ | ||||
| use error::Reason; | ||||
| use frame::{self, Head, Error, Kind, StreamId}; | ||||
| use frame::{self, Head, Error, Kind, StreamId, Reason}; | ||||
|  | ||||
| use bytes::{BufMut, BigEndian}; | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,8 @@ | ||||
| use super::{StreamId, StreamDependency}; | ||||
| use hpack; | ||||
| use frame::{self, Frame, Head, Kind, Error}; | ||||
| use HeaderMap; | ||||
|  | ||||
| use http::{uri, Method, StatusCode, Uri}; | ||||
| use http::{uri, Method, StatusCode, Uri, HeaderMap}; | ||||
| use http::header::{self, HeaderName, HeaderValue}; | ||||
|  | ||||
| use bytes::{BytesMut, Bytes}; | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| use hpack; | ||||
| use error::{ConnectionError, Reason}; | ||||
|  | ||||
| use bytes::Bytes; | ||||
|  | ||||
| @@ -33,6 +32,7 @@ mod head; | ||||
| mod headers; | ||||
| mod ping; | ||||
| mod priority; | ||||
| mod reason; | ||||
| mod reset; | ||||
| mod settings; | ||||
| mod stream_id; | ||||
| @@ -45,6 +45,7 @@ pub use self::head::{Head, Kind}; | ||||
| pub use self::headers::{Headers, PushPromise, Continuation, Pseudo}; | ||||
| pub use self::ping::Ping; | ||||
| pub use self::priority::{Priority, StreamDependency}; | ||||
| pub use self::reason::Reason; | ||||
| pub use self::reset::Reset; | ||||
| pub use self::settings::Settings; | ||||
| pub use self::stream_id::StreamId; | ||||
| @@ -113,15 +114,6 @@ impl<T> fmt::Debug for Frame<T> { | ||||
| /// Errors that can occur during parsing an HTTP/2 frame. | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| pub enum Error { | ||||
|     /// A full frame header was not passed. | ||||
|     Short, | ||||
|  | ||||
|     /// An unsupported value was set for the flag value. | ||||
|     BadFlag, | ||||
|  | ||||
|     /// An unsupported value was set for the frame kind. | ||||
|     BadKind, | ||||
|  | ||||
|     /// A length value other than 8 was set on a PING message. | ||||
|     BadFrameSize, | ||||
|  | ||||
| @@ -129,19 +121,6 @@ pub enum Error { | ||||
|     /// length of the payload. | ||||
|     TooMuchPadding, | ||||
|  | ||||
|     /// The payload length specified by the frame header was shorter than | ||||
|     /// necessary for the parser settings specified and the frame type. | ||||
|     /// | ||||
|     /// This happens if, for instance, the priority flag is set and the | ||||
|     /// header length is shorter than a stream dependency. | ||||
|     /// | ||||
|     /// `PayloadLengthTooShort` should be treated as a protocol error. | ||||
|     PayloadLengthTooShort, | ||||
|  | ||||
|     /// The payload length specified by the frame header of a settings frame | ||||
|     /// was not a round multiple of the size of a single setting. | ||||
|     PartialSettingLength, | ||||
|  | ||||
|     /// An invalid setting value was provided | ||||
|     InvalidSettingValue, | ||||
|  | ||||
| @@ -173,14 +152,3 @@ pub enum Error { | ||||
|     /// Failed to perform HPACK decoding | ||||
|     Hpack(hpack::DecoderError), | ||||
| } | ||||
|  | ||||
| // ===== impl Error ===== | ||||
|  | ||||
| impl From<Error> for ConnectionError { | ||||
|     fn from(src: Error) -> ConnectionError { | ||||
|         match src { | ||||
|             // TODO: implement | ||||
|             _ => ConnectionError::Proto(Reason::ProtocolError), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,40 +0,0 @@ | ||||
| use ConnectionError; | ||||
| use super::Frame; | ||||
| use futures::*; | ||||
| use bytes::BytesMut; | ||||
| use std::io; | ||||
|  | ||||
| pub struct Reader<T> { | ||||
|     inner: T, | ||||
| } | ||||
|  | ||||
| impl<T> Stream for Reader<T> | ||||
|     where T: Stream<Item = BytesMut, Error = io::Error>, | ||||
| { | ||||
|     type Item = Frame; | ||||
|     type Error = ConnectionError; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Option<Frame>, ConnectionError> { | ||||
|         match try_ready!(self.inner.poll()) { | ||||
|             Some(bytes) => { | ||||
|                 Frame::load(bytes.freeze()) | ||||
|                     .map(|frame| Async::Ready(Some(frame))) | ||||
|                     .map_err(ConnectionError::from) | ||||
|             } | ||||
|             None => Ok(Async::Ready(None)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Sink> Sink for Reader<T> { | ||||
|     type SinkItem = T::SinkItem; | ||||
|     type SinkError = T::SinkError; | ||||
|  | ||||
|     fn start_send(&mut self, item: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> { | ||||
|         self.inner.start_send(item) | ||||
|     } | ||||
|  | ||||
|     fn poll_complete(&mut self) -> Poll<(), T::SinkError> { | ||||
|         self.inner.poll_complete() | ||||
|     } | ||||
| } | ||||
							
								
								
									
										101
									
								
								src/frame/reason.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/frame/reason.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| use std::fmt; | ||||
|  | ||||
| #[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 | ||||
| } | ||||
|  | ||||
| // ===== impl Reason ===== | ||||
|  | ||||
| impl Reason { | ||||
|     pub fn description(&self) -> &str { | ||||
|         use self::Reason::*; | ||||
|  | ||||
|         match *self { | ||||
|             NoError => "not a result of an error", | ||||
|             ProtocolError => "unspecific protocol error detected", | ||||
|             InternalError => "unexpected internal error encountered", | ||||
|             FlowControlError => "flow-control protocol violated", | ||||
|             SettingsTimeout => "settings ACK not received in timely manner", | ||||
|             StreamClosed => "received frame when stream half-closed", | ||||
|             FrameSizeError => "frame sent with invalid size", | ||||
|             RefusedStream => "refused stream before processing any application logic", | ||||
|             Cancel => "stream no longer needed", | ||||
|             CompressionError => "unable to maintain the header compression context", | ||||
|             ConnectError => "connection established in response to a CONNECT request was reset or abnormally closed", | ||||
|             EnhanceYourCalm => "detected excessive load generating behavior", | ||||
|             InadequateSecurity => "security properties do not meet minimum requirements", | ||||
|             Http11Required => "endpoint requires HTTP/1.1", | ||||
|             Other(_) => "other reason (ain't no tellin')", | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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()) | ||||
|     } | ||||
| } | ||||
| @@ -1,5 +1,4 @@ | ||||
| use error::Reason; | ||||
| use frame::{self, Head, Error, Kind, StreamId}; | ||||
| use frame::{self, Head, Error, Kind, StreamId, Reason}; | ||||
|  | ||||
| use bytes::{BufMut, BigEndian}; | ||||
|  | ||||
|   | ||||
| @@ -1,33 +0,0 @@ | ||||
| use frame::{Frame, Head, Error}; | ||||
| use bytes::{Bytes, BytesMut, BufMut}; | ||||
|  | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| pub struct Unknown { | ||||
|     head: Head, | ||||
|     payload: Bytes, | ||||
| } | ||||
|  | ||||
| impl Unknown { | ||||
|     pub fn new(head: Head, payload: Bytes) -> Unknown { | ||||
|         Unknown { | ||||
|             head: head, | ||||
|             payload: payload, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn encode_len(&self) -> usize { | ||||
|         self.head.encode_len() + self.payload.len() | ||||
|     } | ||||
|  | ||||
|     pub fn encode(&self, dst: &mut BytesMut) -> Result<(), Error> { | ||||
|         try!(self.head.encode(self.payload.len(), dst)); | ||||
|         dst.put(&self.payload); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Unknown> for Frame { | ||||
|     fn from(src: Unknown) -> Frame { | ||||
|         Frame::Unknown(src) | ||||
|     } | ||||
| } | ||||
| @@ -1 +0,0 @@ | ||||
| pub struct Writer; | ||||
		Reference in New Issue
	
	Block a user