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