wip
This commit is contained in:
@@ -56,8 +56,12 @@ impl Peer for Client {
|
|||||||
id.is_client_initiated()
|
id.is_client_initiated()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_valid_remote_stream_id(_id: StreamId) -> bool {
|
fn is_valid_remote_stream_id(id: StreamId) -> bool {
|
||||||
false
|
id.is_server_initiated()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_create_local_stream() -> bool {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_send_message(
|
fn convert_send_message(
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ pub enum User {
|
|||||||
Corrupt,
|
Corrupt,
|
||||||
|
|
||||||
/// The stream state has been reset.
|
/// The stream state has been reset.
|
||||||
StreamReset,
|
StreamReset(Reason),
|
||||||
|
|
||||||
/// The application attempted to initiate too many streams to remote.
|
/// The application attempted to initiate too many streams to remote.
|
||||||
MaxConcurrencyExceeded,
|
MaxConcurrencyExceeded,
|
||||||
@@ -125,7 +125,7 @@ macro_rules! user_desc {
|
|||||||
InactiveStreamId => concat!($prefix, "inactive stream ID"),
|
InactiveStreamId => concat!($prefix, "inactive stream ID"),
|
||||||
UnexpectedFrameType => concat!($prefix, "unexpected frame type"),
|
UnexpectedFrameType => concat!($prefix, "unexpected frame type"),
|
||||||
FlowControlViolation => concat!($prefix, "flow control violation"),
|
FlowControlViolation => concat!($prefix, "flow control violation"),
|
||||||
StreamReset => concat!($prefix, "frame sent on reset stream"),
|
StreamReset(_) => concat!($prefix, "frame sent on reset stream"),
|
||||||
Corrupt => concat!($prefix, "connection state corrupt"),
|
Corrupt => concat!($prefix, "connection state corrupt"),
|
||||||
MaxConcurrencyExceeded => concat!($prefix, "stream would exceed remote max concurrency"),
|
MaxConcurrencyExceeded => concat!($prefix, "stream would exceed remote max concurrency"),
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/lib.rs
11
src/lib.rs
@@ -85,6 +85,17 @@ pub trait Peer {
|
|||||||
/// remote node.
|
/// remote node.
|
||||||
fn is_valid_remote_stream_id(id: StreamId) -> bool;
|
fn is_valid_remote_stream_id(id: StreamId) -> bool;
|
||||||
|
|
||||||
|
|
||||||
|
fn can_create_local_stream() -> bool;
|
||||||
|
fn can_create_remote_stream() -> bool {
|
||||||
|
!Self::can_create_local_stream()
|
||||||
|
}
|
||||||
|
|
||||||
|
//fn can_reserve_local_stream() -> bool;
|
||||||
|
// fn can_reserve_remote_stream() -> bool {
|
||||||
|
// !self.can_reserve_local_stream
|
||||||
|
// }
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn convert_send_message(
|
fn convert_send_message(
|
||||||
id: StreamId,
|
id: StreamId,
|
||||||
|
|||||||
@@ -216,10 +216,6 @@ impl<T, P, B> Sink for Connection<T, P, B>
|
|||||||
|
|
||||||
match item {
|
match item {
|
||||||
Frame::Headers { id, headers, end_of_stream } => {
|
Frame::Headers { id, headers, end_of_stream } => {
|
||||||
if self.inner.stream_is_reset(id).is_some() {
|
|
||||||
return Err(error::User::StreamReset.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a one-way conversion. By checking `poll_ready` first (above),
|
// This is a one-way conversion. By checking `poll_ready` first (above),
|
||||||
// it's already been determined that the inner `Sink` can accept the item.
|
// it's already been determined that the inner `Sink` can accept the item.
|
||||||
// If the item is rejected, then there is a bug.
|
// If the item is rejected, then there is a bug.
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ impl<T: ControlStreams> FlowControl<T> {
|
|||||||
if id.is_zero() {
|
if id.is_zero() {
|
||||||
Some(&mut self.local_connection)
|
Some(&mut self.local_connection)
|
||||||
} else {
|
} else {
|
||||||
self.inner.streams_mut().get_mut(id).and_then(|s| s.local_flow_controller())
|
self.inner.local_flow_controller(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,23 +76,31 @@ impl<T: ControlStreams> FlowControl<T> {
|
|||||||
if id.is_zero() {
|
if id.is_zero() {
|
||||||
Some(&mut self.remote_connection)
|
Some(&mut self.remote_connection)
|
||||||
} else {
|
} else {
|
||||||
self.inner.streams_mut().get_mut(id).and_then(|s| s.remote_flow_controller())
|
self.inner.remote_flow_controller(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Proxies access to streams.
|
/// Proxies access to streams.
|
||||||
impl<T: ControlStreams> ControlStreams for FlowControl<T> {
|
impl<T: ControlStreams> ControlStreams for FlowControl<T> {
|
||||||
fn streams(&self) -> &StreamMap {
|
fn local_streams(&self) -> &StreamMap {
|
||||||
self.inner.streams()
|
self.inner.local_streams()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn streams_mut(&mut self) -> &mut StreamMap {
|
fn local_streams_mut(&mut self) -> &mut StreamMap {
|
||||||
self.inner.streams_mut()
|
self.inner.local_streams_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream_is_reset(&self, id: StreamId) -> Option<Reason> {
|
fn remote_streams(&self) -> &StreamMap {
|
||||||
self.inner.stream_is_reset(id)
|
self.inner.local_streams()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remote_streams_mut(&mut self) -> &mut StreamMap {
|
||||||
|
self.inner.local_streams_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid_local_id(id: StreamId) -> bool {
|
||||||
|
T::is_valid_local_id(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,14 +109,14 @@ impl<T: ControlStreams> ControlFlow for FlowControl<T> {
|
|||||||
fn poll_window_update(&mut self) -> Poll<WindowUpdate, ConnectionError> {
|
fn poll_window_update(&mut self) -> Poll<WindowUpdate, ConnectionError> {
|
||||||
// This biases connection window updates, which probably makese sense.
|
// This biases connection window updates, which probably makese sense.
|
||||||
if let Some(incr) = self.remote_connection.apply_window_update() {
|
if let Some(incr) = self.remote_connection.apply_window_update() {
|
||||||
return Ok(Async::Ready(WindowUpdate(StreamId::zero(), incr)));
|
return Ok(Async::Ready(WindowUpdate::new(StreamId::zero(), incr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this should probably account for stream priority?
|
// TODO this should probably account for stream priority?
|
||||||
while let Some(id) = self.remote_pending_streams.pop_front() {
|
while let Some(id) = self.remote_pending_streams.pop_front() {
|
||||||
if let Some(mut flow) = self.remote_flow_controller(id) {
|
if let Some(mut flow) = self.remote_flow_controller(id) {
|
||||||
if let Some(incr) = flow.apply_window_update() {
|
if let Some(incr) = flow.apply_window_update() {
|
||||||
return Ok(Async::Ready(WindowUpdate(id, incr)));
|
return Ok(Async::Ready(WindowUpdate::new(id, incr)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,8 +139,8 @@ impl<T: ControlStreams> ControlFlow for FlowControl<T> {
|
|||||||
self.local_pending_streams.push_back(id);
|
self.local_pending_streams.push_back(id);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if self.stream_is_reset(id).is_some() {
|
} else if let Some(rst) = self.get_reset(id) {
|
||||||
Err(error::User::StreamReset.into())
|
Err(error::User::StreamReset(rst).into())
|
||||||
} else {
|
} else {
|
||||||
Err(error::User::InvalidStreamId.into())
|
Err(error::User::InvalidStreamId.into())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ mod ping_pong;
|
|||||||
mod ready;
|
mod ready;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod state;
|
mod state;
|
||||||
mod stream_tracker;
|
mod stream_recv;
|
||||||
|
mod stream_send;
|
||||||
|
|
||||||
pub use self::connection::Connection;
|
pub use self::connection::Connection;
|
||||||
pub use self::flow_control::FlowControl;
|
pub use self::flow_control::FlowControl;
|
||||||
@@ -26,7 +27,8 @@ pub use self::framed_write::FramedWrite;
|
|||||||
pub use self::ping_pong::PingPong;
|
pub use self::ping_pong::PingPong;
|
||||||
pub use self::ready::ReadySink;
|
pub use self::ready::ReadySink;
|
||||||
pub use self::settings::Settings;
|
pub use self::settings::Settings;
|
||||||
pub use self::stream_tracker::StreamTracker;
|
pub use self::stream_recv::StreamRecv;
|
||||||
|
pub use self::stream_send::StreamSend;
|
||||||
|
|
||||||
use self::state::{StreamMap, StreamState};
|
use self::state::{StreamMap, StreamState};
|
||||||
|
|
||||||
@@ -82,14 +84,19 @@ use self::state::{StreamMap, StreamState};
|
|||||||
///
|
///
|
||||||
type Transport<T, P, B>=
|
type Transport<T, P, B>=
|
||||||
Settings<
|
Settings<
|
||||||
FlowControl<
|
Streams<
|
||||||
StreamTracker<
|
PingPong<
|
||||||
PingPong<
|
Codec<T, B>,
|
||||||
Framer<T, B>,
|
B>,
|
||||||
B>,
|
P>>;
|
||||||
P>>>;
|
|
||||||
|
|
||||||
type Framer<T, B> =
|
type Streams<T, P> =
|
||||||
|
StreamSend<
|
||||||
|
FlowControl<
|
||||||
|
StreamRecv<T, P>>,
|
||||||
|
P>;
|
||||||
|
|
||||||
|
type Codec<T, B> =
|
||||||
FramedRead<
|
FramedRead<
|
||||||
FramedWrite<T, B>>;
|
FramedWrite<T, B>>;
|
||||||
|
|
||||||
@@ -111,14 +118,22 @@ pub trait ApplySettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct WindowUpdate(pub StreamId, pub WindowSize);
|
pub struct WindowUpdate {
|
||||||
|
stream_id: StreamId,
|
||||||
|
increment: WindowSize
|
||||||
|
}
|
||||||
|
|
||||||
impl WindowUpdate {
|
impl WindowUpdate {
|
||||||
|
pub fn new(stream_id: StreamId, increment: WindowSize) -> WindowUpdate {
|
||||||
|
WindowUpdate { stream_id, increment }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stream_id(&self) -> StreamId {
|
pub fn stream_id(&self) -> StreamId {
|
||||||
self.0
|
self.stream_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn increment(&self) -> WindowSize {
|
pub fn increment(&self) -> WindowSize {
|
||||||
self.1
|
self.increment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,14 +154,35 @@ pub trait ControlFlow {
|
|||||||
/// Exposes stream states to "upper" layers of the transport (i.e. from StreamTracker up
|
/// Exposes stream states to "upper" layers of the transport (i.e. from StreamTracker up
|
||||||
/// to Connection).
|
/// to Connection).
|
||||||
pub trait ControlStreams {
|
pub trait ControlStreams {
|
||||||
/// Accesses the map of all active streams.
|
fn is_valid_local_id(id: StreamId) -> bool;
|
||||||
fn streams(&self)-> &StreamMap;
|
fn is_valid_remote_id(id: StreamId) -> bool {
|
||||||
|
!id.is_zero() && !Self::is_valid_local_id(id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Mutably accesses the map of all active streams.
|
fn get_active(&self, id: StreamId) -> Option<&StreamState> {
|
||||||
fn streams_mut(&mut self) -> &mut StreamMap;
|
self.streams(id).get_active(id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks whether a stream has been reset.
|
fn get_active_mut(&mut self, id: StreamId) -> Option<&mut StreamState> {
|
||||||
fn stream_is_reset(&self, id: StreamId) -> Option<Reason>;
|
self.streams_mut(id).get_active_mut(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_reset(&self, id: StreamId) -> Option<Reason> {
|
||||||
|
self.streams(id).get_reset(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self, id: StreamId, cause: Reason) {
|
||||||
|
self.streams_mut(id).reset(id, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn local_flow_controller(&mut self, id: StreamId) -> Option<&mut FlowControlState> {
|
||||||
|
self.streams_mut(id).local_flow_controller(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remote_flow_controller(&mut self, id: StreamId) -> Option<&mut FlowControlState> {
|
||||||
|
self.streams_mut(id).remote_flow_controller(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PingPayload = [u8; 8];
|
pub type PingPayload = [u8; 8];
|
||||||
@@ -206,26 +242,24 @@ pub fn from_server_handshaker<T, P, B>(settings: Settings<FramedWrite<T, B::Buf>
|
|||||||
// Replace Settings' writer with a full transport.
|
// Replace Settings' writer with a full transport.
|
||||||
let transport = settings.swap_inner(|io| {
|
let transport = settings.swap_inner(|io| {
|
||||||
// Delimit the frames.
|
// Delimit the frames.
|
||||||
let framer = length_delimited::Builder::new()
|
let framed = length_delimited::Builder::new()
|
||||||
.big_endian()
|
.big_endian()
|
||||||
.length_field_length(3)
|
.length_field_length(3)
|
||||||
.length_adjustment(9)
|
.length_adjustment(9)
|
||||||
.num_skip(0) // Don't skip the header
|
.num_skip(0) // Don't skip the header
|
||||||
.new_read(io);
|
.new_read(io);
|
||||||
|
|
||||||
FlowControl::new(
|
StreamSend::new(
|
||||||
initial_local_window_size,
|
|
||||||
initial_remote_window_size,
|
initial_remote_window_size,
|
||||||
StreamTracker::new(
|
remote_max_concurrency,
|
||||||
|
FlowControl::new(
|
||||||
initial_local_window_size,
|
initial_local_window_size,
|
||||||
initial_remote_window_size,
|
initial_remote_window_size,
|
||||||
local_max_concurrency,
|
StreamRecv::new(
|
||||||
remote_max_concurrency,
|
initial_local_window_size,
|
||||||
PingPong::new(
|
local_max_concurrency,
|
||||||
FramedRead::new(framer)
|
PingPong::new(
|
||||||
)
|
FramedRead::new(framed)))))
|
||||||
)
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
connection::new(transport)
|
connection::new(transport)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pub struct Settings<T> {
|
|||||||
remaining_acks: usize,
|
remaining_acks: usize,
|
||||||
|
|
||||||
// True when the local settings must be flushed to the remote
|
// True when the local settings must be flushed to the remote
|
||||||
is_local_dirty: bool,
|
is_valid_local_id_dirty: bool,
|
||||||
|
|
||||||
// True when we have received a settings frame from the remote.
|
// True when we have received a settings frame from the remote.
|
||||||
received_remote: bool,
|
received_remote: bool,
|
||||||
@@ -39,7 +39,7 @@ impl<T, U> Settings<T>
|
|||||||
local: local,
|
local: local,
|
||||||
remote: SettingSet::default(),
|
remote: SettingSet::default(),
|
||||||
remaining_acks: 0,
|
remaining_acks: 0,
|
||||||
is_local_dirty: true,
|
is_valid_local_id_dirty: true,
|
||||||
received_remote: false,
|
received_remote: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,18 +61,18 @@ impl<T, U> Settings<T>
|
|||||||
local: self.local,
|
local: self.local,
|
||||||
remote: self.remote,
|
remote: self.remote,
|
||||||
remaining_acks: self.remaining_acks,
|
remaining_acks: self.remaining_acks,
|
||||||
is_local_dirty: self.is_local_dirty,
|
is_valid_local_id_dirty: self.is_valid_local_id_dirty,
|
||||||
received_remote: self.received_remote,
|
received_remote: self.received_remote,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_send_pending(&mut self) -> Poll<(), ConnectionError> {
|
fn try_send_pending(&mut self) -> Poll<(), ConnectionError> {
|
||||||
trace!("try_send_pending; dirty={} acks={}", self.is_local_dirty, self.remaining_acks);
|
trace!("try_send_pending; dirty={} acks={}", self.is_valid_local_id_dirty, self.remaining_acks);
|
||||||
if self.is_local_dirty {
|
if self.is_valid_local_id_dirty {
|
||||||
let frame = frame::Settings::new(self.local.clone());
|
let frame = frame::Settings::new(self.local.clone());
|
||||||
try_ready!(self.try_send(frame));
|
try_ready!(self.try_send(frame));
|
||||||
|
|
||||||
self.is_local_dirty = false;
|
self.is_valid_local_id_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while self.remaining_acks > 0 {
|
while self.remaining_acks > 0 {
|
||||||
@@ -96,16 +96,24 @@ impl<T, U> Settings<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ControlStreams> ControlStreams for Settings<T> {
|
impl<T: ControlStreams> ControlStreams for Settings<T> {
|
||||||
fn streams(&self) -> &StreamMap {
|
fn local_streams(&self) -> &StreamMap {
|
||||||
self.inner.streams()
|
self.inner.local_streams()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn streams_mut(&mut self) -> &mut StreamMap {
|
fn local_streams_mut(&mut self) -> &mut StreamMap {
|
||||||
self.inner.streams_mut()
|
self.inner.local_streams_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream_is_reset(&self, id: StreamId) -> Option<Reason> {
|
fn remote_streams(&self) -> &StreamMap {
|
||||||
self.inner.stream_is_reset(id)
|
self.inner.local_streams()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remote_streams_mut(&mut self) -> &mut StreamMap {
|
||||||
|
self.inner.local_streams_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid_local_id(id: StreamId) -> bool {
|
||||||
|
T::is_valid_local_id(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +140,7 @@ impl<T: ControlPing> ControlPing for Settings<T> {
|
|||||||
impl<T> ControlSettings for Settings<T>{
|
impl<T> ControlSettings for Settings<T>{
|
||||||
fn update_local_settings(&mut self, local: frame::SettingSet) -> Result<(), ConnectionError> {
|
fn update_local_settings(&mut self, local: frame::SettingSet) -> Result<(), ConnectionError> {
|
||||||
self.local = local;
|
self.local = local;
|
||||||
self.is_local_dirty = true;
|
self.is_valid_local_id_dirty = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use {Peer, StreamId};
|
use {Peer, StreamId};
|
||||||
use error::ConnectionError;
|
use error::{ConnectionError, Reason};
|
||||||
use error::Reason::*;
|
use error::Reason::*;
|
||||||
use error::User::*;
|
use error::User::*;
|
||||||
use proto::{FlowControlState, WindowSize};
|
use proto::{FlowControlState, WindowSize};
|
||||||
@@ -7,6 +7,7 @@ use proto::{FlowControlState, WindowSize};
|
|||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use ordermap::{Entry, OrderMap};
|
use ordermap::{Entry, OrderMap};
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
/// Represents the state of an H2 stream
|
/// Represents the state of an H2 stream
|
||||||
///
|
///
|
||||||
@@ -76,10 +77,9 @@ impl StreamState {
|
|||||||
///
|
///
|
||||||
/// Returns true if this state transition results in iniitializing the
|
/// Returns true if this state transition results in iniitializing the
|
||||||
/// stream id. `Err` is returned if this is an invalid state transition.
|
/// stream id. `Err` is returned if this is an invalid state transition.
|
||||||
pub fn recv_headers<P: Peer>(&mut self,
|
pub fn recv_headers<P>(&mut self, eos: bool, initial_window_size: WindowSize)
|
||||||
eos: bool,
|
|
||||||
initial_recv_window_size: WindowSize)
|
|
||||||
-> Result<bool, ConnectionError>
|
-> Result<bool, ConnectionError>
|
||||||
|
where P: Peer
|
||||||
{
|
{
|
||||||
use self::StreamState::*;
|
use self::StreamState::*;
|
||||||
use self::PeerState::*;
|
use self::PeerState::*;
|
||||||
@@ -90,7 +90,7 @@ impl StreamState {
|
|||||||
if eos {
|
if eos {
|
||||||
*self = HalfClosedRemote(local);
|
*self = HalfClosedRemote(local);
|
||||||
} else {
|
} else {
|
||||||
let remote = Data(FlowControlState::with_initial_size(initial_recv_window_size));
|
let remote = Data(FlowControlState::with_initial_size(initial_window_size));
|
||||||
*self = Open { local, remote };
|
*self = Open { local, remote };
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
@@ -111,7 +111,8 @@ impl StreamState {
|
|||||||
if eos {
|
if eos {
|
||||||
*self = Closed;
|
*self = Closed;
|
||||||
} else {
|
} else {
|
||||||
*self = HalfClosedLocal(Data(FlowControlState::with_initial_size(initial_recv_window_size)));
|
let remote = FlowControlState::with_initial_size(initial_window_size);
|
||||||
|
*self = HalfClosedLocal(Data(remote));
|
||||||
};
|
};
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
@@ -291,46 +292,76 @@ impl PeerState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO track reserved streams
|
||||||
|
// TODO constrain the size of `reset`
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct StreamMap {
|
pub struct StreamMap<P> {
|
||||||
inner: OrderMap<StreamId, StreamState, BuildHasherDefault<FnvHasher>>
|
/// Holds active streams initiated by the local endpoint.
|
||||||
|
local_active: OrderMap<StreamId, StreamState, BuildHasherDefault<FnvHasher>>,
|
||||||
|
|
||||||
|
/// Holds active streams initiated by the remote endpoint.
|
||||||
|
remote_active: OrderMap<StreamId, StreamState, BuildHasherDefault<FnvHasher>>,
|
||||||
|
|
||||||
|
/// Holds active streams initiated by the remote.
|
||||||
|
reset: OrderMap<StreamId, Reason, BuildHasherDefault<FnvHasher>>,
|
||||||
|
|
||||||
|
_phantom: PhantomData<P>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamMap {
|
impl<P: Peer> StreamMap<P> {
|
||||||
pub fn get_mut(&mut self, id: StreamId) -> Option<&mut StreamState> {
|
pub fn active(&mut self, id: StreamId) -> Option<&StreamState> {
|
||||||
self.inner.get_mut(&id)
|
assert!(!id.is_zero());
|
||||||
|
if P::is_valid_local_stream_id(id) {
|
||||||
|
self.local_active.get(id)
|
||||||
|
} else {
|
||||||
|
self.remote_active.get(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_mut(&mut self, id: StreamId) -> Option<&mut StreamState> {
|
||||||
|
assert!(!id.is_zero());
|
||||||
|
if P::is_valid_local_stream_id(id) {
|
||||||
|
self.local_active.get_mut(id)
|
||||||
|
} else {
|
||||||
|
self.remote_active.get_mut(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_active(&self, id: StreamId) -> Option<&StreamState> {
|
||||||
|
self.local_active.get(&id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_active_mut(&mut self, id: StreamId) -> Option<&mut StreamState> {
|
||||||
|
self.local_active.get_mut(&id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_flow_controller(&mut self, id: StreamId) -> Option<&mut FlowControlState> {
|
pub fn local_flow_controller(&mut self, id: StreamId) -> Option<&mut FlowControlState> {
|
||||||
self.inner.get_mut(&id).and_then(|s| s.local_flow_controller())
|
self.get_active_mut(id).and_then(|s| s.local_flow_controller())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remote_flow_controller(&mut self, id: StreamId) -> Option<&mut FlowControlState> {
|
pub fn remote_flow_controller(&mut self, id: StreamId) -> Option<&mut FlowControlState> {
|
||||||
self.inner.get_mut(&id).and_then(|s| s.remote_flow_controller())
|
self.get_active_mut(id).and_then(|s| s.remote_flow_controller())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_stream(&mut self, id: StreamId) -> bool {
|
pub fn localis_active(&mut self, id: StreamId) -> bool {
|
||||||
self.inner.contains_key(&id)
|
self.active.contains_key(&id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn active_count(&self) -> usize {
|
||||||
self.inner.is_empty()
|
self.active.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn reset(&mut self, id: StreamId, cause: Reason) {
|
||||||
self.inner.len()
|
self.reset.insert(id, cause);
|
||||||
|
self.active.remove(&id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entry(&mut self, id: StreamId) -> Entry<StreamId, StreamState, BuildHasherDefault<FnvHasher>> {
|
pub fn get_reset(&mut self, id: StreamId) -> Option<Reason> {
|
||||||
self.inner.entry(id)
|
self.reset.get(&id).map(|r| *r)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(&mut self, id: StreamId) -> Option<StreamState> {
|
|
||||||
self.inner.remove(&id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shrink_all_local_windows(&mut self, decr: u32) {
|
pub fn shrink_all_local_windows(&mut self, decr: u32) {
|
||||||
for (_, mut s) in &mut self.inner {
|
for (_, mut s) in &mut self.active {
|
||||||
if let Some(fc) = s.local_flow_controller() {
|
if let Some(fc) = s.local_flow_controller() {
|
||||||
fc.shrink_window(decr);
|
fc.shrink_window(decr);
|
||||||
}
|
}
|
||||||
@@ -338,7 +369,7 @@ impl StreamMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_all_local_windows(&mut self, incr: u32) {
|
pub fn expand_all_local_windows(&mut self, incr: u32) {
|
||||||
for (_, mut s) in &mut self.inner {
|
for (_, mut s) in &mut self.active {
|
||||||
if let Some(fc) = s.local_flow_controller() {
|
if let Some(fc) = s.local_flow_controller() {
|
||||||
fc.expand_window(incr);
|
fc.expand_window(incr);
|
||||||
}
|
}
|
||||||
@@ -346,7 +377,7 @@ impl StreamMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn shrink_all_remote_windows(&mut self, decr: u32) {
|
pub fn shrink_all_remote_windows(&mut self, decr: u32) {
|
||||||
for (_, mut s) in &mut self.inner {
|
for (_, mut s) in &mut self.active {
|
||||||
if let Some(fc) = s.remote_flow_controller() {
|
if let Some(fc) = s.remote_flow_controller() {
|
||||||
fc.shrink_window(decr);
|
fc.shrink_window(decr);
|
||||||
}
|
}
|
||||||
@@ -354,7 +385,7 @@ impl StreamMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_all_remote_windows(&mut self, incr: u32) {
|
pub fn expand_all_remote_windows(&mut self, incr: u32) {
|
||||||
for (_, mut s) in &mut self.inner {
|
for (_, mut s) in &mut self.active {
|
||||||
if let Some(fc) = s.remote_flow_controller() {
|
if let Some(fc) = s.remote_flow_controller() {
|
||||||
fc.expand_window(incr);
|
fc.expand_window(incr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use {ConnectionError};
|
use ConnectionError;
|
||||||
|
use client::Client;
|
||||||
use error::Reason;
|
use error::Reason;
|
||||||
use error::User;
|
use error::User;
|
||||||
use frame::{self, Frame};
|
use frame::{self, Frame};
|
||||||
use proto::*;
|
use proto::*;
|
||||||
|
use server::Server;
|
||||||
|
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use ordermap::OrderMap;
|
use ordermap::OrderMap;
|
||||||
@@ -16,82 +18,93 @@ use std::marker::PhantomData;
|
|||||||
|
|
||||||
/// Tracks a connection's streams.
|
/// Tracks a connection's streams.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StreamTracker<T, P> {
|
pub struct StreamRecv<T, P> {
|
||||||
inner: T,
|
inner: T,
|
||||||
peer: PhantomData<P>,
|
peer: PhantomData<P>,
|
||||||
|
|
||||||
active_streams: StreamMap,
|
local: StreamMap,
|
||||||
// TODO reserved_streams: HashSet<StreamId>
|
|
||||||
reset_streams: OrderMap<StreamId, Reason, BuildHasherDefault<FnvHasher>>,
|
|
||||||
|
|
||||||
local_max_concurrency: Option<u32>,
|
local_max_concurrency: Option<u32>,
|
||||||
remote_max_concurrency: Option<u32>,
|
local_initial_window_size: WindowSize,
|
||||||
initial_local_window_size: WindowSize,
|
|
||||||
initial_remote_window_size: WindowSize,
|
|
||||||
|
|
||||||
pending_refused_stream: Option<StreamId>,
|
remote: StreamMap,
|
||||||
|
remote_max_concurrency: Option<u32>,
|
||||||
|
remote_initial_window_size: WindowSize,
|
||||||
|
remote_pending_refuse: Option<StreamId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P, U> StreamTracker<T, P>
|
impl<T, P, U> StreamRecv<T, P>
|
||||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
P: Peer
|
P: Peer
|
||||||
{
|
{
|
||||||
pub fn new(initial_local_window_size: WindowSize,
|
pub fn new(initial_window_size: WindowSize,
|
||||||
initial_remote_window_size: WindowSize,
|
max_concurrency: Option<u32>,
|
||||||
local_max_concurrency: Option<u32>,
|
|
||||||
remote_max_concurrency: Option<u32>,
|
|
||||||
inner: T)
|
inner: T)
|
||||||
-> StreamTracker<T, P>
|
-> StreamRecv<T, P>
|
||||||
{
|
{
|
||||||
StreamTracker {
|
StreamRecv {
|
||||||
inner,
|
inner,
|
||||||
peer: PhantomData,
|
peer: PhantomData,
|
||||||
|
|
||||||
active_streams: StreamMap::default(),
|
local: StreamMap::default(),
|
||||||
reset_streams: OrderMap::default(),
|
remote: StreamMap::default(),
|
||||||
pending_refused_stream: None,
|
max_concurrency,
|
||||||
|
initial_window_size,
|
||||||
local_max_concurrency,
|
remote_pending_refuse: None,
|
||||||
remote_max_concurrency,
|
|
||||||
initial_local_window_size,
|
|
||||||
initial_remote_window_size,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_open_remote(&mut self, frame: Frame) -> Result<(), ConnectionError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_close(&mut self, frame: Frame) -> Result<(), ConnectionError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P, U> StreamTracker<T, P>
|
impl<T, P, U> StreamRecv<T, P>
|
||||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
P: Peer
|
P: Peer
|
||||||
{
|
{
|
||||||
fn send_refusal(&mut self, id: StreamId) -> Poll<(), ConnectionError> {
|
fn send_refusal(&mut self, id: StreamId) -> Poll<(), ConnectionError> {
|
||||||
debug_assert!(self.pending_refused_stream.is_none());
|
debug_assert!(self.remote_pending_refused.is_none());
|
||||||
|
|
||||||
let f = frame::Reset::new(id, Reason::RefusedStream);
|
let f = frame::Reset::new(id, Reason::RefusedStream);
|
||||||
match self.inner.start_send(f.into())? {
|
match self.inner.start_send(f.into())? {
|
||||||
AsyncSink::Ready => {
|
AsyncSink::Ready => {
|
||||||
self.reset_streams.insert(id, Reason::RefusedStream);
|
self.reset(id, Reason::RefusedStream);
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
AsyncSink::NotReady(_) => {
|
AsyncSink::NotReady(_) => {
|
||||||
self.pending_refused_stream = Some(id);
|
self.pending_refused = Some(id);
|
||||||
Ok(Async::NotReady)
|
Ok(Async::NotReady)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P> ControlStreams for StreamTracker<T, P> {
|
impl<T, P> ControlStreams for StreamRecv<T, P>
|
||||||
fn streams(&self) -> &StreamMap {
|
where P: Peer
|
||||||
&self.active_streams
|
{
|
||||||
|
fn local_streams(&self) -> &StreamMap {
|
||||||
|
&self.local
|
||||||
}
|
}
|
||||||
|
|
||||||
fn streams_mut(&mut self) -> &mut StreamMap {
|
fn local_streams_mut(&mut self) -> &mut StreamMap {
|
||||||
&mut self.active_streams
|
&mut self.local
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream_is_reset(&self, id: StreamId) -> Option<Reason> {
|
fn remote_streams(&self) -> &StreamMap {
|
||||||
self.reset_streams.get(&id).map(|r| *r)
|
&self.remote
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remote_streams_mut(&mut self) -> &mut StreamMap {
|
||||||
|
&mut self.remote
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid_local_id(id: StreamId) -> bool {
|
||||||
|
P::is_valid_local_stream_id(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,23 +127,21 @@ impl<T, P> ControlStreams for StreamTracker<T, P> {
|
|||||||
/// > exceed the new value or allow streams to complete.
|
/// > exceed the new value or allow streams to complete.
|
||||||
///
|
///
|
||||||
/// This module does NOT close streams when the setting changes.
|
/// This module does NOT close streams when the setting changes.
|
||||||
impl<T, P> ApplySettings for StreamTracker<T, P>
|
impl<T, P> ApplySettings for StreamRecv<T, P>
|
||||||
where T: ApplySettings
|
where T: ApplySettings
|
||||||
{
|
{
|
||||||
fn apply_local_settings(&mut self, set: &frame::SettingSet) -> Result<(), ConnectionError> {
|
fn apply_local_settings(&mut self, set: &frame::SettingSet) -> Result<(), ConnectionError> {
|
||||||
self.local_max_concurrency = set.max_concurrent_streams();
|
self.max_concurrency = set.max_concurrent_streams();
|
||||||
self.initial_local_window_size = set.initial_window_size();
|
self.initial_window_size = set.initial_window_size();
|
||||||
self.inner.apply_local_settings(set)
|
self.inner.apply_local_settings(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_remote_settings(&mut self, set: &frame::SettingSet) -> Result<(), ConnectionError> {
|
fn apply_remote_settings(&mut self, set: &frame::SettingSet) -> Result<(), ConnectionError> {
|
||||||
self.remote_max_concurrency = set.max_concurrent_streams();
|
|
||||||
self.initial_remote_window_size = set.initial_window_size();
|
|
||||||
self.inner.apply_remote_settings(set)
|
self.inner.apply_remote_settings(set)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P> ControlPing for StreamTracker<T, P>
|
impl<T, P> ControlPing for StreamRecv<T, P>
|
||||||
where T: ControlPing
|
where T: ControlPing
|
||||||
{
|
{
|
||||||
fn start_ping(&mut self, body: PingPayload) -> StartSend<PingPayload, ConnectionError> {
|
fn start_ping(&mut self, body: PingPayload) -> StartSend<PingPayload, ConnectionError> {
|
||||||
@@ -142,7 +153,7 @@ impl<T, P> ControlPing for StreamTracker<T, P>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P, U> Stream for StreamTracker<T, P>
|
impl<T, P, U> Stream for StreamRecv<T, P>
|
||||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
P: Peer,
|
P: Peer,
|
||||||
@@ -155,7 +166,7 @@ impl<T, P, U> Stream for StreamTracker<T, P>
|
|||||||
|
|
||||||
// Since there's only one slot for pending refused streams, it must be cleared
|
// Since there's only one slot for pending refused streams, it must be cleared
|
||||||
// before polling a frame from the transport.
|
// before polling a frame from the transport.
|
||||||
if let Some(id) = self.pending_refused_stream.take() {
|
if let Some(id) = self.pending_refused.take() {
|
||||||
try_ready!(self.send_refusal(id));
|
try_ready!(self.send_refusal(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,17 +176,24 @@ impl<T, P, U> Stream for StreamTracker<T, P>
|
|||||||
let id = v.stream_id();
|
let id = v.stream_id();
|
||||||
let eos = v.is_end_stream();
|
let eos = v.is_end_stream();
|
||||||
|
|
||||||
if self.reset_streams.contains_key(&id) {
|
if self.get_reset(id).is_some() {
|
||||||
|
// TODO send the remote errors when it sends us frames on reset
|
||||||
|
// streams.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(mut s) = self.get_active_mut(id) {
|
||||||
|
let created = s.recv_headers(eos, self.initial_window_size)?;
|
||||||
|
assert!(!created);
|
||||||
|
return Ok(Async::Ready(Some(Headers(v))));
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure that receiving this frame will not violate the local max
|
// Ensure that receiving this frame will not violate the local max
|
||||||
// stream concurrency setting. Ensure that the stream is refused
|
// stream concurrency setting. Ensure that the stream is refused
|
||||||
// before processing additional frames.
|
// before processing additional frames.
|
||||||
if let Some(max) = self.local_max_concurrency {
|
if let Some(max) = self.max_concurrency {
|
||||||
let max = max as usize;
|
let max = max as usize;
|
||||||
if !self.active_streams.has_stream(id)
|
if !self.local.is_active(id) && self.local.active_count() >= max - 1 {
|
||||||
&& self.active_streams.len() >= max - 1 {
|
|
||||||
// This frame would violate our local max concurrency, so reject
|
// This frame would violate our local max concurrency, so reject
|
||||||
// the stream.
|
// the stream.
|
||||||
try_ready!(self.send_refusal(id));
|
try_ready!(self.send_refusal(id));
|
||||||
@@ -191,7 +209,7 @@ impl<T, P, U> Stream for StreamTracker<T, P>
|
|||||||
.or_insert_with(|| StreamState::default());
|
.or_insert_with(|| StreamState::default());
|
||||||
|
|
||||||
let initialized =
|
let initialized =
|
||||||
stream.recv_headers::<P>(eos, self.initial_local_window_size)?;
|
stream.recv_headers(eos, self.initial_window_size)?;
|
||||||
|
|
||||||
if initialized {
|
if initialized {
|
||||||
if !P::is_valid_remote_stream_id(id) {
|
if !P::is_valid_remote_stream_id(id) {
|
||||||
@@ -213,7 +231,9 @@ impl<T, P, U> Stream for StreamTracker<T, P>
|
|||||||
Some(Data(v)) => {
|
Some(Data(v)) => {
|
||||||
let id = v.stream_id();
|
let id = v.stream_id();
|
||||||
|
|
||||||
if self.reset_streams.contains_key(&id) {
|
if self.get_reset(id).is_some() {
|
||||||
|
// TODO send the remote errors when it sends us frames on reset
|
||||||
|
// streams.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,28 +247,24 @@ impl<T, P, U> Stream for StreamTracker<T, P>
|
|||||||
};
|
};
|
||||||
|
|
||||||
if is_closed {
|
if is_closed {
|
||||||
self.active_streams.remove(id);
|
self.reset(id, Reason::NoError);
|
||||||
self.reset_streams.insert(id, Reason::NoError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Async::Ready(Some(Data(v))));
|
return Ok(Async::Ready(Some(Data(v))));
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Reset(v)) => {
|
Some(Reset(v)) => {
|
||||||
let id = v.stream_id();
|
|
||||||
|
|
||||||
// Set or update the reset reason.
|
// Set or update the reset reason.
|
||||||
self.reset_streams.insert(id, v.reason());
|
self.reset(v.stream_id(), v.reason());
|
||||||
|
return Ok(Async::Ready(Some(Reset(v))));
|
||||||
if self.active_streams.remove(id).is_some() {
|
|
||||||
return Ok(Async::Ready(Some(Reset(v))));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(f) => {
|
Some(f) => {
|
||||||
let id = f.stream_id();
|
let id = f.stream_id();
|
||||||
|
|
||||||
if self.reset_streams.contains_key(&id) {
|
if self.get_reset(id).is_some() {
|
||||||
|
// TODO send the remote errors when it sends us frames on reset
|
||||||
|
// streams.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,14 +279,14 @@ impl<T, P, U> Stream for StreamTracker<T, P>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P, U> Sink for StreamTracker<T, P>
|
impl<T, P, U> Sink for StreamRecv<T, P>
|
||||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
P: Peer,
|
P: Peer,
|
||||||
{
|
{
|
||||||
type SinkItem = T::SinkItem;
|
type SinkItem = T::SinkItem;
|
||||||
type SinkError = T::SinkError;
|
type SinkError = T::SinkError;
|
||||||
|
|
||||||
fn start_send(&mut self, item: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> {
|
fn start_send(&mut self, frame: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> {
|
||||||
use frame::Frame::*;
|
use frame::Frame::*;
|
||||||
|
|
||||||
// Must be enforced through higher levels.
|
// Must be enforced through higher levels.
|
||||||
@@ -278,13 +294,13 @@ impl<T, P, U> Sink for StreamTracker<T, P>
|
|||||||
|
|
||||||
// The local must complete refusing the remote stream before sending any other
|
// The local must complete refusing the remote stream before sending any other
|
||||||
// frames.
|
// frames.
|
||||||
if let Some(id) = self.pending_refused_stream.take() {
|
if let Some(id) = self.pending_refused.take() {
|
||||||
if self.send_refusal(id)?.is_not_ready() {
|
if self.send_refusal(id)?.is_not_ready() {
|
||||||
return Ok(AsyncSink::NotReady(item));
|
return Ok(AsyncSink::NotReady(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match item {
|
match frame {
|
||||||
Headers(v) => {
|
Headers(v) => {
|
||||||
let id = v.stream_id();
|
let id = v.stream_id();
|
||||||
let eos = v.is_end_stream();
|
let eos = v.is_end_stream();
|
||||||
@@ -366,7 +382,7 @@ impl<T, P, U> Sink for StreamTracker<T, P>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
||||||
if let Some(id) = self.pending_refused_stream.take() {
|
if let Some(id) = self.pending_refused.take() {
|
||||||
try_ready!(self.send_refusal(id));
|
try_ready!(self.send_refusal(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,14 +391,14 @@ impl<T, P, U> Sink for StreamTracker<T, P>
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T, P, U> ReadySink for StreamTracker<T, P>
|
impl<T, P, U> ReadySink for StreamRecv<T, P>
|
||||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
T: ReadySink,
|
T: ReadySink,
|
||||||
P: Peer,
|
P: Peer,
|
||||||
{
|
{
|
||||||
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
||||||
if let Some(id) = self.pending_refused_stream.take() {
|
if let Some(id) = self.pending_refused.take() {
|
||||||
try_ready!(self.send_refusal(id));
|
try_ready!(self.send_refusal(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
217
src/proto/stream_send.rs
Normal file
217
src/proto/stream_send.rs
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
use {ConnectionError};
|
||||||
|
use error::Reason;
|
||||||
|
use error::User;
|
||||||
|
use frame::{self, Frame};
|
||||||
|
use proto::*;
|
||||||
|
|
||||||
|
use fnv::FnvHasher;
|
||||||
|
use ordermap::OrderMap;
|
||||||
|
use std::hash::BuildHasherDefault;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
// TODO track "last stream id" for GOAWAY.
|
||||||
|
// TODO track/provide "next" stream id.
|
||||||
|
// TODO reset_streams needs to be bounded.
|
||||||
|
// TODO track reserved streams (PUSH_PROMISE).
|
||||||
|
|
||||||
|
/// Tracks a connection's streams.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StreamSend<T, P> {
|
||||||
|
inner: T,
|
||||||
|
peer: PhantomData<P>,
|
||||||
|
max_concurrency: Option<u32>,
|
||||||
|
initial_window_size: WindowSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, P, U> StreamSend<T, P>
|
||||||
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
|
P: Peer
|
||||||
|
{
|
||||||
|
pub fn new(initial_window_size: WindowSize,
|
||||||
|
max_concurrency: Option<u32>,
|
||||||
|
inner: T)
|
||||||
|
-> StreamSend<T, P>
|
||||||
|
{
|
||||||
|
StreamSend {
|
||||||
|
inner,
|
||||||
|
peer: PhantomData,
|
||||||
|
max_concurrency,
|
||||||
|
initial_window_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_open_local(&mut self, frame: Frame) -> Result<(), ConnectionError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_close(&mut self, frame: Frame) -> Result<(), ConnectionError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Handles updates to `SETTINGS_MAX_CONCURRENT_STREAMS`.
|
||||||
|
///
|
||||||
|
/// > Indicates the maximum number of concurrent streams that the senderg will allow. This
|
||||||
|
/// > limit is directional: it applies to the number of streams that the sender permits
|
||||||
|
/// > the receiver to create. Initially, there is no limit to this value. It is
|
||||||
|
/// > recommended that this value be no smaller than 100, so as to not unnecessarily limit
|
||||||
|
/// > parallelism.
|
||||||
|
/// >
|
||||||
|
/// > A value of 0 for SETTINGS_MAX_CONCURRENT_STREAMS SHOULD NOT be treated as special by
|
||||||
|
/// > endpoints. A zero value does prevent the creation of new streams; however, this can
|
||||||
|
/// > also happen for any limit that is exhausted with active streams. Servers SHOULD only
|
||||||
|
/// > set a zero value for short durations; if a server does not wish to accept requests,
|
||||||
|
/// > closing the connection is more appropriate.
|
||||||
|
///
|
||||||
|
/// > An endpoint that wishes to reduce the value of SETTINGS_MAX_CONCURRENT_STREAMS to a
|
||||||
|
/// > value that is below the current number of open streams can either close streams that
|
||||||
|
/// > exceed the new value or allow streams to complete.
|
||||||
|
///
|
||||||
|
/// This module does NOT close streams when the setting changes.
|
||||||
|
impl<T, P> ApplySettings for StreamSend<T, P>
|
||||||
|
where T: ApplySettings
|
||||||
|
{
|
||||||
|
fn apply_local_settings(&mut self, set: &frame::SettingSet) -> Result<(), ConnectionError> {
|
||||||
|
self.inner.apply_local_settings(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_remote_settings(&mut self, set: &frame::SettingSet) -> Result<(), ConnectionError> {
|
||||||
|
self.max_concurrency = set.max_concurrent_streams();
|
||||||
|
self.initial_window_size = set.initial_window_size();
|
||||||
|
self.inner.apply_remote_settings(set)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, P> ControlPing for StreamSend<T, P>
|
||||||
|
where T: ControlPing
|
||||||
|
{
|
||||||
|
fn start_ping(&mut self, body: PingPayload) -> StartSend<PingPayload, ConnectionError> {
|
||||||
|
self.inner.start_ping(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_pong(&mut self) -> Option<PingPayload> {
|
||||||
|
self.inner.take_pong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, P, U> Stream for StreamSend<T, P>
|
||||||
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
|
T: ControlStreams,
|
||||||
|
P: Peer,
|
||||||
|
{
|
||||||
|
type Item = T::Item;
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> {
|
||||||
|
self.inner.poll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, P, U> Sink for StreamSend<T, P>
|
||||||
|
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
|
T: ControlStreams,
|
||||||
|
P: Peer,
|
||||||
|
{
|
||||||
|
type SinkItem = T::SinkItem;
|
||||||
|
type SinkError = T::SinkError;
|
||||||
|
|
||||||
|
fn start_send(&mut self, item: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> {
|
||||||
|
use frame::Frame::*;
|
||||||
|
|
||||||
|
// Must be enforced through higher levels.
|
||||||
|
if let Some(rst) = self.inner.get_reset(item.stream_id()) {
|
||||||
|
return Err(User::StreamReset(rst).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
match item {
|
||||||
|
Headers(v) => {
|
||||||
|
let id = v.stream_id();
|
||||||
|
let eos = v.is_end_stream();
|
||||||
|
|
||||||
|
// Transition the stream state, creating a new entry if needed
|
||||||
|
//
|
||||||
|
// TODO: Response can send multiple headers frames before body (1xx
|
||||||
|
// responses).
|
||||||
|
//
|
||||||
|
// ACTUALLY(ver), maybe not?
|
||||||
|
// https://github.com/http2/http2-spec/commit/c83c8d911e6b6226269877e446a5cad8db921784
|
||||||
|
|
||||||
|
// Ensure that sending this frame would not violate the remote's max
|
||||||
|
// stream concurrency setting.
|
||||||
|
if let Some(max) = self.max_concurrency {
|
||||||
|
let max = max as usize;
|
||||||
|
let streams = self.inner.streams();
|
||||||
|
if !streams.is_active(id) && streams.active_count() >= max - 1 {
|
||||||
|
return Err(User::MaxConcurrencyExceeded.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_closed = {
|
||||||
|
let stream = self.active_streams.entry(id)
|
||||||
|
.or_insert_with(|| StreamState::default());
|
||||||
|
|
||||||
|
let initialized =
|
||||||
|
stream.send_headers::<P>(eos, self.initial_window_size)?;
|
||||||
|
|
||||||
|
if initialized {
|
||||||
|
// TODO: Ensure available capacity for a new stream
|
||||||
|
// This won't be as simple as self.streams.len() as closed
|
||||||
|
// connections should not be factored.
|
||||||
|
if !P::is_valid_local_stream_id(id) {
|
||||||
|
// TODO: clear state
|
||||||
|
return Err(User::InvalidStreamId.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.is_closed()
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_closed {
|
||||||
|
self.active_streams.remove(id);
|
||||||
|
self.reset_streams.insert(id, Reason::NoError);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner.start_send(Headers(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
Data(v) => {
|
||||||
|
match self.active_streams.get_mut(v.stream_id()) {
|
||||||
|
None => return Err(User::InactiveStreamId.into()),
|
||||||
|
Some(stream) => {
|
||||||
|
stream.send_data(v.is_end_stream())?;
|
||||||
|
self.inner.start_send(Data(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Reset(v) => {
|
||||||
|
let id = v.stream_id();
|
||||||
|
self.active_streams.remove(id);
|
||||||
|
self.reset_streams.insert(id, v.reason());
|
||||||
|
self.inner.start_send(Reset(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
frame => self.inner.start_send(frame),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
||||||
|
self.inner.poll_complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, P, U> ReadySink for StreamSend<T, P>
|
||||||
|
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||||
|
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||||
|
T: ControlStreams,
|
||||||
|
T: ReadySink,
|
||||||
|
P: Peer,
|
||||||
|
{
|
||||||
|
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
||||||
|
self.inner.poll_ready()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,13 +111,17 @@ impl Peer for Server {
|
|||||||
type Poll = http::request::Head;
|
type Poll = http::request::Head;
|
||||||
|
|
||||||
fn is_valid_local_stream_id(_id: StreamId) -> bool {
|
fn is_valid_local_stream_id(_id: StreamId) -> bool {
|
||||||
false
|
id.is_server_initiated()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_valid_remote_stream_id(id: StreamId) -> bool {
|
fn is_valid_remote_stream_id(id: StreamId) -> bool {
|
||||||
id.is_client_initiated()
|
id.is_client_initiated()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_create_local_stream() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn convert_send_message(
|
fn convert_send_message(
|
||||||
id: StreamId,
|
id: StreamId,
|
||||||
headers: Self::Send,
|
headers: Self::Send,
|
||||||
|
|||||||
Reference in New Issue
Block a user