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:
Carl Lerche
2017-09-02 11:12:50 -07:00
committed by GitHub
parent 6fd9674759
commit c122e97127
37 changed files with 1043 additions and 1027 deletions

View File

@@ -1,109 +1,35 @@
mod codec;
mod connection;
mod framed_read;
mod framed_write;
mod error;
mod peer;
mod ping_pong;
mod settings;
mod streams;
pub(crate) use self::connection::Connection;
pub(crate) use self::error::Error;
pub(crate) use self::peer::Peer;
pub(crate) use self::streams::{Streams, StreamRef};
use self::codec::Codec;
use self::framed_read::FramedRead;
use self::framed_write::FramedWrite;
use codec::Codec;
use self::ping_pong::PingPong;
use self::settings::Settings;
use self::streams::Prioritized;
use ConnectionError;
use error::Reason;
use frame::{self, Frame, StreamId};
use frame::{self, Frame};
use futures::{self, task, Poll, Async, AsyncSink};
use futures::{task, Poll, Async};
use futures::task::Task;
use bytes::{Buf, IntoBuf};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::length_delimited;
use std::{fmt, io};
use bytes::Buf;
/// Either a Client or a Server
pub trait Peer {
/// Message type sent into the transport
type Send;
/// Message type polled from the transport
type Poll: fmt::Debug;
fn is_server() -> bool;
fn convert_send_message(
id: StreamId,
headers: Self::Send,
end_of_stream: bool) -> frame::Headers;
fn convert_poll_message(headers: frame::Headers) -> Result<Self::Poll, ProtoError>;
}
use tokio_io::AsyncWrite;
pub type PingPayload = [u8; 8];
pub type WindowSize = u32;
/// Errors that are received
#[derive(Debug)]
pub enum ProtoError {
Connection(Reason),
Stream {
id: StreamId,
reason: Reason,
},
Io(io::Error),
}
// Constants
// TODO: Move these into `frame`
pub const DEFAULT_INITIAL_WINDOW_SIZE: WindowSize = 65_535;
pub const MAX_WINDOW_SIZE: WindowSize = (1 << 31) - 1;
/// Create a transport prepared to handle the server handshake.
///
/// When the server is performing the handshake, it is able to only send
/// `Settings` frames and is expected to receive the client preface as a byte
/// stream. To represent this, `Settings<FramedWrite<T>>` is returned.
pub(crate) fn framed_write<T, B>(io: T) -> FramedWrite<T, B>
where T: AsyncRead + AsyncWrite,
B: Buf,
{
FramedWrite::new(io)
}
/// Create a full H2 transport from the server handshaker
pub(crate) fn from_framed_write<T, P, B>(framed_write: FramedWrite<T, Prioritized<B::Buf>>)
-> Connection<T, P, B>
where T: AsyncRead + AsyncWrite,
P: Peer,
B: IntoBuf,
{
// Delimit the frames.
let framed = length_delimited::Builder::new()
.big_endian()
.length_field_length(3)
.length_adjustment(9)
.num_skip(0) // Don't skip the header
// TODO: make this configurable and allow it to be changed during
// runtime.
.max_frame_length(frame::DEFAULT_MAX_FRAME_SIZE as usize)
.new_read(framed_write);
let codec = Codec::from_framed(FramedRead::new(framed));
Connection::new(codec)
}
// ===== impl ProtoError =====
impl From<io::Error> for ProtoError {
fn from(src: io::Error) -> Self {
ProtoError::Io(src)
}
}