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:
@@ -8,7 +8,7 @@ futures = "0.1"
|
|||||||
tokio-io = "0.1.3"
|
tokio-io = "0.1.3"
|
||||||
tokio-timer = "0.1"
|
tokio-timer = "0.1"
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
http = { git = "https://github.com/carllerche/http", branch = "lower-case-header-name-parsing" }
|
http = { git = "https://github.com/carllerche/http", rev = "2dd15d9" }
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
log = "0.3.8"
|
log = "0.3.8"
|
||||||
fnv = "1.0.5"
|
fnv = "1.0.5"
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ struct Process {
|
|||||||
|
|
||||||
impl Future for Process {
|
impl Future for Process {
|
||||||
type Item = ();
|
type Item = ();
|
||||||
type Error = ConnectionError;
|
type Error = h2::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<(), ConnectionError> {
|
fn poll(&mut self) -> Poll<(), h2::Error> {
|
||||||
loop {
|
loop {
|
||||||
if self.trailers {
|
if self.trailers {
|
||||||
let trailers = try_ready!(self.body.poll_trailers());
|
let trailers = try_ready!(self.body.poll_trailers());
|
||||||
@@ -71,7 +71,7 @@ pub fn main() {
|
|||||||
.uri("https://http2.akamai.com/")
|
.uri("https://http2.akamai.com/")
|
||||||
.body(()).unwrap();
|
.body(()).unwrap();
|
||||||
|
|
||||||
let mut trailers = h2::HeaderMap::new();
|
let mut trailers = HeaderMap::new();
|
||||||
trailers.insert("zomg", "hello".parse().unwrap());
|
trailers.insert("zomg", "hello".parse().unwrap());
|
||||||
|
|
||||||
let mut stream = client.request(request, false).unwrap();
|
let mut stream = client.request(request, false).unwrap();
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use {frame, HeaderMap, ConnectionError};
|
use frame::{StreamId, Headers, Pseudo, Settings};
|
||||||
use frame::StreamId;
|
use frame::Reason::*;
|
||||||
use proto::{self, Connection, WindowSize, ProtoError};
|
use codec::{Codec, RecvError};
|
||||||
use error::Reason::*;
|
use proto::{self, Connection, WindowSize};
|
||||||
|
|
||||||
use http::{Request, Response};
|
use http::{Request, Response, HeaderMap};
|
||||||
use futures::{Future, Poll, Sink, Async, AsyncSink, AndThen, MapErr};
|
use futures::{Future, Poll, Sink, Async, AsyncSink, AndThen, MapErr};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
use tokio_io::io::WriteAll;
|
use tokio_io::io::WriteAll;
|
||||||
@@ -16,9 +16,9 @@ use std::io::Error as IoError;
|
|||||||
pub struct Handshake<T: AsyncRead + AsyncWrite, B: IntoBuf = Bytes> {
|
pub struct Handshake<T: AsyncRead + AsyncWrite, B: IntoBuf = Bytes> {
|
||||||
inner:
|
inner:
|
||||||
AndThen<
|
AndThen<
|
||||||
MapErr<WriteAll<T, &'static [u8]>, fn(IoError) -> ConnectionError>,
|
MapErr<WriteAll<T, &'static [u8]>, fn(IoError) -> ::Error>,
|
||||||
Result<Client<T, B>, ConnectionError>,
|
Result<Client<T, B>, ::Error>,
|
||||||
fn((T, &'static [u8])) -> Result<Client<T, B>, ConnectionError>
|
fn((T, &'static [u8])) -> Result<Client<T, B>, ::Error>
|
||||||
>
|
>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -63,28 +63,31 @@ impl<T, B> Client<T, B>
|
|||||||
debug!("binding client connection");
|
debug!("binding client connection");
|
||||||
|
|
||||||
let bind: fn((T, &'static [u8]))
|
let bind: fn((T, &'static [u8]))
|
||||||
-> Result<Client<T, B>, ConnectionError> =
|
-> Result<Client<T, B>, ::Error> =
|
||||||
|(io, _)| {
|
|(io, _)| {
|
||||||
debug!("client connection bound");
|
debug!("client connection bound");
|
||||||
|
|
||||||
let mut framed_write = proto::framed_write(io);
|
// Create the codec
|
||||||
let settings = frame::Settings::default();
|
let mut codec = Codec::new(io);
|
||||||
|
|
||||||
|
// Create the initial SETTINGS frame
|
||||||
|
let settings = Settings::default();
|
||||||
|
|
||||||
// Send initial settings frame
|
// Send initial settings frame
|
||||||
match framed_write.start_send(settings.into()) {
|
match codec.start_send(settings.into()) {
|
||||||
Ok(AsyncSink::Ready) => {
|
Ok(AsyncSink::Ready) => {
|
||||||
let connection = proto::from_framed_write(framed_write);
|
let connection = Connection::new(codec);
|
||||||
Ok(Client { connection })
|
Ok(Client { connection })
|
||||||
}
|
}
|
||||||
Ok(_) => unreachable!(),
|
Ok(_) => unreachable!(),
|
||||||
Err(e) => Err(ConnectionError::from(e)),
|
Err(e) => Err(::Error::from(e)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let msg: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
let msg: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
||||||
let handshake = io::write_all(io, msg)
|
let handshake = io::write_all(io, msg)
|
||||||
.map_err(ConnectionError::from as
|
.map_err(::Error::from as
|
||||||
fn(IoError) -> ConnectionError
|
fn(IoError) -> ::Error
|
||||||
)
|
)
|
||||||
.and_then(bind);
|
.and_then(bind);
|
||||||
|
|
||||||
@@ -93,15 +96,16 @@ impl<T, B> Client<T, B>
|
|||||||
|
|
||||||
/// Returns `Ready` when the connection can initialize a new HTTP 2.0
|
/// Returns `Ready` when the connection can initialize a new HTTP 2.0
|
||||||
/// stream.
|
/// stream.
|
||||||
pub fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
pub fn poll_ready(&mut self) -> Poll<(), ::Error> {
|
||||||
self.connection.poll_send_request_ready()
|
Ok(self.connection.poll_send_request_ready())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a request on a new HTTP 2.0 stream
|
/// Send a request on a new HTTP 2.0 stream
|
||||||
pub fn request(&mut self, request: Request<()>, end_of_stream: bool)
|
pub fn request(&mut self, request: Request<()>, end_of_stream: bool)
|
||||||
-> Result<Stream<B>, ConnectionError>
|
-> Result<Stream<B>, ::Error>
|
||||||
{
|
{
|
||||||
self.connection.send_request(request, end_of_stream)
|
self.connection.send_request(request, end_of_stream)
|
||||||
|
.map_err(Into::into)
|
||||||
.map(|stream| Stream {
|
.map(|stream| Stream {
|
||||||
inner: stream,
|
inner: stream,
|
||||||
})
|
})
|
||||||
@@ -114,10 +118,11 @@ impl<T, B> Future for Client<T, B>
|
|||||||
B: IntoBuf,
|
B: IntoBuf,
|
||||||
{
|
{
|
||||||
type Item = ();
|
type Item = ();
|
||||||
type Error = ConnectionError;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<(), ConnectionError> {
|
fn poll(&mut self) -> Poll<(), ::Error> {
|
||||||
self.connection.poll()
|
self.connection.poll()
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +144,7 @@ impl<T, B> fmt::Debug for Client<T, B>
|
|||||||
impl<T, B: IntoBuf> Future for Handshake<T, B>
|
impl<T, B: IntoBuf> Future for Handshake<T, B>
|
||||||
where T: AsyncRead + AsyncWrite {
|
where T: AsyncRead + AsyncWrite {
|
||||||
type Item = Client<T, B>;
|
type Item = Client<T, B>;
|
||||||
type Error = ConnectionError;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
self.inner.poll()
|
self.inner.poll()
|
||||||
@@ -161,7 +166,7 @@ impl<T, B> fmt::Debug for Handshake<T, B>
|
|||||||
|
|
||||||
impl<B: IntoBuf> Stream<B> {
|
impl<B: IntoBuf> Stream<B> {
|
||||||
/// Receive the HTTP/2.0 response, if it is ready.
|
/// Receive the HTTP/2.0 response, if it is ready.
|
||||||
pub fn poll_response(&mut self) -> Poll<Response<Body<B>>, ConnectionError> {
|
pub fn poll_response(&mut self) -> Poll<Response<Body<B>>, ::Error> {
|
||||||
let (parts, _) = try_ready!(self.inner.poll_response()).into_parts();
|
let (parts, _) = try_ready!(self.inner.poll_response()).into_parts();
|
||||||
let body = Body { inner: self.inner.clone() };
|
let body = Body { inner: self.inner.clone() };
|
||||||
|
|
||||||
@@ -180,29 +185,31 @@ impl<B: IntoBuf> Stream<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Request to be notified when the stream's capacity increases
|
/// Request to be notified when the stream's capacity increases
|
||||||
pub fn poll_capacity(&mut self) -> Poll<Option<usize>, ConnectionError> {
|
pub fn poll_capacity(&mut self) -> Poll<Option<usize>, ::Error> {
|
||||||
let res = try_ready!(self.inner.poll_capacity());
|
let res = try_ready!(self.inner.poll_capacity());
|
||||||
Ok(Async::Ready(res.map(|v| v as usize)))
|
Ok(Async::Ready(res.map(|v| v as usize)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send data
|
/// Send data
|
||||||
pub fn send_data(&mut self, data: B, end_of_stream: bool)
|
pub fn send_data(&mut self, data: B, end_of_stream: bool)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), ::Error>
|
||||||
{
|
{
|
||||||
self.inner.send_data(data.into_buf(), end_of_stream)
|
self.inner.send_data(data.into_buf(), end_of_stream)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send trailers
|
/// Send trailers
|
||||||
pub fn send_trailers(&mut self, trailers: HeaderMap)
|
pub fn send_trailers(&mut self, trailers: HeaderMap)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), ::Error>
|
||||||
{
|
{
|
||||||
self.inner.send_trailers(trailers)
|
self.inner.send_trailers(trailers)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: IntoBuf> Future for Stream<B> {
|
impl<B: IntoBuf> Future for Stream<B> {
|
||||||
type Item = Response<Body<B>>;
|
type Item = Response<Body<B>>;
|
||||||
type Error = ConnectionError;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
self.poll_response()
|
self.poll_response()
|
||||||
@@ -217,24 +224,27 @@ impl<B: IntoBuf> Body<B> {
|
|||||||
self.inner.body_is_empty()
|
self.inner.body_is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release_capacity(&mut self, sz: usize) -> Result<(), ConnectionError> {
|
pub fn release_capacity(&mut self, sz: usize) -> Result<(), ::Error> {
|
||||||
self.inner.release_capacity(sz as proto::WindowSize)
|
self.inner.release_capacity(sz as proto::WindowSize)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Poll trailers
|
/// Poll trailers
|
||||||
///
|
///
|
||||||
/// This function **must** not be called until `Body::poll` returns `None`.
|
/// This function **must** not be called until `Body::poll` returns `None`.
|
||||||
pub fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, ConnectionError> {
|
pub fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, ::Error> {
|
||||||
self.inner.poll_trailers()
|
self.inner.poll_trailers()
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: IntoBuf> ::futures::Stream for Body<B> {
|
impl<B: IntoBuf> ::futures::Stream for Body<B> {
|
||||||
type Item = Bytes;
|
type Item = Bytes;
|
||||||
type Error = ConnectionError;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||||
self.inner.poll_data()
|
self.inner.poll_data()
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +261,7 @@ impl proto::Peer for Peer {
|
|||||||
fn convert_send_message(
|
fn convert_send_message(
|
||||||
id: StreamId,
|
id: StreamId,
|
||||||
request: Self::Send,
|
request: Self::Send,
|
||||||
end_of_stream: bool) -> frame::Headers
|
end_of_stream: bool) -> Headers
|
||||||
{
|
{
|
||||||
use http::request::Parts;
|
use http::request::Parts;
|
||||||
|
|
||||||
@@ -259,10 +269,10 @@ impl proto::Peer for Peer {
|
|||||||
|
|
||||||
// Build the set pseudo header set. All requests will include `method`
|
// Build the set pseudo header set. All requests will include `method`
|
||||||
// and `path`.
|
// and `path`.
|
||||||
let pseudo = frame::Pseudo::request(method, uri);
|
let pseudo = Pseudo::request(method, uri);
|
||||||
|
|
||||||
// Create the HEADERS frame
|
// Create the HEADERS frame
|
||||||
let mut frame = frame::Headers::new(id, pseudo, headers);
|
let mut frame = Headers::new(id, pseudo, headers);
|
||||||
|
|
||||||
if end_of_stream {
|
if end_of_stream {
|
||||||
frame.set_end_stream()
|
frame.set_end_stream()
|
||||||
@@ -271,7 +281,7 @@ impl proto::Peer for Peer {
|
|||||||
frame
|
frame
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_poll_message(headers: frame::Headers) -> Result<Self::Poll, ProtoError> {
|
fn convert_poll_message(headers: Headers) -> Result<Self::Poll, RecvError> {
|
||||||
let mut b = Response::builder();
|
let mut b = Response::builder();
|
||||||
|
|
||||||
let stream_id = headers.stream_id();
|
let stream_id = headers.stream_id();
|
||||||
@@ -286,7 +296,7 @@ impl proto::Peer for Peer {
|
|||||||
Err(_) => {
|
Err(_) => {
|
||||||
// TODO: Should there be more specialized handling for different
|
// TODO: Should there be more specialized handling for different
|
||||||
// kinds of errors
|
// kinds of errors
|
||||||
return Err(ProtoError::Stream {
|
return Err(RecvError::Stream {
|
||||||
id: stream_id,
|
id: stream_id,
|
||||||
reason: ProtocolError,
|
reason: ProtocolError,
|
||||||
});
|
});
|
||||||
|
|||||||
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
use {hpack, ConnectionError};
|
use codec::RecvError;
|
||||||
use frame::{self, Frame, Kind};
|
use frame::{self, Frame, Kind};
|
||||||
use frame::DEFAULT_SETTINGS_HEADER_TABLE_SIZE;
|
use frame::DEFAULT_SETTINGS_HEADER_TABLE_SIZE;
|
||||||
use proto::*;
|
use frame::Reason::*;
|
||||||
use error::Reason::*;
|
|
||||||
|
use hpack;
|
||||||
|
|
||||||
use futures::*;
|
use futures::*;
|
||||||
|
|
||||||
@@ -11,8 +12,6 @@ use bytes::BytesMut;
|
|||||||
use tokio_io::AsyncRead;
|
use tokio_io::AsyncRead;
|
||||||
use tokio_io::codec::length_delimited;
|
use tokio_io::codec::length_delimited;
|
||||||
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FramedRead<T> {
|
pub struct FramedRead<T> {
|
||||||
inner: length_delimited::FramedRead<T>,
|
inner: length_delimited::FramedRead<T>,
|
||||||
@@ -54,8 +53,8 @@ impl<T> FramedRead<T> {
|
|||||||
// TODO: Is this needed?
|
// TODO: Is this needed?
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_frame(&mut self, mut bytes: BytesMut) -> Result<Option<Frame>, ProtoError> {
|
fn decode_frame(&mut self, mut bytes: BytesMut) -> Result<Option<Frame>, RecvError> {
|
||||||
use self::ProtoError::*;
|
use self::RecvError::*;
|
||||||
|
|
||||||
trace!("decoding frame from {}B", bytes.len());
|
trace!("decoding frame from {}B", bytes.len());
|
||||||
|
|
||||||
@@ -226,11 +225,11 @@ impl<T> FramedRead<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> futures::Stream for FramedRead<T>
|
impl<T> Stream for FramedRead<T>
|
||||||
where T: AsyncRead,
|
where T: AsyncRead,
|
||||||
{
|
{
|
||||||
type Item = Frame;
|
type Item = Frame;
|
||||||
type Error = ProtoError;
|
type Error = RecvError;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Frame>, Self::Error> {
|
fn poll(&mut self) -> Poll<Option<Frame>, Self::Error> {
|
||||||
loop {
|
loop {
|
||||||
@@ -247,32 +246,3 @@ impl<T> futures::Stream for FramedRead<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use {hpack, ConnectionError};
|
use codec::UserError;
|
||||||
use error::User::*;
|
use codec::UserError::*;
|
||||||
use frame::{self, Frame, FrameSize};
|
use frame::{self, Frame, FrameSize};
|
||||||
|
use hpack;
|
||||||
|
|
||||||
use futures::*;
|
use futures::*;
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
@@ -64,10 +65,14 @@ impl<T, B> FramedWrite<T, B>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
/// 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() {
|
if !self.has_capacity() {
|
||||||
// Try flushing
|
// Try flushing
|
||||||
try!(self.poll_complete());
|
try!(self.flush());
|
||||||
|
|
||||||
if !self.has_capacity() {
|
if !self.has_capacity() {
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::NotReady);
|
||||||
@@ -77,47 +82,13 @@ impl<T, B> FramedWrite<T, B>
|
|||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_capacity(&self) -> bool {
|
/// Buffer a frame.
|
||||||
self.next.is_none() && self.buf.get_ref().remaining_mut() >= MIN_BUFFER_CAPACITY
|
///
|
||||||
}
|
/// `poll_ready` must be called first to ensure that a frame may be
|
||||||
|
/// accepted.
|
||||||
fn is_empty(&self) -> bool {
|
pub fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError> {
|
||||||
match self.next {
|
// Ensure that we have enough capacity to accept the write.
|
||||||
Some(Next::Data(ref frame)) => !frame.payload().has_remaining(),
|
assert!(self.has_capacity());
|
||||||
_ => !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);
|
debug!("send; frame={:?}", item);
|
||||||
|
|
||||||
@@ -127,7 +98,7 @@ impl<T, B> Sink for FramedWrite<T, B>
|
|||||||
let len = v.payload().remaining();
|
let len = v.payload().remaining();
|
||||||
|
|
||||||
if len > self.max_frame_size() {
|
if len > self.max_frame_size() {
|
||||||
return Err(PayloadTooBig.into());
|
return Err(PayloadTooBig);
|
||||||
}
|
}
|
||||||
|
|
||||||
if len >= CHAIN_THRESHOLD {
|
if len >= CHAIN_THRESHOLD {
|
||||||
@@ -188,11 +159,12 @@ impl<T, B> Sink for FramedWrite<T, B>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(AsyncSink::Ready)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_complete(&mut self) -> Poll<(), ConnectionError> {
|
/// Flush buffered data to the wire
|
||||||
trace!("poll_complete");
|
pub fn flush(&mut self) -> Poll<(), io::Error> {
|
||||||
|
trace!("flush");
|
||||||
|
|
||||||
while !self.is_empty() {
|
while !self.is_empty() {
|
||||||
match self.next {
|
match self.next {
|
||||||
@@ -228,18 +200,44 @@ impl<T, B> Sink for FramedWrite<T, B>
|
|||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close(&mut self) -> Poll<(), ConnectionError> {
|
/// Close the codec
|
||||||
try_ready!(self.poll_complete());
|
pub fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||||
|
try_ready!(self.flush());
|
||||||
self.inner.shutdown().map_err(Into::into)
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Stream, B> Stream for FramedWrite<T, B> {
|
fn is_empty(&self) -> bool {
|
||||||
type Item = T::Item;
|
match self.next {
|
||||||
type Error = T::Error;
|
Some(Next::Data(ref frame)) => !frame.payload().has_remaining(),
|
||||||
|
_ => !self.buf.has_remaining(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> {
|
impl<T, B> FramedWrite<T, B> {
|
||||||
self.inner.poll()
|
/// 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
263
src/error.rs
263
src/error.rs
@@ -1,263 +1,101 @@
|
|||||||
use http;
|
use codec::{SendError, UserError};
|
||||||
|
use proto;
|
||||||
|
|
||||||
use std::{error, fmt, io};
|
use std::{error, fmt, io};
|
||||||
|
|
||||||
|
pub use frame::Reason;
|
||||||
|
|
||||||
/// The error type for HTTP/2 operations
|
/// The error type for HTTP/2 operations
|
||||||
///
|
|
||||||
/// XXX does this sufficiently destinguish stream-level errors from connection-level errors?
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ConnectionError {
|
pub struct Error {
|
||||||
|
kind: Kind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Kind {
|
||||||
/// An error caused by an action taken by the remote peer.
|
/// An error caused by an action taken by the remote peer.
|
||||||
///
|
///
|
||||||
/// This is either an error received by the peer or caused by an invalid
|
/// This is either an error received by the peer or caused by an invalid
|
||||||
/// action taken by the peer (i.e. a protocol error).
|
/// action taken by the peer (i.e. a protocol error).
|
||||||
Proto(Reason),
|
Proto(Reason),
|
||||||
|
|
||||||
/// An `io::Error` occurred while trying to read or write.
|
|
||||||
Io(io::Error),
|
|
||||||
|
|
||||||
/// An error resulting from an invalid action taken by the user of this
|
/// An error resulting from an invalid action taken by the user of this
|
||||||
/// library.
|
/// library.
|
||||||
User(User),
|
User(UserError),
|
||||||
|
|
||||||
// TODO: reserve additional variants
|
/// An `io::Error` occurred while trying to read or write.
|
||||||
|
Io(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
// ===== impl Error =====
|
||||||
pub struct StreamError(Reason);
|
|
||||||
|
|
||||||
impl StreamError {
|
impl From<proto::Error> for Error {
|
||||||
pub fn new(r: Reason) -> StreamError {
|
fn from(src: proto::Error) -> Error {
|
||||||
StreamError(r)
|
use proto::Error::*;
|
||||||
|
|
||||||
|
Error {
|
||||||
|
kind: match src {
|
||||||
|
Proto(reason) => Kind::Proto(reason),
|
||||||
|
Io(e) => Kind::Io(e),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reason(&self) -> Reason {
|
|
||||||
self.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Reason> for StreamError {
|
impl From<io::Error> for Error {
|
||||||
fn from(r: Reason) -> Self {
|
fn from(src: io::Error) -> Error {
|
||||||
StreamError(r)
|
Error { kind: Kind::Io(src) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
impl From<Reason> for Error {
|
||||||
pub enum Reason {
|
fn from(src: Reason) -> Error {
|
||||||
NoError,
|
Error { kind: Kind::Proto(src) }
|
||||||
ProtocolError,
|
|
||||||
InternalError,
|
|
||||||
FlowControlError,
|
|
||||||
SettingsTimeout,
|
|
||||||
StreamClosed,
|
|
||||||
FrameSizeError,
|
|
||||||
RefusedStream,
|
|
||||||
Cancel,
|
|
||||||
CompressionError,
|
|
||||||
ConnectError,
|
|
||||||
EnhanceYourCalm,
|
|
||||||
InadequateSecurity,
|
|
||||||
Http11Required,
|
|
||||||
Other(u32),
|
|
||||||
// TODO: reserve additional variants
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum User {
|
|
||||||
/// The specified stream ID is invalid.
|
|
||||||
///
|
|
||||||
/// For example, using a stream ID reserved for a push promise from the
|
|
||||||
/// client or using a non-zero stream ID for settings.
|
|
||||||
InvalidStreamId,
|
|
||||||
|
|
||||||
/// The stream ID is no longer accepting frames.
|
|
||||||
InactiveStreamId,
|
|
||||||
|
|
||||||
/// The stream is not currently expecting a frame of this type.
|
|
||||||
UnexpectedFrameType,
|
|
||||||
|
|
||||||
/// The connection or stream does not have a sufficient flow control window to
|
|
||||||
/// transmit a Data frame to the remote.
|
|
||||||
FlowControlViolation,
|
|
||||||
|
|
||||||
/// The payload size is too big
|
|
||||||
PayloadTooBig,
|
|
||||||
|
|
||||||
/// The connection state is corrupt and the connection should be dropped.
|
|
||||||
Corrupt,
|
|
||||||
|
|
||||||
/// The stream state has been reset.
|
|
||||||
StreamReset(Reason),
|
|
||||||
|
|
||||||
/// The application attempted to initiate too many streams to remote.
|
|
||||||
Rejected,
|
|
||||||
|
|
||||||
// TODO: reserve additional variants
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! reason_desc {
|
|
||||||
($reason:expr) => (reason_desc!($reason, ""));
|
|
||||||
($reason:expr, $prefix:expr) => ({
|
|
||||||
use self::Reason::*;
|
|
||||||
|
|
||||||
match $reason {
|
|
||||||
NoError => concat!($prefix, "not a result of an error"),
|
|
||||||
ProtocolError => concat!($prefix, "unspecific protocol error detected"),
|
|
||||||
InternalError => concat!($prefix, "unexpected internal error encountered"),
|
|
||||||
FlowControlError => concat!($prefix, "flow-control protocol violated"),
|
|
||||||
SettingsTimeout => concat!($prefix, "settings ACK not received in timely manner"),
|
|
||||||
StreamClosed => concat!($prefix, "received frame when stream half-closed"),
|
|
||||||
FrameSizeError => concat!($prefix, "frame sent with invalid size"),
|
|
||||||
RefusedStream => concat!($prefix, "refused stream before processing any application logic"),
|
|
||||||
Cancel => concat!($prefix, "stream no longer needed"),
|
|
||||||
CompressionError => concat!($prefix, "unable to maintain the header compression context"),
|
|
||||||
ConnectError => concat!($prefix, "connection established in response to a CONNECT request was reset or abnormally closed"),
|
|
||||||
EnhanceYourCalm => concat!($prefix, "detected excessive load generating behavior"),
|
|
||||||
InadequateSecurity => concat!($prefix, "security properties do not meet minimum requirements"),
|
|
||||||
Http11Required => concat!($prefix, "endpoint requires HTTP/1.1"),
|
|
||||||
Other(_) => concat!($prefix, "other reason (ain't no tellin')"),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! user_desc {
|
|
||||||
($reason:expr) => (user_desc!($reason, ""));
|
|
||||||
($reason:expr, $prefix:expr) => ({
|
|
||||||
use self::User::*;
|
|
||||||
|
|
||||||
match $reason {
|
|
||||||
InvalidStreamId => concat!($prefix, "invalid stream ID"),
|
|
||||||
InactiveStreamId => concat!($prefix, "inactive stream ID"),
|
|
||||||
UnexpectedFrameType => concat!($prefix, "unexpected frame type"),
|
|
||||||
FlowControlViolation => concat!($prefix, "flow control violation"),
|
|
||||||
StreamReset(_) => concat!($prefix, "frame sent on reset stream"),
|
|
||||||
Corrupt => concat!($prefix, "connection state corrupt"),
|
|
||||||
Rejected => concat!($prefix, "stream would exceed remote max concurrency"),
|
|
||||||
PayloadTooBig => concat!($prefix, "payload too big"),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== impl ConnectionError =====
|
|
||||||
|
|
||||||
impl From<io::Error> for ConnectionError {
|
|
||||||
fn from(src: io::Error) -> ConnectionError {
|
|
||||||
ConnectionError::Io(src)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Reason> for ConnectionError {
|
impl From<SendError> for Error {
|
||||||
fn from(src: Reason) -> ConnectionError {
|
fn from(src: SendError) -> Error {
|
||||||
ConnectionError::Proto(src)
|
match src {
|
||||||
|
SendError::User(e) => e.into(),
|
||||||
|
SendError::Io(e) => e.into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<User> for ConnectionError {
|
impl From<UserError> for Error {
|
||||||
fn from(src: User) -> ConnectionError {
|
fn from(src: UserError) -> Error {
|
||||||
ConnectionError::User(src)
|
Error { kind: Kind::User(src) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConnectionError> for io::Error {
|
impl fmt::Display for Error {
|
||||||
fn from(src: ConnectionError) -> io::Error {
|
|
||||||
io::Error::new(io::ErrorKind::Other, src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<http::Error> for ConnectionError {
|
|
||||||
fn from(_: http::Error) -> Self {
|
|
||||||
// TODO: Should this always be a protocol error?
|
|
||||||
Reason::ProtocolError.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ConnectionError {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use self::ConnectionError::*;
|
use self::Kind::*;
|
||||||
|
|
||||||
match *self {
|
match self.kind {
|
||||||
Proto(reason) => write!(fmt, "protocol error: {}", reason),
|
Proto(ref reason) => write!(fmt, "protocol error: {}", reason),
|
||||||
|
User(ref e) => write!(fmt, "user error: {}", e),
|
||||||
Io(ref e) => fmt::Display::fmt(e, fmt),
|
Io(ref e) => fmt::Display::fmt(e, fmt),
|
||||||
User(e) => write!(fmt, "user error: {}", e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for ConnectionError {
|
impl error::Error for Error {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
use self::ConnectionError::*;
|
use self::Kind::*;
|
||||||
|
|
||||||
match *self {
|
match self.kind {
|
||||||
Io(ref e) => error::Error::description(e),
|
Io(ref e) => error::Error::description(e),
|
||||||
Proto(reason) => reason_desc!(reason, "protocol error: "),
|
Proto(ref reason) => reason.description(),
|
||||||
User(user) => user_desc!(user, "user error: "),
|
User(ref user) => user.description(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl Reason =====
|
|
||||||
|
|
||||||
impl Reason {
|
|
||||||
pub fn description(&self) -> &str {
|
|
||||||
reason_desc!(*self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== impl User =====
|
// ===== impl User =====
|
||||||
|
|
||||||
|
/*
|
||||||
impl User {
|
impl User {
|
||||||
pub fn description(&self) -> &str {
|
pub fn description(&self) -> &str {
|
||||||
user_desc!(*self)
|
user_desc!(*self)
|
||||||
@@ -269,3 +107,4 @@ impl fmt::Display for User {
|
|||||||
write!(fmt, "{}", self.description())
|
write!(fmt, "{}", self.description())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use error::Reason;
|
use frame::{self, Head, Error, Kind, StreamId, Reason};
|
||||||
use frame::{self, Head, Error, Kind, StreamId};
|
|
||||||
|
|
||||||
use bytes::{BufMut, BigEndian};
|
use bytes::{BufMut, BigEndian};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use super::{StreamId, StreamDependency};
|
use super::{StreamId, StreamDependency};
|
||||||
use hpack;
|
use hpack;
|
||||||
use frame::{self, Frame, Head, Kind, Error};
|
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 http::header::{self, HeaderName, HeaderValue};
|
||||||
|
|
||||||
use bytes::{BytesMut, Bytes};
|
use bytes::{BytesMut, Bytes};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use hpack;
|
use hpack;
|
||||||
use error::{ConnectionError, Reason};
|
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
@@ -33,6 +32,7 @@ mod head;
|
|||||||
mod headers;
|
mod headers;
|
||||||
mod ping;
|
mod ping;
|
||||||
mod priority;
|
mod priority;
|
||||||
|
mod reason;
|
||||||
mod reset;
|
mod reset;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod stream_id;
|
mod stream_id;
|
||||||
@@ -45,6 +45,7 @@ pub use self::head::{Head, Kind};
|
|||||||
pub use self::headers::{Headers, PushPromise, Continuation, Pseudo};
|
pub use self::headers::{Headers, PushPromise, Continuation, Pseudo};
|
||||||
pub use self::ping::Ping;
|
pub use self::ping::Ping;
|
||||||
pub use self::priority::{Priority, StreamDependency};
|
pub use self::priority::{Priority, StreamDependency};
|
||||||
|
pub use self::reason::Reason;
|
||||||
pub use self::reset::Reset;
|
pub use self::reset::Reset;
|
||||||
pub use self::settings::Settings;
|
pub use self::settings::Settings;
|
||||||
pub use self::stream_id::StreamId;
|
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.
|
/// Errors that can occur during parsing an HTTP/2 frame.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Error {
|
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.
|
/// A length value other than 8 was set on a PING message.
|
||||||
BadFrameSize,
|
BadFrameSize,
|
||||||
|
|
||||||
@@ -129,19 +121,6 @@ pub enum Error {
|
|||||||
/// length of the payload.
|
/// length of the payload.
|
||||||
TooMuchPadding,
|
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
|
/// An invalid setting value was provided
|
||||||
InvalidSettingValue,
|
InvalidSettingValue,
|
||||||
|
|
||||||
@@ -173,14 +152,3 @@ pub enum Error {
|
|||||||
/// Failed to perform HPACK decoding
|
/// Failed to perform HPACK decoding
|
||||||
Hpack(hpack::DecoderError),
|
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, Reason};
|
||||||
use frame::{self, Head, Error, Kind, StreamId};
|
|
||||||
|
|
||||||
use bytes::{BufMut, BigEndian};
|
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;
|
|
||||||
11
src/lib.rs
11
src/lib.rs
@@ -24,14 +24,13 @@ extern crate log;
|
|||||||
|
|
||||||
extern crate string;
|
extern crate string;
|
||||||
|
|
||||||
pub mod client;
|
mod error;
|
||||||
pub mod error;
|
mod codec;
|
||||||
mod hpack;
|
mod hpack;
|
||||||
mod proto;
|
mod proto;
|
||||||
mod frame;
|
mod frame;
|
||||||
|
|
||||||
|
pub mod client;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
pub use error::{ConnectionError, Reason};
|
pub use error::{Error, Reason};
|
||||||
|
|
||||||
// TODO: remove if carllerche/http#90 lands
|
|
||||||
pub type HeaderMap = http::HeaderMap<http::header::HeaderValue>;
|
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
use {client, frame, server, ConnectionError};
|
use {client, frame, server, proto};
|
||||||
|
use frame::Reason;
|
||||||
|
use codec::{SendError, RecvError};
|
||||||
|
|
||||||
use proto::*;
|
use proto::*;
|
||||||
|
|
||||||
use http::Request;
|
use http::Request;
|
||||||
use futures::{Sink, Stream};
|
use futures::{Stream};
|
||||||
use bytes::{Bytes, IntoBuf};
|
use bytes::{Bytes, IntoBuf};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
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.
|
/// 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
|
// The order of these calls don't really matter too much as only one
|
||||||
// should have pending work.
|
// should have pending work.
|
||||||
try_ready!(self.ping_pong.send_pending_pong(&mut self.codec));
|
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())
|
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.
|
/// Advances the internal state of the connection.
|
||||||
pub fn poll(&mut self) -> Poll<(), ConnectionError> {
|
pub fn poll(&mut self) -> Poll<(), proto::Error> {
|
||||||
use error::ConnectionError::*;
|
use codec::RecvError::*;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
// TODO: probably clean up this glob of code
|
||||||
match self.state {
|
match self.state {
|
||||||
// When open, continue to poll a frame
|
// When open, continue to poll a frame
|
||||||
State::Open => {},
|
State::Open => {
|
||||||
// In an error state
|
|
||||||
_ => {
|
|
||||||
try_ready!(self.poll_complete());
|
|
||||||
|
|
||||||
// GO_AWAY frame has been sent, return the error
|
|
||||||
return Err(self.state.error().unwrap().into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.poll2() {
|
match self.poll2() {
|
||||||
Err(Proto(e)) => {
|
// 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));
|
||||||
|
|
||||||
|
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);
|
debug!("Connection::poll; err={:?}", e);
|
||||||
|
|
||||||
|
// Reset all active streams
|
||||||
let last_processed_id = self.streams.recv_err(&e.into());
|
let last_processed_id = self.streams.recv_err(&e.into());
|
||||||
|
|
||||||
|
// Create the GO_AWAY frame with the last_processed_id
|
||||||
let frame = frame::GoAway::new(last_processed_id, e);
|
let frame = frame::GoAway::new(last_processed_id, e);
|
||||||
|
|
||||||
|
// Transition to the going away state.
|
||||||
self.state = State::GoAway(frame);
|
self.state = State::GoAway(frame);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
// 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?
|
// TODO: Are I/O errors recoverable?
|
||||||
|
Err(Io(e)) => {
|
||||||
|
let e = e.into();
|
||||||
|
|
||||||
|
// Reset all active streams
|
||||||
self.streams.recv_err(&e);
|
self.streams.recv_err(&e);
|
||||||
|
|
||||||
|
// Return the error
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
ret => return ret,
|
}
|
||||||
|
},
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll2(&mut self) -> Poll<(), ConnectionError> {
|
fn poll2(&mut self) -> Poll<(), RecvError> {
|
||||||
use frame::Frame::*;
|
use frame::Frame::*;
|
||||||
use proto::ProtoError::*;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// First, ensure that the `Connection` is able to receive a frame
|
// First, ensure that the `Connection` is able to receive a frame
|
||||||
try_ready!(self.poll_ready());
|
try_ready!(self.poll_ready());
|
||||||
|
|
||||||
trace!("polling codec");
|
match try_ready!(self.codec.poll()) {
|
||||||
|
|
||||||
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 {
|
|
||||||
Some(Headers(frame)) => {
|
Some(Headers(frame)) => {
|
||||||
trace!("recv HEADERS; frame={:?}", frame);
|
trace!("recv HEADERS; frame={:?}", frame);
|
||||||
try!(self.streams.recv_headers(frame));
|
try!(self.streams.recv_headers(frame));
|
||||||
@@ -184,7 +202,7 @@ impl<T, P, B> Connection<T, P, B>
|
|||||||
Some(GoAway(_)) => {
|
Some(GoAway(_)) => {
|
||||||
// TODO: handle the last_processed_id. Also, should this be
|
// TODO: handle the last_processed_id. Also, should this be
|
||||||
// handled as an error?
|
// handled as an error?
|
||||||
// let _ = ConnectionError::Proto(frame.reason());
|
// let _ = RecvError::Proto(frame.reason());
|
||||||
return Ok(().into());
|
return Ok(().into());
|
||||||
}
|
}
|
||||||
Some(Ping(frame)) => {
|
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>
|
impl<T, B> Connection<T, client::Peer, B>
|
||||||
where T: AsyncRead + AsyncWrite,
|
where T: AsyncRead + AsyncWrite,
|
||||||
B: IntoBuf,
|
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.
|
/// Initialize a new HTTP/2.0 stream and send the message.
|
||||||
pub fn send_request(&mut self, request: Request<()>, end_of_stream: bool)
|
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)
|
self.streams.send_request(request, end_of_stream)
|
||||||
}
|
}
|
||||||
@@ -260,14 +252,3 @@ impl<T, B> Connection<T, server::Peer, B>
|
|||||||
self.streams.next_incoming()
|
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
34
src/proto/error.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,109 +1,35 @@
|
|||||||
mod codec;
|
|
||||||
mod connection;
|
mod connection;
|
||||||
mod framed_read;
|
mod error;
|
||||||
mod framed_write;
|
mod peer;
|
||||||
mod ping_pong;
|
mod ping_pong;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod streams;
|
mod streams;
|
||||||
|
|
||||||
pub(crate) use self::connection::Connection;
|
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};
|
pub(crate) use self::streams::{Streams, StreamRef};
|
||||||
|
|
||||||
use self::codec::Codec;
|
use codec::Codec;
|
||||||
use self::framed_read::FramedRead;
|
|
||||||
use self::framed_write::FramedWrite;
|
|
||||||
use self::ping_pong::PingPong;
|
use self::ping_pong::PingPong;
|
||||||
use self::settings::Settings;
|
use self::settings::Settings;
|
||||||
use self::streams::Prioritized;
|
use self::streams::Prioritized;
|
||||||
|
|
||||||
use ConnectionError;
|
use frame::{self, Frame};
|
||||||
use error::Reason;
|
|
||||||
use frame::{self, Frame, StreamId};
|
|
||||||
|
|
||||||
use futures::{self, task, Poll, Async, AsyncSink};
|
use futures::{task, Poll, Async};
|
||||||
use futures::task::Task;
|
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
|
use tokio_io::AsyncWrite;
|
||||||
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>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type PingPayload = [u8; 8];
|
pub type PingPayload = [u8; 8];
|
||||||
|
|
||||||
pub type WindowSize = u32;
|
pub type WindowSize = u32;
|
||||||
|
|
||||||
/// Errors that are received
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ProtoError {
|
|
||||||
Connection(Reason),
|
|
||||||
Stream {
|
|
||||||
id: StreamId,
|
|
||||||
reason: Reason,
|
|
||||||
},
|
|
||||||
Io(io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
|
// TODO: Move these into `frame`
|
||||||
pub const DEFAULT_INITIAL_WINDOW_SIZE: WindowSize = 65_535;
|
pub const DEFAULT_INITIAL_WINDOW_SIZE: WindowSize = 65_535;
|
||||||
pub const MAX_WINDOW_SIZE: WindowSize = (1 << 31) - 1;
|
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
22
src/proto/peer.rs
Normal 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>;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use frame::Ping;
|
use frame::Ping;
|
||||||
use proto::*;
|
use proto::*;
|
||||||
|
|
||||||
use futures::Sink;
|
use std::io;
|
||||||
|
|
||||||
/// Acknowledges ping requests from the remote.
|
/// Acknowledges ping requests from the remote.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -45,15 +45,16 @@ impl<B> PingPong<B>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Send any pending pongs.
|
/// 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,
|
where T: AsyncWrite,
|
||||||
{
|
{
|
||||||
if let Some(pong) = self.sending_pong.take() {
|
if let Some(pong) = self.sending_pong.take() {
|
||||||
if let AsyncSink::NotReady(pong) = dst.start_send(pong)? {
|
if !dst.poll_ready()?.is_ready() {
|
||||||
// If the pong can't be sent, save it.
|
|
||||||
self.sending_pong = Some(pong);
|
self.sending_pong = Some(pong);
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::NotReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dst.buffer(pong).ok().expect("invalid pong frame");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use frame;
|
use frame;
|
||||||
|
use codec::RecvError;
|
||||||
use proto::*;
|
use proto::*;
|
||||||
|
|
||||||
use futures::Sink;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Settings {
|
pub(crate) struct Settings {
|
||||||
/// Received SETTINGS frame pending processing. The ACK must be written to
|
/// 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,
|
pub fn send_pending_ack<T, B, C, P>(&mut self,
|
||||||
dst: &mut Codec<T, B>,
|
dst: &mut Codec<T, B>,
|
||||||
streams: &mut Streams<C, P>)
|
streams: &mut Streams<C, P>)
|
||||||
-> Poll<(), ConnectionError>
|
-> Poll<(), RecvError>
|
||||||
where T: AsyncWrite,
|
where T: AsyncWrite,
|
||||||
B: Buf,
|
B: Buf,
|
||||||
C: Buf,
|
C: Buf,
|
||||||
@@ -40,13 +39,17 @@ impl Settings {
|
|||||||
trace!("send_pending_ack; pending={:?}", self.pending);
|
trace!("send_pending_ack; pending={:?}", self.pending);
|
||||||
|
|
||||||
if let Some(ref settings) = self.pending {
|
if let Some(ref settings) = self.pending {
|
||||||
let frame = frame::Settings::ack();
|
if !dst.poll_ready()?.is_ready() {
|
||||||
|
|
||||||
if let AsyncSink::NotReady(_) = dst.start_send(frame.into())? {
|
|
||||||
trace!("failed to send ACK");
|
trace!("failed to send ACK");
|
||||||
return Ok(Async::NotReady);
|
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");
|
trace!("ACK sent; applying settings");
|
||||||
|
|
||||||
dst.apply_remote_settings(settings);
|
dst.apply_remote_settings(settings);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use ConnectionError;
|
use frame::Reason;
|
||||||
|
use frame::Reason::*;
|
||||||
use proto::*;
|
use proto::*;
|
||||||
use error::Reason::*;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct FlowControl {
|
pub struct FlowControl {
|
||||||
@@ -67,15 +67,15 @@ impl FlowControl {
|
|||||||
/// Increase the window size.
|
/// Increase the window size.
|
||||||
///
|
///
|
||||||
/// This is called after receiving a WINDOW_UPDATE frame
|
/// 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);
|
let (val, overflow) = self.window_size.overflowing_add(sz as i32);
|
||||||
|
|
||||||
if overflow {
|
if overflow {
|
||||||
return Err(FlowControlError.into());
|
return Err(FlowControlError);
|
||||||
}
|
}
|
||||||
|
|
||||||
if val > MAX_WINDOW_SIZE as i32 {
|
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);
|
trace!("inc_window; sz={}; old={}; new={}", sz, self.window_size, val);
|
||||||
|
|||||||
@@ -20,11 +20,9 @@ use self::state::State;
|
|||||||
use self::store::{Store, Entry};
|
use self::store::{Store, Entry};
|
||||||
use self::stream::Stream;
|
use self::stream::Stream;
|
||||||
|
|
||||||
use {frame, ConnectionError};
|
|
||||||
use frame::StreamId;
|
use frame::StreamId;
|
||||||
use proto::*;
|
use proto::*;
|
||||||
use error::Reason::*;
|
use error::Reason::*;
|
||||||
use error::User::*;
|
|
||||||
|
|
||||||
use http::{Request, Response};
|
use http::{Request, Response};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use super::store::Resolve;
|
use super::store::Resolve;
|
||||||
|
|
||||||
use bytes::buf::Take;
|
use frame::Reason;
|
||||||
use futures::Sink;
|
|
||||||
|
|
||||||
|
use codec::UserError;
|
||||||
|
use codec::UserError::*;
|
||||||
|
|
||||||
|
use bytes::buf::Take;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
use std::{fmt, cmp};
|
use std::{fmt, cmp};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -80,7 +85,7 @@ impl<B, P> Prioritize<B, P>
|
|||||||
frame: frame::Data<B>,
|
frame: frame::Data<B>,
|
||||||
stream: &mut store::Ptr<B, P>,
|
stream: &mut store::Ptr<B, P>,
|
||||||
task: &mut Option<Task>)
|
task: &mut Option<Task>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), UserError>
|
||||||
{
|
{
|
||||||
let sz = frame.payload().remaining();
|
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_send_streaming() {
|
||||||
if stream.state.is_closed() {
|
if stream.state.is_closed() {
|
||||||
return Err(InactiveStreamId.into());
|
return Err(InactiveStreamId);
|
||||||
} else {
|
} else {
|
||||||
return Err(UnexpectedFrameType.into());
|
return Err(UnexpectedFrameType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +120,7 @@ impl<B, P> Prioritize<B, P>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if frame.is_end_stream() {
|
if frame.is_end_stream() {
|
||||||
try!(stream.state.send_close());
|
stream.state.send_close();
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!("send_data (2); available={}; buffered={}",
|
trace!("send_data (2); available={}; buffered={}",
|
||||||
@@ -161,7 +166,7 @@ impl<B, P> Prioritize<B, P>
|
|||||||
pub fn recv_stream_window_update(&mut self,
|
pub fn recv_stream_window_update(&mut self,
|
||||||
inc: WindowSize,
|
inc: WindowSize,
|
||||||
stream: &mut store::Ptr<B, P>)
|
stream: &mut store::Ptr<B, P>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), Reason>
|
||||||
{
|
{
|
||||||
trace!("recv_stream_window_update; stream={:?}; state={:?}; inc={}; flow={:?}",
|
trace!("recv_stream_window_update; stream={:?}; state={:?}; inc={}; flow={:?}",
|
||||||
stream.id, stream.state, inc, stream.send_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,
|
pub fn recv_connection_window_update(&mut self,
|
||||||
inc: WindowSize,
|
inc: WindowSize,
|
||||||
store: &mut Store<B, P>)
|
store: &mut Store<B, P>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), Reason>
|
||||||
{
|
{
|
||||||
// Update the connection's window
|
// Update the connection's window
|
||||||
self.flow.inc_window(inc)?;
|
self.flow.inc_window(inc)?;
|
||||||
@@ -284,7 +289,7 @@ impl<B, P> Prioritize<B, P>
|
|||||||
pub fn poll_complete<T>(&mut self,
|
pub fn poll_complete<T>(&mut self,
|
||||||
store: &mut Store<B, P>,
|
store: &mut Store<B, P>,
|
||||||
dst: &mut Codec<T, Prioritized<B>>)
|
dst: &mut Codec<T, Prioritized<B>>)
|
||||||
-> Poll<(), ConnectionError>
|
-> Poll<(), io::Error>
|
||||||
where T: AsyncWrite,
|
where T: AsyncWrite,
|
||||||
{
|
{
|
||||||
// Ensure codec is ready
|
// Ensure codec is ready
|
||||||
@@ -303,22 +308,17 @@ impl<B, P> Prioritize<B, P>
|
|||||||
Some(frame) => {
|
Some(frame) => {
|
||||||
trace!("writing frame={:?}", frame);
|
trace!("writing frame={:?}", frame);
|
||||||
|
|
||||||
let res = dst.start_send(frame)?;
|
dst.buffer(frame).ok().expect("invalid frame");
|
||||||
|
|
||||||
// We already verified that `dst` is ready to accept the
|
|
||||||
// write
|
|
||||||
assert!(res.is_ready());
|
|
||||||
|
|
||||||
// Ensure the codec is ready to try the loop again.
|
// Ensure the codec is ready to try the loop again.
|
||||||
try_ready!(dst.poll_ready());
|
try_ready!(dst.poll_ready());
|
||||||
|
|
||||||
// Because, always try to reclaim...
|
// Because, always try to reclaim...
|
||||||
self.reclaim_frame(store, dst);
|
self.reclaim_frame(store, dst);
|
||||||
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Try to flush the codec.
|
// Try to flush the codec.
|
||||||
try_ready!(dst.poll_complete());
|
try_ready!(dst.flush());
|
||||||
|
|
||||||
// This might release a data frame...
|
// This might release a data frame...
|
||||||
if !self.reclaim_frame(store, dst) {
|
if !self.reclaim_frame(store, dst) {
|
||||||
|
|||||||
@@ -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 proto::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use error::Reason::*;
|
use http::HeaderMap;
|
||||||
use futures::Sink;
|
|
||||||
|
|
||||||
|
use std::io;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -41,14 +43,14 @@ pub(super) struct Recv<B, P>
|
|||||||
/// Refused StreamId, this represents a frame that must be sent out.
|
/// Refused StreamId, this represents a frame that must be sent out.
|
||||||
refused: Option<StreamId>,
|
refused: Option<StreamId>,
|
||||||
|
|
||||||
_p: PhantomData<(B)>,
|
_p: PhantomData<B>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) enum Event<T> {
|
pub(super) enum Event<T> {
|
||||||
Headers(T),
|
Headers(T),
|
||||||
Data(Bytes),
|
Data(Bytes),
|
||||||
Trailers(::HeaderMap),
|
Trailers(HeaderMap),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@@ -103,7 +105,7 @@ impl<B, P> Recv<B, P>
|
|||||||
///
|
///
|
||||||
/// Returns the stream state if successful. `None` if refused
|
/// Returns the stream state if successful. `None` if refused
|
||||||
pub fn open(&mut self, id: StreamId)
|
pub fn open(&mut self, id: StreamId)
|
||||||
-> Result<Option<StreamId>, ConnectionError>
|
-> Result<Option<StreamId>, RecvError>
|
||||||
{
|
{
|
||||||
assert!(self.refused.is_none());
|
assert!(self.refused.is_none());
|
||||||
|
|
||||||
@@ -123,7 +125,7 @@ impl<B, P> Recv<B, P>
|
|||||||
pub fn recv_headers(&mut self,
|
pub fn recv_headers(&mut self,
|
||||||
frame: frame::Headers,
|
frame: frame::Headers,
|
||||||
stream: &mut store::Ptr<B, P>)
|
stream: &mut store::Ptr<B, P>)
|
||||||
-> Result<(), ProtoError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
trace!("opening stream; init_window={}", self.init_window_sz);
|
trace!("opening stream; init_window={}", self.init_window_sz);
|
||||||
let is_initial = stream.state.recv_open(frame.is_end_stream())?;
|
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 = frame.stream_id();
|
||||||
self.next_stream_id.increment();
|
self.next_stream_id.increment();
|
||||||
} else {
|
} else {
|
||||||
return Err(ProtoError::Connection(ProtocolError));
|
return Err(RecvError::Connection(ProtocolError));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: be smarter about this logic
|
// TODO: be smarter about this logic
|
||||||
@@ -184,13 +186,13 @@ impl<B, P> Recv<B, P>
|
|||||||
pub fn recv_trailers(&mut self,
|
pub fn recv_trailers(&mut self,
|
||||||
frame: frame::Headers,
|
frame: frame::Headers,
|
||||||
stream: &mut store::Ptr<B, P>)
|
stream: &mut store::Ptr<B, P>)
|
||||||
-> Result<(), ProtoError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
// Transition the state
|
// Transition the state
|
||||||
stream.state.recv_close()?;
|
stream.state.recv_close()?;
|
||||||
|
|
||||||
if stream.ensure_content_length_zero().is_err() {
|
if stream.ensure_content_length_zero().is_err() {
|
||||||
return Err(ProtoError::Stream {
|
return Err(RecvError::Stream {
|
||||||
id: stream.id,
|
id: stream.id,
|
||||||
reason: ProtocolError,
|
reason: ProtocolError,
|
||||||
});
|
});
|
||||||
@@ -205,11 +207,12 @@ impl<B, P> Recv<B, P>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Releases capacity back to the connection
|
||||||
pub fn release_capacity(&mut self,
|
pub fn release_capacity(&mut self,
|
||||||
capacity: WindowSize,
|
capacity: WindowSize,
|
||||||
stream: &mut store::Ptr<B, P>,
|
stream: &mut store::Ptr<B, P>,
|
||||||
task: &mut Option<Task>)
|
task: &mut Option<Task>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), UserError>
|
||||||
{
|
{
|
||||||
if capacity > stream.in_flight_recv_data {
|
if capacity > stream.in_flight_recv_data {
|
||||||
// TODO: Handle error
|
// TODO: Handle error
|
||||||
@@ -246,7 +249,7 @@ impl<B, P> Recv<B, P>
|
|||||||
pub fn recv_data(&mut self,
|
pub fn recv_data(&mut self,
|
||||||
frame: frame::Data,
|
frame: frame::Data,
|
||||||
stream: &mut store::Ptr<B, P>)
|
stream: &mut store::Ptr<B, P>)
|
||||||
-> Result<(), ProtoError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
let sz = frame.payload().len();
|
let sz = frame.payload().len();
|
||||||
|
|
||||||
@@ -259,7 +262,7 @@ impl<B, P> Recv<B, P>
|
|||||||
if !stream.state.is_recv_streaming() {
|
if !stream.state.is_recv_streaming() {
|
||||||
// Receiving a DATA frame when not expecting one is a protocol
|
// Receiving a DATA frame when not expecting one is a protocol
|
||||||
// error.
|
// error.
|
||||||
return Err(ProtoError::Connection(ProtocolError));
|
return Err(RecvError::Connection(ProtocolError));
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!("recv_data; size={}; connection={}; stream={}",
|
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
|
// Ensure that there is enough capacity on the connection before acting
|
||||||
// on the stream.
|
// on the stream.
|
||||||
if self.flow.window_size() < sz || stream.recv_flow.window_size() < sz {
|
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
|
// Update connection level flow control
|
||||||
@@ -281,7 +284,7 @@ impl<B, P> Recv<B, P>
|
|||||||
stream.in_flight_recv_data += sz;
|
stream.in_flight_recv_data += sz;
|
||||||
|
|
||||||
if stream.dec_content_length(frame.payload().len()).is_err() {
|
if stream.dec_content_length(frame.payload().len()).is_err() {
|
||||||
return Err(ProtoError::Stream {
|
return Err(RecvError::Stream {
|
||||||
id: stream.id,
|
id: stream.id,
|
||||||
reason: ProtocolError,
|
reason: ProtocolError,
|
||||||
});
|
});
|
||||||
@@ -289,14 +292,14 @@ impl<B, P> Recv<B, P>
|
|||||||
|
|
||||||
if frame.is_end_stream() {
|
if frame.is_end_stream() {
|
||||||
if stream.ensure_content_length_zero().is_err() {
|
if stream.ensure_content_length_zero().is_err() {
|
||||||
return Err(ProtoError::Stream {
|
return Err(RecvError::Stream {
|
||||||
id: stream.id,
|
id: stream.id,
|
||||||
reason: ProtocolError,
|
reason: ProtocolError,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if stream.state.recv_close().is_err() {
|
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>,
|
send: &Send<B, P>,
|
||||||
stream: store::Key,
|
stream: store::Key,
|
||||||
store: &mut Store<B, P>)
|
store: &mut Store<B, P>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
// First, make sure that the values are legit
|
// First, make sure that the values are legit
|
||||||
self.ensure_can_reserve(frame.promised_id())?;
|
self.ensure_can_reserve(frame.promised_id())?;
|
||||||
|
|
||||||
// Make sure that the stream state is valid
|
// 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
|
// TODO: Streams in the reserved states do not count towards the concurrency
|
||||||
// limit. However, it seems like there should be a cap otherwise this
|
// limit. However, it seems like there should be a cap otherwise this
|
||||||
@@ -361,18 +365,19 @@ impl<B, P> Recv<B, P>
|
|||||||
Ok(())
|
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 {
|
if id >= self.next_stream_id {
|
||||||
return Err(ProtocolError.into());
|
return Err(ProtocolError);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv_reset(&mut self, frame: frame::Reset, stream: &mut Stream<B, P>)
|
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
|
// Notify the stream
|
||||||
stream.state.recv_err(&err);
|
stream.state.recv_err(&err);
|
||||||
@@ -381,7 +386,7 @@ impl<B, P> Recv<B, P>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a received error
|
/// 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
|
// Receive an error
|
||||||
stream.state.recv_err(err);
|
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.
|
/// Returns true if the remote peer can initiate a stream with the given ID.
|
||||||
fn ensure_can_open(&self, id: StreamId)
|
fn ensure_can_open(&self, id: StreamId)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
if !P::is_server() {
|
if !P::is_server() {
|
||||||
// Remote is a server and cannot open streams. PushPromise is
|
// Remote is a server and cannot open streams. PushPromise is
|
||||||
// registered by reserving, so does not go through this path.
|
// 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
|
// Ensure that the ID is a valid server initiated ID
|
||||||
if !id.is_client_initiated() {
|
if !id.is_client_initiated() {
|
||||||
return Err(ProtocolError.into());
|
return Err(RecvError::Connection(ProtocolError));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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.
|
/// Returns true if the remote peer can reserve a stream with the given ID.
|
||||||
fn ensure_can_reserve(&self, promised_id: StreamId)
|
fn ensure_can_reserve(&self, promised_id: StreamId)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
// TODO: Are there other rules?
|
// TODO: Are there other rules?
|
||||||
if P::is_server() {
|
if P::is_server() {
|
||||||
// The remote is a client and cannot reserve
|
// The remote is a client and cannot reserve
|
||||||
return Err(ProtocolError.into());
|
return Err(RecvError::Connection(ProtocolError));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !promised_id.is_server_initiated() {
|
if !promised_id.is_server_initiated() {
|
||||||
return Err(ProtocolError.into());
|
return Err(RecvError::Connection(ProtocolError));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -450,31 +455,28 @@ impl<B, P> Recv<B, P>
|
|||||||
|
|
||||||
/// Send any pending refusals.
|
/// Send any pending refusals.
|
||||||
pub fn send_pending_refusal<T>(&mut self, dst: &mut Codec<T, Prioritized<B>>)
|
pub fn send_pending_refusal<T>(&mut self, dst: &mut Codec<T, Prioritized<B>>)
|
||||||
-> Poll<(), ConnectionError>
|
-> Poll<(), io::Error>
|
||||||
where T: AsyncWrite,
|
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);
|
let frame = frame::Reset::new(stream_id, RefusedStream);
|
||||||
|
|
||||||
match dst.start_send(frame.into())? {
|
// Buffer the frame
|
||||||
AsyncSink::Ready => {
|
dst.buffer(frame.into()).ok().expect("invalid RST_STREAM frame");
|
||||||
self.reset(stream_id, RefusedStream);
|
|
||||||
return Ok(Async::Ready(()));
|
|
||||||
}
|
|
||||||
AsyncSink::NotReady(_) => {
|
|
||||||
self.refused = Some(stream_id);
|
|
||||||
return Ok(Async::NotReady);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.refused = None;
|
||||||
|
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_complete<T>(&mut self,
|
pub fn poll_complete<T>(&mut self,
|
||||||
store: &mut Store<B, P>,
|
store: &mut Store<B, P>,
|
||||||
dst: &mut Codec<T, Prioritized<B>>)
|
dst: &mut Codec<T, Prioritized<B>>)
|
||||||
-> Poll<(), ConnectionError>
|
-> Poll<(), io::Error>
|
||||||
where T: AsyncWrite,
|
where T: AsyncWrite,
|
||||||
{
|
{
|
||||||
// Send any pending connection level window updates
|
// Send any pending connection level window updates
|
||||||
@@ -488,7 +490,7 @@ impl<B, P> Recv<B, P>
|
|||||||
|
|
||||||
/// Send connection level window update
|
/// Send connection level window update
|
||||||
fn send_connection_window_update<T>(&mut self, dst: &mut Codec<T, Prioritized<B>>)
|
fn send_connection_window_update<T>(&mut self, dst: &mut Codec<T, Prioritized<B>>)
|
||||||
-> Poll<(), ConnectionError>
|
-> Poll<(), io::Error>
|
||||||
where T: AsyncWrite,
|
where T: AsyncWrite,
|
||||||
{
|
{
|
||||||
let incr = self.flow.unclaimed_capacity();
|
let incr = self.flow.unclaimed_capacity();
|
||||||
@@ -496,11 +498,14 @@ impl<B, P> Recv<B, P>
|
|||||||
if incr > 0 {
|
if incr > 0 {
|
||||||
let frame = frame::WindowUpdate::new(StreamId::zero(), incr);
|
let frame = frame::WindowUpdate::new(StreamId::zero(), incr);
|
||||||
|
|
||||||
if dst.start_send(frame.into())?.is_ready() {
|
// 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");
|
self.flow.inc_window(incr).ok().expect("unexpected flow control state");
|
||||||
} else {
|
|
||||||
return Ok(Async::NotReady);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
@@ -511,7 +516,7 @@ impl<B, P> Recv<B, P>
|
|||||||
pub fn send_stream_window_updates<T>(&mut self,
|
pub fn send_stream_window_updates<T>(&mut self,
|
||||||
store: &mut Store<B, P>,
|
store: &mut Store<B, P>,
|
||||||
dst: &mut Codec<T, Prioritized<B>>)
|
dst: &mut Codec<T, Prioritized<B>>)
|
||||||
-> Poll<(), ConnectionError>
|
-> Poll<(), io::Error>
|
||||||
where T: AsyncWrite,
|
where T: AsyncWrite,
|
||||||
{
|
{
|
||||||
loop {
|
loop {
|
||||||
@@ -534,10 +539,11 @@ impl<B, P> Recv<B, P>
|
|||||||
let incr = stream.recv_flow.unclaimed_capacity();
|
let incr = stream.recv_flow.unclaimed_capacity();
|
||||||
|
|
||||||
if incr > 0 {
|
if incr > 0 {
|
||||||
|
// Create the WINDOW_UPDATE frame
|
||||||
let frame = frame::WindowUpdate::new(stream.id, incr);
|
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>)
|
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) {
|
match stream.pending_recv.pop_front(&mut self.buffer) {
|
||||||
Some(Event::Data(payload)) => {
|
Some(Event::Data(payload)) => {
|
||||||
Ok(Some(payload).into())
|
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>)
|
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) {
|
match stream.pending_recv.pop_front(&mut self.buffer) {
|
||||||
Some(Event::Trailers(trailers)) => {
|
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>
|
impl<B> Recv<B, server::Peer>
|
||||||
@@ -610,15 +613,10 @@ impl<B> Recv<B, server::Peer>
|
|||||||
{
|
{
|
||||||
/// TODO: Should this fn return `Result`?
|
/// TODO: Should this fn return `Result`?
|
||||||
pub fn take_request(&mut self, stream: &mut store::Ptr<B, server::Peer>)
|
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) {
|
match stream.pending_recv.pop_front(&mut self.buffer) {
|
||||||
Some(Event::Headers(request)) => Ok(request),
|
Some(Event::Headers(request)) => request,
|
||||||
/*
|
|
||||||
// TODO: This error should probably be caught on receipt of the
|
|
||||||
// frame vs. now.
|
|
||||||
Ok(server::Peer::convert_poll_message(frame)?)
|
|
||||||
*/
|
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -628,7 +626,7 @@ impl<B> Recv<B, client::Peer>
|
|||||||
where B: Buf,
|
where B: Buf,
|
||||||
{
|
{
|
||||||
pub fn poll_response(&mut self, stream: &mut store::Ptr<B, client::Peer>)
|
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
|
// If the buffer is not empty, then the first frame must be a HEADERS
|
||||||
// frame or the user violated the contract.
|
// frame or the user violated the contract.
|
||||||
match stream.pending_recv.pop_front(&mut self.buffer) {
|
match stream.pending_recv.pop_front(&mut self.buffer) {
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
use {frame, ConnectionError};
|
use client;
|
||||||
|
use frame::{self, Reason};
|
||||||
|
use codec::{RecvError, UserError};
|
||||||
|
use codec::UserError::*;
|
||||||
use proto::*;
|
use proto::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use error::User::*;
|
|
||||||
|
|
||||||
use bytes::Buf;
|
use bytes::Buf;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
/// Manages state transitions related to outbound frames.
|
/// Manages state transitions related to outbound frames.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct Send<B, P>
|
pub(super) struct Send<B, P>
|
||||||
@@ -53,24 +56,11 @@ where B: Buf,
|
|||||||
self.init_window_sz
|
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
|
/// Update state reflecting a new, locally opened stream
|
||||||
///
|
///
|
||||||
/// Returns the stream state if successful. `None` if refused
|
/// Returns the stream state if successful. `None` if refused
|
||||||
pub fn open(&mut self)
|
pub fn open(&mut self)
|
||||||
-> Result<StreamId, ConnectionError>
|
-> Result<StreamId, UserError>
|
||||||
{
|
{
|
||||||
try!(self.ensure_can_open());
|
try!(self.ensure_can_open());
|
||||||
|
|
||||||
@@ -93,7 +83,7 @@ where B: Buf,
|
|||||||
frame: frame::Headers,
|
frame: frame::Headers,
|
||||||
stream: &mut store::Ptr<B, P>,
|
stream: &mut store::Ptr<B, P>,
|
||||||
task: &mut Option<Task>)
|
task: &mut Option<Task>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), UserError>
|
||||||
{
|
{
|
||||||
trace!("send_headers; frame={:?}; init_window={:?}", frame, self.init_window_sz);
|
trace!("send_headers; frame={:?}; init_window={:?}", frame, self.init_window_sz);
|
||||||
// Update the state
|
// Update the state
|
||||||
@@ -145,7 +135,7 @@ where B: Buf,
|
|||||||
frame: frame::Data<B>,
|
frame: frame::Data<B>,
|
||||||
stream: &mut store::Ptr<B, P>,
|
stream: &mut store::Ptr<B, P>,
|
||||||
task: &mut Option<Task>)
|
task: &mut Option<Task>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), UserError>
|
||||||
{
|
{
|
||||||
self.prioritize.send_data(frame, stream, task)
|
self.prioritize.send_data(frame, stream, task)
|
||||||
}
|
}
|
||||||
@@ -154,14 +144,14 @@ where B: Buf,
|
|||||||
frame: frame::Headers,
|
frame: frame::Headers,
|
||||||
stream: &mut store::Ptr<B, P>,
|
stream: &mut store::Ptr<B, P>,
|
||||||
task: &mut Option<Task>)
|
task: &mut Option<Task>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), UserError>
|
||||||
{
|
{
|
||||||
// TODO: Should this logic be moved into state.rs?
|
// TODO: Should this logic be moved into state.rs?
|
||||||
if !stream.state.is_send_streaming() {
|
if !stream.state.is_send_streaming() {
|
||||||
return Err(UnexpectedFrameType.into());
|
return Err(UnexpectedFrameType.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.state.send_close()?;
|
stream.state.send_close();
|
||||||
|
|
||||||
trace!("send_trailers -- queuing; frame={:?}", frame);
|
trace!("send_trailers -- queuing; frame={:?}", frame);
|
||||||
self.prioritize.queue_frame(frame.into(), stream, task);
|
self.prioritize.queue_frame(frame.into(), stream, task);
|
||||||
@@ -172,7 +162,7 @@ where B: Buf,
|
|||||||
pub fn poll_complete<T>(&mut self,
|
pub fn poll_complete<T>(&mut self,
|
||||||
store: &mut Store<B, P>,
|
store: &mut Store<B, P>,
|
||||||
dst: &mut Codec<T, Prioritized<B>>)
|
dst: &mut Codec<T, Prioritized<B>>)
|
||||||
-> Poll<(), ConnectionError>
|
-> Poll<(), io::Error>
|
||||||
where T: AsyncWrite,
|
where T: AsyncWrite,
|
||||||
{
|
{
|
||||||
self.prioritize.poll_complete(store, dst)
|
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>)
|
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() {
|
if !stream.state.is_send_streaming() {
|
||||||
return Ok(Async::Ready(None));
|
return Ok(Async::Ready(None));
|
||||||
@@ -214,7 +204,7 @@ where B: Buf,
|
|||||||
pub fn recv_connection_window_update(&mut self,
|
pub fn recv_connection_window_update(&mut self,
|
||||||
frame: frame::WindowUpdate,
|
frame: frame::WindowUpdate,
|
||||||
store: &mut Store<B, P>)
|
store: &mut Store<B, P>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), Reason>
|
||||||
{
|
{
|
||||||
self.prioritize.recv_connection_window_update(frame.size_increment(), store)
|
self.prioritize.recv_connection_window_update(frame.size_increment(), store)
|
||||||
}
|
}
|
||||||
@@ -223,11 +213,13 @@ where B: Buf,
|
|||||||
sz: WindowSize,
|
sz: WindowSize,
|
||||||
stream: &mut store::Ptr<B, P>,
|
stream: &mut store::Ptr<B, P>,
|
||||||
task: &mut Option<Task>)
|
task: &mut Option<Task>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), Reason>
|
||||||
{
|
{
|
||||||
if let Err(e) = self.prioritize.recv_stream_window_update(sz, stream) {
|
if let Err(e) = self.prioritize.recv_stream_window_update(sz, stream) {
|
||||||
debug!("recv_stream_window_update !!; err={:?}", e);
|
debug!("recv_stream_window_update !!; err={:?}", e);
|
||||||
self.send_reset(FlowControlError.into(), stream, task);
|
self.send_reset(FlowControlError.into(), stream, task);
|
||||||
|
|
||||||
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -237,7 +229,7 @@ where B: Buf,
|
|||||||
settings: &frame::Settings,
|
settings: &frame::Settings,
|
||||||
store: &mut Store<B, P>,
|
store: &mut Store<B, P>,
|
||||||
task: &mut Option<Task>)
|
task: &mut Option<Task>)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
if let Some(val) = settings.max_concurrent_streams() {
|
if let Some(val) = settings.max_concurrent_streams() {
|
||||||
self.max_streams = Some(val as usize);
|
self.max_streams = Some(val as usize);
|
||||||
@@ -283,13 +275,14 @@ where B: Buf,
|
|||||||
|
|
||||||
// TODO: Should this notify the producer?
|
// TODO: Should this notify the producer?
|
||||||
|
|
||||||
Ok(())
|
Ok::<_, RecvError>(())
|
||||||
})?;
|
})?;
|
||||||
} else if val > old_val {
|
} else if val > old_val {
|
||||||
let inc = val - old_val;
|
let inc = val - old_val;
|
||||||
|
|
||||||
store.for_each(|mut stream| {
|
store.for_each(|mut stream| {
|
||||||
self.recv_stream_window_update(inc, &mut stream, task)
|
self.recv_stream_window_update(inc, &mut stream, task)
|
||||||
|
.map_err(RecvError::Connection)
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,9 +290,9 @@ where B: Buf,
|
|||||||
Ok(())
|
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 {
|
if id >= self.next_stream_id {
|
||||||
return Err(ProtocolError.into());
|
return Err(ProtocolError);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -316,10 +309,10 @@ where B: Buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the local actor can initiate a stream with the given ID.
|
/// 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() {
|
if P::is_server() {
|
||||||
// Servers cannot open streams. PushPromise must first be reserved.
|
// Servers cannot open streams. PushPromise must first be reserved.
|
||||||
return Err(UnexpectedFrameType.into());
|
return Err(UnexpectedFrameType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle StreamId overflow
|
// TODO: Handle StreamId overflow
|
||||||
@@ -327,3 +320,18 @@ where B: Buf,
|
|||||||
Ok(())
|
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(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use ConnectionError;
|
use frame::Reason;
|
||||||
use proto::ProtoError;
|
use frame::Reason::*;
|
||||||
use error::Reason;
|
use codec::{RecvError, UserError};
|
||||||
use error::Reason::*;
|
use codec::UserError::*;
|
||||||
use error::User::*;
|
use proto;
|
||||||
|
|
||||||
use self::Inner::*;
|
use self::Inner::*;
|
||||||
use self::Peer::*;
|
use self::Peer::*;
|
||||||
@@ -82,7 +82,7 @@ enum Cause {
|
|||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
/// Opens the send-half of a stream if it is not already open.
|
/// 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;
|
let local = Peer::Streaming;
|
||||||
|
|
||||||
self.inner = match self.inner {
|
self.inner = match self.inner {
|
||||||
@@ -115,7 +115,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// All other transitions result in a protocol error
|
// All other transitions result in a protocol error
|
||||||
return Err(UnexpectedFrameType.into());
|
return Err(UnexpectedFrameType);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ impl State {
|
|||||||
/// frame is received.
|
/// frame is received.
|
||||||
///
|
///
|
||||||
/// Returns true if this transitions the state to Open
|
/// 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 remote = Peer::Streaming;
|
||||||
let mut initial = false;
|
let mut initial = false;
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// All other transitions result in a protocol error
|
// 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
|
/// Transition from Idle -> ReservedRemote
|
||||||
pub fn reserve_remote(&mut self) -> Result<(), ConnectionError> {
|
pub fn reserve_remote(&mut self) -> Result<(), RecvError> {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
Idle => {
|
Idle => {
|
||||||
self.inner = ReservedRemote;
|
self.inner = ReservedRemote;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(ProtocolError.into()),
|
_ => Err(RecvError::Connection(ProtocolError)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates that the remote side will not send more data to the local.
|
/// 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 {
|
match self.inner {
|
||||||
Open { local, .. } => {
|
Open { local, .. } => {
|
||||||
// The remote side will continue to receive data.
|
// The remote side will continue to receive data.
|
||||||
@@ -206,39 +206,38 @@ impl State {
|
|||||||
self.inner = Closed(None);
|
self.inner = Closed(None);
|
||||||
Ok(())
|
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 {
|
match self.inner {
|
||||||
Closed(..) => {}
|
Closed(..) => {}
|
||||||
_ => {
|
_ => {
|
||||||
trace!("recv_err; err={:?}", err);
|
trace!("recv_err; err={:?}", err);
|
||||||
self.inner = Closed(match *err {
|
self.inner = Closed(match *err {
|
||||||
ConnectionError::Proto(reason) => Some(Cause::Proto(reason)),
|
Proto(reason) => Some(Cause::Proto(reason)),
|
||||||
ConnectionError::Io(..) => Some(Cause::Io),
|
Io(..) => Some(Cause::Io),
|
||||||
ref e => panic!("cannot terminate stream with user error; err={:?}", e),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates that the local side will not send more data to the local.
|
/// 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 {
|
match self.inner {
|
||||||
Open { remote, .. } => {
|
Open { remote, .. } => {
|
||||||
// The remote side will continue to receive data.
|
// The remote side will continue to receive data.
|
||||||
trace!("send_close: Open => HalfClosedLocal({:?})", remote);
|
trace!("send_close: Open => HalfClosedLocal({:?})", remote);
|
||||||
self.inner = HalfClosedLocal(remote);
|
self.inner = HalfClosedLocal(remote);
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
HalfClosedRemote(..) => {
|
HalfClosedRemote(..) => {
|
||||||
trace!("send_close: HalfClosedRemote => Closed");
|
trace!("send_close: HalfClosedRemote => Closed");
|
||||||
self.inner = Closed(None);
|
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;
|
use std::io;
|
||||||
|
|
||||||
// TODO: Is this correct?
|
// TODO: Is this correct?
|
||||||
match self.inner {
|
match self.inner {
|
||||||
Closed(Some(Cause::Proto(reason))) => {
|
Closed(Some(Cause::Proto(reason))) => {
|
||||||
Err(ConnectionError::Proto(reason))
|
Err(proto::Error::Proto(reason))
|
||||||
}
|
}
|
||||||
Closed(Some(Cause::Io)) => {
|
Closed(Some(Cause::Io)) => {
|
||||||
Err(ConnectionError::Io(io::ErrorKind::BrokenPipe.into()))
|
Err(proto::Error::Io(io::ErrorKind::BrokenPipe.into()))
|
||||||
}
|
}
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,8 +127,8 @@ impl<B, P> Store<B, P>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_each<F>(&mut self, mut f: F) -> Result<(), ConnectionError>
|
pub fn for_each<F, E>(&mut self, mut f: F) -> Result<(), E>
|
||||||
where F: FnMut(Ptr<B, P>) -> Result<(), ConnectionError>,
|
where F: FnMut(Ptr<B, P>) -> Result<(), E>,
|
||||||
{
|
{
|
||||||
for &key in self.ids.values() {
|
for &key in self.ids.values() {
|
||||||
f(Ptr {
|
f(Ptr {
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
use {client, server, HeaderMap};
|
use {client, server, proto};
|
||||||
|
use frame::Reason;
|
||||||
|
use codec::{SendError, RecvError, UserError};
|
||||||
use proto::*;
|
use proto::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::store::Resolve;
|
use super::store::Resolve;
|
||||||
|
|
||||||
|
use http::HeaderMap;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -66,7 +71,7 @@ impl<B, P> Streams<B, P>
|
|||||||
|
|
||||||
/// Process inbound headers
|
/// Process inbound headers
|
||||||
pub fn recv_headers(&mut self, frame: frame::Headers)
|
pub fn recv_headers(&mut self, frame: frame::Headers)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
let id = frame.stream_id();
|
let id = frame.stream_id();
|
||||||
let mut me = self.inner.lock().unwrap();
|
let mut me = self.inner.lock().unwrap();
|
||||||
@@ -97,7 +102,7 @@ impl<B, P> Streams<B, P>
|
|||||||
} else {
|
} else {
|
||||||
if !frame.is_end_stream() {
|
if !frame.is_end_stream() {
|
||||||
// TODO: Is this the right error
|
// TODO: Is this the right error
|
||||||
return Err(ProtocolError.into());
|
return Err(RecvError::Connection(ProtocolError));
|
||||||
}
|
}
|
||||||
|
|
||||||
actions.recv.recv_trailers(frame, stream)
|
actions.recv.recv_trailers(frame, stream)
|
||||||
@@ -105,20 +110,18 @@ impl<B, P> Streams<B, P>
|
|||||||
|
|
||||||
// TODO: extract this
|
// TODO: extract this
|
||||||
match res {
|
match res {
|
||||||
Ok(()) => Ok(()),
|
Err(RecvError::Stream { reason, .. }) => {
|
||||||
Err(ProtoError::Connection(reason)) => Err(reason.into()),
|
|
||||||
Err(ProtoError::Stream { reason, .. }) => {
|
|
||||||
// Reset the stream.
|
// Reset the stream.
|
||||||
actions.send.send_reset(reason, stream, &mut actions.task);
|
actions.send.send_reset(reason, stream, &mut actions.task);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(ProtoError::Io(_)) => unreachable!(),
|
res => res,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv_data(&mut self, frame: frame::Data)
|
pub fn recv_data(&mut self, frame: frame::Data)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
let mut me = self.inner.lock().unwrap();
|
let mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
@@ -127,25 +130,23 @@ impl<B, P> Streams<B, P>
|
|||||||
|
|
||||||
let stream = match me.store.find_mut(&id) {
|
let stream = match me.store.find_mut(&id) {
|
||||||
Some(stream) => stream,
|
Some(stream) => stream,
|
||||||
None => return Err(ProtocolError.into()),
|
None => return Err(RecvError::Connection(ProtocolError)),
|
||||||
};
|
};
|
||||||
|
|
||||||
me.actions.transition(stream, |actions, stream| {
|
me.actions.transition(stream, |actions, stream| {
|
||||||
match actions.recv.recv_data(frame, stream) {
|
match actions.recv.recv_data(frame, stream) {
|
||||||
Ok(()) => Ok(()),
|
Err(RecvError::Stream { reason, .. }) => {
|
||||||
Err(ProtoError::Connection(reason)) => Err(reason.into()),
|
|
||||||
Err(ProtoError::Stream { reason, .. }) => {
|
|
||||||
// Reset the stream.
|
// Reset the stream.
|
||||||
actions.send.send_reset(reason, stream, &mut actions.task);
|
actions.send.send_reset(reason, stream, &mut actions.task);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(ProtoError::Io(_)) => unreachable!(),
|
res => res,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv_reset(&mut self, frame: frame::Reset)
|
pub fn recv_reset(&mut self, frame: frame::Reset)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
let mut me = self.inner.lock().unwrap();
|
let mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
@@ -153,14 +154,16 @@ impl<B, P> Streams<B, P>
|
|||||||
let id = frame.stream_id();
|
let id = frame.stream_id();
|
||||||
|
|
||||||
if id.is_zero() {
|
if id.is_zero() {
|
||||||
return Err(ProtocolError.into());
|
return Err(RecvError::Connection(ProtocolError));
|
||||||
}
|
}
|
||||||
|
|
||||||
let stream = match me.store.find_mut(&id) {
|
let stream = match me.store.find_mut(&id) {
|
||||||
Some(stream) => stream,
|
Some(stream) => stream,
|
||||||
None => {
|
None => {
|
||||||
// TODO: Are there other error cases?
|
// TODO: Are there other error cases?
|
||||||
me.actions.ensure_not_idle(id)?;
|
me.actions.ensure_not_idle(id)
|
||||||
|
.map_err(RecvError::Connection)?;
|
||||||
|
|
||||||
return Ok(());
|
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.
|
/// 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 mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
|
|
||||||
@@ -182,14 +185,14 @@ impl<B, P> Streams<B, P>
|
|||||||
|
|
||||||
me.store.for_each(|mut stream| {
|
me.store.for_each(|mut stream| {
|
||||||
actions.recv.recv_err(err, &mut *stream);
|
actions.recv.recv_err(err, &mut *stream);
|
||||||
Ok(())
|
Ok::<_, ()>(())
|
||||||
}).ok().expect("unexpected error processing error");
|
}).ok().expect("unexpected error processing error");
|
||||||
|
|
||||||
last_processed_id
|
last_processed_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv_window_update(&mut self, frame: frame::WindowUpdate)
|
pub fn recv_window_update(&mut self, frame: frame::WindowUpdate)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
let id = frame.stream_id();
|
let id = frame.stream_id();
|
||||||
let mut me = self.inner.lock().unwrap();
|
let mut me = self.inner.lock().unwrap();
|
||||||
@@ -197,15 +200,22 @@ impl<B, P> Streams<B, P>
|
|||||||
|
|
||||||
if id.is_zero() {
|
if id.is_zero() {
|
||||||
me.actions.send.recv_connection_window_update(
|
me.actions.send.recv_connection_window_update(
|
||||||
frame, &mut me.store)?;
|
frame, &mut me.store)
|
||||||
|
.map_err(RecvError::Connection)?;
|
||||||
} else {
|
} else {
|
||||||
// The remote may send window updates for streams that the local now
|
// The remote may send window updates for streams that the local now
|
||||||
// considers closed. It's ok...
|
// considers closed. It's ok...
|
||||||
if let Some(mut stream) = me.store.find_mut(&id) {
|
if let Some(mut stream) = me.store.find_mut(&id) {
|
||||||
me.actions.send.recv_stream_window_update(
|
// This result is ignored as there is nothing to do when there
|
||||||
frame.size_increment(), &mut stream, &mut me.actions.task)?;
|
// 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 {
|
} 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)
|
pub fn recv_push_promise(&mut self, frame: frame::PushPromise)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
let mut me = self.inner.lock().unwrap();
|
let mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
@@ -222,7 +232,7 @@ impl<B, P> Streams<B, P>
|
|||||||
|
|
||||||
let stream = match me.store.find_mut(&id) {
|
let stream = match me.store.find_mut(&id) {
|
||||||
Some(stream) => stream.key(),
|
Some(stream) => stream.key(),
|
||||||
None => return Err(ProtocolError.into()),
|
None => return Err(RecvError::Connection(ProtocolError)),
|
||||||
};
|
};
|
||||||
|
|
||||||
me.actions.recv.recv_push_promise(
|
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>>)
|
pub fn send_pending_refusal<T>(&mut self, dst: &mut Codec<T, Prioritized<B>>)
|
||||||
-> Poll<(), ConnectionError>
|
-> Poll<(), io::Error>
|
||||||
where T: AsyncWrite,
|
where T: AsyncWrite,
|
||||||
{
|
{
|
||||||
let mut me = self.inner.lock().unwrap();
|
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>>)
|
pub fn poll_complete<T>(&mut self, dst: &mut Codec<T, Prioritized<B>>)
|
||||||
-> Poll<(), ConnectionError>
|
-> Poll<(), io::Error>
|
||||||
where T: AsyncWrite,
|
where T: AsyncWrite,
|
||||||
{
|
{
|
||||||
let mut me = self.inner.lock().unwrap();
|
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)
|
pub fn apply_remote_settings(&mut self, frame: &frame::Settings)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), RecvError>
|
||||||
{
|
{
|
||||||
let mut me = self.inner.lock().unwrap();
|
let mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
@@ -286,15 +296,8 @@ impl<B, P> Streams<B, P>
|
|||||||
frame, &mut me.store, &mut me.actions.task)
|
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)
|
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 http::method;
|
||||||
use super::stream::ContentLength;
|
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 StreamRef =====
|
||||||
|
|
||||||
impl<B, P> StreamRef<B, P>
|
impl<B, P> StreamRef<B, P>
|
||||||
@@ -377,7 +391,7 @@ impl<B, P> StreamRef<B, P>
|
|||||||
P: Peer,
|
P: Peer,
|
||||||
{
|
{
|
||||||
pub fn send_data(&mut self, data: B, end_of_stream: bool)
|
pub fn send_data(&mut self, data: B, end_of_stream: bool)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), UserError>
|
||||||
{
|
{
|
||||||
let mut me = self.inner.lock().unwrap();
|
let mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
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 mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
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)
|
pub fn send_response(&mut self, response: Response<()>, end_of_stream: bool)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), UserError>
|
||||||
{
|
{
|
||||||
let mut me = self.inner.lock().unwrap();
|
let mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
@@ -444,7 +459,7 @@ impl<B, P> StreamRef<B, P>
|
|||||||
me.actions.recv.body_is_empty(&stream)
|
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 mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
|
|
||||||
@@ -453,7 +468,7 @@ impl<B, P> StreamRef<B, P>
|
|||||||
me.actions.recv.poll_data(&mut stream)
|
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 mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
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
|
/// Releases recv capacity back to the peer. This will result in sending
|
||||||
/// WINDOW_UPDATE frames on both the stream and connection.
|
/// WINDOW_UPDATE frames on both the stream and connection.
|
||||||
pub fn release_capacity(&mut self, capacity: WindowSize)
|
pub fn release_capacity(&mut self, capacity: WindowSize)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), UserError>
|
||||||
{
|
{
|
||||||
let mut me = self.inner.lock().unwrap();
|
let mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
@@ -497,7 +512,7 @@ impl<B, P> StreamRef<B, P>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Request to be notified when the stream's capacity increases
|
/// 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 mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
|
|
||||||
@@ -517,7 +532,7 @@ impl<B> StreamRef<B, server::Peer>
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This function panics if the request isn't present.
|
/// 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 mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
|
|
||||||
@@ -529,7 +544,7 @@ impl<B> StreamRef<B, server::Peer>
|
|||||||
impl<B> StreamRef<B, client::Peer>
|
impl<B> StreamRef<B, client::Peer>
|
||||||
where B: Buf,
|
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 mut me = self.inner.lock().unwrap();
|
||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
|
|
||||||
@@ -557,7 +572,7 @@ impl<B, P> Actions<B, P>
|
|||||||
P: Peer,
|
P: Peer,
|
||||||
{
|
{
|
||||||
fn ensure_not_idle(&mut self, id: StreamId)
|
fn ensure_not_idle(&mut self, id: StreamId)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), Reason>
|
||||||
{
|
{
|
||||||
if self.is_local_init(id) {
|
if self.is_local_init(id) {
|
||||||
self.send.ensure_not_idle(id)
|
self.send.ensure_not_idle(id)
|
||||||
|
|||||||
141
src/server.rs
141
src/server.rs
@@ -1,20 +1,19 @@
|
|||||||
use {HeaderMap, ConnectionError};
|
use frame::{self, StreamId, Reason};
|
||||||
use frame::{self, StreamId};
|
use frame::Reason::*;
|
||||||
use proto::{self, Connection, WindowSize, ProtoError};
|
use codec::{Codec, RecvError};
|
||||||
use error::Reason;
|
use proto::{self, Connection, WindowSize};
|
||||||
use error::Reason::*;
|
|
||||||
|
|
||||||
use http::{Request, Response};
|
use http::{Request, Response, HeaderMap};
|
||||||
use futures::{self, Future, Sink, Poll, Async, AsyncSink, IntoFuture};
|
use futures::{self, Future, Poll, Async};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
use bytes::{Bytes, IntoBuf};
|
use bytes::{Bytes, Buf, IntoBuf};
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// In progress H2 connection binding
|
/// In progress H2 connection binding
|
||||||
pub struct Handshake<T, B: IntoBuf = Bytes> {
|
pub struct Handshake<T, B: IntoBuf = Bytes> {
|
||||||
// TODO: unbox
|
// TODO: unbox
|
||||||
inner: Box<Future<Item = Server<T, B>, Error = ConnectionError>>,
|
inner: Box<Future<Item = Server<T, B>, Error = ::Error>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marker type indicating a client peer
|
/// Marker type indicating a client peer
|
||||||
@@ -43,13 +42,13 @@ pub struct Send<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Flush a Sink
|
/// Flush a Sink
|
||||||
struct Flush<T> {
|
struct Flush<T, B> {
|
||||||
inner: Option<T>,
|
codec: Option<Codec<T, B>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the client connection preface
|
/// Read the client connection preface
|
||||||
struct ReadPreface<T> {
|
struct ReadPreface<T, B> {
|
||||||
inner: Option<T>,
|
codec: Option<Codec<T, B>>,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,25 +76,21 @@ impl<T, B> Server<T, B>
|
|||||||
/// Returns a future which resolves to the connection value once the H2
|
/// Returns a future which resolves to the connection value once the H2
|
||||||
/// handshake has been completed.
|
/// handshake has been completed.
|
||||||
pub fn handshake2(io: T) -> Handshake<T, B> {
|
pub fn handshake2(io: T) -> Handshake<T, B> {
|
||||||
let mut framed_write = proto::framed_write(io);
|
// Create the codec
|
||||||
|
let mut codec = Codec::new(io);
|
||||||
|
|
||||||
|
// Create the initial SETTINGS frame
|
||||||
let settings = frame::Settings::default();
|
let settings = frame::Settings::default();
|
||||||
|
|
||||||
// Send initial settings frame
|
// Send initial settings frame
|
||||||
match framed_write.start_send(settings.into()) {
|
codec.buffer(settings.into())
|
||||||
Ok(AsyncSink::Ready) => {}
|
.ok().expect("invalid SETTINGS frame");
|
||||||
Ok(_) => unreachable!(),
|
|
||||||
Err(e) => {
|
|
||||||
return Handshake {
|
|
||||||
inner: Box::new(Err(ConnectionError::from(e)).into_future()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush pending settings frame and then wait for the client preface
|
// Flush pending settings frame and then wait for the client preface
|
||||||
let handshake = Flush::new(framed_write)
|
let handshake = Flush::new(codec)
|
||||||
.and_then(ReadPreface::new)
|
.and_then(ReadPreface::new)
|
||||||
.map(move |framed_write| {
|
.map(move |codec| {
|
||||||
let connection = proto::from_framed_write(framed_write);
|
let connection = Connection::new(codec);
|
||||||
Server { connection }
|
Server { connection }
|
||||||
})
|
})
|
||||||
;
|
;
|
||||||
@@ -104,8 +99,9 @@ impl<T, B> Server<T, B>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `Ready` when the underlying connection has closed.
|
/// Returns `Ready` when the underlying connection has closed.
|
||||||
pub fn poll_close(&mut self) -> Poll<(), ConnectionError> {
|
pub fn poll_close(&mut self) -> Poll<(), ::Error> {
|
||||||
self.connection.poll()
|
self.connection.poll()
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,9 +110,9 @@ impl<T, B> futures::Stream for Server<T, B>
|
|||||||
B: IntoBuf + 'static,
|
B: IntoBuf + 'static,
|
||||||
{
|
{
|
||||||
type Item = (Request<Body<B>>, Stream<B>);
|
type Item = (Request<Body<B>>, Stream<B>);
|
||||||
type Error = ConnectionError;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, ConnectionError> {
|
fn poll(&mut self) -> Poll<Option<Self::Item>, ::Error> {
|
||||||
// Always try to advance the internal state. Getting NotReady also is
|
// Always try to advance the internal state. Getting NotReady also is
|
||||||
// needed to allow this function to return NotReady.
|
// needed to allow this function to return NotReady.
|
||||||
match self.poll_close()? {
|
match self.poll_close()? {
|
||||||
@@ -130,7 +126,7 @@ impl<T, B> futures::Stream for Server<T, B>
|
|||||||
|
|
||||||
if let Some(inner) = self.connection.next_incoming() {
|
if let Some(inner) = self.connection.next_incoming() {
|
||||||
trace!("received incoming");
|
trace!("received incoming");
|
||||||
let (head, _) = inner.take_request()?.into_parts();
|
let (head, _) = inner.take_request().into_parts();
|
||||||
let body = Body { inner: inner.clone() };
|
let body = Body { inner: inner.clone() };
|
||||||
|
|
||||||
let request = Request::from_parts(head, body);
|
let request = Request::from_parts(head, body);
|
||||||
@@ -160,9 +156,10 @@ impl<T, B> fmt::Debug for Server<T, B>
|
|||||||
impl<B: IntoBuf> Stream<B> {
|
impl<B: IntoBuf> Stream<B> {
|
||||||
/// Send a response
|
/// Send a response
|
||||||
pub fn send_response(&mut self, response: Response<()>, end_of_stream: bool)
|
pub fn send_response(&mut self, response: Response<()>, end_of_stream: bool)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), ::Error>
|
||||||
{
|
{
|
||||||
self.inner.send_response(response, end_of_stream)
|
self.inner.send_response(response, end_of_stream)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request capacity to send data
|
/// Request capacity to send data
|
||||||
@@ -177,23 +174,25 @@ impl<B: IntoBuf> Stream<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Request to be notified when the stream's capacity increases
|
/// Request to be notified when the stream's capacity increases
|
||||||
pub fn poll_capacity(&mut self) -> Poll<Option<usize>, ConnectionError> {
|
pub fn poll_capacity(&mut self) -> Poll<Option<usize>, ::Error> {
|
||||||
let res = try_ready!(self.inner.poll_capacity());
|
let res = try_ready!(self.inner.poll_capacity());
|
||||||
Ok(Async::Ready(res.map(|v| v as usize)))
|
Ok(Async::Ready(res.map(|v| v as usize)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a single data frame
|
/// Send a single data frame
|
||||||
pub fn send_data(&mut self, data: B, end_of_stream: bool)
|
pub fn send_data(&mut self, data: B, end_of_stream: bool)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), ::Error>
|
||||||
{
|
{
|
||||||
self.inner.send_data(data.into_buf(), end_of_stream)
|
self.inner.send_data(data.into_buf(), end_of_stream)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send trailers
|
/// Send trailers
|
||||||
pub fn send_trailers(&mut self, trailers: HeaderMap)
|
pub fn send_trailers(&mut self, trailers: HeaderMap)
|
||||||
-> Result<(), ConnectionError>
|
-> Result<(), ::Error>
|
||||||
{
|
{
|
||||||
self.inner.send_trailers(trailers)
|
self.inner.send_trailers(trailers)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_reset(mut self, reason: Reason) {
|
pub fn send_reset(mut self, reason: Reason) {
|
||||||
@@ -204,7 +203,7 @@ impl<B: IntoBuf> Stream<B> {
|
|||||||
impl Stream<Bytes> {
|
impl Stream<Bytes> {
|
||||||
/// Send the body
|
/// Send the body
|
||||||
pub fn send<T>(self, src: T, end_of_stream: bool,) -> Send<T>
|
pub fn send<T>(self, src: T, end_of_stream: bool,) -> Send<T>
|
||||||
where T: futures::Stream<Item = Bytes, Error = ConnectionError>,
|
where T: futures::Stream<Item = Bytes, Error = ::Error>,
|
||||||
{
|
{
|
||||||
Send {
|
Send {
|
||||||
src: src,
|
src: src,
|
||||||
@@ -223,34 +222,37 @@ impl<B: IntoBuf> Body<B> {
|
|||||||
self.inner.body_is_empty()
|
self.inner.body_is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release_capacity(&mut self, sz: usize) -> Result<(), ConnectionError> {
|
pub fn release_capacity(&mut self, sz: usize) -> Result<(), ::Error> {
|
||||||
self.inner.release_capacity(sz as proto::WindowSize)
|
self.inner.release_capacity(sz as proto::WindowSize)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Poll trailers
|
/// Poll trailers
|
||||||
///
|
///
|
||||||
/// This function **must** not be called until `Body::poll` returns `None`.
|
/// This function **must** not be called until `Body::poll` returns `None`.
|
||||||
pub fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, ConnectionError> {
|
pub fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, ::Error> {
|
||||||
self.inner.poll_trailers()
|
self.inner.poll_trailers()
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: IntoBuf> futures::Stream for Body<B> {
|
impl<B: IntoBuf> futures::Stream for Body<B> {
|
||||||
type Item = Bytes;
|
type Item = Bytes;
|
||||||
type Error = ConnectionError;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||||
self.inner.poll_data()
|
self.inner.poll_data()
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl Send =====
|
// ===== impl Send =====
|
||||||
|
|
||||||
impl<T> Future for Send<T>
|
impl<T> Future for Send<T>
|
||||||
where T: futures::Stream<Item = Bytes, Error = ConnectionError>,
|
where T: futures::Stream<Item = Bytes, Error = ::Error>,
|
||||||
{
|
{
|
||||||
type Item = Stream<Bytes>;
|
type Item = Stream<Bytes>;
|
||||||
type Error = ConnectionError;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
loop {
|
loop {
|
||||||
@@ -299,41 +301,54 @@ impl<T> Future for Send<T>
|
|||||||
|
|
||||||
// ===== impl Flush =====
|
// ===== impl Flush =====
|
||||||
|
|
||||||
impl<T> Flush<T> {
|
impl<T, B: Buf> Flush<T, B> {
|
||||||
fn new(inner: T) -> Self {
|
fn new(codec: Codec<T, B>) -> Self {
|
||||||
Flush { inner: Some(inner) }
|
Flush { codec: Some(codec) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Sink> Future for Flush<T> {
|
impl<T, B> Future for Flush<T, B>
|
||||||
type Item = T;
|
where T: AsyncWrite,
|
||||||
type Error = T::SinkError;
|
B: Buf,
|
||||||
|
{
|
||||||
|
type Item = Codec<T, B>;
|
||||||
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<T, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
try_ready!(self.inner.as_mut().unwrap().poll_complete());
|
// Flush the codec
|
||||||
Ok(Async::Ready(self.inner.take().unwrap()))
|
try_ready!(self.codec.as_mut().unwrap().flush());
|
||||||
|
|
||||||
|
// Return the codec
|
||||||
|
Ok(Async::Ready(self.codec.take().unwrap()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ReadPreface<T> {
|
impl<T, B: Buf> ReadPreface<T, B> {
|
||||||
fn new(inner: T) -> Self {
|
fn new(codec: Codec<T, B>) -> Self {
|
||||||
ReadPreface {
|
ReadPreface {
|
||||||
inner: Some(inner),
|
codec: Some(codec),
|
||||||
pos: 0,
|
pos: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn inner_mut(&mut self) -> &mut T {
|
||||||
|
self.codec.as_mut().unwrap().get_mut()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsyncRead> Future for ReadPreface<T> {
|
impl<T, B> Future for ReadPreface<T, B>
|
||||||
type Item = T;
|
where T: AsyncRead,
|
||||||
type Error = ConnectionError;
|
B: Buf,
|
||||||
|
{
|
||||||
|
type Item = Codec<T, B>;
|
||||||
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<T, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
let mut buf = [0; 24];
|
let mut buf = [0; 24];
|
||||||
let mut rem = PREFACE.len() - self.pos;
|
let mut rem = PREFACE.len() - self.pos;
|
||||||
|
|
||||||
while rem > 0 {
|
while rem > 0 {
|
||||||
let n = try_nb!(self.inner.as_mut().unwrap().read(&mut buf[..rem]));
|
let n = try_nb!(self.inner_mut().read(&mut buf[..rem]));
|
||||||
|
|
||||||
if PREFACE[self.pos..self.pos+n] != buf[..n] {
|
if PREFACE[self.pos..self.pos+n] != buf[..n] {
|
||||||
// TODO: Should this just write the GO_AWAY frame directly?
|
// TODO: Should this just write the GO_AWAY frame directly?
|
||||||
@@ -344,7 +359,7 @@ impl<T: AsyncRead> Future for ReadPreface<T> {
|
|||||||
rem -= n; // TODO test
|
rem -= n; // TODO test
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Async::Ready(self.inner.take().unwrap()))
|
Ok(Async::Ready(self.codec.take().unwrap()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +367,7 @@ impl<T: AsyncRead> Future for ReadPreface<T> {
|
|||||||
|
|
||||||
impl<T, B: IntoBuf> Future for Handshake<T, B> {
|
impl<T, B: IntoBuf> Future for Handshake<T, B> {
|
||||||
type Item = Server<T, B>;
|
type Item = Server<T, B>;
|
||||||
type Error = ConnectionError;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
self.inner.poll()
|
self.inner.poll()
|
||||||
@@ -401,7 +416,7 @@ impl proto::Peer for Peer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn convert_poll_message(headers: frame::Headers)
|
fn convert_poll_message(headers: frame::Headers)
|
||||||
-> Result<Self::Poll, ProtoError>
|
-> Result<Self::Poll, RecvError>
|
||||||
{
|
{
|
||||||
use http::{version, uri};
|
use http::{version, uri};
|
||||||
|
|
||||||
@@ -412,7 +427,7 @@ impl proto::Peer for Peer {
|
|||||||
|
|
||||||
macro_rules! malformed {
|
macro_rules! malformed {
|
||||||
() => {
|
() => {
|
||||||
return Err(ProtoError::Stream {
|
return Err(RecvError::Stream {
|
||||||
id: stream_id,
|
id: stream_id,
|
||||||
reason: ProtocolError,
|
reason: ProtocolError,
|
||||||
});
|
});
|
||||||
@@ -429,7 +444,7 @@ impl proto::Peer for Peer {
|
|||||||
|
|
||||||
// Specifying :status for a request is a protocol error
|
// Specifying :status for a request is a protocol error
|
||||||
if pseudo.status.is_some() {
|
if pseudo.status.is_some() {
|
||||||
return Err(ProtoError::Connection(ProtocolError));
|
return Err(RecvError::Connection(ProtocolError));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the URI
|
// Convert the URI
|
||||||
@@ -464,7 +479,7 @@ impl proto::Peer for Peer {
|
|||||||
Err(_) => {
|
Err(_) => {
|
||||||
// TODO: Should there be more specialized handling for different
|
// TODO: Should there be more specialized handling for different
|
||||||
// kinds of errors
|
// kinds of errors
|
||||||
return Err(ProtoError::Stream {
|
return Err(RecvError::Stream {
|
||||||
id: stream_id,
|
id: stream_id,
|
||||||
reason: ProtocolError,
|
reason: ProtocolError,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -53,10 +53,10 @@ fn recv_invalid_server_stream_id() {
|
|||||||
let stream = h2.request(request, true).unwrap();
|
let stream = h2.request(request, true).unwrap();
|
||||||
|
|
||||||
// The connection errors
|
// The connection errors
|
||||||
assert_proto_err!(h2.wait().unwrap_err(), ProtocolError);
|
assert!(h2.wait().is_err());
|
||||||
|
|
||||||
// The stream errors
|
// The stream errors
|
||||||
assert_proto_err!(stream.wait().unwrap_err(), ProtocolError);
|
assert!(stream.wait().is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -89,27 +89,3 @@ pub mod frames {
|
|||||||
pub const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
|
pub const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
|
||||||
pub const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
|
pub const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! assert_user_err {
|
|
||||||
($actual:expr, $err:ident) => {{
|
|
||||||
use h2::error::{ConnectionError, User};
|
|
||||||
|
|
||||||
match $actual {
|
|
||||||
ConnectionError::User(e) => assert_eq!(e, User::$err),
|
|
||||||
_ => panic!("unexpected connection error type"),
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! assert_proto_err {
|
|
||||||
($actual:expr, $err:ident) => {{
|
|
||||||
use h2::error::{ConnectionError, Reason};
|
|
||||||
|
|
||||||
match $actual {
|
|
||||||
ConnectionError::Proto(e) => assert_eq!(e, Reason::$err),
|
|
||||||
_ => panic!("unexpected connection error type"),
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|||||||
18
util/h2-codec/Cargo.toml
Normal file
18
util/h2-codec/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "h2-codec"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Carl Lerche <me@carllerche.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
http = { git = "https://github.com/carllerche/http" }
|
||||||
|
log = "0.3.8"
|
||||||
|
fnv = "1.0.5"
|
||||||
|
bytes = "0.4"
|
||||||
|
string = { git = "https://github.com/carllerche/string" }
|
||||||
|
byteorder = "1.0"
|
||||||
|
futures = "0.1"
|
||||||
|
tokio-io = "0.1.3"
|
||||||
|
|
||||||
|
# tokio-timer = "0.1"
|
||||||
|
# http = { git = "https://github.com/carllerche/http", branch = "lower-case-header-name-parsing" }
|
||||||
|
# slab = "0.4.0"
|
||||||
22
util/h2-codec/src/lib.rs
Normal file
22
util/h2-codec/src/lib.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
extern crate http;
|
||||||
|
extern crate fnv;
|
||||||
|
extern crate bytes;
|
||||||
|
extern crate string;
|
||||||
|
extern crate byteorder;
|
||||||
|
|
||||||
|
extern crate futures;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate tokio_io;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
#[path = "../../../src/hpack/mod.rs"]
|
||||||
|
mod hpack;
|
||||||
|
|
||||||
|
#[path = "../../../src/frame/mod.rs"]
|
||||||
|
mod frame;
|
||||||
|
|
||||||
|
#[path = "../../../src/codec/mod.rs"]
|
||||||
|
mod codec;
|
||||||
Reference in New Issue
Block a user