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,77 +0,0 @@
use super::*;
use futures::*;
#[derive(Debug)]
pub struct Codec<T, B> {
inner: FramedRead<FramedWrite<T, B>>,
}
impl<T, B> Codec<T, B> {
pub fn apply_remote_settings(&mut self, frame: &frame::Settings) {
self.framed_read().apply_remote_settings(frame);
self.framed_write().apply_remote_settings(frame);
}
/// Takes the data payload value that was fully written to the socket
pub(crate) fn take_last_data_frame(&mut self) -> Option<frame::Data<B>> {
self.framed_write().take_last_data_frame()
}
pub fn max_send_frame_size(&self) -> usize {
self.inner.get_ref().max_frame_size()
}
fn framed_read(&mut self) -> &mut FramedRead<FramedWrite<T, B>> {
&mut self.inner
}
fn framed_write(&mut self) -> &mut FramedWrite<T, B> {
self.inner.get_mut()
}
}
impl<T, B> Codec<T, B>
where T: AsyncRead + AsyncWrite,
B: Buf,
{
pub fn from_framed(inner: FramedRead<FramedWrite<T, B>>) -> Self {
Codec { inner }
}
}
impl<T, B> Codec<T, B>
where T: AsyncWrite,
B: Buf,
{
pub fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
self.inner.poll_ready()
}
}
impl<T, B> futures::Stream for Codec<T, B>
where T: AsyncRead,
{
type Item = Frame;
type Error = ProtoError;
fn poll(&mut self) -> Poll<Option<Frame>, Self::Error> {
self.inner.poll()
}
}
impl<T, B> Sink for Codec<T, B>
where T: AsyncWrite,
B: Buf,
{
type SinkItem = Frame<B>;
type SinkError = ConnectionError;
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
self.inner.start_send(item)
}
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
self.inner.poll_complete()
}
}

View File

@@ -1,9 +1,11 @@
use {client, frame, server, ConnectionError};
use {client, frame, server, proto};
use frame::Reason;
use codec::{SendError, RecvError};
use proto::*;
use http::Request;
use futures::{Sink, Stream};
use futures::{Stream};
use bytes::{Bytes, IntoBuf};
use tokio_io::{AsyncRead, AsyncWrite};
@@ -73,7 +75,10 @@ impl<T, P, B> Connection<T, P, B>
}
/// Returns `Ready` when the connection is ready to receive a frame.
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
///
/// Returns `RecvError` as this may raise errors that are caused by delayed
/// processing of received frames.
fn poll_ready(&mut self) -> Poll<(), RecvError> {
// The order of these calls don't really matter too much as only one
// should have pending work.
try_ready!(self.ping_pong.send_pending_pong(&mut self.codec));
@@ -83,84 +88,97 @@ impl<T, P, B> Connection<T, P, B>
Ok(().into())
}
/// Returns `Ready` when new the connection is able to support a new request stream.
pub fn poll_send_request_ready(&mut self) -> Poll<(), ConnectionError> {
self.streams.poll_send_request_ready()
}
/// Advances the internal state of the connection.
pub fn poll(&mut self) -> Poll<(), ConnectionError> {
use error::ConnectionError::*;
pub fn poll(&mut self) -> Poll<(), proto::Error> {
use codec::RecvError::*;
loop {
// TODO: probably clean up this glob of code
match self.state {
// When open, continue to poll a frame
State::Open => {},
// In an error state
_ => {
try_ready!(self.poll_complete());
State::Open => {
match self.poll2() {
// The connection has shutdown normally
Ok(Async::Ready(())) => return Ok(().into()),
// The connection is not ready to make progress
Ok(Async::NotReady) => {
// Ensure all window updates have been sent.
//
// This will also handle flushing `self.codec`
try_ready!(self.streams.poll_complete(&mut self.codec));
// GO_AWAY frame has been sent, return the error
return Err(self.state.error().unwrap().into());
}
}
return Ok(Async::NotReady);
}
// Attempting to read a frame resulted in a connection level
// error. This is handled by setting a GO_AWAY frame followed by
// terminating the connection.
Err(Connection(e)) => {
debug!("Connection::poll; err={:?}", e);
match self.poll2() {
Err(Proto(e)) => {
debug!("Connection::poll; err={:?}", e);
let last_processed_id = self.streams.recv_err(&e.into());
let frame = frame::GoAway::new(last_processed_id, e);
// Reset all active streams
let last_processed_id = self.streams.recv_err(&e.into());
self.state = State::GoAway(frame);
// Create the GO_AWAY frame with the last_processed_id
let frame = frame::GoAway::new(last_processed_id, e);
// Transition to the going away state.
self.state = State::GoAway(frame);
}
// Attempting to read a frame resulted in a stream level error.
// This is handled by resetting the frame then trying to read
// another frame.
Err(Stream { id, reason }) => {
trace!("stream level error; id={:?}; reason={:?}", id, reason);
self.streams.send_reset(id, reason);
}
// Attempting to read a frame resulted in an I/O error. All
// active streams must be reset.
//
// TODO: Are I/O errors recoverable?
Err(Io(e)) => {
let e = e.into();
// Reset all active streams
self.streams.recv_err(&e);
// Return the error
return Err(e);
}
}
},
State::GoAway(frame) => {
// Ensure the codec is ready to accept the frame
try_ready!(self.codec.poll_ready());
// Buffer the GO_AWAY frame
self.codec.buffer(frame.into())
.ok().expect("invalid GO_AWAY frame");
// GO_AWAY sent, transition the connection to an errored state
self.state = State::Flush(frame.reason());
}
Err(e) => {
// TODO: Are I/O errors recoverable?
self.streams.recv_err(&e);
return Err(e);
State::Flush(reason) => {
// Flush the codec
try_ready!(self.codec.flush());
// Transition the state to error
self.state = State::Error(reason);
}
State::Error(reason) => {
return Err(reason.into());
}
ret => return ret,
}
}
}
fn poll2(&mut self) -> Poll<(), ConnectionError> {
fn poll2(&mut self) -> Poll<(), RecvError> {
use frame::Frame::*;
use proto::ProtoError::*;
loop {
// First, ensure that the `Connection` is able to receive a frame
try_ready!(self.poll_ready());
trace!("polling codec");
let frame = match self.codec.poll() {
// Receive a frame
Ok(Async::Ready(frame)) => frame,
// Socket not ready, try to flush any pending data
Ok(Async::NotReady) => {
// Flush any pending writes
let _ = try!(self.poll_complete());
return Ok(Async::NotReady);
}
// Connection level error, set GO_AWAY and close connection
Err(Connection(reason)) => {
return Err(ConnectionError::Proto(reason));
}
// Stream level error, reset the stream
Err(Stream { id, reason }) => {
trace!("stream level error; id={:?}; reason={:?}", id, reason);
self.streams.send_reset(id, reason);
continue;
}
// I/O error, nothing more can be done
Err(Io(err)) => {
return Err(err.into());
}
};
debug!("recv; frame={:?}", frame);
match frame {
match try_ready!(self.codec.poll()) {
Some(Headers(frame)) => {
trace!("recv HEADERS; frame={:?}", frame);
try!(self.streams.recv_headers(frame));
@@ -184,7 +202,7 @@ impl<T, P, B> Connection<T, P, B>
Some(GoAway(_)) => {
// TODO: handle the last_processed_id. Also, should this be
// handled as an error?
// let _ = ConnectionError::Proto(frame.reason());
// let _ = RecvError::Proto(frame.reason());
return Ok(().into());
}
Some(Ping(frame)) => {
@@ -207,46 +225,20 @@ impl<T, P, B> Connection<T, P, B>
}
}
}
fn poll_complete(&mut self) -> Poll<(), ConnectionError> {
loop {
match self.state {
State::Open => {
try_ready!(self.poll_ready());
// Ensure all window updates have been sent.
try_ready!(self.streams.poll_complete(&mut self.codec));
return Ok(().into());
}
State::GoAway(frame) => {
if !self.codec.start_send(frame.into())?.is_ready() {
// Not ready to send the frame... try again later.
return Ok(Async::NotReady);
}
// GO_AWAY sent, transition the connection to an errored state
self.state = State::Flush(frame.reason());
}
State::Flush(reason) => {
try_ready!(self.codec.poll_complete());
self.state = State::Error(reason);
}
State::Error(..) => {
return Ok(().into());
}
}
}
}
}
impl<T, B> Connection<T, client::Peer, B>
where T: AsyncRead + AsyncWrite,
B: IntoBuf,
{
/// Returns `Ready` when new the connection is able to support a new request stream.
pub fn poll_send_request_ready(&mut self) -> Async<()> {
self.streams.poll_send_request_ready()
}
/// Initialize a new HTTP/2.0 stream and send the message.
pub fn send_request(&mut self, request: Request<()>, end_of_stream: bool)
-> Result<StreamRef<B::Buf, client::Peer>, ConnectionError>
-> Result<StreamRef<B::Buf, client::Peer>, SendError>
{
self.streams.send_request(request, end_of_stream)
}
@@ -260,14 +252,3 @@ impl<T, B> Connection<T, server::Peer, B>
self.streams.next_incoming()
}
}
// ====== impl State =====
impl State {
fn error(&self) -> Option<Reason> {
match *self {
State::Error(reason) => Some(reason),
_ => None,
}
}
}

34
src/proto/error.rs Normal file
View File

@@ -0,0 +1,34 @@
use frame::Reason;
use codec::RecvError;
use std::io;
/// Either an H2 reason or an I/O error
#[derive(Debug)]
pub enum Error {
Proto(Reason),
Io(io::Error),
}
impl Error {
pub fn into_connection_recv_error(self) -> RecvError {
use self::Error::*;
match self {
Proto(reason) => RecvError::Connection(reason),
Io(e) => RecvError::Io(e),
}
}
}
impl From<Reason> for Error {
fn from(src: Reason) -> Self {
Error::Proto(src)
}
}
impl From<io::Error> for Error {
fn from(src: io::Error) -> Self {
Error::Io(src)
}
}

View File

@@ -1,278 +0,0 @@
use {hpack, ConnectionError};
use frame::{self, Frame, Kind};
use frame::DEFAULT_SETTINGS_HEADER_TABLE_SIZE;
use proto::*;
use error::Reason::*;
use futures::*;
use bytes::BytesMut;
use tokio_io::AsyncRead;
use tokio_io::codec::length_delimited;
use std::io;
#[derive(Debug)]
pub struct FramedRead<T> {
inner: length_delimited::FramedRead<T>,
// hpack decoder state
hpack: hpack::Decoder,
partial: Option<Partial>,
}
/// Partially loaded headers frame
#[derive(Debug)]
struct Partial {
/// Empty frame
frame: Continuable,
/// Partial header payload
buf: BytesMut,
}
#[derive(Debug)]
enum Continuable {
Headers(frame::Headers),
// Decode the Continuation frame but ignore it...
// Ignore(StreamId),
// PushPromise(frame::PushPromise),
}
impl<T> FramedRead<T> {
pub fn new(inner: length_delimited::FramedRead<T>) -> FramedRead<T> {
FramedRead {
inner: inner,
hpack: hpack::Decoder::new(DEFAULT_SETTINGS_HEADER_TABLE_SIZE),
partial: None,
}
}
pub fn apply_remote_settings(&mut self, _settings: &frame::Settings) {
// TODO: Is this needed?
}
fn decode_frame(&mut self, mut bytes: BytesMut) -> Result<Option<Frame>, ProtoError> {
use self::ProtoError::*;
trace!("decoding frame from {}B", bytes.len());
// Parse the head
let head = frame::Head::parse(&bytes);
if self.partial.is_some() && head.kind() != Kind::Continuation {
return Err(Connection(ProtocolError));
}
let kind = head.kind();
trace!(" -> kind={:?}", kind);
let frame = match kind {
Kind::Settings => {
let res = frame::Settings::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::Ping => {
let res = frame::Ping::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::WindowUpdate => {
let res = frame::WindowUpdate::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::Data => {
let _ = bytes.split_to(frame::HEADER_LEN);
let res = frame::Data::load(head, bytes.freeze());
// TODO: Should this always be connection level? Probably not...
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::Headers => {
// Drop the frame header
// TODO: Change to drain: carllerche/bytes#130
let _ = bytes.split_to(frame::HEADER_LEN);
// Parse the header frame w/o parsing the payload
let (mut headers, payload) = match frame::Headers::load(head, bytes) {
Ok(res) => res,
Err(frame::Error::InvalidDependencyId) => {
// A stream cannot depend on itself. An endpoint MUST
// treat this as a stream error (Section 5.4.2) of type
// `PROTOCOL_ERROR`.
return Err(Stream {
id: head.stream_id(),
reason: ProtocolError,
});
}
_ => return Err(Connection(ProtocolError)),
};
if headers.is_end_headers() {
// Load the HPACK encoded headers & return the frame
match headers.load_hpack(payload, &mut self.hpack) {
Ok(_) => {}
Err(frame::Error::MalformedMessage) => {
return Err(Stream {
id: head.stream_id(),
reason: ProtocolError,
});
}
Err(_) => return Err(Connection(ProtocolError)),
}
headers.into()
} else {
// Defer loading the frame
self.partial = Some(Partial {
frame: Continuable::Headers(headers),
buf: payload,
});
return Ok(None);
}
}
Kind::Reset => {
let res = frame::Reset::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::GoAway => {
let res = frame::GoAway::load(&bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::PushPromise => {
let res = frame::PushPromise::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::Priority => {
if head.stream_id() == 0 {
// Invalid stream identifier
return Err(Connection(ProtocolError));
}
match frame::Priority::load(head, &bytes[frame::HEADER_LEN..]) {
Ok(frame) => frame.into(),
Err(frame::Error::InvalidDependencyId) => {
// A stream cannot depend on itself. An endpoint MUST
// treat this as a stream error (Section 5.4.2) of type
// `PROTOCOL_ERROR`.
return Err(Stream {
id: head.stream_id(),
reason: ProtocolError,
});
}
Err(_) => return Err(Connection(ProtocolError)),
}
}
Kind::Continuation => {
// TODO: Un-hack this
let end_of_headers = (head.flag() & 0x4) == 0x4;
let mut partial = match self.partial.take() {
Some(partial) => partial,
None => return Err(Connection(ProtocolError)),
};
// Extend the buf
partial.buf.extend_from_slice(&bytes[frame::HEADER_LEN..]);
if !end_of_headers {
self.partial = Some(partial);
return Ok(None);
}
match partial.frame {
Continuable::Headers(mut frame) => {
// The stream identifiers must match
if frame.stream_id() != head.stream_id() {
return Err(Connection(ProtocolError));
}
match frame.load_hpack(partial.buf, &mut self.hpack) {
Ok(_) => {}
Err(frame::Error::MalformedMessage) => {
return Err(Stream {
id: head.stream_id(),
reason: ProtocolError,
});
}
Err(_) => return Err(Connection(ProtocolError)),
}
frame.into()
}
}
}
Kind::Unknown => {
// Unknown frames are ignored
return Ok(None);
}
};
Ok(Some(frame))
}
pub fn get_ref(&self) -> &T {
self.inner.get_ref()
}
pub fn get_mut(&mut self) -> &mut T {
self.inner.get_mut()
}
}
impl<T> futures::Stream for FramedRead<T>
where T: AsyncRead,
{
type Item = Frame;
type Error = ProtoError;
fn poll(&mut self) -> Poll<Option<Frame>, Self::Error> {
loop {
trace!("poll");
let bytes = match try_ready!(self.inner.poll()) {
Some(bytes) => bytes,
None => return Ok(Async::Ready(None)),
};
trace!("poll; bytes={}B", bytes.len());
if let Some(frame) = try!(self.decode_frame(bytes)) {
return Ok(Async::Ready(Some(frame)));
}
}
}
}
impl<T: Sink> Sink for FramedRead<T> {
type SinkItem = T::SinkItem;
type SinkError = T::SinkError;
fn start_send(&mut self, item: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> {
self.inner.get_mut().start_send(item)
}
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
self.inner.get_mut().poll_complete()
}
}
impl<T: AsyncWrite, B: Buf> FramedRead<FramedWrite<T, B>> {
pub fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
self.inner.get_mut().poll_ready()
}
}
impl<T: io::Write> io::Write for FramedRead<T> {
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
self.inner.get_mut().write(src)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.get_mut().flush()
}
}

View File

@@ -1,262 +0,0 @@
use {hpack, ConnectionError};
use error::User::*;
use frame::{self, Frame, FrameSize};
use futures::*;
use tokio_io::{AsyncRead, AsyncWrite};
use bytes::{BytesMut, Buf, BufMut};
use std::io::{self, Cursor};
#[derive(Debug)]
pub struct FramedWrite<T, B> {
/// Upstream `AsyncWrite`
inner: T,
/// HPACK encoder
hpack: hpack::Encoder,
/// Write buffer
///
/// TODO: Should this be a ring buffer?
buf: Cursor<BytesMut>,
/// Next frame to encode
next: Option<Next<B>>,
/// Last data frame
last_data_frame: Option<frame::Data<B>>,
/// Max frame size, this is specified by the peer
max_frame_size: FrameSize,
}
#[derive(Debug)]
enum Next<B> {
Data(frame::Data<B>),
Continuation(frame::Continuation),
}
/// Initialze the connection with this amount of write buffer.
const DEFAULT_BUFFER_CAPACITY: usize = 4 * 1_024;
/// Min buffer required to attempt to write a frame
const MIN_BUFFER_CAPACITY: usize = frame::HEADER_LEN + CHAIN_THRESHOLD;
/// Chain payloads bigger than this. The remote will never advertise a max frame
/// size less than this (well, the spec says the max frame size can't be less
/// than 16kb, so not even close).
const CHAIN_THRESHOLD: usize = 256;
// TODO: Make generic
impl<T, B> FramedWrite<T, B>
where T: AsyncWrite,
B: Buf,
{
pub fn new(inner: T) -> FramedWrite<T, B> {
FramedWrite {
inner: inner,
hpack: hpack::Encoder::default(),
buf: Cursor::new(BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY)),
next: None,
last_data_frame: None,
max_frame_size: frame::DEFAULT_MAX_FRAME_SIZE,
}
}
pub fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
if !self.has_capacity() {
// Try flushing
try!(self.poll_complete());
if !self.has_capacity() {
return Ok(Async::NotReady);
}
}
Ok(Async::Ready(()))
}
fn has_capacity(&self) -> bool {
self.next.is_none() && self.buf.get_ref().remaining_mut() >= MIN_BUFFER_CAPACITY
}
fn is_empty(&self) -> bool {
match self.next {
Some(Next::Data(ref frame)) => !frame.payload().has_remaining(),
_ => !self.buf.has_remaining(),
}
}
}
impl<T, B> FramedWrite<T, B> {
pub fn max_frame_size(&self) -> usize {
self.max_frame_size as usize
}
pub fn apply_remote_settings(&mut self, settings: &frame::Settings) {
if let Some(val) = settings.max_frame_size() {
self.max_frame_size = val;
}
}
pub fn take_last_data_frame(&mut self) -> Option<frame::Data<B>> {
self.last_data_frame.take()
}
}
impl<T, B> Sink for FramedWrite<T, B>
where T: AsyncWrite,
B: Buf,
{
type SinkItem = Frame<B>;
type SinkError = ConnectionError;
fn start_send(&mut self, item: Self::SinkItem)
-> StartSend<Self::SinkItem, ConnectionError>
{
if !try!(self.poll_ready()).is_ready() {
return Ok(AsyncSink::NotReady(item));
}
debug!("send; frame={:?}", item);
match item {
Frame::Data(mut v) => {
// Ensure that the payload is not greater than the max frame.
let len = v.payload().remaining();
if len > self.max_frame_size() {
return Err(PayloadTooBig.into());
}
if len >= CHAIN_THRESHOLD {
let head = v.head();
// Encode the frame head to the buffer
head.encode(len, self.buf.get_mut());
// Save the data frame
self.next = Some(Next::Data(v));
} else {
v.encode_chunk(self.buf.get_mut());
// The chunk has been fully encoded, so there is no need to
// keep it around
assert_eq!(v.payload().remaining(), 0, "chunk not fully encoded");
// Save off the last frame...
self.last_data_frame = Some(v);
}
}
Frame::Headers(v) => {
if let Some(continuation) = v.encode(&mut self.hpack, self.buf.get_mut()) {
self.next = Some(Next::Continuation(continuation));
}
}
Frame::PushPromise(v) => {
debug!("unimplemented PUSH_PROMISE write; frame={:?}", v);
unimplemented!();
}
Frame::Settings(v) => {
v.encode(self.buf.get_mut());
trace!("encoded settings; rem={:?}", self.buf.remaining());
}
Frame::GoAway(v) => {
v.encode(self.buf.get_mut());
trace!("encoded go_away; rem={:?}", self.buf.remaining());
}
Frame::Ping(v) => {
v.encode(self.buf.get_mut());
trace!("encoded ping; rem={:?}", self.buf.remaining());
}
Frame::WindowUpdate(v) => {
v.encode(self.buf.get_mut());
trace!("encoded window_update; rem={:?}", self.buf.remaining());
}
Frame::Priority(_) => {
/*
v.encode(self.buf.get_mut());
trace!("encoded priority; rem={:?}", self.buf.remaining());
*/
unimplemented!();
}
Frame::Reset(v) => {
v.encode(self.buf.get_mut());
trace!("encoded reset; rem={:?}", self.buf.remaining());
}
}
Ok(AsyncSink::Ready)
}
fn poll_complete(&mut self) -> Poll<(), ConnectionError> {
trace!("poll_complete");
while !self.is_empty() {
match self.next {
Some(Next::Data(ref mut frame)) => {
let mut buf = Buf::by_ref(&mut self.buf).chain(frame.payload_mut());
try_ready!(self.inner.write_buf(&mut buf));
}
_ => {
try_ready!(self.inner.write_buf(&mut self.buf));
}
}
}
// The data frame has been written, so unset it
match self.next.take() {
Some(Next::Data(frame)) => {
self.last_data_frame = Some(frame);
}
Some(Next::Continuation(_)) => {
unimplemented!();
}
None => {}
}
trace!("flushing buffer");
// Flush the upstream
try_nb!(self.inner.flush());
// Clear internal buffer
self.buf.set_position(0);
self.buf.get_mut().clear();
Ok(Async::Ready(()))
}
fn close(&mut self) -> Poll<(), ConnectionError> {
try_ready!(self.poll_complete());
self.inner.shutdown().map_err(Into::into)
}
}
impl<T: Stream, B> Stream for FramedWrite<T, B> {
type Item = T::Item;
type Error = T::Error;
fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> {
self.inner.poll()
}
}
impl<T: io::Read, B> io::Read for FramedWrite<T, B> {
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
self.inner.read(dst)
}
}
impl<T: AsyncRead, B> AsyncRead for FramedWrite<T, B> {
fn read_buf<B2: BufMut>(&mut self, buf: &mut B2) -> Poll<usize, io::Error>
where Self: Sized,
{
self.inner.read_buf(buf)
}
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
self.inner.prepare_uninitialized_buffer(buf)
}
}

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

22
src/proto/peer.rs Normal file
View File

@@ -0,0 +1,22 @@
use frame::{Headers, StreamId};
use codec::RecvError;
use std::fmt;
/// 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) -> Headers;
fn convert_poll_message(headers: Headers) -> Result<Self::Poll, RecvError>;
}

View File

@@ -1,7 +1,7 @@
use frame::Ping;
use proto::*;
use futures::Sink;
use std::io;
/// Acknowledges ping requests from the remote.
#[derive(Debug)]
@@ -45,15 +45,16 @@ impl<B> PingPong<B>
}
/// Send any pending pongs.
pub fn send_pending_pong<T>(&mut self, dst: &mut Codec<T, B>) -> Poll<(), ConnectionError>
pub fn send_pending_pong<T>(&mut self, dst: &mut Codec<T, B>) -> Poll<(), io::Error>
where T: AsyncWrite,
{
if let Some(pong) = self.sending_pong.take() {
if let AsyncSink::NotReady(pong) = dst.start_send(pong)? {
// If the pong can't be sent, save it.
if !dst.poll_ready()?.is_ready() {
self.sending_pong = Some(pong);
return Ok(Async::NotReady);
}
dst.buffer(pong).ok().expect("invalid pong frame");
}
Ok(Async::Ready(()))

View File

@@ -1,8 +1,7 @@
use frame;
use codec::RecvError;
use proto::*;
use futures::Sink;
#[derive(Debug)]
pub(crate) struct Settings {
/// Received SETTINGS frame pending processing. The ACK must be written to
@@ -31,7 +30,7 @@ impl Settings {
pub fn send_pending_ack<T, B, C, P>(&mut self,
dst: &mut Codec<T, B>,
streams: &mut Streams<C, P>)
-> Poll<(), ConnectionError>
-> Poll<(), RecvError>
where T: AsyncWrite,
B: Buf,
C: Buf,
@@ -40,13 +39,17 @@ impl Settings {
trace!("send_pending_ack; pending={:?}", self.pending);
if let Some(ref settings) = self.pending {
let frame = frame::Settings::ack();
if let AsyncSink::NotReady(_) = dst.start_send(frame.into())? {
if !dst.poll_ready()?.is_ready() {
trace!("failed to send ACK");
return Ok(Async::NotReady);
}
// Create an ACK settings frame
let frame = frame::Settings::ack();
// Buffer the settings frame
dst.buffer(frame.into()).ok().expect("invalid settings frame");
trace!("ACK sent; applying settings");
dst.apply_remote_settings(settings);

View File

@@ -1,6 +1,6 @@
use ConnectionError;
use frame::Reason;
use frame::Reason::*;
use proto::*;
use error::Reason::*;
#[derive(Copy, Clone, Debug)]
pub struct FlowControl {
@@ -67,15 +67,15 @@ impl FlowControl {
/// Increase the window size.
///
/// This is called after receiving a WINDOW_UPDATE frame
pub fn inc_window(&mut self, sz: WindowSize) -> Result<(), ConnectionError> {
pub fn inc_window(&mut self, sz: WindowSize) -> Result<(), Reason> {
let (val, overflow) = self.window_size.overflowing_add(sz as i32);
if overflow {
return Err(FlowControlError.into());
return Err(FlowControlError);
}
if val > MAX_WINDOW_SIZE as i32 {
return Err(FlowControlError.into());
return Err(FlowControlError);
}
trace!("inc_window; sz={}; old={}; new={}", sz, self.window_size, val);

View File

@@ -20,11 +20,9 @@ use self::state::State;
use self::store::{Store, Entry};
use self::stream::Stream;
use {frame, ConnectionError};
use frame::StreamId;
use proto::*;
use error::Reason::*;
use error::User::*;
use http::{Request, Response};
use bytes::Bytes;

View File

@@ -1,9 +1,14 @@
use super::*;
use super::store::Resolve;
use bytes::buf::Take;
use futures::Sink;
use frame::Reason;
use codec::UserError;
use codec::UserError::*;
use bytes::buf::Take;
use std::io;
use std::{fmt, cmp};
#[derive(Debug)]
@@ -80,7 +85,7 @@ impl<B, P> Prioritize<B, P>
frame: frame::Data<B>,
stream: &mut store::Ptr<B, P>,
task: &mut Option<Task>)
-> Result<(), ConnectionError>
-> Result<(), UserError>
{
let sz = frame.payload().remaining();
@@ -93,9 +98,9 @@ impl<B, P> Prioritize<B, P>
if !stream.state.is_send_streaming() {
if stream.state.is_closed() {
return Err(InactiveStreamId.into());
return Err(InactiveStreamId);
} else {
return Err(UnexpectedFrameType.into());
return Err(UnexpectedFrameType);
}
}
@@ -115,7 +120,7 @@ impl<B, P> Prioritize<B, P>
}
if frame.is_end_stream() {
try!(stream.state.send_close());
stream.state.send_close();
}
trace!("send_data (2); available={}; buffered={}",
@@ -161,7 +166,7 @@ impl<B, P> Prioritize<B, P>
pub fn recv_stream_window_update(&mut self,
inc: WindowSize,
stream: &mut store::Ptr<B, P>)
-> Result<(), ConnectionError>
-> Result<(), Reason>
{
trace!("recv_stream_window_update; stream={:?}; state={:?}; inc={}; flow={:?}",
stream.id, stream.state, inc, stream.send_flow);
@@ -179,7 +184,7 @@ impl<B, P> Prioritize<B, P>
pub fn recv_connection_window_update(&mut self,
inc: WindowSize,
store: &mut Store<B, P>)
-> Result<(), ConnectionError>
-> Result<(), Reason>
{
// Update the connection's window
self.flow.inc_window(inc)?;
@@ -284,7 +289,7 @@ impl<B, P> Prioritize<B, P>
pub fn poll_complete<T>(&mut self,
store: &mut Store<B, P>,
dst: &mut Codec<T, Prioritized<B>>)
-> Poll<(), ConnectionError>
-> Poll<(), io::Error>
where T: AsyncWrite,
{
// Ensure codec is ready
@@ -303,22 +308,17 @@ impl<B, P> Prioritize<B, P>
Some(frame) => {
trace!("writing frame={:?}", frame);
let res = dst.start_send(frame)?;
// We already verified that `dst` is ready to accept the
// write
assert!(res.is_ready());
dst.buffer(frame).ok().expect("invalid frame");
// Ensure the codec is ready to try the loop again.
try_ready!(dst.poll_ready());
// Because, always try to reclaim...
self.reclaim_frame(store, dst);
}
None => {
// Try to flush the codec.
try_ready!(dst.poll_complete());
try_ready!(dst.flush());
// This might release a data frame...
if !self.reclaim_frame(store, dst) {

View File

@@ -1,10 +1,12 @@
use {client, server, frame, HeaderMap, ConnectionError};
use {client, server, frame, proto};
use frame::Reason;
use codec::{RecvError, UserError};
use proto::*;
use super::*;
use error::Reason::*;
use futures::Sink;
use http::HeaderMap;
use std::io;
use std::marker::PhantomData;
#[derive(Debug)]
@@ -41,14 +43,14 @@ pub(super) struct Recv<B, P>
/// Refused StreamId, this represents a frame that must be sent out.
refused: Option<StreamId>,
_p: PhantomData<(B)>,
_p: PhantomData<B>,
}
#[derive(Debug)]
pub(super) enum Event<T> {
Headers(T),
Data(Bytes),
Trailers(::HeaderMap),
Trailers(HeaderMap),
}
#[derive(Debug, Clone, Copy)]
@@ -103,7 +105,7 @@ impl<B, P> Recv<B, P>
///
/// Returns the stream state if successful. `None` if refused
pub fn open(&mut self, id: StreamId)
-> Result<Option<StreamId>, ConnectionError>
-> Result<Option<StreamId>, RecvError>
{
assert!(self.refused.is_none());
@@ -123,7 +125,7 @@ impl<B, P> Recv<B, P>
pub fn recv_headers(&mut self,
frame: frame::Headers,
stream: &mut store::Ptr<B, P>)
-> Result<(), ProtoError>
-> Result<(), RecvError>
{
trace!("opening stream; init_window={}", self.init_window_sz);
let is_initial = stream.state.recv_open(frame.is_end_stream())?;
@@ -137,7 +139,7 @@ impl<B, P> Recv<B, P>
self.next_stream_id = frame.stream_id();
self.next_stream_id.increment();
} else {
return Err(ProtoError::Connection(ProtocolError));
return Err(RecvError::Connection(ProtocolError));
}
// TODO: be smarter about this logic
@@ -184,13 +186,13 @@ impl<B, P> Recv<B, P>
pub fn recv_trailers(&mut self,
frame: frame::Headers,
stream: &mut store::Ptr<B, P>)
-> Result<(), ProtoError>
-> Result<(), RecvError>
{
// Transition the state
stream.state.recv_close()?;
if stream.ensure_content_length_zero().is_err() {
return Err(ProtoError::Stream {
return Err(RecvError::Stream {
id: stream.id,
reason: ProtocolError,
});
@@ -205,11 +207,12 @@ impl<B, P> Recv<B, P>
Ok(())
}
/// Releases capacity back to the connection
pub fn release_capacity(&mut self,
capacity: WindowSize,
stream: &mut store::Ptr<B, P>,
task: &mut Option<Task>)
-> Result<(), ConnectionError>
-> Result<(), UserError>
{
if capacity > stream.in_flight_recv_data {
// TODO: Handle error
@@ -246,7 +249,7 @@ impl<B, P> Recv<B, P>
pub fn recv_data(&mut self,
frame: frame::Data,
stream: &mut store::Ptr<B, P>)
-> Result<(), ProtoError>
-> Result<(), RecvError>
{
let sz = frame.payload().len();
@@ -259,7 +262,7 @@ impl<B, P> Recv<B, P>
if !stream.state.is_recv_streaming() {
// Receiving a DATA frame when not expecting one is a protocol
// error.
return Err(ProtoError::Connection(ProtocolError));
return Err(RecvError::Connection(ProtocolError));
}
trace!("recv_data; size={}; connection={}; stream={}",
@@ -268,7 +271,7 @@ impl<B, P> Recv<B, P>
// Ensure that there is enough capacity on the connection before acting
// on the stream.
if self.flow.window_size() < sz || stream.recv_flow.window_size() < sz {
return Err(ProtoError::Connection(FlowControlError));
return Err(RecvError::Connection(FlowControlError));
}
// Update connection level flow control
@@ -281,7 +284,7 @@ impl<B, P> Recv<B, P>
stream.in_flight_recv_data += sz;
if stream.dec_content_length(frame.payload().len()).is_err() {
return Err(ProtoError::Stream {
return Err(RecvError::Stream {
id: stream.id,
reason: ProtocolError,
});
@@ -289,14 +292,14 @@ impl<B, P> Recv<B, P>
if frame.is_end_stream() {
if stream.ensure_content_length_zero().is_err() {
return Err(ProtoError::Stream {
return Err(RecvError::Stream {
id: stream.id,
reason: ProtocolError,
});
}
if stream.state.recv_close().is_err() {
return Err(ProtoError::Connection(ProtocolError));
return Err(RecvError::Connection(ProtocolError));
}
}
@@ -314,13 +317,14 @@ impl<B, P> Recv<B, P>
send: &Send<B, P>,
stream: store::Key,
store: &mut Store<B, P>)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
// First, make sure that the values are legit
self.ensure_can_reserve(frame.promised_id())?;
// Make sure that the stream state is valid
store[stream].state.ensure_recv_open()?;
store[stream].state.ensure_recv_open()
.map_err(|e| e.into_connection_recv_error())?;
// TODO: Streams in the reserved states do not count towards the concurrency
// limit. However, it seems like there should be a cap otherwise this
@@ -361,18 +365,19 @@ impl<B, P> Recv<B, P>
Ok(())
}
pub fn ensure_not_idle(&self, id: StreamId) -> Result<(), ConnectionError> {
/// Ensures that `id` is not in the `Idle` state.
pub fn ensure_not_idle(&self, id: StreamId) -> Result<(), Reason> {
if id >= self.next_stream_id {
return Err(ProtocolError.into());
return Err(ProtocolError);
}
Ok(())
}
pub fn recv_reset(&mut self, frame: frame::Reset, stream: &mut Stream<B, P>)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
let err = ConnectionError::Proto(frame.reason());
let err = proto::Error::Proto(frame.reason());
// Notify the stream
stream.state.recv_err(&err);
@@ -381,7 +386,7 @@ impl<B, P> Recv<B, P>
}
/// Handle a received error
pub fn recv_err(&mut self, err: &ConnectionError, stream: &mut Stream<B, P>) {
pub fn recv_err(&mut self, err: &proto::Error, stream: &mut Stream<B, P>) {
// Receive an error
stream.state.recv_err(err);
@@ -415,17 +420,17 @@ impl<B, P> Recv<B, P>
/// Returns true if the remote peer can initiate a stream with the given ID.
fn ensure_can_open(&self, id: StreamId)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
if !P::is_server() {
// Remote is a server and cannot open streams. PushPromise is
// registered by reserving, so does not go through this path.
return Err(ProtocolError.into());
return Err(RecvError::Connection(ProtocolError));
}
// Ensure that the ID is a valid server initiated ID
if !id.is_client_initiated() {
return Err(ProtocolError.into());
return Err(RecvError::Connection(ProtocolError));
}
Ok(())
@@ -433,16 +438,16 @@ impl<B, P> Recv<B, P>
/// Returns true if the remote peer can reserve a stream with the given ID.
fn ensure_can_reserve(&self, promised_id: StreamId)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
// TODO: Are there other rules?
if P::is_server() {
// The remote is a client and cannot reserve
return Err(ProtocolError.into());
return Err(RecvError::Connection(ProtocolError));
}
if !promised_id.is_server_initiated() {
return Err(ProtocolError.into());
return Err(RecvError::Connection(ProtocolError));
}
Ok(())
@@ -450,31 +455,28 @@ impl<B, P> Recv<B, P>
/// Send any pending refusals.
pub fn send_pending_refusal<T>(&mut self, dst: &mut Codec<T, Prioritized<B>>)
-> Poll<(), ConnectionError>
-> Poll<(), io::Error>
where T: AsyncWrite,
{
if let Some(stream_id) = self.refused.take() {
if let Some(stream_id) = self.refused {
try_ready!(dst.poll_ready());
// Create the RST_STREAM frame
let frame = frame::Reset::new(stream_id, RefusedStream);
match dst.start_send(frame.into())? {
AsyncSink::Ready => {
self.reset(stream_id, RefusedStream);
return Ok(Async::Ready(()));
}
AsyncSink::NotReady(_) => {
self.refused = Some(stream_id);
return Ok(Async::NotReady);
}
}
// Buffer the frame
dst.buffer(frame.into()).ok().expect("invalid RST_STREAM frame");
}
self.refused = None;
Ok(Async::Ready(()))
}
pub fn poll_complete<T>(&mut self,
store: &mut Store<B, P>,
dst: &mut Codec<T, Prioritized<B>>)
-> Poll<(), ConnectionError>
-> Poll<(), io::Error>
where T: AsyncWrite,
{
// Send any pending connection level window updates
@@ -488,7 +490,7 @@ impl<B, P> Recv<B, P>
/// Send connection level window update
fn send_connection_window_update<T>(&mut self, dst: &mut Codec<T, Prioritized<B>>)
-> Poll<(), ConnectionError>
-> Poll<(), io::Error>
where T: AsyncWrite,
{
let incr = self.flow.unclaimed_capacity();
@@ -496,11 +498,14 @@ impl<B, P> Recv<B, P>
if incr > 0 {
let frame = frame::WindowUpdate::new(StreamId::zero(), incr);
if dst.start_send(frame.into())?.is_ready() {
self.flow.inc_window(incr).ok().expect("unexpected flow control state");
} else {
return Ok(Async::NotReady);
}
// Ensure the codec has capacity
try_ready!(dst.poll_ready());
// Buffer the WINDOW_UPDATE frame
dst.buffer(frame.into()).ok().expect("invalid WINDOW_UPDATE frame");
// Update flow control
self.flow.inc_window(incr).ok().expect("unexpected flow control state");
}
Ok(().into())
@@ -511,7 +516,7 @@ impl<B, P> Recv<B, P>
pub fn send_stream_window_updates<T>(&mut self,
store: &mut Store<B, P>,
dst: &mut Codec<T, Prioritized<B>>)
-> Poll<(), ConnectionError>
-> Poll<(), io::Error>
where T: AsyncWrite,
{
loop {
@@ -534,10 +539,11 @@ impl<B, P> Recv<B, P>
let incr = stream.recv_flow.unclaimed_capacity();
if incr > 0 {
// Create the WINDOW_UPDATE frame
let frame = frame::WindowUpdate::new(stream.id, incr);
let res = dst.start_send(frame.into())?;
assert!(res.is_ready());
// Buffer it
dst.buffer(frame.into()).ok().expect("invalid WINDOW_UPDATE frame");
}
}
}
@@ -548,8 +554,9 @@ impl<B, P> Recv<B, P>
}
pub fn poll_data(&mut self, stream: &mut Stream<B, P>)
-> Poll<Option<Bytes>, ConnectionError>
-> Poll<Option<Bytes>, proto::Error>
{
// TODO: Return error when the stream is reset
match stream.pending_recv.pop_front(&mut self.buffer) {
Some(Event::Data(payload)) => {
Ok(Some(payload).into())
@@ -575,7 +582,7 @@ impl<B, P> Recv<B, P>
}
pub fn poll_trailers(&mut self, stream: &mut Stream<B, P>)
-> Poll<Option<HeaderMap>, ConnectionError>
-> Poll<Option<HeaderMap>, proto::Error>
{
match stream.pending_recv.pop_front(&mut self.buffer) {
Some(Event::Trailers(trailers)) => {
@@ -599,10 +606,6 @@ impl<B, P> Recv<B, P>
}
}
}
fn reset(&mut self, _stream_id: StreamId, _reason: Reason) {
unimplemented!();
}
}
impl<B> Recv<B, server::Peer>
@@ -610,15 +613,10 @@ impl<B> Recv<B, server::Peer>
{
/// TODO: Should this fn return `Result`?
pub fn take_request(&mut self, stream: &mut store::Ptr<B, server::Peer>)
-> Result<Request<()>, ConnectionError>
-> Request<()>
{
match stream.pending_recv.pop_front(&mut self.buffer) {
Some(Event::Headers(request)) => Ok(request),
/*
// TODO: This error should probably be caught on receipt of the
// frame vs. now.
Ok(server::Peer::convert_poll_message(frame)?)
*/
Some(Event::Headers(request)) => request,
_ => panic!(),
}
}
@@ -628,7 +626,7 @@ impl<B> Recv<B, client::Peer>
where B: Buf,
{
pub fn poll_response(&mut self, stream: &mut store::Ptr<B, client::Peer>)
-> Poll<Response<()>, ConnectionError> {
-> Poll<Response<()>, proto::Error> {
// If the buffer is not empty, then the first frame must be a HEADERS
// frame or the user violated the contract.
match stream.pending_recv.pop_front(&mut self.buffer) {

View File

@@ -1,11 +1,14 @@
use {frame, ConnectionError};
use client;
use frame::{self, Reason};
use codec::{RecvError, UserError};
use codec::UserError::*;
use proto::*;
use super::*;
use error::User::*;
use bytes::Buf;
use std::io;
/// Manages state transitions related to outbound frames.
#[derive(Debug)]
pub(super) struct Send<B, P>
@@ -53,24 +56,11 @@ where B: Buf,
self.init_window_sz
}
pub fn poll_open_ready(&mut self) -> Poll<(), ConnectionError> {
try!(self.ensure_can_open());
if let Some(max) = self.max_streams {
if max <= self.num_streams {
self.blocked_open = Some(task::current());
return Ok(Async::NotReady);
}
}
return Ok(Async::Ready(()));
}
/// Update state reflecting a new, locally opened stream
///
/// Returns the stream state if successful. `None` if refused
pub fn open(&mut self)
-> Result<StreamId, ConnectionError>
-> Result<StreamId, UserError>
{
try!(self.ensure_can_open());
@@ -93,7 +83,7 @@ where B: Buf,
frame: frame::Headers,
stream: &mut store::Ptr<B, P>,
task: &mut Option<Task>)
-> Result<(), ConnectionError>
-> Result<(), UserError>
{
trace!("send_headers; frame={:?}; init_window={:?}", frame, self.init_window_sz);
// Update the state
@@ -145,7 +135,7 @@ where B: Buf,
frame: frame::Data<B>,
stream: &mut store::Ptr<B, P>,
task: &mut Option<Task>)
-> Result<(), ConnectionError>
-> Result<(), UserError>
{
self.prioritize.send_data(frame, stream, task)
}
@@ -154,14 +144,14 @@ where B: Buf,
frame: frame::Headers,
stream: &mut store::Ptr<B, P>,
task: &mut Option<Task>)
-> Result<(), ConnectionError>
-> Result<(), UserError>
{
// TODO: Should this logic be moved into state.rs?
if !stream.state.is_send_streaming() {
return Err(UnexpectedFrameType.into());
}
stream.state.send_close()?;
stream.state.send_close();
trace!("send_trailers -- queuing; frame={:?}", frame);
self.prioritize.queue_frame(frame.into(), stream, task);
@@ -172,7 +162,7 @@ where B: Buf,
pub fn poll_complete<T>(&mut self,
store: &mut Store<B, P>,
dst: &mut Codec<T, Prioritized<B>>)
-> Poll<(), ConnectionError>
-> Poll<(), io::Error>
where T: AsyncWrite,
{
self.prioritize.poll_complete(store, dst)
@@ -184,7 +174,7 @@ where B: Buf,
}
pub fn poll_capacity(&mut self, stream: &mut store::Ptr<B, P>)
-> Poll<Option<WindowSize>, ConnectionError>
-> Poll<Option<WindowSize>, UserError>
{
if !stream.state.is_send_streaming() {
return Ok(Async::Ready(None));
@@ -214,7 +204,7 @@ where B: Buf,
pub fn recv_connection_window_update(&mut self,
frame: frame::WindowUpdate,
store: &mut Store<B, P>)
-> Result<(), ConnectionError>
-> Result<(), Reason>
{
self.prioritize.recv_connection_window_update(frame.size_increment(), store)
}
@@ -223,11 +213,13 @@ where B: Buf,
sz: WindowSize,
stream: &mut store::Ptr<B, P>,
task: &mut Option<Task>)
-> Result<(), ConnectionError>
-> Result<(), Reason>
{
if let Err(e) = self.prioritize.recv_stream_window_update(sz, stream) {
debug!("recv_stream_window_update !!; err={:?}", e);
self.send_reset(FlowControlError.into(), stream, task);
return Err(e);
}
Ok(())
@@ -237,7 +229,7 @@ where B: Buf,
settings: &frame::Settings,
store: &mut Store<B, P>,
task: &mut Option<Task>)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
if let Some(val) = settings.max_concurrent_streams() {
self.max_streams = Some(val as usize);
@@ -283,13 +275,14 @@ where B: Buf,
// TODO: Should this notify the producer?
Ok(())
Ok::<_, RecvError>(())
})?;
} else if val > old_val {
let inc = val - old_val;
store.for_each(|mut stream| {
self.recv_stream_window_update(inc, &mut stream, task)
.map_err(RecvError::Connection)
})?;
}
}
@@ -297,9 +290,9 @@ where B: Buf,
Ok(())
}
pub fn ensure_not_idle(&self, id: StreamId) -> Result<(), ConnectionError> {
pub fn ensure_not_idle(&self, id: StreamId) -> Result<(), Reason> {
if id >= self.next_stream_id {
return Err(ProtocolError.into());
return Err(ProtocolError);
}
Ok(())
@@ -316,10 +309,10 @@ where B: Buf,
}
/// Returns true if the local actor can initiate a stream with the given ID.
fn ensure_can_open(&self) -> Result<(), ConnectionError> {
fn ensure_can_open(&self) -> Result<(), UserError> {
if P::is_server() {
// Servers cannot open streams. PushPromise must first be reserved.
return Err(UnexpectedFrameType.into());
return Err(UnexpectedFrameType);
}
// TODO: Handle StreamId overflow
@@ -327,3 +320,18 @@ where B: Buf,
Ok(())
}
}
impl<B> Send<B, client::Peer>
where B: Buf,
{
pub fn poll_open_ready(&mut self) -> Async<()> {
if let Some(max) = self.max_streams {
if max <= self.num_streams {
self.blocked_open = Some(task::current());
return Async::NotReady;
}
}
return Async::Ready(());
}
}

View File

@@ -1,8 +1,8 @@
use ConnectionError;
use proto::ProtoError;
use error::Reason;
use error::Reason::*;
use error::User::*;
use frame::Reason;
use frame::Reason::*;
use codec::{RecvError, UserError};
use codec::UserError::*;
use proto;
use self::Inner::*;
use self::Peer::*;
@@ -82,7 +82,7 @@ enum Cause {
impl State {
/// Opens the send-half of a stream if it is not already open.
pub fn send_open(&mut self, eos: bool) -> Result<(), ConnectionError> {
pub fn send_open(&mut self, eos: bool) -> Result<(), UserError> {
let local = Peer::Streaming;
self.inner = match self.inner {
@@ -115,7 +115,7 @@ impl State {
}
_ => {
// All other transitions result in a protocol error
return Err(UnexpectedFrameType.into());
return Err(UnexpectedFrameType);
}
};
@@ -126,7 +126,7 @@ impl State {
/// frame is received.
///
/// Returns true if this transitions the state to Open
pub fn recv_open(&mut self, eos: bool) -> Result<bool, ProtoError> {
pub fn recv_open(&mut self, eos: bool) -> Result<bool, RecvError> {
let remote = Peer::Streaming;
let mut initial = false;
@@ -174,7 +174,7 @@ impl State {
}
_ => {
// All other transitions result in a protocol error
return Err(ProtoError::Connection(ProtocolError));
return Err(RecvError::Connection(ProtocolError));
}
};
@@ -182,18 +182,18 @@ impl State {
}
/// Transition from Idle -> ReservedRemote
pub fn reserve_remote(&mut self) -> Result<(), ConnectionError> {
pub fn reserve_remote(&mut self) -> Result<(), RecvError> {
match self.inner {
Idle => {
self.inner = ReservedRemote;
Ok(())
}
_ => Err(ProtocolError.into()),
_ => Err(RecvError::Connection(ProtocolError)),
}
}
/// Indicates that the remote side will not send more data to the local.
pub fn recv_close(&mut self) -> Result<(), ProtoError> {
pub fn recv_close(&mut self) -> Result<(), RecvError> {
match self.inner {
Open { local, .. } => {
// The remote side will continue to receive data.
@@ -206,39 +206,38 @@ impl State {
self.inner = Closed(None);
Ok(())
}
_ => Err(ProtoError::Connection(ProtocolError)),
_ => Err(RecvError::Connection(ProtocolError)),
}
}
pub fn recv_err(&mut self, err: &ConnectionError) {
pub fn recv_err(&mut self, err: &proto::Error) {
use proto::Error::*;
match self.inner {
Closed(..) => {}
_ => {
trace!("recv_err; err={:?}", err);
self.inner = Closed(match *err {
ConnectionError::Proto(reason) => Some(Cause::Proto(reason)),
ConnectionError::Io(..) => Some(Cause::Io),
ref e => panic!("cannot terminate stream with user error; err={:?}", e),
Proto(reason) => Some(Cause::Proto(reason)),
Io(..) => Some(Cause::Io),
});
}
}
}
/// Indicates that the local side will not send more data to the local.
pub fn send_close(&mut self) -> Result<(), ConnectionError> {
pub fn send_close(&mut self) {
match self.inner {
Open { remote, .. } => {
// The remote side will continue to receive data.
trace!("send_close: Open => HalfClosedLocal({:?})", remote);
self.inner = HalfClosedLocal(remote);
Ok(())
}
HalfClosedRemote(..) => {
trace!("send_close: HalfClosedRemote => Closed");
self.inner = Closed(None);
Ok(())
}
_ => Err(ProtocolError.into()),
_ => panic!("transition send_close on unexpected state"),
}
}
@@ -307,16 +306,16 @@ impl State {
}
}
pub fn ensure_recv_open(&self) -> Result<(), ConnectionError> {
pub fn ensure_recv_open(&self) -> Result<(), proto::Error> {
use std::io;
// TODO: Is this correct?
match self.inner {
Closed(Some(Cause::Proto(reason))) => {
Err(ConnectionError::Proto(reason))
Err(proto::Error::Proto(reason))
}
Closed(Some(Cause::Io)) => {
Err(ConnectionError::Io(io::ErrorKind::BrokenPipe.into()))
Err(proto::Error::Io(io::ErrorKind::BrokenPipe.into()))
}
_ => Ok(()),
}

View File

@@ -127,8 +127,8 @@ impl<B, P> Store<B, P>
}
}
pub fn for_each<F>(&mut self, mut f: F) -> Result<(), ConnectionError>
where F: FnMut(Ptr<B, P>) -> Result<(), ConnectionError>,
pub fn for_each<F, E>(&mut self, mut f: F) -> Result<(), E>
where F: FnMut(Ptr<B, P>) -> Result<(), E>,
{
for &key in self.ids.values() {
f(Ptr {

View File

@@ -1,8 +1,13 @@
use {client, server, HeaderMap};
use {client, server, proto};
use frame::Reason;
use codec::{SendError, RecvError, UserError};
use proto::*;
use super::*;
use super::store::Resolve;
use http::HeaderMap;
use std::io;
use std::sync::{Arc, Mutex};
#[derive(Debug)]
@@ -66,7 +71,7 @@ impl<B, P> Streams<B, P>
/// Process inbound headers
pub fn recv_headers(&mut self, frame: frame::Headers)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
let id = frame.stream_id();
let mut me = self.inner.lock().unwrap();
@@ -97,7 +102,7 @@ impl<B, P> Streams<B, P>
} else {
if !frame.is_end_stream() {
// TODO: Is this the right error
return Err(ProtocolError.into());
return Err(RecvError::Connection(ProtocolError));
}
actions.recv.recv_trailers(frame, stream)
@@ -105,20 +110,18 @@ impl<B, P> Streams<B, P>
// TODO: extract this
match res {
Ok(()) => Ok(()),
Err(ProtoError::Connection(reason)) => Err(reason.into()),
Err(ProtoError::Stream { reason, .. }) => {
Err(RecvError::Stream { reason, .. }) => {
// Reset the stream.
actions.send.send_reset(reason, stream, &mut actions.task);
Ok(())
}
Err(ProtoError::Io(_)) => unreachable!(),
res => res,
}
})
}
pub fn recv_data(&mut self, frame: frame::Data)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -127,25 +130,23 @@ impl<B, P> Streams<B, P>
let stream = match me.store.find_mut(&id) {
Some(stream) => stream,
None => return Err(ProtocolError.into()),
None => return Err(RecvError::Connection(ProtocolError)),
};
me.actions.transition(stream, |actions, stream| {
match actions.recv.recv_data(frame, stream) {
Ok(()) => Ok(()),
Err(ProtoError::Connection(reason)) => Err(reason.into()),
Err(ProtoError::Stream { reason, .. }) => {
Err(RecvError::Stream { reason, .. }) => {
// Reset the stream.
actions.send.send_reset(reason, stream, &mut actions.task);
Ok(())
}
Err(ProtoError::Io(_)) => unreachable!(),
res => res,
}
})
}
pub fn recv_reset(&mut self, frame: frame::Reset)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -153,14 +154,16 @@ impl<B, P> Streams<B, P>
let id = frame.stream_id();
if id.is_zero() {
return Err(ProtocolError.into());
return Err(RecvError::Connection(ProtocolError));
}
let stream = match me.store.find_mut(&id) {
Some(stream) => stream,
None => {
// TODO: Are there other error cases?
me.actions.ensure_not_idle(id)?;
me.actions.ensure_not_idle(id)
.map_err(RecvError::Connection)?;
return Ok(());
}
};
@@ -173,7 +176,7 @@ impl<B, P> Streams<B, P>
}
/// Handle a received error and return the ID of the last processed stream.
pub fn recv_err(&mut self, err: &ConnectionError) -> StreamId {
pub fn recv_err(&mut self, err: &proto::Error) -> StreamId {
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -182,14 +185,14 @@ impl<B, P> Streams<B, P>
me.store.for_each(|mut stream| {
actions.recv.recv_err(err, &mut *stream);
Ok(())
Ok::<_, ()>(())
}).ok().expect("unexpected error processing error");
last_processed_id
}
pub fn recv_window_update(&mut self, frame: frame::WindowUpdate)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
let id = frame.stream_id();
let mut me = self.inner.lock().unwrap();
@@ -197,15 +200,22 @@ impl<B, P> Streams<B, P>
if id.is_zero() {
me.actions.send.recv_connection_window_update(
frame, &mut me.store)?;
frame, &mut me.store)
.map_err(RecvError::Connection)?;
} else {
// The remote may send window updates for streams that the local now
// considers closed. It's ok...
if let Some(mut stream) = me.store.find_mut(&id) {
me.actions.send.recv_stream_window_update(
frame.size_increment(), &mut stream, &mut me.actions.task)?;
// This result is ignored as there is nothing to do when there
// is an error. The stream is reset by the function on error and
// the error is informational.
let _ = me.actions.send.recv_stream_window_update(
frame.size_increment(),
&mut stream,
&mut me.actions.task);
} else {
me.actions.recv.ensure_not_idle(id)?;
me.actions.recv.ensure_not_idle(id)
.map_err(RecvError::Connection)?;
}
}
@@ -213,7 +223,7 @@ impl<B, P> Streams<B, P>
}
pub fn recv_push_promise(&mut self, frame: frame::PushPromise)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -222,7 +232,7 @@ impl<B, P> Streams<B, P>
let stream = match me.store.find_mut(&id) {
Some(stream) => stream.key(),
None => return Err(ProtocolError.into()),
None => return Err(RecvError::Connection(ProtocolError)),
};
me.actions.recv.recv_push_promise(
@@ -246,7 +256,7 @@ impl<B, P> Streams<B, P>
}
pub fn send_pending_refusal<T>(&mut self, dst: &mut Codec<T, Prioritized<B>>)
-> Poll<(), ConnectionError>
-> Poll<(), io::Error>
where T: AsyncWrite,
{
let mut me = self.inner.lock().unwrap();
@@ -255,7 +265,7 @@ impl<B, P> Streams<B, P>
}
pub fn poll_complete<T>(&mut self, dst: &mut Codec<T, Prioritized<B>>)
-> Poll<(), ConnectionError>
-> Poll<(), io::Error>
where T: AsyncWrite,
{
let mut me = self.inner.lock().unwrap();
@@ -277,7 +287,7 @@ impl<B, P> Streams<B, P>
}
pub fn apply_remote_settings(&mut self, frame: &frame::Settings)
-> Result<(), ConnectionError>
-> Result<(), RecvError>
{
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -286,15 +296,8 @@ impl<B, P> Streams<B, P>
frame, &mut me.store, &mut me.actions.task)
}
pub fn poll_send_request_ready(&mut self) -> Poll<(), ConnectionError> {
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
me.actions.send.poll_open_ready()
}
pub fn send_request(&mut self, request: Request<()>, end_of_stream: bool)
-> Result<StreamRef<B, P>, ConnectionError>
-> Result<StreamRef<B, P>, SendError>
{
use http::method;
use super::stream::ContentLength;
@@ -370,6 +373,17 @@ impl<B, P> Streams<B, P>
}
}
impl<B> Streams<B, client::Peer>
where B: Buf,
{
pub fn poll_send_request_ready(&mut self) -> Async<()> {
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
me.actions.send.poll_open_ready()
}
}
// ===== impl StreamRef =====
impl<B, P> StreamRef<B, P>
@@ -377,7 +391,7 @@ impl<B, P> StreamRef<B, P>
P: Peer,
{
pub fn send_data(&mut self, data: B, end_of_stream: bool)
-> Result<(), ConnectionError>
-> Result<(), UserError>
{
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -393,7 +407,8 @@ impl<B, P> StreamRef<B, P>
})
}
pub fn send_trailers(&mut self, trailers: HeaderMap) -> Result<(), ConnectionError>
pub fn send_trailers(&mut self, trailers: HeaderMap)
-> Result<(), UserError>
{
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -420,7 +435,7 @@ impl<B, P> StreamRef<B, P>
}
pub fn send_response(&mut self, response: Response<()>, end_of_stream: bool)
-> Result<(), ConnectionError>
-> Result<(), UserError>
{
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -444,7 +459,7 @@ impl<B, P> StreamRef<B, P>
me.actions.recv.body_is_empty(&stream)
}
pub fn poll_data(&mut self) -> Poll<Option<Bytes>, ConnectionError> {
pub fn poll_data(&mut self) -> Poll<Option<Bytes>, proto::Error> {
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -453,7 +468,7 @@ impl<B, P> StreamRef<B, P>
me.actions.recv.poll_data(&mut stream)
}
pub fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, ConnectionError> {
pub fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, proto::Error> {
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -465,7 +480,7 @@ impl<B, P> StreamRef<B, P>
/// Releases recv capacity back to the peer. This will result in sending
/// WINDOW_UPDATE frames on both the stream and connection.
pub fn release_capacity(&mut self, capacity: WindowSize)
-> Result<(), ConnectionError>
-> Result<(), UserError>
{
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -497,7 +512,7 @@ impl<B, P> StreamRef<B, P>
}
/// Request to be notified when the stream's capacity increases
pub fn poll_capacity(&mut self) -> Poll<Option<WindowSize>, ConnectionError> {
pub fn poll_capacity(&mut self) -> Poll<Option<WindowSize>, UserError> {
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -517,7 +532,7 @@ impl<B> StreamRef<B, server::Peer>
/// # Panics
///
/// This function panics if the request isn't present.
pub fn take_request(&self) -> Result<Request<()>, ConnectionError> {
pub fn take_request(&self) -> Request<()> {
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -529,7 +544,7 @@ impl<B> StreamRef<B, server::Peer>
impl<B> StreamRef<B, client::Peer>
where B: Buf,
{
pub fn poll_response(&mut self) -> Poll<Response<()>, ConnectionError> {
pub fn poll_response(&mut self) -> Poll<Response<()>, proto::Error> {
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
@@ -557,7 +572,7 @@ impl<B, P> Actions<B, P>
P: Peer,
{
fn ensure_not_idle(&mut self, id: StreamId)
-> Result<(), ConnectionError>
-> Result<(), Reason>
{
if self.is_local_init(id) {
self.send.ensure_not_idle(id)