wip: Sketch out stream state refactor
introduce the StreamTransporter trait, which exposes a map containing all active stream states. add skeletons for StreamTracker and FlowControl. StreamTracker drives all state changes
This commit is contained in:
@@ -2,7 +2,7 @@ use {Frame, FrameSize};
|
||||
use client::Client;
|
||||
use error::{self, ConnectionError};
|
||||
use frame::{self, StreamId};
|
||||
use proto::{self, Peer, ReadySink, State, FlowController, WindowSize};
|
||||
use proto::{self, Peer, ReadySink, StreamState, FlowController, WindowSize};
|
||||
use server::Server;
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
@@ -14,15 +14,14 @@ use futures::*;
|
||||
|
||||
use ordermap::OrderMap;
|
||||
use fnv::FnvHasher;
|
||||
|
||||
use std::hash::BuildHasherDefault;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// An H2 connection
|
||||
#[derive(Debug)]
|
||||
pub struct Connection<T, P, B: IntoBuf = Bytes> {
|
||||
inner: proto::Inner<T, B::Buf>,
|
||||
streams: StreamMap<State>,
|
||||
inner: proto::Transport<T, B::Buf>,
|
||||
streams: StreamMap<StreamState>,
|
||||
peer: PhantomData<P>,
|
||||
|
||||
/// Tracks the connection-level flow control window for receiving data from the
|
||||
@@ -41,7 +40,7 @@ pub struct Connection<T, P, B: IntoBuf = Bytes> {
|
||||
|
||||
type StreamMap<T> = OrderMap<StreamId, T, BuildHasherDefault<FnvHasher>>;
|
||||
|
||||
pub fn new<T, P, B>(transport: proto::Inner<T, B::Buf>)
|
||||
pub fn new<T, P, B>(transport: proto::Transport<T, B::Buf>)
|
||||
-> Connection<T, P, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
P: Peer,
|
||||
@@ -256,7 +255,7 @@ impl<T, P, B> Stream for Connection<T, P, B>
|
||||
let init_window_size = self.inner.local_settings().initial_window_size();
|
||||
|
||||
let stream_initialized = try!(self.streams.entry(stream_id)
|
||||
.or_insert(State::default())
|
||||
.or_insert(StreamState::default())
|
||||
.recv_headers::<P>(end_of_stream, init_window_size));
|
||||
|
||||
if stream_initialized {
|
||||
@@ -347,7 +346,7 @@ impl<T, P, B> Sink for Connection<T, P, B>
|
||||
// ACTUALLY(ver), maybe not?
|
||||
// https://github.com/http2/http2-spec/commit/c83c8d911e6b6226269877e446a5cad8db921784
|
||||
let stream_initialized = try!(self.streams.entry(id)
|
||||
.or_insert(State::default())
|
||||
.or_insert(StreamState::default())
|
||||
.send_headers::<P>(end_of_stream, init_window_size));
|
||||
|
||||
if stream_initialized {
|
||||
|
||||
@@ -1,77 +1,70 @@
|
||||
use proto::WindowSize;
|
||||
use ConnectionError;
|
||||
use frame::{self, Frame};
|
||||
use proto::{ReadySink, StreamMap, StreamTransporter, WindowSize};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct WindowUnderflow;
|
||||
use futures::*;
|
||||
|
||||
pub const DEFAULT_INITIAL_WINDOW_SIZE: WindowSize = 65_535;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct FlowController {
|
||||
/// Amount that may be claimed.
|
||||
window_size: WindowSize,
|
||||
/// Amount to be removed by future increments.
|
||||
underflow: WindowSize,
|
||||
/// The amount that has been incremented but not yet advertised (to the application or
|
||||
/// the remote).
|
||||
next_window_update: WindowSize,
|
||||
#[derive(Debug)]
|
||||
pub struct FlowControl<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl Default for FlowController {
|
||||
fn default() -> Self {
|
||||
Self::new(DEFAULT_INITIAL_WINDOW_SIZE)
|
||||
impl<T, U> FlowControl<T>
|
||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
T: StreamTransporter
|
||||
{
|
||||
pub fn new(inner: T) -> FlowControl<T> {
|
||||
FlowControl { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl FlowController {
|
||||
pub fn new(window_size: WindowSize) -> FlowController {
|
||||
FlowController {
|
||||
window_size,
|
||||
underflow: 0,
|
||||
next_window_update: 0,
|
||||
}
|
||||
impl<T: StreamTransporter> StreamTransporter for FlowControl<T> {
|
||||
fn streams(&self) -> &StreamMap {
|
||||
self.inner.streams()
|
||||
}
|
||||
|
||||
/// Reduce future capacity of the window.
|
||||
///
|
||||
/// This accomodates updates to SETTINGS_INITIAL_WINDOW_SIZE.
|
||||
pub fn shrink_window(&mut self, decr: WindowSize) {
|
||||
self.underflow += decr;
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// have been previously claimed.
|
||||
pub fn claim_window(&mut self, sz: WindowSize) -> Result<(), WindowUnderflow> {
|
||||
if self.window_size < sz {
|
||||
return Err(WindowUnderflow);
|
||||
}
|
||||
|
||||
self.window_size -= sz;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Applies a window increment immediately.
|
||||
pub fn increment_window_size(&mut self, sz: WindowSize) {
|
||||
if sz <= self.underflow {
|
||||
self.underflow -= sz;
|
||||
return;
|
||||
}
|
||||
|
||||
let added = sz - self.underflow;
|
||||
self.window_size += added;
|
||||
self.next_window_update += added;
|
||||
self.underflow = 0;
|
||||
}
|
||||
|
||||
/// Obtains and clears an unadvertised window update.
|
||||
pub fn take_window_update(&mut self) -> Option<WindowSize> {
|
||||
if self.next_window_update == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let incr = self.next_window_update;
|
||||
self.next_window_update = 0;
|
||||
Some(incr)
|
||||
fn streams_mut(&mut self) -> &mut StreamMap {
|
||||
self.inner.streams_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for FlowControl<T>
|
||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||
T: StreamTransporter,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T, U> Sink for FlowControl<T>
|
||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
T: StreamTransporter,
|
||||
{
|
||||
type SinkItem = T::SinkItem;
|
||||
type SinkError = T::SinkError;
|
||||
|
||||
fn start_send(&mut self, item: Frame<U>) -> StartSend<T::SinkItem, T::SinkError> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ReadySink for FlowControl<T>
|
||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
T: ReadySink,
|
||||
T: StreamTransporter,
|
||||
{
|
||||
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
}
|
||||
|
||||
83
src/proto/flow_controller.rs
Normal file
83
src/proto/flow_controller.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use proto::WindowSize;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct WindowUnderflow;
|
||||
|
||||
pub const DEFAULT_INITIAL_WINDOW_SIZE: WindowSize = 65_535;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct FlowController {
|
||||
/// Amount that may be claimed.
|
||||
window_size: WindowSize,
|
||||
/// Amount to be removed by future increments.
|
||||
underflow: WindowSize,
|
||||
/// The amount that has been incremented but not yet advertised (to the application or
|
||||
/// the remote).
|
||||
next_window_update: WindowSize,
|
||||
}
|
||||
|
||||
impl Default for FlowController {
|
||||
fn default() -> Self {
|
||||
Self::new(DEFAULT_INITIAL_WINDOW_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
impl FlowController {
|
||||
pub fn new(window_size: WindowSize) -> FlowController {
|
||||
FlowController {
|
||||
window_size,
|
||||
underflow: 0,
|
||||
next_window_update: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reduce future capacity of the window.
|
||||
///
|
||||
/// This accomodates updates to SETTINGS_INITIAL_WINDOW_SIZE.
|
||||
pub fn shrink_window(&mut self, decr: WindowSize) {
|
||||
self.underflow += decr;
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// have been previously claimed.
|
||||
pub fn claim_window(&mut self, sz: WindowSize) -> Result<(), WindowUnderflow> {
|
||||
if self.window_size < sz {
|
||||
return Err(WindowUnderflow);
|
||||
}
|
||||
|
||||
self.window_size -= sz;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Applies a window increment immediately.
|
||||
pub fn increment_window_size(&mut self, sz: WindowSize) {
|
||||
if sz <= self.underflow {
|
||||
self.underflow -= sz;
|
||||
return;
|
||||
}
|
||||
|
||||
let added = sz - self.underflow;
|
||||
self.window_size += added;
|
||||
self.next_window_update += added;
|
||||
self.underflow = 0;
|
||||
}
|
||||
|
||||
/// Obtains and clears an unadvertised window update.
|
||||
pub fn take_window_update(&mut self) -> Option<WindowSize> {
|
||||
if self.next_window_update == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let incr = self.next_window_update;
|
||||
self.next_window_update = 0;
|
||||
Some(incr)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut fc = FlowController::new(65_535);
|
||||
|
||||
}
|
||||
@@ -1,43 +1,62 @@
|
||||
mod connection;
|
||||
mod flow_control;
|
||||
mod flow_controller;
|
||||
mod framed_read;
|
||||
mod framed_write;
|
||||
mod ping_pong;
|
||||
mod ready;
|
||||
mod settings;
|
||||
mod state;
|
||||
mod window_update;
|
||||
mod stream_tracker;
|
||||
|
||||
pub use self::connection::Connection;
|
||||
pub use self::flow_control::FlowController;
|
||||
pub use self::flow_control::FlowControl;
|
||||
pub use self::flow_controller::FlowController;
|
||||
pub use self::framed_read::FramedRead;
|
||||
pub use self::framed_write::FramedWrite;
|
||||
pub use self::ping_pong::PingPong;
|
||||
pub use self::ready::ReadySink;
|
||||
pub use self::settings::Settings;
|
||||
pub use self::state::{PeerState, State};
|
||||
pub use self::window_update::WindowUpdate;
|
||||
pub use self::stream_tracker::StreamTracker;
|
||||
use self::state::StreamState;
|
||||
|
||||
use {frame, Peer};
|
||||
use {frame, Peer, StreamId};
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_io::codec::length_delimited;
|
||||
|
||||
use bytes::{Buf, IntoBuf};
|
||||
|
||||
type Inner<T, B> =
|
||||
Settings<
|
||||
PingPong<
|
||||
Framed<T, B>,
|
||||
B>>;
|
||||
use ordermap::OrderMap;
|
||||
use fnv::FnvHasher;
|
||||
use std::hash::BuildHasherDefault;
|
||||
|
||||
type Framed<T, B> =
|
||||
/// Represents
|
||||
type Transport<T, B> =
|
||||
Settings<
|
||||
FlowControl<
|
||||
StreamTracker<
|
||||
PingPong<
|
||||
Framer<T, B>,
|
||||
B>>>>;
|
||||
|
||||
type Framer<T, B> =
|
||||
FramedRead<
|
||||
FramedWrite<T, B>>;
|
||||
|
||||
|
||||
pub type WindowSize = u32;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct StreamMap {
|
||||
inner: OrderMap<StreamId, StreamState, BuildHasherDefault<FnvHasher>>
|
||||
}
|
||||
|
||||
trait StreamTransporter {
|
||||
fn streams(&self)-> &StreamMap;
|
||||
fn streams_mut(&mut self) -> &mut StreamMap;
|
||||
}
|
||||
|
||||
/// Create a full H2 transport from an I/O handle.
|
||||
///
|
||||
/// This is called as the final step of the client handshake future.
|
||||
@@ -91,8 +110,10 @@ pub fn from_server_handshaker<T, P, B>(transport: Settings<FramedWrite<T, B::Buf
|
||||
// Map to `Frame` types
|
||||
let framed = FramedRead::new(framed_read);
|
||||
|
||||
// Add ping/pong responder.
|
||||
PingPong::new(framed)
|
||||
FlowControl::new(
|
||||
StreamTracker::new(
|
||||
PingPong::new(
|
||||
framed)))
|
||||
});
|
||||
|
||||
// Finally, return the constructed `Connection`
|
||||
|
||||
@@ -20,7 +20,11 @@ impl<T, U> PingPong<T, U>
|
||||
pong: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> PingPong<T, U>
|
||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
{
|
||||
fn try_send_pong(&mut self) -> Poll<(), ConnectionError> {
|
||||
if let Some(pong) = self.pong.take() {
|
||||
if let AsyncSink::NotReady(pong) = self.inner.start_send(pong)? {
|
||||
@@ -77,8 +81,7 @@ impl<T, U> Stream for PingPong<T, U>
|
||||
}
|
||||
|
||||
impl<T, U> Sink for PingPong<T, U>
|
||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
{
|
||||
type SinkItem = Frame<U>;
|
||||
type SinkError = ConnectionError;
|
||||
@@ -103,8 +106,7 @@ impl<T, U> Sink for PingPong<T, U>
|
||||
}
|
||||
|
||||
impl<T, U> ReadySink for PingPong<T, U>
|
||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
T: ReadySink,
|
||||
{
|
||||
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
||||
|
||||
@@ -45,7 +45,7 @@ use proto::FlowController;
|
||||
/// R: RST_STREAM frame
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum State {
|
||||
pub enum StreamState {
|
||||
Idle,
|
||||
ReservedLocal,
|
||||
ReservedRemote,
|
||||
@@ -58,7 +58,7 @@ pub enum State {
|
||||
Closed,
|
||||
}
|
||||
|
||||
impl State {
|
||||
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
|
||||
@@ -66,7 +66,7 @@ impl State {
|
||||
///
|
||||
/// If the remote is closed, None is returned.
|
||||
pub fn increment_send_window_size(&mut self, incr: u32) {
|
||||
use self::State::*;
|
||||
use self::StreamState::*;
|
||||
use self::PeerState::*;
|
||||
|
||||
if incr == 0 {
|
||||
@@ -83,7 +83,7 @@ impl State {
|
||||
/// Consumes newly-advertised capacity to inform the local endpoint it may send more
|
||||
/// data.
|
||||
pub fn take_send_window_update(&mut self) -> Option<u32> {
|
||||
use self::State::*;
|
||||
use self::StreamState::*;
|
||||
use self::PeerState::*;
|
||||
|
||||
match self {
|
||||
@@ -99,7 +99,7 @@ impl State {
|
||||
/// 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 increment_recv_window_size(&mut self, incr: u32) {
|
||||
use self::State::*;
|
||||
use self::StreamState::*;
|
||||
use self::PeerState::*;
|
||||
|
||||
if incr == 0 {
|
||||
@@ -116,7 +116,7 @@ impl State {
|
||||
/// Consumes newly-advertised capacity to inform the local endpoint it may send more
|
||||
/// data.
|
||||
pub fn take_recv_window_update(&mut self) -> Option<u32> {
|
||||
use self::State::*;
|
||||
use self::StreamState::*;
|
||||
use self::PeerState::*;
|
||||
|
||||
match self {
|
||||
@@ -143,7 +143,7 @@ impl State {
|
||||
/// > receives WINDOW_UPDATE frames that cause the flow-control window to become
|
||||
/// > positive.
|
||||
pub fn update_initial_recv_window_size(&mut self, old: u32, new: u32) {
|
||||
use self::State::*;
|
||||
use self::StreamState::*;
|
||||
use self::PeerState::*;
|
||||
|
||||
match self {
|
||||
@@ -161,7 +161,7 @@ impl State {
|
||||
|
||||
/// TODO Connection doesn't have an API for local updates yet.
|
||||
pub fn update_initial_send_window_size(&mut self, _old: u32, _new: u32) {
|
||||
//use self::State::*;
|
||||
//use self::StreamState::*;
|
||||
//use self::PeerState::*;
|
||||
unimplemented!()
|
||||
}
|
||||
@@ -175,7 +175,7 @@ impl State {
|
||||
initial_recv_window_size: u32)
|
||||
-> Result<bool, ConnectionError>
|
||||
{
|
||||
use self::State::*;
|
||||
use self::StreamState::*;
|
||||
use self::PeerState::*;
|
||||
|
||||
match *self {
|
||||
@@ -218,7 +218,7 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn recv_data(&mut self, eos: bool, len: FrameSize) -> Result<(), ConnectionError> {
|
||||
use self::State::*;
|
||||
use self::StreamState::*;
|
||||
|
||||
match *self {
|
||||
Open { local, mut remote } => {
|
||||
@@ -256,7 +256,7 @@ impl State {
|
||||
initial_window_size: u32)
|
||||
-> Result<bool, ConnectionError>
|
||||
{
|
||||
use self::State::*;
|
||||
use self::StreamState::*;
|
||||
use self::PeerState::*;
|
||||
|
||||
match *self {
|
||||
@@ -307,7 +307,7 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn send_data(&mut self, eos: bool, len: FrameSize) -> Result<(), ConnectionError> {
|
||||
use self::State::*;
|
||||
use self::StreamState::*;
|
||||
|
||||
match *self {
|
||||
Open { mut local, remote } => {
|
||||
@@ -337,9 +337,9 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> State {
|
||||
State::Idle
|
||||
impl Default for StreamState {
|
||||
fn default() -> StreamState {
|
||||
StreamState::Idle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
67
src/proto/stream_tracker.rs
Normal file
67
src/proto/stream_tracker.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use ConnectionError;
|
||||
use frame::{self, Frame};
|
||||
use proto::{ReadySink, StreamMap, StreamTransporter, WindowSize};
|
||||
|
||||
use futures::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StreamTracker<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T, U> StreamTracker<T>
|
||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>
|
||||
{
|
||||
pub fn new(inner: T) -> StreamTracker<T> {
|
||||
StreamTracker { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> StreamTransporter for StreamTracker<T> {
|
||||
fn streams(&self) -> &StreamMap {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn streams_mut(&mut self) -> &mut StreamMap {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Stream for StreamTracker<T>
|
||||
where T: Stream<Item = Frame<U>, Error = ConnectionError>,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T, U> Sink for StreamTracker<T>
|
||||
where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
{
|
||||
type SinkItem = T::SinkItem;
|
||||
type SinkError = T::SinkError;
|
||||
|
||||
fn start_send(&mut self, item: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T, U> ReadySink for StreamTracker<T>
|
||||
where T: Stream<Item = Frame, Error = ConnectionError>,
|
||||
T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>,
|
||||
T: ReadySink,
|
||||
{
|
||||
fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
use StreamId;
|
||||
use frame;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WindowUpdate {
|
||||
Connection { increment: u32 },
|
||||
Stream { id: StreamId, increment: u32 },
|
||||
}
|
||||
|
||||
impl WindowUpdate {
|
||||
pub fn increment(&self) -> u32 {
|
||||
match *self {
|
||||
WindowUpdate::Connection { increment } |
|
||||
WindowUpdate::Stream { increment, .. } => increment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WindowUpdate> for frame::WindowUpdate {
|
||||
fn from(src: WindowUpdate) -> Self {
|
||||
match src {
|
||||
WindowUpdate::Connection { increment } => {
|
||||
frame::WindowUpdate::new(StreamId::zero(), increment)
|
||||
}
|
||||
WindowUpdate::Stream { id, increment } => {
|
||||
frame::WindowUpdate::new(id, increment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WindowUpdate> for frame::Frame {
|
||||
fn from(src: WindowUpdate) -> Self {
|
||||
frame::Frame::WindowUpdate(src.into())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user