Merge branch 'master' into ver/flowio
This commit is contained in:
@@ -43,9 +43,21 @@ pub fn main() {
|
|||||||
println!("Zomg frame; {:?}", frame);
|
println!("Zomg frame; {:?}", frame);
|
||||||
|
|
||||||
let mut response = response::Head::default();
|
let mut response = response::Head::default();
|
||||||
response.status = status::NO_CONTENT;
|
response.status = status::OK;
|
||||||
|
|
||||||
conn.send_response(1.into(), response, true)
|
conn.send_response(1.into(), response, false)
|
||||||
|
})
|
||||||
|
.then(|res| {
|
||||||
|
let conn = res.unwrap();
|
||||||
|
println!("... sending data frame");
|
||||||
|
|
||||||
|
conn.send_data(1.into(), "hello".into(), false)
|
||||||
|
})
|
||||||
|
.then(|res| {
|
||||||
|
let conn = res.unwrap();
|
||||||
|
println!("... sending next frame");
|
||||||
|
|
||||||
|
conn.send_data(1.into(), "world".into(), true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(|res| {
|
.then(|res| {
|
||||||
|
|||||||
@@ -3,26 +3,33 @@ use {frame, proto, Peer, ConnectionError, StreamId};
|
|||||||
use http;
|
use http;
|
||||||
use futures::{Future, Poll};
|
use futures::{Future, Poll};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
|
use bytes::{Bytes, IntoBuf};
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// In progress H2 connection binding
|
/// In progress H2 connection binding
|
||||||
pub struct Handshake<T> {
|
pub struct Handshake<T, B: IntoBuf = Bytes> {
|
||||||
// TODO: unbox
|
// TODO: unbox
|
||||||
inner: Box<Future<Item = Connection<T>, Error = ConnectionError>>,
|
inner: Box<Future<Item = Connection<T, B>, Error = ConnectionError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marker type indicating a client peer
|
/// Marker type indicating a client peer
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Client;
|
pub struct Client;
|
||||||
|
|
||||||
pub type Connection<T> = super::Connection<T, Client>;
|
pub type Connection<T, B = Bytes> = super::Connection<T, Client, B>;
|
||||||
|
|
||||||
|
pub fn handshake<T>(io: T) -> Handshake<T, Bytes>
|
||||||
|
where T: AsyncRead + AsyncWrite + 'static,
|
||||||
|
{
|
||||||
|
handshake2(io)
|
||||||
|
}
|
||||||
|
|
||||||
/// Bind an H2 client connection.
|
/// Bind an H2 client connection.
|
||||||
///
|
///
|
||||||
/// 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 handshake<T>(io: T) -> Handshake<T>
|
pub fn handshake2<T, B: IntoBuf>(io: T) -> Handshake<T, B>
|
||||||
where T: AsyncRead + AsyncWrite + 'static,
|
where T: AsyncRead + AsyncWrite + 'static,
|
||||||
{
|
{
|
||||||
use tokio_io::io;
|
use tokio_io::io;
|
||||||
@@ -97,8 +104,8 @@ impl Peer for Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Future for Handshake<T> {
|
impl<T, B: IntoBuf> Future for Handshake<T, B> {
|
||||||
type Item = Connection<T>;
|
type Item = Connection<T, B>;
|
||||||
type Error = ConnectionError;
|
type Error = ConnectionError;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
@@ -106,7 +113,10 @@ impl<T> Future for Handshake<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: fmt::Debug> fmt::Debug for Handshake<T> {
|
impl<T, B> fmt::Debug for Handshake<T, B>
|
||||||
|
where T: fmt::Debug,
|
||||||
|
B: fmt::Debug + IntoBuf,
|
||||||
|
{
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(fmt, "client::Handshake")
|
write!(fmt, "client::Handshake")
|
||||||
}
|
}
|
||||||
|
|||||||
99
src/error.rs
99
src/error.rs
@@ -3,14 +3,24 @@ use std::{error, fmt, io};
|
|||||||
/// The error type for HTTP/2 operations
|
/// The error type for HTTP/2 operations
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ConnectionError {
|
pub enum ConnectionError {
|
||||||
/// The HTTP/2 stream was reset
|
/// 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
|
||||||
|
/// action taken by the peer (i.e. a protocol error).
|
||||||
Proto(Reason),
|
Proto(Reason),
|
||||||
|
|
||||||
/// An `io::Error` occurred while trying to read or write.
|
/// An `io::Error` occurred while trying to read or write.
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
|
|
||||||
|
/// An error resulting from an invalid action taken by the user of this
|
||||||
|
/// library.
|
||||||
|
User(User),
|
||||||
|
|
||||||
|
// TODO: reserve additional variants
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StreamError(Reason);
|
pub struct Stream(Reason);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub enum Reason {
|
pub enum Reason {
|
||||||
@@ -29,27 +39,60 @@ pub enum Reason {
|
|||||||
InadequateSecurity,
|
InadequateSecurity,
|
||||||
Http11Required,
|
Http11Required,
|
||||||
Other(u32),
|
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,
|
||||||
|
|
||||||
|
// TODO: reserve additional variants
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! reason_desc {
|
macro_rules! reason_desc {
|
||||||
($reason:expr) => (reason_desc!($reason, ""));
|
($reason:expr) => (reason_desc!($reason, ""));
|
||||||
($reason:expr, $prefix:expr) => ({
|
($reason:expr, $prefix:expr) => ({
|
||||||
|
use self::Reason::*;
|
||||||
|
|
||||||
match $reason {
|
match $reason {
|
||||||
Reason::NoError => concat!($prefix, "not a result of an error"),
|
NoError => concat!($prefix, "not a result of an error"),
|
||||||
Reason::ProtocolError => concat!($prefix, "unspecific protocol error detected"),
|
ProtocolError => concat!($prefix, "unspecific protocol error detected"),
|
||||||
Reason::InternalError => concat!($prefix, "unexpected internal error encountered"),
|
InternalError => concat!($prefix, "unexpected internal error encountered"),
|
||||||
Reason::FlowControlError => concat!($prefix, "flow-control protocol violated"),
|
FlowControlError => concat!($prefix, "flow-control protocol violated"),
|
||||||
Reason::SettingsTimeout => concat!($prefix, "settings ACK not received in timely manner"),
|
SettingsTimeout => concat!($prefix, "settings ACK not received in timely manner"),
|
||||||
Reason::StreamClosed => concat!($prefix, "received frame when stream half-closed"),
|
StreamClosed => concat!($prefix, "received frame when stream half-closed"),
|
||||||
Reason::FrameSizeError => concat!($prefix, "frame sent with invalid size"),
|
FrameSizeError => concat!($prefix, "frame sent with invalid size"),
|
||||||
Reason::RefusedStream => concat!($prefix, "refused stream before processing any application logic"),
|
RefusedStream => concat!($prefix, "refused stream before processing any application logic"),
|
||||||
Reason::Cancel => concat!($prefix, "stream no longer needed"),
|
Cancel => concat!($prefix, "stream no longer needed"),
|
||||||
Reason::CompressionError => concat!($prefix, "unable to maintain the header compression context"),
|
CompressionError => concat!($prefix, "unable to maintain the header compression context"),
|
||||||
Reason::ConnectError => concat!($prefix, "connection established in response to a CONNECT request was reset or abnormally closed"),
|
ConnectError => concat!($prefix, "connection established in response to a CONNECT request was reset or abnormally closed"),
|
||||||
Reason::EnhanceYourCalm => concat!($prefix, "detected excessive load generating behavior"),
|
EnhanceYourCalm => concat!($prefix, "detected excessive load generating behavior"),
|
||||||
Reason::InadequateSecurity => concat!($prefix, "security properties do not meet minimum requirements"),
|
InadequateSecurity => concat!($prefix, "security properties do not meet minimum requirements"),
|
||||||
Reason::Http11Required => concat!($prefix, "endpoint requires HTTP/1.1"),
|
Http11Required => concat!($prefix, "endpoint requires HTTP/1.1"),
|
||||||
Reason::Other(_) => concat!($prefix, "other reason"),
|
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"),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -68,6 +111,12 @@ impl From<Reason> for ConnectionError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<User> for ConnectionError {
|
||||||
|
fn from(src: User) -> ConnectionError {
|
||||||
|
ConnectionError::User(src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ConnectionError> for io::Error {
|
impl From<ConnectionError> for io::Error {
|
||||||
fn from(src: ConnectionError) -> io::Error {
|
fn from(src: ConnectionError) -> io::Error {
|
||||||
io::Error::new(io::ErrorKind::Other, src)
|
io::Error::new(io::ErrorKind::Other, src)
|
||||||
@@ -81,6 +130,7 @@ impl fmt::Display for ConnectionError {
|
|||||||
match *self {
|
match *self {
|
||||||
Proto(reason) => write!(fmt, "protocol error: {}", reason),
|
Proto(reason) => write!(fmt, "protocol error: {}", reason),
|
||||||
Io(ref e) => fmt::Display::fmt(e, fmt),
|
Io(ref e) => fmt::Display::fmt(e, fmt),
|
||||||
|
User(e) => write!(fmt, "user error: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,6 +142,7 @@ impl error::Error for ConnectionError {
|
|||||||
match *self {
|
match *self {
|
||||||
Io(ref e) => error::Error::description(e),
|
Io(ref e) => error::Error::description(e),
|
||||||
Proto(reason) => reason_desc!(reason, "protocol error: "),
|
Proto(reason) => reason_desc!(reason, "protocol error: "),
|
||||||
|
User(user) => user_desc!(user, "user error: "),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,3 +208,17 @@ impl fmt::Display for Reason {
|
|||||||
write!(fmt, "{}", self.description())
|
write!(fmt, "{}", self.description())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== impl User =====
|
||||||
|
|
||||||
|
impl User {
|
||||||
|
pub fn description(&self) -> &str {
|
||||||
|
user_desc!(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for User {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(fmt, "{}", self.description())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use frame::{util, Frame, Head, Error, StreamId, Kind};
|
use frame::{util, Frame, Head, Error, StreamId, Kind};
|
||||||
use bytes::{BufMut, Bytes};
|
use bytes::{BufMut, Bytes, Buf};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Data {
|
pub struct Data<T = Bytes> {
|
||||||
stream_id: StreamId,
|
stream_id: StreamId,
|
||||||
data: Bytes,
|
data: T,
|
||||||
flags: DataFlag,
|
flags: DataFlag,
|
||||||
pad_len: Option<u8>,
|
pad_len: Option<u8>,
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,8 @@ const END_STREAM: u8 = 0x1;
|
|||||||
const PADDED: u8 = 0x8;
|
const PADDED: u8 = 0x8;
|
||||||
const ALL: u8 = END_STREAM | PADDED;
|
const ALL: u8 = END_STREAM | PADDED;
|
||||||
|
|
||||||
impl Data {
|
impl Data<Bytes> {
|
||||||
pub fn load(head: Head, mut payload: Bytes) -> Result<Data, Error> {
|
pub fn load(head: Head, mut payload: Bytes) -> Result<Self, Error> {
|
||||||
let flags = DataFlag::load(head.flag());
|
let flags = DataFlag::load(head.flag());
|
||||||
|
|
||||||
let pad_len = if flags.is_padded() {
|
let pad_len = if flags.is_padded() {
|
||||||
@@ -34,35 +34,56 @@ impl Data {
|
|||||||
pad_len: pad_len,
|
pad_len: pad_len,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Data<T> {
|
||||||
|
pub fn new(stream_id: StreamId, data: T) -> Self {
|
||||||
|
Data {
|
||||||
|
stream_id: stream_id,
|
||||||
|
data: data,
|
||||||
|
flags: DataFlag::default(),
|
||||||
|
pad_len: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stream_id(&self) -> StreamId {
|
pub fn stream_id(&self) -> StreamId {
|
||||||
self.stream_id
|
self.stream_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.data.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_end_stream(&self) -> bool {
|
pub fn is_end_stream(&self) -> bool {
|
||||||
self.flags.is_end_stream()
|
self.flags.is_end_stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode<T: BufMut>(&self, dst: &mut T) {
|
pub fn set_end_stream(&mut self) {
|
||||||
self.head().encode(self.len(), dst);
|
self.flags.set_end_stream()
|
||||||
dst.put(&self.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn head(&self) -> Head {
|
pub fn head(&self) -> Head {
|
||||||
Head::new(Kind::Data, self.flags.into(), self.stream_id)
|
Head::new(Kind::Data, self.flags.into(), self.stream_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_payload(self) -> Bytes {
|
pub fn into_payload(self) -> T {
|
||||||
self.data
|
self.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Data> for Frame {
|
impl<T: Buf> Data<T> {
|
||||||
fn from(src: Data) -> Frame {
|
pub fn len(&self) -> usize {
|
||||||
|
self.data.remaining()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_chunk<U: BufMut>(&mut self, dst: &mut U) {
|
||||||
|
if self.len() > dst.remaining_mut() {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.head().encode(self.len(), dst);
|
||||||
|
dst.put(&mut self.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<Data<T>> for Frame<T> {
|
||||||
|
fn from(src: Data<T>) -> Self {
|
||||||
Frame::Data(src)
|
Frame::Data(src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,11 +107,22 @@ impl DataFlag {
|
|||||||
self.0 & END_STREAM == END_STREAM
|
self.0 & END_STREAM == END_STREAM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_end_stream(&mut self) {
|
||||||
|
self.0 |= END_STREAM
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_padded(&self) -> bool {
|
pub fn is_padded(&self) -> bool {
|
||||||
self.0 & PADDED == PADDED
|
self.0 & PADDED == PADDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for DataFlag {
|
||||||
|
/// Returns a `HeadersFlag` value with `END_HEADERS` set.
|
||||||
|
fn default() -> Self {
|
||||||
|
DataFlag(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<DataFlag> for u8 {
|
impl From<DataFlag> for u8 {
|
||||||
fn from(src: DataFlag) -> u8 {
|
fn from(src: DataFlag) -> u8 {
|
||||||
src.0
|
src.0
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use hpack;
|
use hpack;
|
||||||
use error::{ConnectionError, Reason};
|
use error::{ConnectionError, Reason};
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
|
||||||
/// A helper macro that unpacks a sequence of 4 bytes found in the buffer with
|
/// A helper macro that unpacks a sequence of 4 bytes found in the buffer with
|
||||||
/// the given identifier, starting at the given offset, into the given integer
|
/// the given identifier, starting at the given offset, into the given integer
|
||||||
/// type. Obviously, the integer type should be able to support at least 4
|
/// type. Obviously, the integer type should be able to support at least 4
|
||||||
@@ -53,8 +55,8 @@ pub use self::settings::{
|
|||||||
pub const HEADER_LEN: usize = 9;
|
pub const HEADER_LEN: usize = 9;
|
||||||
|
|
||||||
#[derive(Debug /*, Clone, PartialEq */)]
|
#[derive(Debug /*, Clone, PartialEq */)]
|
||||||
pub enum Frame {
|
pub enum Frame<T = Bytes> {
|
||||||
Data(Data),
|
Data(Data<T>),
|
||||||
Headers(Headers),
|
Headers(Headers),
|
||||||
PushPromise(PushPromise),
|
PushPromise(PushPromise),
|
||||||
Settings(Settings),
|
Settings(Settings),
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ impl Ping {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Ping> for Frame {
|
impl<T> From<Ping> for Frame<T> {
|
||||||
fn from(src: Ping) -> Frame {
|
fn from(src: Ping) -> Frame<T> {
|
||||||
Frame::Ping(src)
|
Frame::Ping(src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,8 +175,8 @@ impl Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Settings> for Frame {
|
impl<T> From<Settings> for Frame<T> {
|
||||||
fn from(src: Settings) -> Frame {
|
fn from(src: Settings) -> Frame<T> {
|
||||||
Frame::Settings(src)
|
Frame::Settings(src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
use StreamId;
|
use StreamId;
|
||||||
use byteorder::{ByteOrder, NetworkEndian};
|
use byteorder::NetworkEndian;
|
||||||
use bytes::{BufMut};
|
use bytes::{BufMut};
|
||||||
use frame::{self, Head, Kind, Error};
|
use frame::{self, Head, Kind, Error};
|
||||||
|
|
||||||
const INCREMENT_MASK: u32 = 1 << 31;
|
const SIZE_INCREMENT_MASK: u32 = 1 << 31;
|
||||||
|
|
||||||
type Increment = u32;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WindowUpdate {
|
pub struct WindowUpdate {
|
||||||
stream_id: StreamId,
|
stream_id: StreamId,
|
||||||
increment: Increment,
|
size_increment: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowUpdate {
|
impl WindowUpdate {
|
||||||
pub fn new(stream_id: StreamId, increment: Increment) -> WindowUpdate {
|
pub fn new(stream_id: StreamId, size_increment: u32) -> WindowUpdate {
|
||||||
WindowUpdate {
|
WindowUpdate {
|
||||||
stream_id,
|
stream_id,
|
||||||
increment,
|
size_increment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,18 +23,24 @@ impl WindowUpdate {
|
|||||||
self.stream_id
|
self.stream_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn increment(&self) -> Increment {
|
pub fn size_increment(&self) -> u32 {
|
||||||
self.increment
|
self.size_increment
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a `WindowUpdate` frame from a raw frame.
|
/// Builds a `WindowUpdate` frame from a raw frame.
|
||||||
pub fn load(head: Head, bytes: &[u8]) -> Result<WindowUpdate, Error> {
|
pub fn load(head: Head, payload: &[u8]) -> Result<WindowUpdate, Error> {
|
||||||
debug_assert_eq!(head.kind(), ::frame::Kind::WindowUpdate);
|
debug_assert_eq!(head.kind(), ::frame::Kind::WindowUpdate);
|
||||||
Ok(WindowUpdate {
|
if payload.len() != 4 {
|
||||||
stream_id: head.stream_id(),
|
return Err(Error::BadFrameSize);
|
||||||
|
}
|
||||||
|
|
||||||
// Clear the most significant bit, as that is reserved and MUST be ignored
|
// Clear the most significant bit, as that is reserved and MUST be ignored
|
||||||
// when received.
|
// when received.
|
||||||
increment: NetworkEndian::read_u32(bytes) & !INCREMENT_MASK,
|
let size_increment = unpack_octets_4!(payload, 0, u32) & !SIZE_INCREMENT_MASK;
|
||||||
|
|
||||||
|
Ok(WindowUpdate {
|
||||||
|
stream_id: head.stream_id(),
|
||||||
|
size_increment,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +48,7 @@ impl WindowUpdate {
|
|||||||
trace!("encoding WINDOW_UPDATE; id={:?}", self.stream_id);
|
trace!("encoding WINDOW_UPDATE; id={:?}", self.stream_id);
|
||||||
let head = Head::new(Kind::Ping, 0, self.stream_id);
|
let head = Head::new(Kind::Ping, 0, self.stream_id);
|
||||||
head.encode(4, dst);
|
head.encode(4, dst);
|
||||||
dst.put_u32::<NetworkEndian>(self.increment);
|
dst.put_u32::<NetworkEndian>(self.size_increment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub mod server;
|
|||||||
|
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
pub use error::{ConnectionError, StreamError, Reason};
|
pub use error::ConnectionError;
|
||||||
pub use frame::{StreamId};
|
pub use frame::{StreamId};
|
||||||
pub use proto::Connection;
|
pub use proto::Connection;
|
||||||
|
|
||||||
@@ -42,15 +42,15 @@ use bytes::Bytes;
|
|||||||
|
|
||||||
/// An H2 connection frame
|
/// An H2 connection frame
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Frame<T> {
|
pub enum Frame<T, B = Bytes> {
|
||||||
Headers {
|
Headers {
|
||||||
id: StreamId,
|
id: StreamId,
|
||||||
headers: T,
|
headers: T,
|
||||||
end_of_stream: bool,
|
end_of_stream: bool,
|
||||||
},
|
},
|
||||||
Body {
|
Data {
|
||||||
id: StreamId,
|
id: StreamId,
|
||||||
chunk: Bytes,
|
data: B,
|
||||||
end_of_stream: bool,
|
end_of_stream: bool,
|
||||||
},
|
},
|
||||||
Trailers {
|
Trailers {
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
use {Frame, ConnectionError, Peer, StreamId};
|
use Frame;
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use frame::{Frame as WireFrame};
|
use error::{self, ConnectionError};
|
||||||
use proto::{self, FlowController, ReadySink, PeerState, State, WindowUpdate};
|
use frame::{self, StreamId};
|
||||||
|
use proto::{self, Peer, ReadySink, State, PeerState, WindowUpdate, FlowController};
|
||||||
use server::Server;
|
use server::Server;
|
||||||
|
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use http::{request, response};
|
use http::{request, response};
|
||||||
|
use bytes::{Bytes, IntoBuf};
|
||||||
|
|
||||||
use futures::*;
|
use futures::*;
|
||||||
|
|
||||||
@@ -21,8 +23,8 @@ use std::marker::PhantomData;
|
|||||||
|
|
||||||
/// An H2 connection
|
/// An H2 connection
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Connection<T, P> {
|
pub struct Connection<T, P, B: IntoBuf = Bytes> {
|
||||||
inner: proto::Inner<T>,
|
inner: proto::Inner<T, B::Buf>,
|
||||||
streams: StreamMap<State>,
|
streams: StreamMap<State>,
|
||||||
peer: PhantomData<P>,
|
peer: PhantomData<P>,
|
||||||
|
|
||||||
@@ -39,12 +41,13 @@ pub struct Connection<T, P> {
|
|||||||
|
|
||||||
type StreamMap<T> = OrderMap<StreamId, T, BuildHasherDefault<FnvHasher>>;
|
type StreamMap<T> = OrderMap<StreamId, T, BuildHasherDefault<FnvHasher>>;
|
||||||
|
|
||||||
pub fn new<T, P>(transport: proto::Inner<T>,
|
pub fn new<T, P, B>(transport: proto::Inner<T, B::Buf>,
|
||||||
initial_local_window_size: u32,
|
initial_local_window_size: u32,
|
||||||
initial_remote_window_size: u32)
|
initial_remote_window_size: u32)
|
||||||
-> Connection<T, P>
|
-> Connection<T, P, B>
|
||||||
where T: AsyncRead + AsyncWrite,
|
where T: AsyncRead + AsyncWrite,
|
||||||
P: Peer,
|
P: Peer,
|
||||||
|
B: IntoBuf,
|
||||||
{
|
{
|
||||||
Connection {
|
Connection {
|
||||||
inner: transport,
|
inner: transport,
|
||||||
@@ -62,7 +65,8 @@ pub fn new<T, P>(transport: proto::Inner<T>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P> Connection<T, P> {
|
impl<T, P, B: IntoBuf> Connection<T, P, B> {
|
||||||
|
|
||||||
/// Publishes local stream window updates to the remote.
|
/// Publishes local stream window updates to the remote.
|
||||||
///
|
///
|
||||||
/// Connection window updates (StreamId=0) and stream window must be published
|
/// Connection window updates (StreamId=0) and stream window must be published
|
||||||
@@ -118,8 +122,28 @@ impl<T, P> Connection<T, P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Connection<T, Client>
|
impl<T, P, B> Connection<T, P, B>
|
||||||
where T: AsyncRead + AsyncWrite,
|
where T: AsyncRead + AsyncWrite,
|
||||||
|
P: Peer,
|
||||||
|
B: IntoBuf,
|
||||||
|
{
|
||||||
|
pub fn send_data(self,
|
||||||
|
id: StreamId,
|
||||||
|
data: B,
|
||||||
|
end_of_stream: bool)
|
||||||
|
-> sink::Send<Self>
|
||||||
|
{
|
||||||
|
self.send(Frame::Data {
|
||||||
|
id: id,
|
||||||
|
data: data,
|
||||||
|
end_of_stream: end_of_stream,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, B> Connection<T, Client, B>
|
||||||
|
where T: AsyncRead + AsyncWrite,
|
||||||
|
B: IntoBuf,
|
||||||
{
|
{
|
||||||
pub fn send_request(self,
|
pub fn send_request(self,
|
||||||
id: StreamId, // TODO: Generate one internally?
|
id: StreamId, // TODO: Generate one internally?
|
||||||
@@ -135,8 +159,9 @@ impl<T> Connection<T, Client>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Connection<T, Server>
|
impl<T, B> Connection<T, Server, B>
|
||||||
where T: AsyncRead + AsyncWrite,
|
where T: AsyncRead + AsyncWrite,
|
||||||
|
B: IntoBuf,
|
||||||
{
|
{
|
||||||
pub fn send_response(self,
|
pub fn send_response(self,
|
||||||
id: StreamId, // TODO: Generate one internally?
|
id: StreamId, // TODO: Generate one internally?
|
||||||
@@ -152,14 +177,16 @@ impl<T> Connection<T, Server>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P> Stream for Connection<T, P>
|
impl<T, P, B> Stream for Connection<T, P, B>
|
||||||
where T: AsyncRead + AsyncWrite,
|
where T: AsyncRead + AsyncWrite,
|
||||||
P: Peer,
|
P: Peer,
|
||||||
|
B: IntoBuf,
|
||||||
{
|
{
|
||||||
type Item = Frame<P::Poll>;
|
type Item = Frame<P::Poll>;
|
||||||
type Error = ConnectionError;
|
type Error = ConnectionError;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, ConnectionError> {
|
fn poll(&mut self) -> Poll<Option<Self::Item>, ConnectionError> {
|
||||||
|
use frame::Frame::*;
|
||||||
trace!("Connection::poll");
|
trace!("Connection::poll");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -176,7 +203,7 @@ impl<T, P> Stream for Connection<T, P>
|
|||||||
trace!("received; frame={:?}", frame);
|
trace!("received; frame={:?}", frame);
|
||||||
|
|
||||||
let frame = match frame {
|
let frame = match frame {
|
||||||
Some(WireFrame::Headers(v)) => {
|
Some(Headers(v)) => {
|
||||||
// TODO: Update stream state
|
// TODO: Update stream state
|
||||||
let stream_id = v.stream_id();
|
let stream_id = v.stream_id();
|
||||||
let end_of_stream = v.is_end_stream();
|
let end_of_stream = v.is_end_stream();
|
||||||
@@ -204,20 +231,24 @@ impl<T, P> Stream for Connection<T, P>
|
|||||||
end_of_stream: end_of_stream,
|
end_of_stream: end_of_stream,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(WireFrame::Data(v)) => {
|
Some(Data(v)) => {
|
||||||
// TODO: Validate frame
|
// TODO: Validate frame
|
||||||
|
|
||||||
let stream_id = v.stream_id();
|
let stream_id = v.stream_id();
|
||||||
let end_of_stream = v.is_end_stream();
|
let end_of_stream = v.is_end_stream();
|
||||||
|
match self.streams.get_mut(&stream_id) {
|
||||||
|
None => return Err(error::Reason::ProtocolError.into()),
|
||||||
|
Some(state) => try!(state.recv_data(end_of_stream)),
|
||||||
|
}
|
||||||
|
|
||||||
Frame::Body {
|
Frame::Data {
|
||||||
id: stream_id,
|
id: stream_id,
|
||||||
chunk: v.into_payload(),
|
data: v.into_payload(),
|
||||||
end_of_stream: end_of_stream,
|
end_of_stream: end_of_stream,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(WireFrame::WindowUpdate(v)) => {
|
Some(WindowUpdate(v)) => {
|
||||||
self.increment_remote_window(v.stream_id(), v.increment());
|
self.increment_remote_window(v.stream_id(), v.size_increment());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Some(frame) => panic!("unexpected frame; frame={:?}", frame),
|
Some(frame) => panic!("unexpected frame; frame={:?}", frame),
|
||||||
@@ -229,16 +260,19 @@ impl<T, P> Stream for Connection<T, P>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P> Sink for Connection<T, P>
|
impl<T, P, B> Sink for Connection<T, P, B>
|
||||||
where T: AsyncRead + AsyncWrite,
|
where T: AsyncRead + AsyncWrite,
|
||||||
P: Peer,
|
P: Peer,
|
||||||
|
B: IntoBuf,
|
||||||
{
|
{
|
||||||
type SinkItem = Frame<P::Send>;
|
type SinkItem = Frame<P::Send, B>;
|
||||||
type SinkError = ConnectionError;
|
type SinkError = ConnectionError;
|
||||||
|
|
||||||
fn start_send(&mut self, item: Self::SinkItem)
|
fn start_send(&mut self, item: Self::SinkItem)
|
||||||
-> StartSend<Self::SinkItem, Self::SinkError>
|
-> StartSend<Self::SinkItem, Self::SinkError>
|
||||||
{
|
{
|
||||||
|
use frame::Frame::Headers;
|
||||||
|
|
||||||
// First ensure that the upstream can process a new item
|
// First ensure that the upstream can process a new item
|
||||||
if !try!(self.poll_ready()).is_ready() {
|
if !try!(self.poll_ready()).is_ready() {
|
||||||
return Ok(AsyncSink::NotReady(item));
|
return Ok(AsyncSink::NotReady(item));
|
||||||
@@ -262,7 +296,8 @@ impl<T, P> Sink for Connection<T, P>
|
|||||||
// connections should not be factored.
|
// connections should not be factored.
|
||||||
//
|
//
|
||||||
if !P::is_valid_local_stream_id(id) {
|
if !P::is_valid_local_stream_id(id) {
|
||||||
unimplemented!();
|
// TODO: clear state
|
||||||
|
return Err(error::User::InvalidStreamId.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +305,7 @@ impl<T, P> Sink for Connection<T, P>
|
|||||||
|
|
||||||
// We already ensured that the upstream can handle the frame, so
|
// We already ensured that the upstream can handle the frame, so
|
||||||
// panic if it gets rejected.
|
// panic if it gets rejected.
|
||||||
let res = try!(self.inner.start_send(WireFrame::Headers(frame)));
|
let res = try!(self.inner.start_send(Headers(frame)));
|
||||||
|
|
||||||
// This is a one-way conversion. By checking `poll_ready` first,
|
// This is a one-way conversion. By checking `poll_ready` first,
|
||||||
// it's already been determined that the inner `Sink` can accept
|
// it's already been determined that the inner `Sink` can accept
|
||||||
@@ -279,6 +314,25 @@ impl<T, P> Sink for Connection<T, P>
|
|||||||
|
|
||||||
Ok(AsyncSink::Ready)
|
Ok(AsyncSink::Ready)
|
||||||
}
|
}
|
||||||
|
Frame::Data { id, data, end_of_stream } => {
|
||||||
|
// The stream must be initialized at this point
|
||||||
|
match self.streams.get_mut(&id) {
|
||||||
|
None => return Err(error::User::InactiveStreamId.into()),
|
||||||
|
Some(state) => try!(state.send_data(end_of_stream)),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut frame = frame::Data::new(id, data.into_buf());
|
||||||
|
|
||||||
|
if end_of_stream {
|
||||||
|
frame.set_end_stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = try!(self.inner.start_send(frame.into()));
|
||||||
|
|
||||||
|
assert!(res.is_ready());
|
||||||
|
|
||||||
|
Ok(AsyncSink::Ready)
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
Frame::Trailers { id, headers } => {
|
Frame::Trailers { id, headers } => {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
@@ -302,9 +356,10 @@ impl<T, P> Sink for Connection<T, P>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P> ReadySink for Connection<T, P>
|
impl<T, P, B> ReadySink for Connection<T, P, B>
|
||||||
where T: AsyncRead + AsyncWrite,
|
where T: AsyncRead + AsyncWrite,
|
||||||
P: Peer,
|
P: Peer,
|
||||||
|
B: IntoBuf,
|
||||||
{
|
{
|
||||||
fn poll_ready(&mut self) -> Poll<(), Self::SinkError> {
|
fn poll_ready(&mut self) -> Poll<(), Self::SinkError> {
|
||||||
self.inner.poll_ready()
|
self.inner.poll_ready()
|
||||||
|
|||||||
@@ -29,10 +29,7 @@ enum Partial {
|
|||||||
// PushPromise(frame::PushPromise),
|
// PushPromise(frame::PushPromise),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> FramedRead<T>
|
impl<T> FramedRead<T> {
|
||||||
where T: AsyncRead,
|
|
||||||
T: Sink<SinkItem = Frame, SinkError = ConnectionError>,
|
|
||||||
{
|
|
||||||
pub fn new(inner: length_delimited::FramedRead<T>) -> FramedRead<T> {
|
pub fn new(inner: length_delimited::FramedRead<T>) -> FramedRead<T> {
|
||||||
FramedRead {
|
FramedRead {
|
||||||
inner: inner,
|
inner: inner,
|
||||||
@@ -40,9 +37,7 @@ impl<T> FramedRead<T>
|
|||||||
partial: None,
|
partial: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FramedRead<T> {
|
|
||||||
fn decode_frame(&mut self, mut bytes: Bytes) -> Result<Option<Frame>, ConnectionError> {
|
fn decode_frame(&mut self, mut bytes: Bytes) -> Result<Option<Frame>, ConnectionError> {
|
||||||
// Parse the head
|
// Parse the head
|
||||||
let head = frame::Head::parse(&bytes);
|
let head = frame::Head::parse(&bytes);
|
||||||
@@ -97,9 +92,11 @@ impl<T> FramedRead<T> {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
Kind::WindowUpdate => {
|
Kind::WindowUpdate => {
|
||||||
|
// TODO: IMPLEMENT
|
||||||
let frame = try!(frame::WindowUpdate::load(head, &bytes[frame::HEADER_LEN..]));
|
let frame = try!(frame::WindowUpdate::load(head, &bytes[frame::HEADER_LEN..]));
|
||||||
frame.into()
|
debug!("decoded; frame={:?}", frame);
|
||||||
}
|
return Ok(None);
|
||||||
|
},
|
||||||
Kind::Continuation => {
|
Kind::Continuation => {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use std::cmp;
|
|||||||
use std::io::{self, Cursor};
|
use std::io::{self, Cursor};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FramedWrite<T> {
|
pub struct FramedWrite<T, B> {
|
||||||
/// Upstream `AsyncWrite`
|
/// Upstream `AsyncWrite`
|
||||||
inner: T,
|
inner: T,
|
||||||
|
|
||||||
@@ -21,20 +21,20 @@ pub struct FramedWrite<T> {
|
|||||||
buf: Cursor<BytesMut>,
|
buf: Cursor<BytesMut>,
|
||||||
|
|
||||||
/// Next frame to encode
|
/// Next frame to encode
|
||||||
next: Option<Next>,
|
next: Option<Next<B>>,
|
||||||
|
|
||||||
/// Max frame size, this is specified by the peer
|
/// Max frame size, this is specified by the peer
|
||||||
max_frame_size: usize,
|
max_frame_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Next {
|
enum Next<B> {
|
||||||
Data {
|
Data {
|
||||||
/// Length of the current frame being written
|
/// Length of the current frame being written
|
||||||
frame_len: usize,
|
frame_len: usize,
|
||||||
|
|
||||||
/// Data frame to encode
|
/// Data frame to encode
|
||||||
data: frame::Data
|
data: frame::Data<B>,
|
||||||
},
|
},
|
||||||
Continuation(frame::Continuation),
|
Continuation(frame::Continuation),
|
||||||
}
|
}
|
||||||
@@ -50,8 +50,12 @@ const MIN_BUFFER_CAPACITY: usize = frame::HEADER_LEN + CHAIN_THRESHOLD;
|
|||||||
/// than 16kb, so not even close).
|
/// than 16kb, so not even close).
|
||||||
const CHAIN_THRESHOLD: usize = 256;
|
const CHAIN_THRESHOLD: usize = 256;
|
||||||
|
|
||||||
impl<T: AsyncWrite> FramedWrite<T> {
|
// TODO: Make generic
|
||||||
pub fn new(inner: T) -> FramedWrite<T> {
|
impl<T, B> FramedWrite<T, B>
|
||||||
|
where T: AsyncWrite,
|
||||||
|
B: Buf,
|
||||||
|
{
|
||||||
|
pub fn new(inner: T) -> FramedWrite<T, B> {
|
||||||
FramedWrite {
|
FramedWrite {
|
||||||
inner: inner,
|
inner: inner,
|
||||||
hpack: hpack::Encoder::default(),
|
hpack: hpack::Encoder::default(),
|
||||||
@@ -69,24 +73,27 @@ impl<T: AsyncWrite> FramedWrite<T> {
|
|||||||
self.next.is_none() && !self.buf.has_remaining()
|
self.next.is_none() && !self.buf.has_remaining()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frame_len(&self, data: &frame::Data) -> usize {
|
fn frame_len(&self, data: &frame::Data<B>) -> usize {
|
||||||
cmp::min(self.max_frame_size, data.len())
|
cmp::min(self.max_frame_size, data.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsyncWrite> Sink for FramedWrite<T> {
|
impl<T, B> Sink for FramedWrite<T, B>
|
||||||
type SinkItem = Frame;
|
where T: AsyncWrite,
|
||||||
|
B: Buf,
|
||||||
|
{
|
||||||
|
type SinkItem = Frame<B>;
|
||||||
type SinkError = ConnectionError;
|
type SinkError = ConnectionError;
|
||||||
|
|
||||||
fn start_send(&mut self, item: Frame) -> StartSend<Frame, ConnectionError> {
|
fn start_send(&mut self, item: Self::SinkItem)
|
||||||
debug!("start_send; frame={:?}", item);
|
-> StartSend<Self::SinkItem, ConnectionError>
|
||||||
|
{
|
||||||
if !try!(self.poll_ready()).is_ready() {
|
if !try!(self.poll_ready()).is_ready() {
|
||||||
return Ok(AsyncSink::NotReady(item));
|
return Ok(AsyncSink::NotReady(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
match item {
|
match item {
|
||||||
Frame::Data(v) => {
|
Frame::Data(mut v) => {
|
||||||
if v.len() >= CHAIN_THRESHOLD {
|
if v.len() >= CHAIN_THRESHOLD {
|
||||||
let head = v.head();
|
let head = v.head();
|
||||||
let len = self.frame_len(&v);
|
let len = self.frame_len(&v);
|
||||||
@@ -100,7 +107,11 @@ impl<T: AsyncWrite> Sink for FramedWrite<T> {
|
|||||||
data: v,
|
data: v,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
v.encode(self.buf.get_mut());
|
v.encode_chunk(self.buf.get_mut());
|
||||||
|
|
||||||
|
// The chunk has been fully encoded, so there is no need to
|
||||||
|
// keep it around
|
||||||
|
assert_eq!(v.len(), 0, "chunk not fully encoded");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Frame::Headers(v) => {
|
Frame::Headers(v) => {
|
||||||
@@ -140,7 +151,6 @@ impl<T: AsyncWrite> Sink for FramedWrite<T> {
|
|||||||
|
|
||||||
// As long as there is data to write, try to write it!
|
// As long as there is data to write, try to write it!
|
||||||
while !self.is_empty() {
|
while !self.is_empty() {
|
||||||
trace!("writing buffer; next={:?}; rem={:?}", self.next, self.buf.remaining());
|
|
||||||
try_ready!(self.inner.write_buf(&mut self.buf));
|
try_ready!(self.inner.write_buf(&mut self.buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +171,10 @@ impl<T: AsyncWrite> Sink for FramedWrite<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsyncWrite> ReadySink for FramedWrite<T> {
|
impl<T, B> ReadySink for FramedWrite<T, B>
|
||||||
|
where T: AsyncWrite,
|
||||||
|
B: Buf,
|
||||||
|
{
|
||||||
fn poll_ready(&mut self) -> Poll<(), Self::SinkError> {
|
fn poll_ready(&mut self) -> Poll<(), Self::SinkError> {
|
||||||
if !self.has_capacity() {
|
if !self.has_capacity() {
|
||||||
// Try flushing
|
// Try flushing
|
||||||
@@ -176,7 +189,7 @@ impl<T: AsyncWrite> ReadySink for FramedWrite<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Stream> Stream for FramedWrite<T> {
|
impl<T: Stream, B> Stream for FramedWrite<T, B> {
|
||||||
type Item = T::Item;
|
type Item = T::Item;
|
||||||
type Error = T::Error;
|
type Error = T::Error;
|
||||||
|
|
||||||
@@ -185,14 +198,14 @@ impl<T: Stream> Stream for FramedWrite<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: io::Read> io::Read for FramedWrite<T> {
|
impl<T: io::Read, B> io::Read for FramedWrite<T, B> {
|
||||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||||
self.inner.read(dst)
|
self.inner.read(dst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsyncRead> AsyncRead for FramedWrite<T> {
|
impl<T: AsyncRead, B> AsyncRead for FramedWrite<T, B> {
|
||||||
fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error>
|
fn read_buf<B2: BufMut>(&mut self, buf: &mut B2) -> Poll<usize, io::Error>
|
||||||
where Self: Sized,
|
where Self: Sized,
|
||||||
{
|
{
|
||||||
self.inner.read_buf(buf)
|
self.inner.read_buf(buf)
|
||||||
|
|||||||
@@ -23,24 +23,28 @@ use {frame, Peer};
|
|||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
use tokio_io::codec::length_delimited;
|
use tokio_io::codec::length_delimited;
|
||||||
|
|
||||||
type Inner<T> =
|
use bytes::{Buf, IntoBuf};
|
||||||
|
|
||||||
|
type Inner<T, B> =
|
||||||
Settings<
|
Settings<
|
||||||
PingPong<
|
PingPong<
|
||||||
Framed<T>>>;
|
Framed<T, B>,
|
||||||
|
B>>;
|
||||||
|
|
||||||
type Framed<T> =
|
type Framed<T, B> =
|
||||||
FramedRead<
|
FramedRead<
|
||||||
FramedWrite<T>>;
|
FramedWrite<T, B>>;
|
||||||
|
|
||||||
/// Create a full H2 transport from an I/O handle.
|
/// Create a full H2 transport from an I/O handle.
|
||||||
///
|
///
|
||||||
/// This is called as the final step of the client handshake future.
|
/// This is called as the final step of the client handshake future.
|
||||||
pub fn from_io<T, P>(io: T, settings: frame::SettingSet)
|
pub fn from_io<T, P, B>(io: T, settings: frame::SettingSet)
|
||||||
-> Connection<T, P>
|
-> Connection<T, P, B>
|
||||||
where T: AsyncRead + AsyncWrite,
|
where T: AsyncRead + AsyncWrite,
|
||||||
P: Peer,
|
P: Peer,
|
||||||
|
B: IntoBuf,
|
||||||
{
|
{
|
||||||
let framed_write = FramedWrite::new(io);
|
let framed_write: FramedWrite<_, B::Buf> = FramedWrite::new(io);
|
||||||
|
|
||||||
// To avoid code duplication, we're going to go this route. It is a bit
|
// To avoid code duplication, we're going to go this route. It is a bit
|
||||||
// weird, but oh well...
|
// weird, but oh well...
|
||||||
@@ -55,9 +59,10 @@ pub fn from_io<T, P>(io: T, settings: frame::SettingSet)
|
|||||||
/// When the server is performing the handshake, it is able to only send
|
/// 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
|
/// `Settings` frames and is expected to receive the client preface as a byte
|
||||||
/// stream. To represent this, `Settings<FramedWrite<T>>` is returned.
|
/// stream. To represent this, `Settings<FramedWrite<T>>` is returned.
|
||||||
pub fn server_handshaker<T>(io: T, settings: frame::SettingSet)
|
pub fn server_handshaker<T, B>(io: T, settings: frame::SettingSet)
|
||||||
-> Settings<FramedWrite<T>>
|
-> Settings<FramedWrite<T, B>>
|
||||||
where T: AsyncRead + AsyncWrite,
|
where T: AsyncRead + AsyncWrite,
|
||||||
|
B: Buf,
|
||||||
{
|
{
|
||||||
let framed_write = FramedWrite::new(io);
|
let framed_write = FramedWrite::new(io);
|
||||||
|
|
||||||
@@ -65,10 +70,11 @@ pub fn server_handshaker<T>(io: T, settings: frame::SettingSet)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a full H2 transport from the server handshaker
|
/// Create a full H2 transport from the server handshaker
|
||||||
pub fn from_server_handshaker<T, P>(transport: Settings<FramedWrite<T>>)
|
pub fn from_server_handshaker<T, P, B>(transport: Settings<FramedWrite<T, B::Buf>>)
|
||||||
-> Connection<T, P>
|
-> Connection<T, P, B>
|
||||||
where T: AsyncRead + AsyncWrite,
|
where T: AsyncRead + AsyncWrite,
|
||||||
P: Peer,
|
P: Peer,
|
||||||
|
B: IntoBuf,
|
||||||
{
|
{
|
||||||
let settings = transport.swap_inner(|io| {
|
let settings = transport.swap_inner(|io| {
|
||||||
// Delimit the frames
|
// Delimit the frames
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ use proto::ReadySink;
|
|||||||
|
|
||||||
/// Acknowledges ping requests from the remote.
|
/// Acknowledges ping requests from the remote.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PingPong<T> {
|
pub struct PingPong<T, U> {
|
||||||
inner: T,
|
inner: T,
|
||||||
pong: Option<Frame>,
|
pong: Option<Frame<U>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PingPong<T>
|
impl<T, U> PingPong<T, U>
|
||||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
T: Sink<SinkItem = Frame, SinkError = ConnectionError>,
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
{
|
{
|
||||||
pub fn new(inner: T) -> PingPong<T> {
|
pub fn new(inner: T) -> Self {
|
||||||
PingPong {
|
PingPong {
|
||||||
inner,
|
inner,
|
||||||
pong: None,
|
pong: None,
|
||||||
@@ -38,9 +38,9 @@ impl<T> PingPong<T>
|
|||||||
/// > a PING frame with the ACK flag set in response, with an identical
|
/// > a PING frame with the ACK flag set in response, with an identical
|
||||||
/// > payload. PING responses SHOULD be given higher priority than any
|
/// > payload. PING responses SHOULD be given higher priority than any
|
||||||
/// > other frame.
|
/// > other frame.
|
||||||
impl<T> Stream for PingPong<T>
|
impl<T, U> Stream for PingPong<T, U>
|
||||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
T: Sink<SinkItem = Frame, SinkError = ConnectionError>,
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
{
|
{
|
||||||
type Item = Frame;
|
type Item = Frame;
|
||||||
type Error = ConnectionError;
|
type Error = ConnectionError;
|
||||||
@@ -76,14 +76,16 @@ impl<T> Stream for PingPong<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Sink for PingPong<T>
|
impl<T, U> Sink for PingPong<T, U>
|
||||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
T: Sink<SinkItem = Frame, SinkError = ConnectionError>,
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
{
|
{
|
||||||
type SinkItem = Frame;
|
type SinkItem = Frame<U>;
|
||||||
type SinkError = ConnectionError;
|
type SinkError = ConnectionError;
|
||||||
|
|
||||||
fn start_send(&mut self, item: Frame) -> StartSend<Frame, ConnectionError> {
|
fn start_send(&mut self, item: Self::SinkItem)
|
||||||
|
-> StartSend<Self::SinkItem, Self::SinkError>
|
||||||
|
{
|
||||||
// Pings _SHOULD_ have priority over other messages, so attempt to send pending
|
// Pings _SHOULD_ have priority over other messages, so attempt to send pending
|
||||||
// ping frames before attempting to send `item`.
|
// ping frames before attempting to send `item`.
|
||||||
if self.try_send_pong()?.is_not_ready() {
|
if self.try_send_pong()?.is_not_ready() {
|
||||||
@@ -100,9 +102,9 @@ impl<T> Sink for PingPong<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ReadySink for PingPong<T>
|
impl<T, U> ReadySink for PingPong<T, U>
|
||||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
T: Sink<SinkItem = Frame, SinkError = ConnectionError>,
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
T: ReadySink,
|
T: ReadySink,
|
||||||
{
|
{
|
||||||
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ pub struct Settings<T> {
|
|||||||
received_remote: bool,
|
received_remote: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Settings<T>
|
impl<T, U> Settings<T>
|
||||||
where T: Sink<SinkItem = Frame, SinkError = ConnectionError>,
|
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
{
|
{
|
||||||
pub fn new(inner: T, local: frame::SettingSet) -> Settings<T> {
|
pub fn new(inner: T, local: frame::SettingSet) -> Settings<T> {
|
||||||
Settings {
|
Settings {
|
||||||
@@ -44,7 +44,7 @@ impl<T> Settings<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Swap the inner transport while maintaining the current state.
|
/// Swap the inner transport while maintaining the current state.
|
||||||
pub fn swap_inner<U, F: FnOnce(T) -> U>(self, f: F) -> Settings<U> {
|
pub fn swap_inner<T2, F: FnOnce(T) -> T2>(self, f: F) -> Settings<T2> {
|
||||||
let inner = f(self.inner);
|
let inner = f(self.inner);
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
@@ -88,9 +88,9 @@ impl<T> Settings<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Stream for Settings<T>
|
impl<T, U> Stream for Settings<T>
|
||||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
T: Sink<SinkItem = Frame, SinkError = ConnectionError>,
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
{
|
{
|
||||||
type Item = Frame;
|
type Item = Frame;
|
||||||
type Error = ConnectionError;
|
type Error = ConnectionError;
|
||||||
@@ -118,13 +118,15 @@ impl<T> Stream for Settings<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Sink for Settings<T>
|
impl<T, U> Sink for Settings<T>
|
||||||
where T: Sink<SinkItem = Frame, SinkError = ConnectionError>,
|
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
{
|
{
|
||||||
type SinkItem = Frame;
|
type SinkItem = Frame<U>;
|
||||||
type SinkError = ConnectionError;
|
type SinkError = ConnectionError;
|
||||||
|
|
||||||
fn start_send(&mut self, item: Frame) -> StartSend<Frame, ConnectionError> {
|
fn start_send(&mut self, item: Self::SinkItem)
|
||||||
|
-> StartSend<Self::SinkItem, Self::SinkError>
|
||||||
|
{
|
||||||
// Settings frames take priority, so `item` cannot be sent if there are
|
// Settings frames take priority, so `item` cannot be sent if there are
|
||||||
// any pending acks OR the local settings have been changed w/o sending
|
// any pending acks OR the local settings have been changed w/o sending
|
||||||
// an associated frame.
|
// an associated frame.
|
||||||
@@ -147,8 +149,8 @@ impl<T> Sink for Settings<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ReadySink for Settings<T>
|
impl<T, U> ReadySink for Settings<T>
|
||||||
where T: Sink<SinkItem = Frame, SinkError = ConnectionError>,
|
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
T: ReadySink,
|
T: ReadySink,
|
||||||
{
|
{
|
||||||
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use {ConnectionError, Reason, Peer};
|
use Peer;
|
||||||
|
use error::ConnectionError;
|
||||||
|
use error::Reason::*;
|
||||||
|
use error::User::*;
|
||||||
use proto::FlowController;
|
use proto::FlowController;
|
||||||
|
|
||||||
/// Represents the state of an H2 stream
|
/// Represents the state of an H2 stream
|
||||||
@@ -101,7 +104,7 @@ impl State {
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
Open { local, remote } => {
|
Open { local, remote } => {
|
||||||
try!(remote.check_is_headers(Reason::ProtocolError));
|
try!(remote.check_is_headers(ProtocolError.into()));
|
||||||
|
|
||||||
*self = if eos {
|
*self = if eos {
|
||||||
HalfClosedRemote(local)
|
HalfClosedRemote(local)
|
||||||
@@ -113,7 +116,7 @@ impl State {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
HalfClosedLocal(remote) => {
|
HalfClosedLocal(remote) => {
|
||||||
try!(remote.check_is_headers(Reason::ProtocolError));
|
try!(remote.check_is_headers(ProtocolError.into()));
|
||||||
|
|
||||||
*self = if eos {
|
*self = if eos {
|
||||||
Closed
|
Closed
|
||||||
@@ -124,7 +127,36 @@ impl State {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
Closed | HalfClosedRemote(..) => {
|
Closed | HalfClosedRemote(..) => {
|
||||||
Err(Reason::ProtocolError.into())
|
Err(ProtocolError.into())
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recv_data(&mut self, eos: bool) -> Result<(), ConnectionError> {
|
||||||
|
use self::State::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Open { local, remote } => {
|
||||||
|
try!(remote.check_is_data(ProtocolError.into()));
|
||||||
|
|
||||||
|
if eos {
|
||||||
|
*self = HalfClosedRemote(local);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
HalfClosedLocal(remote) => {
|
||||||
|
try!(remote.check_is_data(ProtocolError.into()));
|
||||||
|
|
||||||
|
if eos {
|
||||||
|
*self = Closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Closed | HalfClosedRemote(..) => {
|
||||||
|
Err(ProtocolError.into())
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
@@ -152,7 +184,7 @@ impl State {
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
Open { local, remote } => {
|
Open { local, remote } => {
|
||||||
try!(local.check_is_headers(Reason::InternalError));
|
try!(local.check_is_headers(UnexpectedFrameType.into()));
|
||||||
|
|
||||||
*self = if eos {
|
*self = if eos {
|
||||||
HalfClosedLocal(remote)
|
HalfClosedLocal(remote)
|
||||||
@@ -164,7 +196,7 @@ impl State {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
HalfClosedRemote(local) => {
|
HalfClosedRemote(local) => {
|
||||||
try!(local.check_is_headers(Reason::InternalError));
|
try!(local.check_is_headers(UnexpectedFrameType.into()));
|
||||||
|
|
||||||
*self = if eos {
|
*self = if eos {
|
||||||
Closed
|
Closed
|
||||||
@@ -175,7 +207,36 @@ impl State {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
Closed | HalfClosedLocal(..) => {
|
Closed | HalfClosedLocal(..) => {
|
||||||
Err(Reason::InternalError.into())
|
Err(UnexpectedFrameType.into())
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_data(&mut self, eos: bool) -> Result<(), ConnectionError> {
|
||||||
|
use self::State::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Open { local, remote } => {
|
||||||
|
try!(local.check_is_data(UnexpectedFrameType.into()));
|
||||||
|
|
||||||
|
if eos {
|
||||||
|
*self = HalfClosedLocal(remote);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
HalfClosedRemote(local) => {
|
||||||
|
try!(local.check_is_data(UnexpectedFrameType.into()));
|
||||||
|
|
||||||
|
if eos {
|
||||||
|
*self = Closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Closed | HalfClosedLocal(..) => {
|
||||||
|
Err(UnexpectedFrameType.into())
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
@@ -184,12 +245,22 @@ impl State {
|
|||||||
|
|
||||||
impl PeerState {
|
impl PeerState {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn check_is_headers(&self, err: Reason) -> Result<(), ConnectionError> {
|
fn check_is_headers(&self, err: ConnectionError) -> Result<(), ConnectionError> {
|
||||||
use self::PeerState::*;
|
use self::PeerState::*;
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Headers => Ok(()),
|
Headers => Ok(()),
|
||||||
_ => Err(err.into()),
|
_ => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn check_is_data(&self, err: ConnectionError) -> Result<(), ConnectionError> {
|
||||||
|
use self::PeerState::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Data { .. } => Ok(()),
|
||||||
|
_ => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,21 @@ use {frame, proto, Peer, ConnectionError, StreamId};
|
|||||||
use http;
|
use http;
|
||||||
use futures::{Future, Sink, Poll, Async};
|
use futures::{Future, Sink, Poll, Async};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
|
use bytes::{Bytes, IntoBuf};
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// In progress H2 connection binding
|
/// In progress H2 connection binding
|
||||||
pub struct Handshake<T> {
|
pub struct Handshake<T, B: IntoBuf = Bytes> {
|
||||||
// TODO: unbox
|
// TODO: unbox
|
||||||
inner: Box<Future<Item = Connection<T>, Error = ConnectionError>>,
|
inner: Box<Future<Item = Connection<T, B>, Error = ConnectionError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marker type indicating a client peer
|
/// Marker type indicating a client peer
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Server;
|
pub struct Server;
|
||||||
|
|
||||||
pub type Connection<T> = super::Connection<T, Server>;
|
pub type Connection<T, B = Bytes> = super::Connection<T, Server, B>;
|
||||||
|
|
||||||
/// Flush a Sink
|
/// Flush a Sink
|
||||||
struct Flush<T> {
|
struct Flush<T> {
|
||||||
@@ -31,12 +32,19 @@ struct ReadPreface<T> {
|
|||||||
|
|
||||||
const PREFACE: [u8; 24] = *b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
const PREFACE: [u8; 24] = *b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
||||||
|
|
||||||
|
pub fn handshake<T>(io: T) -> Handshake<T, Bytes>
|
||||||
|
where T: AsyncRead + AsyncWrite + 'static,
|
||||||
|
{
|
||||||
|
handshake2(io)
|
||||||
|
}
|
||||||
|
|
||||||
/// Bind an H2 server connection.
|
/// Bind an H2 server connection.
|
||||||
///
|
///
|
||||||
/// 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 handshake<T>(io: T) -> Handshake<T>
|
pub fn handshake2<T, B: IntoBuf>(io: T) -> Handshake<T, B>
|
||||||
where T: AsyncRead + AsyncWrite + 'static,
|
where T: AsyncRead + AsyncWrite + 'static,
|
||||||
|
B: 'static, // TODO: Why is this required but not in client?
|
||||||
{
|
{
|
||||||
let transport = proto::server_handshaker(io, Default::default());
|
let transport = proto::server_handshaker(io, Default::default());
|
||||||
|
|
||||||
@@ -141,8 +149,8 @@ impl Peer for Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Future for Handshake<T> {
|
impl<T, B: IntoBuf> Future for Handshake<T, B> {
|
||||||
type Item = Connection<T>;
|
type Item = Connection<T, B>;
|
||||||
type Error = ConnectionError;
|
type Error = ConnectionError;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
@@ -150,7 +158,10 @@ impl<T> Future for Handshake<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: fmt::Debug> fmt::Debug for Handshake<T> {
|
impl<T, B> fmt::Debug for Handshake<T, B>
|
||||||
|
where T: fmt::Debug,
|
||||||
|
B: fmt::Debug + IntoBuf,
|
||||||
|
{
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(fmt, "server::Handshake")
|
write!(fmt, "server::Handshake")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,26 @@ extern crate http;
|
|||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate mock_io;
|
extern crate mock_io;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
|
extern crate bytes;
|
||||||
|
|
||||||
use h2::client;
|
use h2::client;
|
||||||
use http::request;
|
use http::request;
|
||||||
|
use bytes::Bytes;
|
||||||
|
|
||||||
use futures::*;
|
use futures::*;
|
||||||
|
|
||||||
|
// TODO: move into another file
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn handshake() {
|
fn handshake() {
|
||||||
let _ = ::env_logger::init();
|
let _ = ::env_logger::init();
|
||||||
@@ -52,11 +66,99 @@ fn get_with_204_response() {
|
|||||||
// Get the response
|
// Get the response
|
||||||
let (resp, h2) = h2.into_future().wait().unwrap();
|
let (resp, h2) = h2.into_future().wait().unwrap();
|
||||||
|
|
||||||
println!("RESP: {:?}", resp);
|
assert!(Stream::wait(h2).next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn get_with_200_response() {
|
||||||
|
let _ = ::env_logger::init();
|
||||||
|
|
||||||
|
let mock = mock_io::Builder::new()
|
||||||
|
.handshake()
|
||||||
|
// Write GET /
|
||||||
|
.write(&[
|
||||||
|
0, 0, 0x10, 1, 5, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29,
|
||||||
|
0xAC, 0x4B, 0x8F, 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84,
|
||||||
|
])
|
||||||
|
.write(SETTINGS_ACK)
|
||||||
|
// Read response
|
||||||
|
.read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let mut h2 = client::handshake(mock)
|
||||||
|
.wait().unwrap();
|
||||||
|
|
||||||
|
// Send the request
|
||||||
|
let mut request = request::Head::default();
|
||||||
|
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||||
|
let h2 = h2.send_request(1.into(), request, true).wait().unwrap();
|
||||||
|
|
||||||
|
// Get the response
|
||||||
|
let (resp, h2) = h2.into_future().wait().unwrap();
|
||||||
|
|
||||||
assert!(Stream::wait(h2).next().is_none());
|
assert!(Stream::wait(h2).next().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_with_zero_stream_id() {
|
||||||
|
let mock = mock_io::Builder::new()
|
||||||
|
.handshake()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let h2 = client::handshake(mock)
|
||||||
|
.wait().unwrap();
|
||||||
|
|
||||||
|
// Send the request
|
||||||
|
let mut request = request::Head::default();
|
||||||
|
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||||
|
|
||||||
|
let err = h2.send_request(0.into(), request, true).wait().unwrap_err();
|
||||||
|
assert_user_err!(err, InvalidStreamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_with_server_stream_id() {
|
||||||
|
let mock = mock_io::Builder::new()
|
||||||
|
.handshake()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let h2 = client::handshake(mock)
|
||||||
|
.wait().unwrap();
|
||||||
|
|
||||||
|
// Send the request
|
||||||
|
let mut request = request::Head::default();
|
||||||
|
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||||
|
|
||||||
|
let err = h2.send_request(2.into(), request, true).wait().unwrap_err();
|
||||||
|
assert_user_err!(err, InvalidStreamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn send_data_without_headers() {
|
||||||
|
let mock = mock_io::Builder::new()
|
||||||
|
.handshake()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let h2 = client::handshake(mock)
|
||||||
|
.wait().unwrap();
|
||||||
|
|
||||||
|
// Send the request
|
||||||
|
let mut request = request::Head::default();
|
||||||
|
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
let err = h2.send_request(2.into(), request, true).wait().unwrap_err();
|
||||||
|
assert_user_err!(err, InvalidStreamId);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn send_data_after_headers_eos() {
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn request_without_scheme() {
|
fn request_without_scheme() {
|
||||||
@@ -77,6 +179,11 @@ fn invalid_client_stream_id() {
|
|||||||
fn invalid_server_stream_id() {
|
fn invalid_server_stream_id() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn exceed_max_streams() {
|
||||||
|
}
|
||||||
|
|
||||||
const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
|
const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
|
||||||
const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
|
const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user