Files
h2/src/proto/mod.rs
Oliver Gould df589f2fde Address feedback on ControlFlow and FlowControl
ControlFlow::poll_window_update now exposes, effectively, a Stream of
WindowUpdates.  Callers no longer poll on invidual stream IDs. To
accomplish this, FlowControl maintains a queue of pending remote stream
ids.

Improve/shorten naming throughout FlowControl.

FlowControlState::check_window has been added so that FlowControl is now
consistent in the face of stream-level flow control errors.

Connection now exposes the ControlFlow functions without exposing the
ControlFlow interface publicly.
2017-07-18 22:36:41 +00:00

233 lines
7.4 KiB
Rust

use {frame, ConnectionError, Peer, StreamId};
use error::Reason;
use frame::SettingSet;
use bytes::{Buf, IntoBuf};
use futures::*;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::length_delimited;
mod connection;
mod flow_control;
mod flow_control_state;
mod framed_read;
mod framed_write;
mod ping_pong;
mod ready;
mod settings;
mod state;
mod stream_tracker;
pub use self::connection::Connection;
pub use self::flow_control::FlowControl;
pub use self::flow_control_state::{FlowControlState, WindowUnderflow};
pub use self::framed_read::FramedRead;
pub use self::framed_write::FramedWrite;
pub use self::ping_pong::PingPong;
pub use self::ready::ReadySink;
pub use self::settings::Settings;
pub use self::stream_tracker::StreamTracker;
use self::state::{StreamMap, StreamState};
/// 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.
///
/// ### `FlowControl`
///
/// - Tracks received data frames against the local stream and connection flow control
/// windows.
/// - Tracks sent data frames against the remote stream and connection flow control
/// windows.
/// - Tracks remote settings updates to SETTINGS_INITIAL_WINDOW_SIZE.
/// - Exposes `ControlFlow` upwards.
/// - Tracks received window updates against the remote stream and connection flow
/// control windows so that upper layers may poll for updates.
/// - Sends window updates for the local stream and connection flow control windows as
/// instructed by upper layers.
///
/// ### `StreamTracker`
///
/// - Tracks all active streams.
/// - Tracks all reset 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, P, B>=
Settings<
FlowControl<
StreamTracker<
PingPong<
Framer<T, B>,
B>,
P>>>;
type Framer<T, B> =
FramedRead<
FramedWrite<T, B>>;
pub type WindowSize = u32;
/// Exposes settings to "upper" layers of the transport (i.e. from Settings up to---and
/// above---Connection).
pub trait ControlSettings {
fn update_local_settings(&mut self, set: frame::SettingSet) -> Result<(), ConnectionError>;
fn local_settings(&self) -> &SettingSet;
fn remote_settings(&self) -> &SettingSet;
}
/// Allows settings updates to be pushed "down" the transport (i.e. from Settings down to
/// FramedWrite).
pub trait ApplySettings {
fn apply_local_settings(&mut self, set: &frame::SettingSet) -> Result<(), ConnectionError>;
fn apply_remote_settings(&mut self, set: &frame::SettingSet) -> Result<(), ConnectionError>;
}
#[derive(Debug, Copy, Clone)]
pub struct WindowUpdate(pub StreamId, pub WindowSize);
impl WindowUpdate {
pub fn stream_id(&self) -> StreamId {
self.0
}
pub fn increment(&self) -> WindowSize {
self.1
}
}
/// Exposes flow control states to "upper" layers of the transport (i.e. above
/// FlowControl).
pub trait ControlFlow {
/// Polls for the next window update from the remote.
fn poll_window_update(&mut self) -> Poll<WindowUpdate, ConnectionError>;
/// Increases the local receive capacity of a stream.
///
/// This may cause a window update to be sent to the remote.
///
/// Fails if the given stream is not active.
fn expand_window(&mut self, id: StreamId, incr: WindowSize) -> Result<(), ConnectionError>;
}
/// Exposes stream states to "upper" layers of the transport (i.e. from StreamTracker up
/// to Connection).
pub trait ControlStreams {
/// Accesses the map of all active streams.
fn streams(&self)-> &StreamMap;
/// Mutably accesses the map of all active streams.
fn streams_mut(&mut self) -> &mut StreamMap;
/// Checks whether a stream has been reset.
fn stream_is_reset(&self, id: StreamId) -> Option<Reason>;
}
pub type PingPayload = [u8; 8];
pub trait ControlPing {
fn start_ping(&mut self, body: PingPayload) -> StartSend<PingPayload, ConnectionError>;
fn take_pong(&mut self) -> Option<PingPayload>;
}
/// 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, 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, settings);
from_server_handshaker(transport)
}
/// 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>>)
-> Connection<T, P, B>
where T: AsyncRead + AsyncWrite,
P: Peer,
B: IntoBuf,
{
let initial_local_window_size = settings.local_settings().initial_window_size();
let initial_remote_window_size = settings.remote_settings().initial_window_size();
let local_max_concurrency = settings.local_settings().max_concurrent_streams();
let remote_max_concurrency = settings.remote_settings().max_concurrent_streams();
// Replace Settings' writer with a full transport.
let transport = settings.swap_inner(|io| {
// Delimit the frames.
let framer = length_delimited::Builder::new()
.big_endian()
.length_field_length(3)
.length_adjustment(9)
.num_skip(0) // Don't skip the header
.new_read(io);
FlowControl::new(
initial_local_window_size,
initial_remote_window_size,
StreamTracker::new(
initial_local_window_size,
initial_remote_window_size,
local_max_concurrency,
remote_max_concurrency,
PingPong::new(
FramedRead::new(framer)
)
)
)
});
connection::new(transport)
}