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:
121
src/codec/error.rs
Normal file
121
src/codec/error.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
use frame::{Reason, StreamId};
|
||||
|
||||
use std::{error, fmt, io};
|
||||
|
||||
/// Errors that are received
|
||||
#[derive(Debug)]
|
||||
pub enum RecvError {
|
||||
Connection(Reason),
|
||||
Stream {
|
||||
id: StreamId,
|
||||
reason: Reason,
|
||||
},
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
/// Errors caused by sending a message
|
||||
#[derive(Debug)]
|
||||
pub enum SendError {
|
||||
/// User error
|
||||
User(UserError),
|
||||
|
||||
/// I/O error
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
/// Errors caused by users of the library
|
||||
#[derive(Debug)]
|
||||
pub enum UserError {
|
||||
/// The stream ID is no longer accepting frames.
|
||||
InactiveStreamId,
|
||||
|
||||
/// The stream is not currently expecting a frame of this type.
|
||||
UnexpectedFrameType,
|
||||
|
||||
/// The payload size is too big
|
||||
PayloadTooBig,
|
||||
|
||||
/// The application attempted to initiate too many streams to remote.
|
||||
Rejected,
|
||||
}
|
||||
|
||||
// ===== impl RecvError =====
|
||||
|
||||
impl From<io::Error> for RecvError {
|
||||
fn from(src: io::Error) -> Self {
|
||||
RecvError::Io(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for RecvError {
|
||||
fn description(&self) -> &str {
|
||||
use self::RecvError::*;
|
||||
|
||||
match *self {
|
||||
Connection(ref reason) => reason.description(),
|
||||
Stream { ref reason, .. } => reason.description(),
|
||||
Io(ref e) => e.description(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RecvError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl SendError =====
|
||||
|
||||
impl error::Error for SendError {
|
||||
fn description(&self) -> &str {
|
||||
use self::SendError::*;
|
||||
|
||||
match *self {
|
||||
User(ref e) => e.description(),
|
||||
Io(ref e) => e.description(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SendError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for SendError {
|
||||
fn from(src: io::Error) -> Self {
|
||||
SendError::Io(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UserError> for SendError {
|
||||
fn from(src: UserError) -> Self {
|
||||
SendError::User(src)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl UserError =====
|
||||
|
||||
impl error::Error for UserError {
|
||||
fn description(&self) -> &str {
|
||||
use self::UserError::*;
|
||||
|
||||
match *self {
|
||||
InactiveStreamId => "inactive stream",
|
||||
UnexpectedFrameType => "unexpected frame type",
|
||||
PayloadTooBig => "payload too big",
|
||||
Rejected => "rejected",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UserError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
248
src/codec/framed_read.rs
Normal file
248
src/codec/framed_read.rs
Normal file
@@ -0,0 +1,248 @@
|
||||
use codec::RecvError;
|
||||
use frame::{self, Frame, Kind};
|
||||
use frame::DEFAULT_SETTINGS_HEADER_TABLE_SIZE;
|
||||
use frame::Reason::*;
|
||||
|
||||
use hpack;
|
||||
|
||||
use futures::*;
|
||||
|
||||
use bytes::BytesMut;
|
||||
|
||||
use tokio_io::AsyncRead;
|
||||
use tokio_io::codec::length_delimited;
|
||||
|
||||
#[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>, RecvError> {
|
||||
use self::RecvError::*;
|
||||
|
||||
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> Stream for FramedRead<T>
|
||||
where T: AsyncRead,
|
||||
{
|
||||
type Item = Frame;
|
||||
type Error = RecvError;
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
260
src/codec/framed_write.rs
Normal file
260
src/codec/framed_write.rs
Normal file
@@ -0,0 +1,260 @@
|
||||
use codec::UserError;
|
||||
use codec::UserError::*;
|
||||
use frame::{self, Frame, FrameSize};
|
||||
use hpack;
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Ready` when `send` is able to accept a frame
|
||||
///
|
||||
/// Calling this function may result in the current contents of the buffer
|
||||
/// to be flushed to `T`.
|
||||
pub fn poll_ready(&mut self) -> Poll<(), io::Error> {
|
||||
if !self.has_capacity() {
|
||||
// Try flushing
|
||||
try!(self.flush());
|
||||
|
||||
if !self.has_capacity() {
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
|
||||
/// Buffer a frame.
|
||||
///
|
||||
/// `poll_ready` must be called first to ensure that a frame may be
|
||||
/// accepted.
|
||||
pub fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError> {
|
||||
// Ensure that we have enough capacity to accept the write.
|
||||
assert!(self.has_capacity());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
/// Flush buffered data to the wire
|
||||
pub fn flush(&mut self) -> Poll<(), io::Error> {
|
||||
trace!("flush");
|
||||
|
||||
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(()))
|
||||
}
|
||||
|
||||
/// Close the codec
|
||||
pub fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
try_ready!(self.flush());
|
||||
self.inner.shutdown().map_err(Into::into)
|
||||
}
|
||||
|
||||
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> {
|
||||
/// Returns the max frame size that can be sent
|
||||
pub fn max_frame_size(&self) -> usize {
|
||||
self.max_frame_size as usize
|
||||
}
|
||||
|
||||
/// Apply settings received by the peer
|
||||
pub fn apply_remote_settings(&mut self, settings: &frame::Settings) {
|
||||
if let Some(val) = settings.max_frame_size() {
|
||||
self.max_frame_size = val;
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the last data frame that has been sent
|
||||
pub fn take_last_data_frame(&mut self) -> Option<frame::Data<B>> {
|
||||
self.last_data_frame.take()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
148
src/codec/mod.rs
Normal file
148
src/codec/mod.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
mod error;
|
||||
mod framed_read;
|
||||
mod framed_write;
|
||||
|
||||
pub use self::error::{SendError, RecvError, UserError};
|
||||
|
||||
use self::framed_read::FramedRead;
|
||||
use self::framed_write::FramedWrite;
|
||||
|
||||
use frame::{self, Frame, Data, Settings};
|
||||
|
||||
use futures::*;
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_io::codec::length_delimited;
|
||||
|
||||
use bytes::Buf;
|
||||
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Codec<T, B> {
|
||||
inner: FramedRead<FramedWrite<T, B>>,
|
||||
}
|
||||
|
||||
impl<T, B> Codec<T, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
B: Buf,
|
||||
{
|
||||
pub fn new(io: T) -> Self {
|
||||
// Wrap with writer
|
||||
let framed_write = FramedWrite::new(io);
|
||||
|
||||
// Delimit the frames
|
||||
let delimited = 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 inner = FramedRead::new(delimited);
|
||||
|
||||
Codec { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> Codec<T, B> {
|
||||
/// Apply a settings received from the peer
|
||||
pub fn apply_remote_settings(&mut self, 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 fn take_last_data_frame(&mut self) -> Option<Data<B>> {
|
||||
self.framed_write().take_last_data_frame()
|
||||
}
|
||||
|
||||
/// Returns the max frame size that can be sent to the peer
|
||||
pub fn max_send_frame_size(&self) -> usize {
|
||||
self.inner.get_ref().max_frame_size()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.inner.get_mut().get_mut()
|
||||
}
|
||||
|
||||
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: AsyncWrite,
|
||||
B: Buf,
|
||||
{
|
||||
/// Returns `Ready` when the codec can buffer a frame
|
||||
pub fn poll_ready(&mut self) -> Poll<(), io::Error> {
|
||||
self.framed_write().poll_ready()
|
||||
}
|
||||
|
||||
/// Buffer a frame.
|
||||
///
|
||||
/// `poll_ready` must be called first to ensure that a frame may be
|
||||
/// accepted.
|
||||
///
|
||||
/// TODO: Rename this to avoid conflicts with Sink::buffer
|
||||
pub fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError>
|
||||
{
|
||||
self.framed_write().buffer(item)
|
||||
}
|
||||
|
||||
/// Flush buffered data to the wire
|
||||
pub fn flush(&mut self) -> Poll<(), io::Error> {
|
||||
self.framed_write().flush()
|
||||
}
|
||||
|
||||
/// Shutdown the send half
|
||||
pub fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
self.framed_write().shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> Stream for Codec<T, B>
|
||||
where T: AsyncRead,
|
||||
{
|
||||
type Item = Frame;
|
||||
type Error = RecvError;
|
||||
|
||||
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 = SendError;
|
||||
|
||||
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
|
||||
if !self.poll_ready()?.is_ready() {
|
||||
return Ok(AsyncSink::NotReady(item));
|
||||
}
|
||||
|
||||
self.buffer(item)?;
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.flush()?;
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.shutdown()?;
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user