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.
233 lines
7.4 KiB
Rust
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)
|
|
}
|