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= Settings< FlowControl< StreamTracker< PingPong< Framer, B>, P>>>; type Framer = FramedRead< FramedWrite>; 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; /// 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; } pub type PingPayload = [u8; 8]; pub trait ControlPing { fn start_ping(&mut self, body: PingPayload) -> StartSend; fn take_pong(&mut self) -> Option; } /// 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(io: T, settings: frame::SettingSet) -> Connection 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>` is returned. pub fn server_handshaker(io: T, settings: frame::SettingSet) -> Settings> 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(settings: Settings>) -> Connection 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) }