Restructure proto
The existing code has been moved out and is being copied back piece / by piece while restructuring the code to (hopefully) be more manageable.
This commit is contained in:
332
src/proto/mod.rs
332
src/proto/mod.rs
@@ -1,212 +1,27 @@
|
||||
use {frame, Peer, StreamId};
|
||||
use error::Reason;
|
||||
use frame::Frame;
|
||||
|
||||
use bytes::{Buf, IntoBuf};
|
||||
use futures::*;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_io::codec::length_delimited;
|
||||
|
||||
macro_rules! proxy_stream {
|
||||
($struct:ident $(, $targs:ident)*) => (
|
||||
impl<T: Stream$(, $targs)*> Stream for $struct<T$(, $targs)*> {
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! proxy_sink {
|
||||
($struct:ident $(, $targs:ident)*) => (
|
||||
impl<T, U$(, $targs)*> Sink for $struct<T$(, $targs)*>
|
||||
where T: Sink<SinkItem = frame::Frame<U>, SinkError = ConnectionError>
|
||||
{
|
||||
type SinkItem = frame::Frame<U>;
|
||||
type SinkError = ConnectionError;
|
||||
fn start_send(&mut self, it: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> {
|
||||
self.inner.start_send(it)
|
||||
}
|
||||
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! proxy_ready_sink {
|
||||
($struct:ident $(, $targs:ident)*$(; $constraint:ident)*) => (
|
||||
impl<T, U$(, $targs)*> ReadySink for $struct<T$(, $targs)*>
|
||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
T: ReadySink $(+ $constraint)*
|
||||
{
|
||||
fn poll_ready(&mut self) -> Poll<(), T::SinkError> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// First, pull in the internal interfaces that support macros used throughout this module.
|
||||
|
||||
#[macro_use]
|
||||
mod apply_settings;
|
||||
#[macro_use]
|
||||
mod control_flow;
|
||||
#[macro_use]
|
||||
mod control_ping;
|
||||
mod control_settings;
|
||||
#[macro_use]
|
||||
mod control_streams;
|
||||
|
||||
use self::apply_settings::ApplySettings;
|
||||
use self::control_flow::{ControlFlowRecv, ControlFlowSend};
|
||||
use self::control_ping::ControlPing;
|
||||
use self::control_settings::ControlSettings;
|
||||
use self::control_streams::ControlStreams;
|
||||
|
||||
mod connection;
|
||||
mod flow_control_recv;
|
||||
mod flow_control_send;
|
||||
mod flow_control_state;
|
||||
mod framed_read;
|
||||
mod framed_write;
|
||||
mod ping_pong;
|
||||
mod ready;
|
||||
mod settings;
|
||||
mod stream_recv_close;
|
||||
mod stream_recv_open;
|
||||
mod stream_send_close;
|
||||
mod stream_send_open;
|
||||
mod stream_state;
|
||||
mod stream_states;
|
||||
mod state;
|
||||
mod streams;
|
||||
|
||||
pub use self::connection::Connection;
|
||||
|
||||
use self::flow_control_recv::FlowControlRecv;
|
||||
use self::flow_control_send::FlowControlSend;
|
||||
use self::flow_control_state::FlowControlState;
|
||||
use self::framed_read::FramedRead;
|
||||
use self::framed_write::FramedWrite;
|
||||
use self::ping_pong::PingPong;
|
||||
use self::ready::ReadySink;
|
||||
use self::settings::Settings;
|
||||
use self::stream_recv_close::StreamRecvClose;
|
||||
use self::stream_recv_open::StreamRecvOpen;
|
||||
use self::stream_send_close::StreamSendClose;
|
||||
use self::stream_send_open::StreamSendOpen;
|
||||
use self::stream_states::{StreamStates, Streams};
|
||||
use self::streams::Streams;
|
||||
|
||||
/// Represents the internals of an HTTP/2 connection.
|
||||
///
|
||||
/// A transport consists of several layers (_transporters_) and is arranged from _top_
|
||||
/// (near the application) to _bottom_ (near the network). Each transporter implements a
|
||||
/// Stream of frames received from the remote, and a ReadySink of frames sent to the
|
||||
/// remote.
|
||||
///
|
||||
/// ## Transport Layers
|
||||
///
|
||||
/// ### `Settings`
|
||||
///
|
||||
/// - Receives remote settings frames and applies the settings downward through the
|
||||
/// transport (via the ApplySettings trait) before responding with acknowledgements.
|
||||
/// - Exposes ControlSettings up towards the application and transmits local settings to
|
||||
/// the remote.
|
||||
///
|
||||
/// ### The stream transport
|
||||
///
|
||||
/// The states of all HTTP/2 connections are stored centrally in the `StreamStates` at the
|
||||
/// bottom of the stream transport. Several modules above this access this state via the
|
||||
/// `ControlStreams` API to drive changes to the stream state. In each direction (send
|
||||
/// from local to remote, and recv from remote to local), there is an Stream\*Open module
|
||||
/// responsible for initializing new streams and ensuring that frames do not violate
|
||||
/// stream state. Then, there are modules that operate on streams (for instance,
|
||||
/// FlowControl). Finally, a Stream\*Close module is responsible for acting on END_STREAM
|
||||
/// frames to ensure that stream states are not closed before work is complete.
|
||||
///
|
||||
/// #### `StreamSendOpen`
|
||||
///
|
||||
/// - Initializes streams initiated by the local peer.
|
||||
/// - Ensures that frames sent from the local peer are appropriate for the stream's state.
|
||||
/// - Ensures that the remote's max stream concurrency is not violated.
|
||||
///
|
||||
/// #### `FlowControlSend`
|
||||
///
|
||||
/// - Tracks sent data frames against the remote stream and connection flow control
|
||||
/// windows.
|
||||
/// - Tracks remote settings updates to SETTINGS_INITIAL_WINDOW_SIZE.
|
||||
/// - Exposes `ControlFlowSend` upwards.
|
||||
/// - Tracks received window updates against the remote stream and connection flow
|
||||
/// control windows so that upper layers may poll for updates.
|
||||
///
|
||||
/// #### `StreamSendClose`
|
||||
///
|
||||
/// - Updates the stream state for frames sent with END_STREAM.
|
||||
///
|
||||
/// #### `StreamRecvClose`
|
||||
///
|
||||
/// - Updates the stream state for frames received with END_STREAM.
|
||||
///
|
||||
/// #### `FlowControlRecv`
|
||||
///
|
||||
/// - Tracks received data frames against the local stream and connection flow control
|
||||
/// windows.
|
||||
/// - Tracks remote settings updates to SETTINGS_INITIAL_WINDOW_SIZE.
|
||||
/// - Exposes `ControlFlowRecv` upwards.
|
||||
/// - Sends window updates for the local stream and connection flow control windows as
|
||||
/// instructed by upper layers.
|
||||
///
|
||||
/// #### `StreamRecvOpen`
|
||||
///
|
||||
/// - Initializes streams initiated by the remote peer.
|
||||
/// - Ensures that frames received from the remote peer are appropriate for the stream's
|
||||
/// state.
|
||||
/// - Ensures that the local peer's max stream concurrency is not violated.
|
||||
/// - Emits StreamRefused resets to the remote.
|
||||
///
|
||||
/// #### `StreamStates`
|
||||
///
|
||||
/// - Holds the state of all local & remote active streams.
|
||||
/// - Holds the cause of all reset/closed streams.
|
||||
/// - Exposes `ControlStreams` so that upper layers may share stream state.
|
||||
///
|
||||
/// ### `PingPong`
|
||||
///
|
||||
/// - Acknowleges PINGs from the remote.
|
||||
/// - Exposes ControlPing that allows the application side to send ping requests to the
|
||||
/// remote. Acknowledgements from the remoe are queued to be consumed by the
|
||||
/// application.
|
||||
///
|
||||
/// ### FramedRead
|
||||
///
|
||||
/// - Decodes frames from bytes.
|
||||
///
|
||||
/// ### FramedWrite
|
||||
///
|
||||
/// - Encodes frames to bytes.
|
||||
///
|
||||
type Transport<T, B>=
|
||||
Settings<
|
||||
Streams2<
|
||||
PingPong<
|
||||
Codec<T, B>,
|
||||
B>>>;
|
||||
use {StreamId, Peer};
|
||||
use error::Reason;
|
||||
use frame::Frame;
|
||||
|
||||
// TODO: rename
|
||||
type Streams2<T> =
|
||||
StreamSendOpen<
|
||||
FlowControlSend<
|
||||
StreamSendClose<
|
||||
StreamRecvClose<
|
||||
FlowControlRecv<
|
||||
StreamRecvOpen<
|
||||
StreamStates<T>>>>>>>;
|
||||
|
||||
type Codec<T, B> =
|
||||
FramedRead<
|
||||
FramedWrite<T, B>>;
|
||||
use futures::*;
|
||||
use bytes::{Buf, IntoBuf};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_io::codec::length_delimited;
|
||||
|
||||
pub type PingPayload = [u8; 8];
|
||||
|
||||
@@ -215,12 +30,55 @@ pub type WindowSize = u32;
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WindowUpdate {
|
||||
stream_id: StreamId,
|
||||
increment: WindowSize
|
||||
increment: WindowSize,
|
||||
}
|
||||
|
||||
type Codec<T, B> =
|
||||
FramedRead<
|
||||
FramedWrite<T, B>>;
|
||||
|
||||
// Constants
|
||||
pub const DEFAULT_INITIAL_WINDOW_SIZE: WindowSize = 65_535;
|
||||
pub const MAX_WINDOW_SIZE: WindowSize = ::std::u32::MAX;
|
||||
|
||||
/// 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 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 fn from_framed_write<T, P, B>(framed_write: FramedWrite<T, 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
|
||||
.new_read(framed_write);
|
||||
|
||||
let codec = FramedRead::new(framed);
|
||||
|
||||
connection::new(codec)
|
||||
}
|
||||
|
||||
impl WindowUpdate {
|
||||
pub fn new(stream_id: StreamId, increment: WindowSize) -> WindowUpdate {
|
||||
WindowUpdate { stream_id, increment }
|
||||
WindowUpdate {
|
||||
stream_id,
|
||||
increment
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
@@ -231,83 +89,3 @@ impl WindowUpdate {
|
||||
self.increment
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a full H2 transport from an I/O handle.
|
||||
///
|
||||
/// This is called as the final step of the client handshake future.
|
||||
pub fn from_io<T, P, B>(io: T, local_settings: frame::SettingSet)
|
||||
-> Connection<T, P, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
P: Peer,
|
||||
B: IntoBuf,
|
||||
{
|
||||
let framed_write: FramedWrite<_, B::Buf> = FramedWrite::new(io);
|
||||
|
||||
// To avoid code duplication, we're going to go this route. It is a bit
|
||||
// weird, but oh well...
|
||||
//
|
||||
// We first create a Settings directly around a framed writer
|
||||
let transport = Settings::new(framed_write, local_settings.clone());
|
||||
|
||||
from_server_handshaker(transport, local_settings)
|
||||
}
|
||||
|
||||
/// 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 fn server_handshaker<T, B>(io: T, settings: frame::SettingSet)
|
||||
-> Settings<FramedWrite<T, B>>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
B: Buf,
|
||||
{
|
||||
let framed_write = FramedWrite::new(io);
|
||||
Settings::new(framed_write, settings)
|
||||
}
|
||||
|
||||
/// Create a full H2 transport from the server handshaker
|
||||
pub fn from_server_handshaker<T, P, B>(settings: Settings<FramedWrite<T, B::Buf>>,
|
||||
local_settings: frame::SettingSet)
|
||||
-> Connection<T, P, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
P: Peer,
|
||||
B: IntoBuf,
|
||||
{
|
||||
let initial_recv_window_size = local_settings.initial_window_size().unwrap_or(65_535);
|
||||
let local_max_concurrency = local_settings.max_concurrent_streams();
|
||||
|
||||
let initial_send_window_size = settings.remote_initial_window_size();
|
||||
let remote_max_concurrency = settings.remote_max_concurrent_streams();
|
||||
|
||||
// Replace Settings' writer with a full transport.
|
||||
let transport = settings.swap_inner(|io| {
|
||||
// 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
|
||||
.new_read(io);
|
||||
|
||||
trace!("composing transport");
|
||||
StreamSendOpen::new(
|
||||
initial_send_window_size,
|
||||
remote_max_concurrency,
|
||||
FlowControlSend::new(
|
||||
initial_send_window_size,
|
||||
StreamSendClose::new(
|
||||
StreamRecvClose::new(
|
||||
FlowControlRecv::new(
|
||||
initial_recv_window_size,
|
||||
StreamRecvOpen::new(
|
||||
initial_recv_window_size,
|
||||
local_max_concurrency,
|
||||
StreamStates::new::<P>(
|
||||
PingPong::new(
|
||||
FramedRead::new(framed)))))))))
|
||||
});
|
||||
|
||||
connection::new(transport)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user