expose a ControlPing api
document transports properly handle connection flow control tracking.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
use {ConnectionError, Frame, FrameSize};
|
use {ConnectionError, Frame, FrameSize};
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use frame::{self, SettingSet, StreamId};
|
use frame::{self, SettingSet, StreamId};
|
||||||
use proto::{self, ControlSettings, Peer, ReadySink, ControlFlow, WindowSize};
|
use proto::{self, ControlFlow, ControlPing, ControlSettings, Peer, PingPayload, ReadySink, WindowSize};
|
||||||
use server::Server;
|
use server::Server;
|
||||||
|
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
@@ -58,8 +58,23 @@ impl<T, P, B> ControlFlow for Connection<T, P, B>
|
|||||||
self.inner.poll_remote_window_update(id)
|
self.inner.poll_remote_window_update(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grow_local_window(&mut self, id: StreamId, incr: WindowSize) -> Result<(), ConnectionError> {
|
fn expand_local_window(&mut self, id: StreamId, incr: WindowSize) -> Result<(), ConnectionError> {
|
||||||
self.inner.grow_local_window(id, incr)
|
self.inner.expand_local_window(id, incr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, P, B> ControlPing for Connection<T, P, B>
|
||||||
|
where T: AsyncRead + AsyncWrite,
|
||||||
|
T: ControlPing,
|
||||||
|
P: Peer,
|
||||||
|
B: IntoBuf,
|
||||||
|
{
|
||||||
|
fn start_ping(&mut self, body: PingPayload) -> StartSend<PingPayload, ConnectionError> {
|
||||||
|
self.inner.start_ping(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_pong(&mut self) -> Option<PingPayload> {
|
||||||
|
self.inner.pop_pong()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,15 @@ pub struct FlowControl<T> {
|
|||||||
|
|
||||||
/// Tracks the connection-level flow control window for receiving data from the
|
/// Tracks the connection-level flow control window for receiving data from the
|
||||||
/// remote.
|
/// remote.
|
||||||
local_flow_controller: FlowControlState,
|
connection_local_flow_controller: FlowControlState,
|
||||||
|
|
||||||
/// Tracks the onnection-level flow control window for receiving data from the remote.
|
/// Tracks the onnection-level flow control window for receiving data from the remote.
|
||||||
remote_flow_controller: FlowControlState,
|
connection_remote_flow_controller: FlowControlState,
|
||||||
|
|
||||||
/// Holds the list of streams on which local window updates may be sent.
|
/// Holds the list of streams on which local window updates may be sent.
|
||||||
// XXX It would be cool if this didn't exist.
|
// XXX It would be cool if this didn't exist.
|
||||||
pending_local_window_updates: VecDeque<StreamId>,
|
pending_local_window_updates: VecDeque<StreamId>,
|
||||||
|
pending_local_connection_window_update: bool,
|
||||||
|
|
||||||
/// If a window update can't be sent immediately, it may need to be saved to be sent later.
|
/// If a window update can't be sent immediately, it may need to be saved to be sent later.
|
||||||
sending_local_window_update: Option<frame::WindowUpdate>,
|
sending_local_window_update: Option<frame::WindowUpdate>,
|
||||||
@@ -45,98 +46,31 @@ impl<T, U> FlowControl<T>
|
|||||||
inner,
|
inner,
|
||||||
initial_local_window_size,
|
initial_local_window_size,
|
||||||
initial_remote_window_size,
|
initial_remote_window_size,
|
||||||
local_flow_controller: FlowControlState::with_initial_size(initial_local_window_size),
|
connection_local_flow_controller: FlowControlState::with_initial_size(initial_local_window_size),
|
||||||
remote_flow_controller: FlowControlState::with_next_update(initial_remote_window_size),
|
connection_remote_flow_controller: FlowControlState::with_next_update(initial_remote_window_size),
|
||||||
blocked_remote_window_update: None,
|
blocked_remote_window_update: None,
|
||||||
sending_local_window_update: None,
|
sending_local_window_update: None,
|
||||||
pending_local_window_updates: VecDeque::new(),
|
pending_local_window_updates: VecDeque::new(),
|
||||||
|
pending_local_connection_window_update: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flow control utitlities.
|
// Flow control utitlities.
|
||||||
impl<T: ControlStreams> FlowControl<T> {
|
impl<T: ControlStreams> FlowControl<T> {
|
||||||
fn claim_local_window(&mut self, id: &StreamId, len: WindowSize) -> Result<(), ConnectionError> {
|
fn local_flow_controller(&mut self, id: StreamId) -> Option<&mut FlowControlState> {
|
||||||
let res = if id.is_zero() {
|
|
||||||
self.local_flow_controller.claim_window(len)
|
|
||||||
} else if let Some(mut stream) = self.inner.streams_mut().get_mut(&id) {
|
|
||||||
stream.claim_local_window(len)
|
|
||||||
} else {
|
|
||||||
// Ignore updates for non-existent streams.
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
res.map_err(|_| error::Reason::FlowControlError.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn claim_remote_window(&mut self, id: &StreamId, len: WindowSize) -> Result<(), ConnectionError> {
|
|
||||||
let res = if id.is_zero() {
|
|
||||||
self.local_flow_controller.claim_window(len)
|
|
||||||
} else if let Some(mut stream) = self.inner.streams_mut().get_mut(&id) {
|
|
||||||
stream.claim_remote_window(len)
|
|
||||||
} else {
|
|
||||||
// Ignore updates for non-existent streams.
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
res.map_err(|_| error::Reason::FlowControlError.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handles a window update received from the remote, indicating that the local may
|
|
||||||
/// send `incr` additional bytes.
|
|
||||||
///
|
|
||||||
/// Connection window updates (id=0) and stream window updates are advertised
|
|
||||||
/// distinctly.
|
|
||||||
fn grow_remote_window(&mut self, id: StreamId, incr: WindowSize) {
|
|
||||||
if incr == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if id.is_zero() {
|
if id.is_zero() {
|
||||||
self.remote_flow_controller.grow_window(incr);
|
Some(&mut self.connection_local_flow_controller)
|
||||||
} else if let Some(mut s) = self.inner.streams_mut().get_mut(&id) {
|
|
||||||
s.grow_remote_window(incr);
|
|
||||||
} else {
|
} else {
|
||||||
// Ignore updates for non-existent streams.
|
self.inner.streams_mut().get_mut(&id).and_then(|s| s.local_flow_controller())
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(task) = self.blocked_remote_window_update.take() {
|
|
||||||
task.notify();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Exposes a public upward API for flow control.
|
fn remote_flow_controller(&mut self, id: StreamId) -> Option<&mut FlowControlState> {
|
||||||
impl<T: ControlStreams> ControlFlow for FlowControl<T> {
|
|
||||||
fn poll_remote_window_update(&mut self, id: StreamId) -> Poll<WindowSize, ConnectionError> {
|
|
||||||
if id.is_zero() {
|
if id.is_zero() {
|
||||||
if let Some(sz) = self.remote_flow_controller.take_window_update() {
|
Some(&mut self.connection_remote_flow_controller)
|
||||||
return Ok(Async::Ready(sz));
|
|
||||||
}
|
|
||||||
} else if let Some(mut stream) = self.inner.streams_mut().get_mut(&id) {
|
|
||||||
if let Some(sz) = stream.take_remote_window_update() {
|
|
||||||
return Ok(Async::Ready(sz));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return Err(error::User::InvalidStreamId.into());
|
self.inner.streams_mut().get_mut(&id).and_then(|s| s.remote_flow_controller())
|
||||||
}
|
|
||||||
|
|
||||||
self.blocked_remote_window_update = Some(task::current());
|
|
||||||
return Ok(Async::NotReady);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn grow_local_window(&mut self, id: StreamId, incr: WindowSize) -> Result<(), ConnectionError> {
|
|
||||||
if id.is_zero() {
|
|
||||||
self.local_flow_controller.grow_window(incr);
|
|
||||||
self.pending_local_window_updates.push_back(id);
|
|
||||||
Ok(())
|
|
||||||
} else if let Some(mut stream) = self.inner.streams_mut().get_mut(&id) {
|
|
||||||
stream.grow_local_window(incr);
|
|
||||||
self.pending_local_window_updates.push_back(id);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(error::User::InvalidStreamId.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,6 +88,47 @@ impl<T: ControlStreams> ControlStreams for FlowControl<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Exposes a public upward API for flow control.
|
||||||
|
impl<T: ControlStreams> ControlFlow for FlowControl<T> {
|
||||||
|
fn poll_remote_window_update(&mut self, id: StreamId) -> Poll<WindowSize, ConnectionError> {
|
||||||
|
if let Some(mut flow) = self.remote_flow_controller(id) {
|
||||||
|
if let Some(sz) = flow.apply_window_update() {
|
||||||
|
return Ok(Async::Ready(sz));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(error::User::InvalidStreamId.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.blocked_remote_window_update = Some(task::current());
|
||||||
|
return Ok(Async::NotReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_local_window(&mut self, id: StreamId, incr: WindowSize) -> Result<(), ConnectionError> {
|
||||||
|
if let Some(mut fc) = self.local_flow_controller(id) {
|
||||||
|
fc.expand_window(incr);
|
||||||
|
} else {
|
||||||
|
return Err(error::User::InvalidStreamId.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if id.is_zero() {
|
||||||
|
self.pending_local_connection_window_update = true;
|
||||||
|
} else {
|
||||||
|
self.pending_local_window_updates.push_back(id);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ControlPing> ControlPing for FlowControl<T> {
|
||||||
|
fn start_ping(&mut self, body: PingPayload) -> StartSend<PingPayload, ConnectionError> {
|
||||||
|
self.inner.start_ping(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_pong(&mut self) -> Option<PingPayload> {
|
||||||
|
self.inner.pop_pong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, U> FlowControl<T>
|
impl<T, U> FlowControl<T>
|
||||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
T: ControlStreams,
|
T: ControlStreams,
|
||||||
@@ -161,27 +136,33 @@ impl<T, U> FlowControl<T>
|
|||||||
/// Returns ready when there are no pending window updates to send.
|
/// Returns ready when there are no pending window updates to send.
|
||||||
fn poll_send_local_window_updates(&mut self) -> Poll<(), ConnectionError> {
|
fn poll_send_local_window_updates(&mut self) -> Poll<(), ConnectionError> {
|
||||||
if let Some(f) = self.sending_local_window_update.take() {
|
if let Some(f) = self.sending_local_window_update.take() {
|
||||||
if self.inner.start_send(f.into())?.is_not_ready() {
|
try_ready!(self.try_send(f));
|
||||||
self.sending_local_window_update = Some(f);
|
}
|
||||||
return Ok(Async::NotReady);
|
|
||||||
|
if self.pending_local_connection_window_update {
|
||||||
|
if let Some(incr) = self.connection_local_flow_controller.apply_window_update() {
|
||||||
|
try_ready!(self.try_send(frame::WindowUpdate::new(StreamId::zero(), incr)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(id) = self.pending_local_window_updates.pop_front() {
|
while let Some(id) = self.pending_local_window_updates.pop_front() {
|
||||||
let update = self.inner.streams_mut().get_mut(&id)
|
let update = self.local_flow_controller(id).and_then(|s| s.apply_window_update());
|
||||||
.and_then(|mut s| s.take_local_window_update())
|
if let Some(incr) = update {
|
||||||
.map(|incr| frame::WindowUpdate::new(id, incr));
|
try_ready!(self.try_send(frame::WindowUpdate::new(id, incr)));
|
||||||
|
|
||||||
if let Some(f) = update {
|
|
||||||
if self.inner.start_send(f.into())?.is_not_ready() {
|
|
||||||
self.sending_local_window_update = Some(f);
|
|
||||||
return Ok(Async::NotReady);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_send(&mut self, f: frame::WindowUpdate) -> Poll<(), ConnectionError> {
|
||||||
|
if self.inner.start_send(f.into())?.is_not_ready() {
|
||||||
|
self.sending_local_window_update = Some(f);
|
||||||
|
Ok(Async::NotReady)
|
||||||
|
} else {
|
||||||
|
Ok(Async::Ready(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies an update to an endpoint's initial window size.
|
/// Applies an update to an endpoint's initial window size.
|
||||||
@@ -219,7 +200,7 @@ impl<T> ApplySettings for FlowControl<T>
|
|||||||
streams.shrink_all_local_windows(decr);
|
streams.shrink_all_local_windows(decr);
|
||||||
} else {
|
} else {
|
||||||
let incr = new_window_size - old_window_size;
|
let incr = new_window_size - old_window_size;
|
||||||
streams.grow_all_local_windows(incr);
|
streams.expand_all_local_windows(incr);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.initial_local_window_size = new_window_size;
|
self.initial_local_window_size = new_window_size;
|
||||||
@@ -241,7 +222,7 @@ impl<T> ApplySettings for FlowControl<T>
|
|||||||
streams.shrink_all_remote_windows(decr);
|
streams.shrink_all_remote_windows(decr);
|
||||||
} else {
|
} else {
|
||||||
let incr = new_window_size - old_window_size;
|
let incr = new_window_size - old_window_size;
|
||||||
streams.grow_all_remote_windows(incr);
|
streams.expand_all_remote_windows(incr);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.initial_remote_window_size = new_window_size;
|
self.initial_remote_window_size = new_window_size;
|
||||||
@@ -263,11 +244,20 @@ impl<T> Stream for FlowControl<T>
|
|||||||
loop {
|
loop {
|
||||||
match try_ready!(self.inner.poll()) {
|
match try_ready!(self.inner.poll()) {
|
||||||
Some(WindowUpdate(v)) => {
|
Some(WindowUpdate(v)) => {
|
||||||
self.grow_remote_window(v.stream_id(), v.size_increment());
|
if let Some(fc) = self.remote_flow_controller(v.stream_id()) {
|
||||||
|
fc.expand_window(v.size_increment());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Data(v)) => {
|
Some(Data(v)) => {
|
||||||
self.claim_local_window(&v.stream_id(), v.len())?;
|
if self.connection_local_flow_controller.claim_window(v.len()).is_err() {
|
||||||
|
return Err(error::Reason::FlowControlError.into())
|
||||||
|
}
|
||||||
|
if let Some(fc) = self.local_flow_controller(v.stream_id()) {
|
||||||
|
if fc.claim_window(v.len()).is_err() {
|
||||||
|
return Err(error::Reason::FlowControlError.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
return Ok(Async::Ready(Some(Data(v))));
|
return Ok(Async::Ready(Some(Data(v))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +267,6 @@ impl<T> Stream for FlowControl<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T, U> Sink for FlowControl<T>
|
impl<T, U> Sink for FlowControl<T>
|
||||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
T: ReadySink,
|
T: ReadySink,
|
||||||
@@ -300,7 +289,14 @@ impl<T, U> Sink for FlowControl<T>
|
|||||||
return Ok(AsyncSink::NotReady(Data(v)));
|
return Ok(AsyncSink::NotReady(Data(v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.claim_remote_window(&v.stream_id(), v.len())?;
|
if self.connection_remote_flow_controller.claim_window(v.len()).is_err() {
|
||||||
|
return Err(error::User::FlowControlViolation.into());
|
||||||
|
}
|
||||||
|
if let Some(fc) = self.remote_flow_controller(v.stream_id()) {
|
||||||
|
if fc.claim_window(v.len()).is_err() {
|
||||||
|
return Err(error::User::FlowControlViolation.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let res = self.inner.start_send(Data(v))?;
|
let res = self.inner.start_send(Data(v))?;
|
||||||
assert!(res.is_ready());
|
assert!(res.is_ready());
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ impl FlowControlState {
|
|||||||
|
|
||||||
/// Claims the provided amount from the window, if there is enough space.
|
/// Claims the provided amount from the window, if there is enough space.
|
||||||
///
|
///
|
||||||
/// Fails when `take_window_update()` hasn't returned at least `sz` more bytes than
|
/// Fails when `apply_window_update()` hasn't returned at least `sz` more bytes than
|
||||||
/// have been previously claimed.
|
/// have been previously claimed.
|
||||||
pub fn claim_window(&mut self, sz: WindowSize) -> Result<(), WindowUnderflow> {
|
pub fn claim_window(&mut self, sz: WindowSize) -> Result<(), WindowUnderflow> {
|
||||||
if self.window_size < sz {
|
if self.window_size < sz {
|
||||||
@@ -65,7 +65,7 @@ impl FlowControlState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Increase the _unadvertised_ window capacity.
|
/// Increase the _unadvertised_ window capacity.
|
||||||
pub fn grow_window(&mut self, sz: WindowSize) {
|
pub fn expand_window(&mut self, sz: WindowSize) {
|
||||||
if sz <= self.underflow {
|
if sz <= self.underflow {
|
||||||
self.underflow -= sz;
|
self.underflow -= sz;
|
||||||
return;
|
return;
|
||||||
@@ -77,7 +77,7 @@ impl FlowControlState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Obtains and applies an unadvertised window update.
|
/// Obtains and applies an unadvertised window update.
|
||||||
pub fn take_window_update(&mut self) -> Option<WindowSize> {
|
pub fn apply_window_update(&mut self) -> Option<WindowSize> {
|
||||||
if self.next_window_update == 0 {
|
if self.next_window_update == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -93,29 +93,29 @@ impl FlowControlState {
|
|||||||
fn test_with_initial_size() {
|
fn test_with_initial_size() {
|
||||||
let mut fc = FlowControlState::with_initial_size(10);
|
let mut fc = FlowControlState::with_initial_size(10);
|
||||||
|
|
||||||
fc.grow_window(8);
|
fc.expand_window(8);
|
||||||
assert_eq!(fc.window_size, 10);
|
assert_eq!(fc.window_size, 10);
|
||||||
assert_eq!(fc.next_window_update, 8);
|
assert_eq!(fc.next_window_update, 8);
|
||||||
|
|
||||||
assert_eq!(fc.take_window_update(), Some(8));
|
assert_eq!(fc.apply_window_update(), Some(8));
|
||||||
assert_eq!(fc.window_size, 18);
|
assert_eq!(fc.window_size, 18);
|
||||||
assert_eq!(fc.next_window_update, 0);
|
assert_eq!(fc.next_window_update, 0);
|
||||||
|
|
||||||
assert!(fc.claim_window(13).is_ok());
|
assert!(fc.claim_window(13).is_ok());
|
||||||
assert_eq!(fc.window_size, 5);
|
assert_eq!(fc.window_size, 5);
|
||||||
assert_eq!(fc.next_window_update, 0);
|
assert_eq!(fc.next_window_update, 0);
|
||||||
assert!(fc.take_window_update().is_none());
|
assert!(fc.apply_window_update().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_with_next_update() {
|
fn test_with_next_update() {
|
||||||
let mut fc = FlowControlState::with_next_update(10);
|
let mut fc = FlowControlState::with_next_update(10);
|
||||||
|
|
||||||
fc.grow_window(8);
|
fc.expand_window(8);
|
||||||
assert_eq!(fc.window_size, 0);
|
assert_eq!(fc.window_size, 0);
|
||||||
assert_eq!(fc.next_window_update, 18);
|
assert_eq!(fc.next_window_update, 18);
|
||||||
|
|
||||||
assert_eq!(fc.take_window_update(), Some(18));
|
assert_eq!(fc.apply_window_update(), Some(18));
|
||||||
assert_eq!(fc.window_size, 18);
|
assert_eq!(fc.window_size, 18);
|
||||||
assert_eq!(fc.next_window_update, 0);
|
assert_eq!(fc.next_window_update, 0);
|
||||||
}
|
}
|
||||||
@@ -125,13 +125,13 @@ fn test_grow_accumulates() {
|
|||||||
let mut fc = FlowControlState::with_initial_size(5);
|
let mut fc = FlowControlState::with_initial_size(5);
|
||||||
|
|
||||||
// Updates accumulate, though the window is not made immediately available. Trying to
|
// Updates accumulate, though the window is not made immediately available. Trying to
|
||||||
// claim data not returned by take_window_update results in an underflow.
|
// claim data not returned by apply_window_update results in an underflow.
|
||||||
|
|
||||||
fc.grow_window(2);
|
fc.expand_window(2);
|
||||||
assert_eq!(fc.window_size, 5);
|
assert_eq!(fc.window_size, 5);
|
||||||
assert_eq!(fc.next_window_update, 2);
|
assert_eq!(fc.next_window_update, 2);
|
||||||
|
|
||||||
fc.grow_window(6);
|
fc.expand_window(6);
|
||||||
assert_eq!(fc.window_size, 5);
|
assert_eq!(fc.window_size, 5);
|
||||||
assert_eq!(fc.next_window_update, 8);
|
assert_eq!(fc.next_window_update, 8);
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ fn test_grow_accumulates() {
|
|||||||
assert_eq!(fc.window_size, 5);
|
assert_eq!(fc.window_size, 5);
|
||||||
assert_eq!(fc.next_window_update, 8);
|
assert_eq!(fc.next_window_update, 8);
|
||||||
|
|
||||||
assert_eq!(fc.take_window_update(), Some(8));
|
assert_eq!(fc.apply_window_update(), Some(8));
|
||||||
assert_eq!(fc.window_size, 13);
|
assert_eq!(fc.window_size, 13);
|
||||||
assert_eq!(fc.next_window_update, 0);
|
assert_eq!(fc.next_window_update, 0);
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ fn test_shrink() {
|
|||||||
assert_eq!(fc.window_size, 5);
|
assert_eq!(fc.window_size, 5);
|
||||||
assert_eq!(fc.next_window_update, 0);
|
assert_eq!(fc.next_window_update, 0);
|
||||||
|
|
||||||
fc.grow_window(3);
|
fc.expand_window(3);
|
||||||
assert_eq!(fc.window_size, 5);
|
assert_eq!(fc.window_size, 5);
|
||||||
assert_eq!(fc.next_window_update, 3);
|
assert_eq!(fc.next_window_update, 3);
|
||||||
assert_eq!(fc.underflow, 0);
|
assert_eq!(fc.underflow, 0);
|
||||||
@@ -169,7 +169,7 @@ fn test_shrink() {
|
|||||||
assert_eq!(fc.next_window_update, 0);
|
assert_eq!(fc.next_window_update, 0);
|
||||||
assert_eq!(fc.underflow, 5);
|
assert_eq!(fc.underflow, 5);
|
||||||
|
|
||||||
fc.grow_window(8);
|
fc.expand_window(8);
|
||||||
assert_eq!(fc.window_size, 0);
|
assert_eq!(fc.window_size, 0);
|
||||||
assert_eq!(fc.next_window_update, 3);
|
assert_eq!(fc.next_window_update, 3);
|
||||||
assert_eq!(fc.underflow, 0);
|
assert_eq!(fc.underflow, 0);
|
||||||
|
|||||||
@@ -30,20 +30,56 @@ pub use self::settings::Settings;
|
|||||||
pub use self::stream_tracker::StreamTracker;
|
pub use self::stream_tracker::StreamTracker;
|
||||||
use self::state::StreamState;
|
use self::state::StreamState;
|
||||||
|
|
||||||
/// Represents the internals of an HTTP2 connection.
|
/// Represents the internals of an HTTP/2 connection.
|
||||||
///
|
///
|
||||||
/// A transport consists of several layers (_transporters_) and is arranged from _top_
|
/// A transport consists of several layers (_transporters_) and is arranged from _top_
|
||||||
/// (near the application) to _bottom_ (near the network). Each transporter implements a
|
/// (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
|
/// Stream of frames received from the remote, and a ReadySink of frames sent to the
|
||||||
/// remote.
|
/// remote.
|
||||||
///
|
///
|
||||||
/// At the top of the transport, the Settings module is responsible for:
|
/// ## Transport Layers
|
||||||
/// - Transmitting local settings to the remote.
|
///
|
||||||
/// - Sending settings acknowledgements for all settings frames received from the remote.
|
/// ### `Settings`
|
||||||
/// - Exposing settings upward to the Connection.
|
///
|
||||||
|
/// - 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 the states of each stream.
|
||||||
|
/// - **TODO** Enforces maximum concurrency.
|
||||||
|
/// - 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.
|
||||||
///
|
///
|
||||||
/// All transporters below Settings must apply relevant settings before passing a frame on
|
|
||||||
/// to another level. For example, if the frame writer n
|
|
||||||
type Transport<T, P, B>=
|
type Transport<T, P, B>=
|
||||||
Settings<
|
Settings<
|
||||||
FlowControl<
|
FlowControl<
|
||||||
@@ -75,25 +111,33 @@ impl StreamMap {
|
|||||||
|
|
||||||
fn shrink_all_local_windows(&mut self, decr: u32) {
|
fn shrink_all_local_windows(&mut self, decr: u32) {
|
||||||
for (_, mut s) in &mut self.inner {
|
for (_, mut s) in &mut self.inner {
|
||||||
s.shrink_local_window(decr)
|
if let Some(fc) = s.local_flow_controller() {
|
||||||
|
fc.shrink_window(decr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grow_all_local_windows(&mut self, incr: u32) {
|
fn expand_all_local_windows(&mut self, incr: u32) {
|
||||||
for (_, mut s) in &mut self.inner {
|
for (_, mut s) in &mut self.inner {
|
||||||
s.grow_local_window(incr)
|
if let Some(fc) = s.local_flow_controller() {
|
||||||
|
fc.expand_window(incr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shrink_all_remote_windows(&mut self, decr: u32) {
|
fn shrink_all_remote_windows(&mut self, decr: u32) {
|
||||||
for (_, mut s) in &mut self.inner {
|
for (_, mut s) in &mut self.inner {
|
||||||
s.shrink_remote_window(decr)
|
if let Some(fc) = s.remote_flow_controller() {
|
||||||
|
fc.shrink_window(decr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grow_all_remote_windows(&mut self, incr: u32) {
|
fn expand_all_remote_windows(&mut self, incr: u32) {
|
||||||
for (_, mut s) in &mut self.inner {
|
for (_, mut s) in &mut self.inner {
|
||||||
s.grow_remote_window(incr)
|
if let Some(fc) = s.remote_flow_controller() {
|
||||||
|
fc.expand_window(incr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,6 +164,13 @@ pub trait ControlStreams {
|
|||||||
fn streams_mut(&mut self) -> &mut StreamMap;
|
fn streams_mut(&mut self) -> &mut StreamMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type PingPayload = [u8; 8];
|
||||||
|
|
||||||
|
pub trait ControlPing {
|
||||||
|
fn start_ping(&mut self, body: PingPayload) -> StartSend<PingPayload, ConnectionError>;
|
||||||
|
fn pop_pong(&mut self) -> Option<PingPayload>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Exposes flow control states to "upper" layers of the transport (i.e. above
|
/// Exposes flow control states to "upper" layers of the transport (i.e. above
|
||||||
/// FlowControl).
|
/// FlowControl).
|
||||||
pub trait ControlFlow {
|
pub trait ControlFlow {
|
||||||
@@ -131,7 +182,7 @@ pub trait ControlFlow {
|
|||||||
/// Attempts to increase the receive capacity of a stream.
|
/// Attempts to increase the receive capacity of a stream.
|
||||||
///
|
///
|
||||||
/// Errors if the given stream is not active.
|
/// Errors if the given stream is not active.
|
||||||
fn grow_local_window(&mut self, id: StreamId, incr: WindowSize) -> Result<(), ConnectionError>;
|
fn expand_local_window(&mut self, id: StreamId, incr: WindowSize) -> Result<(), ConnectionError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a full H2 transport from an I/O handle.
|
/// Create a full H2 transport from an I/O handle.
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
use ConnectionError;
|
use ConnectionError;
|
||||||
use frame::{Frame, Ping, SettingSet};
|
use frame::{Frame, Ping, SettingSet};
|
||||||
|
use proto::{ApplySettings, ControlPing, PingPayload, ReadySink};
|
||||||
|
|
||||||
use futures::*;
|
use futures::*;
|
||||||
use proto::{ApplySettings, ReadySink};
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
/// Acknowledges ping requests from the remote.
|
/// Acknowledges ping requests from the remote.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PingPong<T, U> {
|
pub struct PingPong<T, U> {
|
||||||
inner: T,
|
inner: T,
|
||||||
pong: Option<Frame<U>>,
|
sending_pong: Option<Frame<U>>,
|
||||||
|
received_pongs: VecDeque<PingPayload>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> PingPong<T, U>
|
impl<T, U> PingPong<T, U>
|
||||||
@@ -17,7 +20,8 @@ impl<T, U> PingPong<T, U>
|
|||||||
pub fn new(inner: T) -> Self {
|
pub fn new(inner: T) -> Self {
|
||||||
PingPong {
|
PingPong {
|
||||||
inner,
|
inner,
|
||||||
pong: None,
|
sending_pong: None,
|
||||||
|
received_pongs: VecDeque::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,18 +36,37 @@ impl<T: ApplySettings, U> ApplySettings for PingPong<T, U> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, U> ControlPing for PingPong<T, U>
|
||||||
|
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
|
T: ReadySink,
|
||||||
|
{
|
||||||
|
fn start_ping(&mut self, body: PingPayload) -> StartSend<PingPayload, ConnectionError> {
|
||||||
|
if self.inner.poll_ready()?.is_not_ready() {
|
||||||
|
return Ok(AsyncSink::NotReady(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.inner.start_send(Ping::ping(body).into())? {
|
||||||
|
AsyncSink::NotReady(_) => unreachable!(),
|
||||||
|
AsyncSink::Ready => Ok(AsyncSink::Ready),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_pong(&mut self) -> Option<PingPayload> {
|
||||||
|
self.received_pongs.pop_front()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, U> PingPong<T, U>
|
impl<T, U> PingPong<T, U>
|
||||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
{
|
{
|
||||||
fn try_send_pong(&mut self) -> Poll<(), ConnectionError> {
|
fn try_send_pong(&mut self) -> Poll<(), ConnectionError> {
|
||||||
if let Some(pong) = self.pong.take() {
|
if let Some(pong) = self.sending_pong.take() {
|
||||||
if let AsyncSink::NotReady(pong) = self.inner.start_send(pong)? {
|
if let AsyncSink::NotReady(pong) = self.inner.start_send(pong)? {
|
||||||
// If the pong can't be sent, save it.
|
// If the pong can't be sent, save it.
|
||||||
self.pong = Some(pong);
|
self.sending_pong = Some(pong);
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::NotReady);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +101,7 @@ impl<T, U> Stream for PingPong<T, U>
|
|||||||
// Save a pong to be sent when there is nothing more to be returned
|
// Save a pong to be sent when there is nothing more to be returned
|
||||||
// from the stream or when frames are sent to the sink.
|
// from the stream or when frames are sent to the sink.
|
||||||
let pong = Ping::pong(ping.into_payload());
|
let pong = Ping::pong(ping.into_payload());
|
||||||
self.pong = Some(pong.into());
|
self.sending_pong = Some(pong.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything other than ping gets passed through.
|
// Everything other than ping gets passed through.
|
||||||
|
|||||||
@@ -110,8 +110,18 @@ impl<T: ControlFlow> ControlFlow for Settings<T> {
|
|||||||
self.inner.poll_remote_window_update(id)
|
self.inner.poll_remote_window_update(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grow_local_window(&mut self, id: StreamId, incr: WindowSize) -> Result<(), ConnectionError> {
|
fn expand_local_window(&mut self, id: StreamId, incr: WindowSize) -> Result<(), ConnectionError> {
|
||||||
self.inner.grow_local_window(id, incr)
|
self.inner.expand_local_window(id, incr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ControlPing> ControlPing for Settings<T> {
|
||||||
|
fn start_ping(&mut self, body: PingPayload) -> StartSend<PingPayload, ConnectionError> {
|
||||||
|
self.inner.start_ping(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_pong(&mut self) -> Option<PingPayload> {
|
||||||
|
self.inner.pop_pong()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use Peer;
|
|||||||
use error::ConnectionError;
|
use error::ConnectionError;
|
||||||
use error::Reason::*;
|
use error::Reason::*;
|
||||||
use error::User::*;
|
use error::User::*;
|
||||||
use proto::{FlowControlState, WindowSize, WindowUnderflow};
|
use proto::{FlowControlState, WindowSize};
|
||||||
|
|
||||||
/// Represents the state of an H2 stream
|
/// Represents the state of an H2 stream
|
||||||
///
|
///
|
||||||
@@ -221,130 +221,25 @@ impl StreamState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the local flow controller so that the remote may send `incr` more bytes.
|
|
||||||
///
|
|
||||||
/// Returns the amount of capacity created, accounting for window size changes. The
|
|
||||||
/// caller should send the the returned window size increment to the remote.
|
|
||||||
///
|
|
||||||
/// If the remote is closed, None is returned.
|
|
||||||
pub fn grow_remote_window(&mut self, incr: WindowSize) {
|
|
||||||
use self::StreamState::*;
|
|
||||||
use self::PeerState::*;
|
|
||||||
|
|
||||||
if incr == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self {
|
|
||||||
&mut Open { remote: Data(ref mut fc), .. } |
|
|
||||||
&mut HalfClosedLocal(Data(ref mut fc)) => fc.grow_window(incr),
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn claim_remote_window(&mut self, decr: WindowSize) -> Result<(), WindowUnderflow> {
|
pub fn local_flow_controller(&mut self) -> Option<&mut FlowControlState> {
|
||||||
use self::StreamState::*;
|
use self::StreamState::*;
|
||||||
use self::PeerState::*;
|
use self::PeerState::*;
|
||||||
|
|
||||||
if decr == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
match self {
|
|
||||||
&mut Open { remote: Data(ref mut fc), .. } |
|
|
||||||
&mut HalfClosedLocal(Data(ref mut fc)) => fc.claim_window(decr),
|
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shrink_remote_window(&mut self, decr: WindowSize) {
|
|
||||||
use self::StreamState::*;
|
|
||||||
use self::PeerState::*;
|
|
||||||
|
|
||||||
if decr == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
&mut Open { local: Data(ref mut fc), .. } |
|
&mut Open { local: Data(ref mut fc), .. } |
|
||||||
&mut HalfClosedLocal(Data(ref mut fc)) => fc.shrink_window(decr),
|
&mut HalfClosedRemote(Data(ref mut fc)) => Some(fc),
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes newly-advertised capacity to inform the local endpoint it may send more
|
|
||||||
/// data.
|
|
||||||
pub fn take_remote_window_update(&mut self) -> Option<WindowSize> {
|
|
||||||
use self::StreamState::*;
|
|
||||||
use self::PeerState::*;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
&mut Open { remote: Data(ref mut fc), .. } |
|
|
||||||
&mut HalfClosedLocal(Data(ref mut fc)) => fc.take_window_update(),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the remote flow controller so that the remote may receive `incr`
|
pub fn remote_flow_controller(&mut self) -> Option<&mut FlowControlState> {
|
||||||
/// additional bytes.
|
|
||||||
///
|
|
||||||
/// Returns the amount of capacity created, accounting for window size changes. The
|
|
||||||
/// caller should send the the returned window size increment to the remote.
|
|
||||||
pub fn grow_local_window(&mut self, incr: WindowSize) {
|
|
||||||
use self::StreamState::*;
|
|
||||||
use self::PeerState::*;
|
|
||||||
|
|
||||||
if incr == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self {
|
|
||||||
&mut Open { local: Data(ref mut fc), .. } |
|
|
||||||
&mut HalfClosedRemote(Data(ref mut fc)) => fc.grow_window(incr),
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn claim_local_window(&mut self, decr: WindowSize) -> Result<(), WindowUnderflow> {
|
|
||||||
use self::StreamState::*;
|
|
||||||
use self::PeerState::*;
|
|
||||||
|
|
||||||
if decr == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
match self {
|
|
||||||
&mut Open { local: Data(ref mut fc), .. } |
|
|
||||||
&mut HalfClosedRemote(Data(ref mut fc)) => fc.claim_window(decr),
|
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shrink_local_window(&mut self, decr: WindowSize) {
|
|
||||||
use self::StreamState::*;
|
|
||||||
use self::PeerState::*;
|
|
||||||
|
|
||||||
if decr == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self {
|
|
||||||
&mut Open { local: Data(ref mut fc), .. } |
|
|
||||||
&mut HalfClosedRemote(Data(ref mut fc)) => fc.shrink_window(decr),
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes newly-advertised capacity to inform the local endpoint it may send more
|
|
||||||
/// data.
|
|
||||||
pub fn take_local_window_update(&mut self) -> Option<WindowSize> {
|
|
||||||
use self::StreamState::*;
|
use self::StreamState::*;
|
||||||
use self::PeerState::*;
|
use self::PeerState::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
&mut Open { local: Data(ref mut fc), .. } |
|
&mut Open { remote: Data(ref mut fc), .. } |
|
||||||
&mut HalfClosedRemote(Data(ref mut fc)) => fc.take_window_update(),
|
&mut HalfClosedLocal(Data(ref mut fc)) => Some(fc),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,18 @@ impl<T, P> ApplySettings for StreamTracker<T, P>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, P> ControlPing for StreamTracker<T, P>
|
||||||
|
where T: ControlPing
|
||||||
|
{
|
||||||
|
fn start_ping(&mut self, body: PingPayload) -> StartSend<PingPayload, ConnectionError> {
|
||||||
|
self.inner.start_ping(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_pong(&mut self) -> Option<PingPayload> {
|
||||||
|
self.inner.pop_pong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, P> Stream for StreamTracker<T, P>
|
impl<T, P> Stream for StreamTracker<T, P>
|
||||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
P: Peer,
|
P: Peer,
|
||||||
|
|||||||
Reference in New Issue
Block a user