closer to flow control

This commit is contained in:
Oliver Gould
2017-07-12 21:04:58 +00:00
parent b9f3556070
commit 41ffd1d44f
11 changed files with 337 additions and 179 deletions

View File

@@ -58,106 +58,178 @@ pub enum State {
Closed,
}
#[derive(Debug, Copy, Clone)]
pub enum PeerState {
Headers,
Data(FlowController),
}
impl State {
pub fn increment_local_window_size(&mut self, incr: u32) {
/// Updates the local flow controller with the given window size increment.
///
/// 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 send_window_update(&mut self, incr: u32) -> Option<u32> {
use self::State::*;
use self::PeerState::*;
*self = match *self {
Open { local: Data(mut local), remote } => {
local.increment(incr);
Open { local: Data(local), remote }
}
HalfClosedRemote(Data(mut local)) => {
local.increment(incr);
HalfClosedRemote(Data(local))
}
s => s,
if incr == 0 {
return None;
}
match self {
&mut Open { local: Data(ref mut fc), .. } |
&mut HalfClosedRemote(Data(ref mut fc)) => {
fc.add_to_window(incr);
fc.take_window_update()
}
_ => None,
}
}
/// Updates the remote flow controller with the given window size increment.
///
/// 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 recv_window_update(&mut self, incr: u32) {
use self::State::*;
use self::PeerState::*;
if incr == 0 {
return;
}
match self {
&mut Open { remote: Data(ref mut fc), .. } |
&mut HalfClosedLocal(Data(ref mut fc)) => fc.add_to_window(incr),
_ => {},
}
}
pub fn take_remote_window_update(&mut self) -> Option<u32> {
use self::State::*;
use self::PeerState::*;
match self {
&mut Open { remote: Data(ref mut fc), .. } |
&mut HalfClosedLocal(Data(ref mut fc)) => fc.take_window_update(),
_ => None,
}
}
/// Applies an update to the remote's initial window size.
///
/// Per RFC 7540 §6.9.2
///
/// > In addition to changing the flow-control window for streams that are not yet
/// > active, a SETTINGS frame can alter the initial flow-control window size for
/// > streams with active flow-control windows (that is, streams in the "open" or
/// > "half-closed (remote)" state). When the value of SETTINGS_INITIAL_WINDOW_SIZE
/// > changes, a receiver MUST adjust the size of all stream flow-control windows that
/// > it maintains by the difference between the new value and the old value.
/// >
/// > A change to `SETTINGS_INITIAL_WINDOW_SIZE` can cause the available space in a
/// > flow-control window to become negative. A sender MUST track the negative
/// > flow-control window and MUST NOT send new flow-controlled frames until it
/// > receives WINDOW_UPDATE frames that cause the flow-control window to become
/// > positive.
pub fn update_remote_initial_window_size(&mut self, old: u32, new: u32) {
use self::State::*;
use self::PeerState::*;
match self {
&mut Open { remote: Data(ref mut fc), .. } |
&mut HalfClosedLocal(Data(ref mut fc)) => {
if new < old {
fc.shrink_window(old - new);
} else {
fc.add_to_window(new - old);
}
}
_ => {}
}
}
/// TODO Connection doesn't have an API for local updates yet.
pub fn update_local_initial_window_size(&mut self, _old: u32, _new: u32) {
//use self::State::*;
//use self::PeerState::*;
unimplemented!()
}
/// Transition the state to represent headers being received.
///
/// Returns true if this state transition results in iniitializing the
/// stream id. `Err` is returned if this is an invalid state transition.
pub fn recv_headers<P: Peer>(&mut self, eos: bool, remote_window_size: u32) -> Result<bool, ConnectionError> {
pub fn recv_headers<P: Peer>(&mut self,
eos: bool,
remote_window_size: u32)
-> Result<bool, ConnectionError>
{
use self::State::*;
use self::PeerState::*;
match *self {
Idle => {
*self = if eos {
HalfClosedRemote(Headers)
let local = Headers;
if eos {
*self = HalfClosedRemote(local);
} else {
Open {
local: Headers,
remote: Data(FlowController::new(remote_window_size)),
}
};
*self = Open { local, remote: Data(FlowController::new(remote_window_size)) };
}
Ok(true)
}
Open { local, remote } => {
try!(remote.check_is_headers(ProtocolError.into()));
*self = if eos {
HalfClosedRemote(local)
} else {
let remote = Data(FlowController::new(remote_window_size));
Open { local, remote }
};
if !eos {
// Received non-trailers HEADERS on open remote.
return Err(ProtocolError.into());
}
*self = HalfClosedRemote(local);
Ok(false)
}
HalfClosedLocal(remote) => {
try!(remote.check_is_headers(ProtocolError.into()));
*self = if eos {
Closed
HalfClosedLocal(headers) => {
try!(headers.check_is_headers(ProtocolError.into()));
if eos {
*self = Closed;
} else {
HalfClosedLocal(Data(FlowController::new(remote_window_size)))
*self = HalfClosedLocal(Data(FlowController::new(remote_window_size)));
};
Ok(false)
}
Closed | HalfClosedRemote(..) => {
Err(ProtocolError.into())
}
_ => unimplemented!(),
}
}
pub fn recv_data(&mut self, eos: bool) -> Result<(), ConnectionError> {
pub fn recv_data(&mut self, eos: bool, len: usize) -> Result<(), ConnectionError> {
use self::State::*;
match *self {
Open { local, remote } => {
try!(remote.check_is_data(ProtocolError.into()));
try!(remote.check_window_size(len, FlowControlError.into()));
if eos {
*self = HalfClosedRemote(local);
}
Ok(())
}
HalfClosedLocal(remote) => {
try!(remote.check_is_data(ProtocolError.into()));
try!(remote.check_window_size(len, FlowControlError.into()));
if eos {
*self = Closed;
}
Ok(())
}
Closed | HalfClosedRemote(..) => {
Err(ProtocolError.into())
}
_ => unimplemented!(),
}
}
@@ -183,6 +255,7 @@ impl State {
Ok(true)
}
Open { local, remote } => {
try!(local.check_is_headers(UnexpectedFrameType.into()));
@@ -195,6 +268,7 @@ impl State {
Ok(false)
}
HalfClosedRemote(local) => {
try!(local.check_is_headers(UnexpectedFrameType.into()));
@@ -206,67 +280,84 @@ impl State {
Ok(false)
}
Closed | HalfClosedLocal(..) => {
Err(UnexpectedFrameType.into())
}
_ => unimplemented!(),
}
}
pub fn send_data(&mut self, eos: bool) -> Result<(), ConnectionError> {
pub fn send_data(&mut self, eos: bool, len: usize) -> Result<(), ConnectionError> {
use self::State::*;
match *self {
Open { local, remote } => {
try!(local.check_is_data(UnexpectedFrameType.into()));
try!(local.check_window_size(len, FlowControlViolation.into()));
if eos {
*self = HalfClosedLocal(remote);
}
Ok(())
}
HalfClosedRemote(local) => {
try!(local.check_is_data(UnexpectedFrameType.into()));
try!(local.check_window_size(len, FlowControlViolation.into()));
if eos {
*self = Closed;
}
Ok(())
}
Closed | HalfClosedLocal(..) => {
Err(UnexpectedFrameType.into())
}
_ => unimplemented!(),
}
}
}
impl PeerState {
#[inline]
fn check_is_headers(&self, err: ConnectionError) -> Result<(), ConnectionError> {
use self::PeerState::*;
match *self {
Headers => Ok(()),
_ => Err(err),
}
}
#[inline]
fn check_is_data(&self, err: ConnectionError) -> Result<(), ConnectionError> {
use self::PeerState::*;
match *self {
Data { .. } => Ok(()),
_ => Err(err),
}
}
}
impl Default for State {
fn default() -> State {
State::Idle
}
}
#[derive(Debug, Copy, Clone)]
pub enum PeerState {
Headers,
/// Contains a FlowController representing the _receiver_ of this this data stream.
Data(FlowController),
}
impl PeerState {
#[inline]
fn check_is_headers(&self, err: ConnectionError) -> Result<(), ConnectionError> {
use self::PeerState::*;
match self {
&Headers => Ok(()),
_ => Err(err),
}
}
#[inline]
fn check_is_data(&self, err: ConnectionError) -> Result<(), ConnectionError> {
use self::PeerState::*;
match self {
&Data(_) => Ok(()),
_ => Err(err),
}
}
#[inline]
fn check_window_size(&self, len: usize, err: ConnectionError) -> Result<(), ConnectionError> {
use self::PeerState::*;
match self {
&Data(ref fc) if len <= fc.window_size() as usize=> Ok(()),
_ => Err(err),
}
}
}