Work
This commit is contained in:
@@ -8,3 +8,6 @@ futures = "0.1"
|
|||||||
tokio-io = { git = "https://github.com/alexcrichton/tokio-io" }
|
tokio-io = { git = "https://github.com/alexcrichton/tokio-io" }
|
||||||
tokio-timer = { git = "https://github.com/tokio-rs/tokio-timer" }
|
tokio-timer = { git = "https://github.com/tokio-rs/tokio-timer" }
|
||||||
bytes = { git = "https://github.com/carllerche/bytes" }
|
bytes = { git = "https://github.com/carllerche/bytes" }
|
||||||
|
|
||||||
|
[replace]
|
||||||
|
"futures:0.1.10" = { git = "https://github.com/alexcrichton/futures-rs", branch = "close" }
|
||||||
|
|||||||
152
src/error.rs
Normal file
152
src/error.rs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
use std::{error, fmt, io};
|
||||||
|
|
||||||
|
/// The error type for HTTP/2 operations
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ConnectionError {
|
||||||
|
/// The HTTP/2 stream was reset
|
||||||
|
Proto(Reason),
|
||||||
|
/// An `io::Error` occurred while trying to read or write.
|
||||||
|
Io(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StreamError(Reason);
|
||||||
|
|
||||||
|
#[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),
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reason_desc {
|
||||||
|
($reason:expr) => (reason_desc!($reason, ""));
|
||||||
|
($reason:expr, $prefix:expr) => ({
|
||||||
|
match $reason {
|
||||||
|
Reason::NoError => concat!($prefix, "not a result of an error"),
|
||||||
|
Reason::ProtocolError => concat!($prefix, "unspecific protocol error detected"),
|
||||||
|
Reason::InternalError => concat!($prefix, "unexpected internal error encountered"),
|
||||||
|
Reason::FlowControlError => concat!($prefix, "flow-control protocol violated"),
|
||||||
|
Reason::SettingsTimeout => concat!($prefix, "settings ACK not received in timely manner"),
|
||||||
|
Reason::StreamClosed => concat!($prefix, "received frame when stream half-closed"),
|
||||||
|
Reason::FrameSizeError => concat!($prefix, "frame sent with invalid size"),
|
||||||
|
Reason::RefusedStream => concat!($prefix, "refused stream before processing any application logic"),
|
||||||
|
Reason::Cancel => concat!($prefix, "stream no longer needed"),
|
||||||
|
Reason::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"),
|
||||||
|
Reason::EnhanceYourCalm => concat!($prefix, "detected excessive load generating behavior"),
|
||||||
|
Reason::InadequateSecurity => concat!($prefix, "security properties do not meet minimum requirements"),
|
||||||
|
Reason::Http11Required => concat!($prefix, "endpoint requires HTTP/1.1"),
|
||||||
|
Reason::Other(_) => concat!($prefix, "other reason"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== impl ConnectionError =====
|
||||||
|
|
||||||
|
impl From<io::Error> for ConnectionError {
|
||||||
|
fn from(src: io::Error) -> ConnectionError {
|
||||||
|
ConnectionError::Io(src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConnectionError> for io::Error {
|
||||||
|
fn from(src: ConnectionError) -> io::Error {
|
||||||
|
io::Error::new(io::ErrorKind::Other, src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ConnectionError {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use self::ConnectionError::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Proto(reason) => write!(fmt, "protocol error: {}", reason),
|
||||||
|
Io(ref e) => fmt::Display::fmt(e, fmt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for ConnectionError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
use self::ConnectionError::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Io(ref e) => error::Error::description(e),
|
||||||
|
Proto(reason) => reason_desc!(reason, "protocol error: "),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
use super::Error;
|
use super::Error;
|
||||||
|
|
||||||
|
use bytes::{BufMut, BigEndian};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct Head {
|
pub struct Head {
|
||||||
kind: Kind,
|
kind: Kind,
|
||||||
@@ -25,9 +27,19 @@ pub enum Kind {
|
|||||||
|
|
||||||
pub type StreamId = u32;
|
pub type StreamId = u32;
|
||||||
|
|
||||||
|
const STREAM_ID_MASK: StreamId = 0x80000000;
|
||||||
|
|
||||||
// ===== impl Head =====
|
// ===== impl Head =====
|
||||||
|
|
||||||
impl Head {
|
impl Head {
|
||||||
|
pub fn new(kind: Kind, flag: u8, stream_id: StreamId) -> Head {
|
||||||
|
Head {
|
||||||
|
kind: kind,
|
||||||
|
flag: flag,
|
||||||
|
stream_id: stream_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse an HTTP/2.0 frame header
|
/// Parse an HTTP/2.0 frame header
|
||||||
pub fn parse(header: &[u8]) -> Head {
|
pub fn parse(header: &[u8]) -> Head {
|
||||||
Head {
|
Head {
|
||||||
@@ -48,6 +60,21 @@ impl Head {
|
|||||||
pub fn flag(&self) -> u8 {
|
pub fn flag(&self) -> u8 {
|
||||||
self.flag
|
self.flag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn encode_len(&self) -> usize {
|
||||||
|
super::FRAME_HEADER_LEN
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode<T: BufMut>(&self, payload_len: usize, dst: &mut T) -> Result<(), Error> {
|
||||||
|
debug_assert_eq!(self.encode_len(), dst.remaining_mut());
|
||||||
|
debug_assert!(self.stream_id & STREAM_ID_MASK == 0);
|
||||||
|
|
||||||
|
dst.put_uint::<BigEndian>(payload_len as u64, 3);
|
||||||
|
dst.put_u8(self.kind as u8);
|
||||||
|
dst.put_u8(self.flag);
|
||||||
|
dst.put_u32::<BigEndian>(self.stream_id);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the next 4 octets in the given buffer, assuming they represent an
|
/// Parse the next 4 octets in the given buffer, assuming they represent an
|
||||||
@@ -58,7 +85,7 @@ impl Head {
|
|||||||
fn parse_stream_id(buf: &[u8]) -> StreamId {
|
fn parse_stream_id(buf: &[u8]) -> StreamId {
|
||||||
let unpacked = unpack_octets_4!(buf, 0, u32);
|
let unpacked = unpack_octets_4!(buf, 0, u32);
|
||||||
// Now clear the most significant bit, as that is reserved and MUST be ignored when received.
|
// Now clear the most significant bit, as that is reserved and MUST be ignored when received.
|
||||||
unpacked & !0x80000000
|
unpacked & !STREAM_ID_MASK
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl Kind =====
|
// ===== impl Kind =====
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use bytes::Bytes;
|
use error::{ConnectionError, Reason};
|
||||||
|
use bytes::{Bytes, BytesMut, BufMut};
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
/// 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
|
||||||
@@ -29,10 +32,27 @@ mod util;
|
|||||||
|
|
||||||
pub use self::data::Data;
|
pub use self::data::Data;
|
||||||
pub use self::head::{Head, Kind, StreamId};
|
pub use self::head::{Head, Kind, StreamId};
|
||||||
|
pub use self::settings::Settings;
|
||||||
pub use self::unknown::Unknown;
|
pub use self::unknown::Unknown;
|
||||||
|
|
||||||
const FRAME_HEADER_LEN: usize = 9;
|
const FRAME_HEADER_LEN: usize = 9;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Frame {
|
||||||
|
/*
|
||||||
|
Data(DataFrame<'a>),
|
||||||
|
HeadersFrame(HeadersFrame<'a>),
|
||||||
|
RstStreamFrame(RstStreamFrame),
|
||||||
|
SettingsFrame(SettingsFrame),
|
||||||
|
PingFrame(PingFrame),
|
||||||
|
GoawayFrame(GoawayFrame<'a>),
|
||||||
|
WindowUpdateFrame(WindowUpdateFrame),
|
||||||
|
UnknownFrame(RawFrame<'a>),
|
||||||
|
*/
|
||||||
|
Settings(Settings),
|
||||||
|
Unknown(Unknown),
|
||||||
|
}
|
||||||
|
|
||||||
/// 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 {
|
||||||
@@ -64,26 +84,22 @@ pub enum Error {
|
|||||||
|
|
||||||
/// The payload length specified by the frame header was not the
|
/// The payload length specified by the frame header was not the
|
||||||
/// value necessary for the specific frame type.
|
/// value necessary for the specific frame type.
|
||||||
InvalidPayloadLength
|
InvalidPayloadLength,
|
||||||
|
|
||||||
|
/// Received a payload with an ACK settings frame
|
||||||
|
InvalidPayloadAckSettings,
|
||||||
|
|
||||||
|
/// An invalid stream identifier was provided.
|
||||||
|
///
|
||||||
|
/// This is returned if a settings frame is received with a stream
|
||||||
|
/// identifier other than zero.
|
||||||
|
InvalidStreamId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
// ===== impl Frame ======
|
||||||
pub enum Frame {
|
|
||||||
/*
|
|
||||||
Data(DataFrame<'a>),
|
|
||||||
HeadersFrame(HeadersFrame<'a>),
|
|
||||||
RstStreamFrame(RstStreamFrame),
|
|
||||||
SettingsFrame(SettingsFrame),
|
|
||||||
PingFrame(PingFrame),
|
|
||||||
GoawayFrame(GoawayFrame<'a>),
|
|
||||||
WindowUpdateFrame(WindowUpdateFrame),
|
|
||||||
UnknownFrame(RawFrame<'a>),
|
|
||||||
*/
|
|
||||||
Unknown(Unknown),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Frame {
|
impl Frame {
|
||||||
pub fn load(mut frame: Bytes) -> Frame {
|
pub fn load(mut frame: Bytes) -> Result<Frame, Error> {
|
||||||
let head = Head::parse(&frame);
|
let head = Head::parse(&frame);
|
||||||
|
|
||||||
// Extract the payload from the frame
|
// Extract the payload from the frame
|
||||||
@@ -91,8 +107,44 @@ impl Frame {
|
|||||||
|
|
||||||
|
|
||||||
match head.kind() {
|
match head.kind() {
|
||||||
Kind::Unknown => Frame::Unknown(Unknown::new(head, frame)),
|
Kind::Unknown => {
|
||||||
|
let unknown = Unknown::new(head, frame);
|
||||||
|
Ok(Frame::Unknown(unknown))
|
||||||
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn encode_len(&self) -> usize {
|
||||||
|
use self::Frame::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Settings(ref frame) => frame.encode_len(),
|
||||||
|
Unknown(ref frame) => frame.encode_len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(&self, dst: &mut BytesMut) -> Result<(), Error> {
|
||||||
|
use self::Frame::*;
|
||||||
|
|
||||||
|
debug_assert!(dst.remaining_mut() >= self.encode_len());
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Settings(ref frame) => frame.encode(dst),
|
||||||
|
Unknown(ref frame) => frame.encode(dst),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== impl Error =====
|
||||||
|
|
||||||
|
impl From<Error> for ConnectionError {
|
||||||
|
fn from(src: Error) -> ConnectionError {
|
||||||
|
use self::Error::*;
|
||||||
|
|
||||||
|
match src {
|
||||||
|
// TODO: implement
|
||||||
|
_ => ConnectionError::Proto(Reason::ProtocolError),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
src/frame/reader.rs
Normal file
40
src/frame/reader.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use frame::{Error, Head};
|
use frame::{Error, Head, Kind};
|
||||||
use bytes::Bytes;
|
use bytes::{Bytes, BytesMut, BufMut, BigEndian};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
flag: SettingsFlag,
|
flag: SettingsFlag,
|
||||||
// Fields
|
// Fields
|
||||||
@@ -41,8 +41,7 @@ impl Settings {
|
|||||||
debug_assert_eq!(head.kind(), ::frame::Kind::Settings);
|
debug_assert_eq!(head.kind(), ::frame::Kind::Settings);
|
||||||
|
|
||||||
if head.stream_id() != 0 {
|
if head.stream_id() != 0 {
|
||||||
// TODO: raise ProtocolError
|
return Err(Error::InvalidStreamId);
|
||||||
unimplemented!();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the flag
|
// Load the flag
|
||||||
@@ -51,8 +50,7 @@ impl Settings {
|
|||||||
if flag.is_ack() {
|
if flag.is_ack() {
|
||||||
// Ensure that the payload is empty
|
// Ensure that the payload is empty
|
||||||
if payload.len() > 0 {
|
if payload.len() > 0 {
|
||||||
// TODO: raise a FRAME_SIZE_ERROR
|
return Err(Error::InvalidPayloadLength);
|
||||||
unimplemented!();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the ACK frame
|
// Return the ACK frame
|
||||||
@@ -64,7 +62,7 @@ impl Settings {
|
|||||||
|
|
||||||
// Ensure the payload length is correct, each setting is 6 bytes long.
|
// Ensure the payload length is correct, each setting is 6 bytes long.
|
||||||
if payload.len() % 6 != 0 {
|
if payload.len() % 6 != 0 {
|
||||||
return Err(Error::PartialSettingLength);
|
return Err(Error::InvalidPayloadAckSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut settings = Settings::default();
|
let mut settings = Settings::default();
|
||||||
@@ -96,6 +94,57 @@ impl Settings {
|
|||||||
|
|
||||||
Ok(settings)
|
Ok(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn encode_len(&self) -> usize {
|
||||||
|
super::FRAME_HEADER_LEN + self.payload_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn payload_len(&self) -> usize {
|
||||||
|
let mut len = 0;
|
||||||
|
self.for_each(|_| len += 6);
|
||||||
|
len
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(&self, dst: &mut BytesMut) -> Result<(), Error> {
|
||||||
|
// Create & encode an appropriate frame head
|
||||||
|
let head = Head::new(Kind::Settings, self.flag.into(), 0);
|
||||||
|
let payload_len = self.payload_len();
|
||||||
|
|
||||||
|
try!(head.encode(payload_len, dst));
|
||||||
|
|
||||||
|
// Encode the settings
|
||||||
|
self.for_each(|setting| setting.encode(dst));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn for_each<F: FnMut(Setting)>(&self, mut f: F) {
|
||||||
|
use self::Setting::*;
|
||||||
|
|
||||||
|
if let Some(v) = self.header_table_size {
|
||||||
|
f(HeaderTableSize(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = self.enable_push {
|
||||||
|
f(EnablePush(if v { 1 } else { 0 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = self.max_concurrent_streams {
|
||||||
|
f(MaxConcurrentStreams(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = self.initial_window_size {
|
||||||
|
f(InitialWindowSize(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = self.max_frame_size {
|
||||||
|
f(MaxFrameSize(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = self.max_header_list_size {
|
||||||
|
f(MaxHeaderListSize(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl Setting =====
|
// ===== impl Setting =====
|
||||||
@@ -134,6 +183,22 @@ impl Setting {
|
|||||||
|
|
||||||
Setting::from_id(id, val)
|
Setting::from_id(id, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn encode(&self, dst: &mut BytesMut) {
|
||||||
|
use self::Setting::*;
|
||||||
|
|
||||||
|
let (kind, val) = match *self {
|
||||||
|
HeaderTableSize(v) => (1, v),
|
||||||
|
EnablePush(v) => (2, v),
|
||||||
|
MaxConcurrentStreams(v) => (3, v),
|
||||||
|
InitialWindowSize(v) => (4, v),
|
||||||
|
MaxFrameSize(v) => (5, v),
|
||||||
|
MaxHeaderListSize(v) => (6, v),
|
||||||
|
};
|
||||||
|
|
||||||
|
dst.put_u16::<BigEndian>(kind);
|
||||||
|
dst.put_u32::<BigEndian>(val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl SettingsFlag =====
|
// ===== impl SettingsFlag =====
|
||||||
@@ -151,3 +216,9 @@ impl SettingsFlag {
|
|||||||
self.0 & ACK == ACK
|
self.0 & ACK == ACK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<SettingsFlag> for u8 {
|
||||||
|
fn from(src: SettingsFlag) -> u8 {
|
||||||
|
src.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use frame::Head;
|
use frame::{Head, Error};
|
||||||
use bytes::Bytes;
|
use bytes::{Bytes, BytesMut, BufMut};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Unknown {
|
pub struct Unknown {
|
||||||
@@ -14,4 +14,14 @@ impl Unknown {
|
|||||||
payload: payload,
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/frame/writer.rs
Normal file
1
src/frame/writer.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub struct Writer;
|
||||||
47
src/lib.rs
47
src/lib.rs
@@ -1,12 +1,17 @@
|
|||||||
#![allow(warnings)]
|
#![allow(warnings)]
|
||||||
|
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
|
#[macro_use]
|
||||||
extern crate tokio_io;
|
extern crate tokio_io;
|
||||||
extern crate tokio_timer;
|
extern crate tokio_timer;
|
||||||
extern crate bytes;
|
extern crate bytes;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod proto;
|
||||||
pub mod frame;
|
pub mod frame;
|
||||||
|
|
||||||
|
pub use error::{ConnectionError, StreamError, Reason};
|
||||||
|
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
use tokio_io::codec::length_delimited;
|
use tokio_io::codec::length_delimited;
|
||||||
|
|
||||||
@@ -16,15 +21,39 @@ pub struct Transport<T> {
|
|||||||
inner: length_delimited::FramedRead<T>,
|
inner: length_delimited::FramedRead<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind<T: AsyncRead + AsyncRead>(io: T) -> Transport<T> {
|
impl<T: AsyncRead + AsyncWrite> Transport<T> {
|
||||||
let framed = length_delimited::Builder::new()
|
pub fn bind(io: T) -> Transport<T> {
|
||||||
.big_endian()
|
let framed = length_delimited::Builder::new()
|
||||||
.length_field_length(3)
|
.big_endian()
|
||||||
.length_adjustment(6)
|
.length_field_length(3)
|
||||||
.num_skip(0) // Don't skip the header
|
.length_adjustment(6)
|
||||||
.new_read(io);
|
.num_skip(0) // Don't skip the header
|
||||||
|
.new_read(io);
|
||||||
|
|
||||||
Transport {
|
Transport {
|
||||||
inner: framed,
|
inner: framed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsyncRead + AsyncWrite> Stream for Transport<T> {
|
||||||
|
type Item = frame::Frame;
|
||||||
|
type Error = ConnectionError;
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<Option<frame::Frame>, ConnectionError> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsyncRead + AsyncWrite> Sink for Transport<T> {
|
||||||
|
type SinkItem = frame::Frame;
|
||||||
|
type SinkError = ConnectionError;
|
||||||
|
|
||||||
|
fn start_send(&mut self, item: frame::Frame) -> StartSend<frame::Frame, ConnectionError> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_complete(&mut self) -> Poll<(), ConnectionError> {
|
||||||
|
unimplemented!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/proto/framed_read.rs
Normal file
43
src/proto/framed_read.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use ConnectionError;
|
||||||
|
use frame::Frame;
|
||||||
|
|
||||||
|
use futures::*;
|
||||||
|
use bytes::BytesMut;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
pub struct FramedRead<T> {
|
||||||
|
inner: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Stream for FramedRead<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 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.start_send(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
||||||
|
self.inner.poll_complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
89
src/proto/framed_write.rs
Normal file
89
src/proto/framed_write.rs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
use {ConnectionError, Reason};
|
||||||
|
use frame::{Frame, Error};
|
||||||
|
|
||||||
|
use tokio_io::AsyncWrite;
|
||||||
|
use futures::*;
|
||||||
|
use bytes::{BytesMut, Buf, BufMut};
|
||||||
|
|
||||||
|
use std::io::{self, Cursor};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FramedWrite<T> {
|
||||||
|
inner: T,
|
||||||
|
buf: Cursor<BytesMut>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_BUFFER_CAPACITY: usize = 8 * 1_024;
|
||||||
|
const MAX_BUFFER_CAPACITY: usize = 16 * 1_024;
|
||||||
|
|
||||||
|
impl<T: AsyncWrite> FramedWrite<T> {
|
||||||
|
pub fn new(inner: T) -> FramedWrite<T> {
|
||||||
|
let buf = BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY);
|
||||||
|
|
||||||
|
FramedWrite {
|
||||||
|
inner: inner,
|
||||||
|
buf: Cursor::new(buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_buf(&mut self) -> &mut BytesMut {
|
||||||
|
self.buf.get_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsyncWrite> Sink for FramedWrite<T> {
|
||||||
|
type SinkItem = Frame;
|
||||||
|
type SinkError = ConnectionError;
|
||||||
|
|
||||||
|
fn start_send(&mut self, item: Frame) -> StartSend<Frame, ConnectionError> {
|
||||||
|
let len = item.encode_len();
|
||||||
|
|
||||||
|
if len > MAX_BUFFER_CAPACITY {
|
||||||
|
// This case should never happen. Large frames should be chunked at
|
||||||
|
// a higher level, so this is an internal error.
|
||||||
|
return Err(ConnectionError::Proto(Reason::InternalError));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.write_buf().remaining_mut() <= len {
|
||||||
|
// Try flushing the buffer
|
||||||
|
try!(self.poll_complete());
|
||||||
|
|
||||||
|
let rem = self.write_buf().remaining_mut();
|
||||||
|
let additional = len - rem;
|
||||||
|
|
||||||
|
if self.write_buf().capacity() + additional > MAX_BUFFER_CAPACITY {
|
||||||
|
return Ok(AsyncSink::NotReady(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grow the buffer
|
||||||
|
self.write_buf().reserve(additional);
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, the buffer contains enough space
|
||||||
|
item.encode(self.write_buf());
|
||||||
|
|
||||||
|
Ok(AsyncSink::Ready)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_complete(&mut self) -> Poll<(), ConnectionError> {
|
||||||
|
while self.buf.has_remaining() {
|
||||||
|
try_ready!(self.inner.write_buf(&mut self.buf));
|
||||||
|
|
||||||
|
if !self.buf.has_remaining() {
|
||||||
|
// Reset the buffer
|
||||||
|
self.write_buf().clear();
|
||||||
|
self.buf.set_position(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try flushing the underlying IO
|
||||||
|
try_nb!(self.inner.flush());
|
||||||
|
|
||||||
|
return Ok(Async::Ready(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self) -> Poll<(), ConnectionError> {
|
||||||
|
try_ready!(self.poll_complete());
|
||||||
|
self.inner.shutdown().map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/proto/mod.rs
Normal file
4
src/proto/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
mod framed_read;
|
||||||
|
mod framed_write;
|
||||||
|
|
||||||
|
pub use self::framed_read::FramedRead;
|
||||||
Reference in New Issue
Block a user