wip: start splitting out stream management
This commit is contained in:
		| @@ -1,8 +1,7 @@ | |||||||
| use {Frame, FrameSize}; | use {ConnectionError, Frame, FrameSize}; | ||||||
| use client::Client; | use client::Client; | ||||||
| use error::{self, ConnectionError}; |  | ||||||
| use frame::{self, StreamId}; | use frame::{self, StreamId}; | ||||||
| use proto::{self, Peer, ReadySink, StreamState, FlowController, WindowSize}; | use proto::{self, Peer, ReadySink, WindowSize}; | ||||||
| use server::Server; | use server::Server; | ||||||
|  |  | ||||||
| use tokio_io::{AsyncRead, AsyncWrite}; | use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
| @@ -12,152 +11,65 @@ use bytes::{Bytes, IntoBuf}; | |||||||
|  |  | ||||||
| use futures::*; | use futures::*; | ||||||
|  |  | ||||||
| use ordermap::OrderMap; |  | ||||||
| use fnv::FnvHasher; |  | ||||||
| use std::hash::BuildHasherDefault; |  | ||||||
| use std::marker::PhantomData; | use std::marker::PhantomData; | ||||||
|  |  | ||||||
| /// An H2 connection | /// An H2 connection | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Connection<T, P, B: IntoBuf = Bytes> { | pub struct Connection<T, P, B: IntoBuf = Bytes> { | ||||||
|     inner: proto::Transport<T, B::Buf>, |     inner: proto::Transport<T, P, B::Buf>, | ||||||
|     streams: StreamMap<StreamState>, |  | ||||||
|     peer: PhantomData<P>, |     peer: PhantomData<P>, | ||||||
|  |  | ||||||
|     /// Tracks the connection-level flow control window for receiving data from the |  | ||||||
|     /// remote. |  | ||||||
|     local_flow_controller: FlowController, |  | ||||||
|  |  | ||||||
|     /// Tracks the onnection-level flow control window for receiving data from the remote. |  | ||||||
|     remote_flow_controller: FlowController, |  | ||||||
|  |  | ||||||
|     /// When `poll_window_update` is not ready, then the calling task is saved to be |  | ||||||
|     /// notified later. Access to poll_window_update must not be shared across tasks. |  | ||||||
|     blocked_window_update: Option<task::Task>, |  | ||||||
|  |  | ||||||
|     sending_window_update: Option<frame::WindowUpdate>, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| type StreamMap<T> = OrderMap<StreamId, T, BuildHasherDefault<FnvHasher>>; | pub fn new<T, P, B>(transport: proto::Transport<T, P, B::Buf>) | ||||||
|  |  | ||||||
| pub fn new<T, P, B>(transport: proto::Transport<T, B::Buf>) |  | ||||||
|     -> Connection<T, P, B> |     -> Connection<T, P, B> | ||||||
|     where T: AsyncRead + AsyncWrite, |     where T: AsyncRead + AsyncWrite, | ||||||
|           P: Peer, |           P: Peer, | ||||||
|           B: IntoBuf, |           B: IntoBuf, | ||||||
| { | { | ||||||
|     let recv_window_size = transport.local_settings().initial_window_size(); |  | ||||||
|     let send_window_size = transport.remote_settings().initial_window_size(); |  | ||||||
|     Connection { |     Connection { | ||||||
|         inner: transport, |         inner: transport, | ||||||
|         streams: StreamMap::default(), |  | ||||||
|         peer: PhantomData, |         peer: PhantomData, | ||||||
|  |  | ||||||
|         local_flow_controller: FlowController::new(recv_window_size), |  | ||||||
|         remote_flow_controller: FlowController::new(send_window_size), |  | ||||||
|  |  | ||||||
|         blocked_window_update: None, |  | ||||||
|         sending_window_update: None, |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T, P, B: IntoBuf> Connection<T, P, B> { | impl<T, P, B: IntoBuf> Connection<T, P, B> { | ||||||
|     #[inline] |  | ||||||
|     fn claim_local_window(&mut self, len: WindowSize) -> Result<(), ConnectionError> { |  | ||||||
|         self.local_flow_controller.claim_window(len) |  | ||||||
|             .map_err(|_| error::Reason::FlowControlError.into()) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     #[inline] |  | ||||||
|     fn claim_remote_window(&mut self, len: WindowSize) -> Result<(), ConnectionError> { |  | ||||||
|         self.remote_flow_controller.claim_window(len) |  | ||||||
|             .map_err(|_| error::User::FlowControlViolation.into()) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Polls for the amount of additional data that may be sent to a remote. |     /// Polls for the amount of additional data that may be sent to a remote. | ||||||
|     /// |     /// | ||||||
|     /// Connection  and stream updates are distinct. |     /// Connection  and stream updates are distinct. | ||||||
|     pub fn poll_window_update(&mut self, id: StreamId) -> Poll<WindowSize, ConnectionError> { |     pub fn poll_window_update(&mut self, _id: StreamId) -> Poll<WindowSize, ConnectionError> { | ||||||
|         let added = if id.is_zero() { |         // let added = if id.is_zero() { | ||||||
|             self.remote_flow_controller.take_window_update() |         //     self.remote_flow_controller.take_window_update() | ||||||
|         } else { |         // } else { | ||||||
|             self.streams.get_mut(&id).and_then(|s| s.take_send_window_update()) |         //     self.streams.get_mut(&id).and_then(|s| s.take_send_window_update()) | ||||||
|         }; |         // }; | ||||||
|  |         // match added { | ||||||
|         match added { |         //     Some(incr) => Ok(Async::Ready(incr)), | ||||||
|             Some(incr) => Ok(Async::Ready(incr)), |         //     None => { | ||||||
|             None => { |         //         self.blocked_window_update = Some(task::current()); | ||||||
|                 self.blocked_window_update = Some(task::current()); |         //         Ok(Async::NotReady) | ||||||
|                 Ok(Async::NotReady) |         //     } | ||||||
|  |         // } | ||||||
|  |         unimplemented!() | ||||||
|     } |     } | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     /// Increases the amount of data that the remote endpoint may send. |     /// Increases the amount of data that the remote endpoint may send. | ||||||
|     /// |     /// | ||||||
|     /// Connection and stream updates are distinct. |     /// Connection and stream updates are distinct. | ||||||
|     pub fn increment_window_size(&mut self, id: StreamId, incr: WindowSize) { |     pub fn increment_window_size(&mut self, _id: StreamId, _incr: WindowSize) { | ||||||
|         assert!(self.sending_window_update.is_none()); |         // assert!(self.sending_window_update.is_none()); | ||||||
|  |         // let added = if id.is_zero() { | ||||||
|         let added = if id.is_zero() { |         //     self.local_flow_controller.grow_window(incr); | ||||||
|             self.local_flow_controller.grow_window(incr); |         //     self.local_flow_controller.take_window_update() | ||||||
|             self.local_flow_controller.take_window_update() |         // } else { | ||||||
|         } else { |         //     self.streams.get_mut(&id).and_then(|s| { | ||||||
|             self.streams.get_mut(&id).and_then(|s| { |         //         s.grow_recv_window(incr); | ||||||
|                 s.grow_recv_window(incr); |         //         s.take_recv_window_update() | ||||||
|                 s.take_recv_window_update() |         //     }) | ||||||
|             }) |         // }; | ||||||
|         }; |         // if let Some(added) = added { | ||||||
|  |         //     self.sending_window_update = Some(frame::WindowUpdate::new(id, added)); | ||||||
|         if let Some(added) = added { |         // } | ||||||
|             self.sending_window_update = Some(frame::WindowUpdate::new(id, added)); |         unimplemented!() | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// 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 increment_send_window_size(&mut self, id: StreamId, incr: WindowSize) { |  | ||||||
|         if incr == 0 { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         let added = if id.is_zero() { |  | ||||||
|             self.remote_flow_controller.grow_window(incr); |  | ||||||
|             true |  | ||||||
|         } else if let Some(mut s) = self.streams.get_mut(&id) { |  | ||||||
|             s.grow_send_window(incr); |  | ||||||
|             true |  | ||||||
|         } else { |  | ||||||
|             false |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         if added { |  | ||||||
|             if let Some(task) = self.blocked_window_update.take() { |  | ||||||
|                 task.notify(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<T, P, B> Connection<T, P, B> |  | ||||||
|     where T: AsyncRead + AsyncWrite, |  | ||||||
|           P: Peer, |  | ||||||
|           B: IntoBuf |  | ||||||
| { |  | ||||||
|     /// Attempts to send a window update to the remote, if one is pending. |  | ||||||
|     fn poll_sending_window_update(&mut self) -> Poll<(), ConnectionError> { |  | ||||||
|         if let Some(f) = self.sending_window_update.take() { |  | ||||||
|             if self.inner.start_send(f.into())?.is_not_ready() { |  | ||||||
|                 self.sending_window_update = Some(f); |  | ||||||
|                 return Ok(Async::NotReady); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Ok(Async::Ready(())) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -252,22 +164,6 @@ impl<T, P, B> Stream for Connection<T, P, B> | |||||||
|                     let stream_id = v.stream_id(); |                     let stream_id = v.stream_id(); | ||||||
|                     let end_of_stream = v.is_end_stream(); |                     let end_of_stream = v.is_end_stream(); | ||||||
|  |  | ||||||
|                     let init_window_size = self.inner.local_settings().initial_window_size(); |  | ||||||
|  |  | ||||||
|                     let stream_initialized = try!(self.streams.entry(stream_id) |  | ||||||
|                         .or_insert(StreamState::default()) |  | ||||||
|                         .recv_headers::<P>(end_of_stream, init_window_size)); |  | ||||||
|  |  | ||||||
|                     if stream_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_remote_stream_id(stream_id) { |  | ||||||
|                             unimplemented!(); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     Frame::Headers { |                     Frame::Headers { | ||||||
|                         id: stream_id, |                         id: stream_id, | ||||||
|                         headers: P::convert_poll_message(v), |                         headers: P::convert_poll_message(v), | ||||||
| @@ -279,12 +175,6 @@ impl<T, P, B> Stream for Connection<T, P, B> | |||||||
|                     let id = v.stream_id(); |                     let id = v.stream_id(); | ||||||
|                     let end_of_stream = v.is_end_stream(); |                     let end_of_stream = v.is_end_stream(); | ||||||
|  |  | ||||||
|                     self.claim_local_window(v.len())?; |  | ||||||
|                     match self.streams.get_mut(&id) { |  | ||||||
|                         None => return Err(error::Reason::ProtocolError.into()), |  | ||||||
|                         Some(state) => state.recv_data(end_of_stream, v.len())?, |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     Frame::Data { |                     Frame::Data { | ||||||
|                         id, |                         id, | ||||||
|                         end_of_stream, |                         end_of_stream, | ||||||
| @@ -293,16 +183,6 @@ impl<T, P, B> Stream for Connection<T, P, B> | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 Some(WindowUpdate(v)) => { |  | ||||||
|                     // When a window update is received from the remote, apply that update |  | ||||||
|                     // to the proper stream so that more data may be sent to the remote. |  | ||||||
|                     self.increment_send_window_size(v.stream_id(), v.size_increment()); |  | ||||||
|  |  | ||||||
|                     // There's nothing to return yet, so continue attempting to read |  | ||||||
|                     // additional frames. |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 Some(frame) => panic!("unexpected frame; frame={:?}", frame), |                 Some(frame) => panic!("unexpected frame; frame={:?}", frame), | ||||||
|                 None => return Ok(Async::Ready(None)), |                 None => return Ok(Async::Ready(None)), | ||||||
|             }; |             }; | ||||||
| @@ -332,33 +212,9 @@ impl<T, P, B> Sink for Connection<T, P, B> | |||||||
|         if self.poll_ready()? == Async::NotReady { |         if self.poll_ready()? == Async::NotReady { | ||||||
|             return Ok(AsyncSink::NotReady(item)); |             return Ok(AsyncSink::NotReady(item)); | ||||||
|         } |         } | ||||||
|         assert!(self.sending_window_update.is_none()); |  | ||||||
|  |  | ||||||
|         match item { |         match item { | ||||||
|             Frame::Headers { id, headers, end_of_stream } => { |             Frame::Headers { id, headers, end_of_stream } => { | ||||||
|                 let init_window_size = self.inner.remote_settings().initial_window_size(); |  | ||||||
|  |  | ||||||
|                 // 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 |  | ||||||
|                 let stream_initialized = try!(self.streams.entry(id) |  | ||||||
|                      .or_insert(StreamState::default()) |  | ||||||
|                      .send_headers::<P>(end_of_stream, init_window_size)); |  | ||||||
|  |  | ||||||
|                 if stream_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(error::User::InvalidStreamId.into()); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 let frame = P::convert_send_message(id, headers, end_of_stream); |                 let frame = P::convert_send_message(id, headers, end_of_stream); | ||||||
|  |  | ||||||
|                 // We already ensured that the upstream can handle the frame, so |                 // We already ensured that the upstream can handle the frame, so | ||||||
| @@ -373,15 +229,7 @@ impl<T, P, B> Sink for Connection<T, P, B> | |||||||
|                 Ok(AsyncSink::Ready) |                 Ok(AsyncSink::Ready) | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             Frame::Data { id, data, data_len, end_of_stream } => { |             Frame::Data { id, data, end_of_stream, .. } => { | ||||||
|                 try!(self.claim_remote_window(data_len)); |  | ||||||
|  |  | ||||||
|                 // The stream must be initialized at this point. |  | ||||||
|                 match self.streams.get_mut(&id) { |  | ||||||
|                     None => return Err(error::User::InactiveStreamId.into()), |  | ||||||
|                     Some(mut s) => try!(s.send_data(end_of_stream, data_len)), |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 let mut frame = frame::Data::from_buf(id, data.into_buf()); |                 let mut frame = frame::Data::from_buf(id, data.into_buf()); | ||||||
|                 if end_of_stream { |                 if end_of_stream { | ||||||
|                     frame.set_end_stream(); |                     frame.set_end_stream(); | ||||||
| @@ -412,13 +260,7 @@ impl<T, P, B> Sink for Connection<T, P, B> | |||||||
|  |  | ||||||
|     fn poll_complete(&mut self) -> Poll<(), ConnectionError> { |     fn poll_complete(&mut self) -> Poll<(), ConnectionError> { | ||||||
|         trace!("poll_complete"); |         trace!("poll_complete"); | ||||||
|  |         self.inner.poll_complete() | ||||||
|         try_ready!(self.inner.poll_complete()); |  | ||||||
|  |  | ||||||
|         // TODO check for settings updates and update the initial window size of all |  | ||||||
|         // streams. |  | ||||||
|  |  | ||||||
|         self.poll_sending_window_update() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -429,7 +271,6 @@ impl<T, P, B> ReadySink for Connection<T, P, B> | |||||||
| { | { | ||||||
|     fn poll_ready(&mut self) -> Poll<(), Self::SinkError> { |     fn poll_ready(&mut self) -> Poll<(), Self::SinkError> { | ||||||
|         trace!("poll_ready"); |         trace!("poll_ready"); | ||||||
|         try_ready!(self.inner.poll_ready()); |         self.inner.poll_ready() | ||||||
|         self.poll_sending_window_update() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| use ConnectionError; | use ConnectionError; | ||||||
|  | use error; | ||||||
| use frame::{self, Frame}; | use frame::{self, Frame}; | ||||||
| use proto::{ReadySink, StreamMap, ConnectionTransporter, StreamTransporter}; | use proto::*; | ||||||
|  |  | ||||||
| use futures::*; | use futures::*; | ||||||
|  |  | ||||||
| @@ -9,6 +10,19 @@ pub struct FlowControl<T>  { | |||||||
|     inner: T, |     inner: T, | ||||||
|     initial_local_window_size: u32, |     initial_local_window_size: u32, | ||||||
|     initial_remote_window_size: u32, |     initial_remote_window_size: u32, | ||||||
|  |  | ||||||
|  |     /// Tracks the connection-level flow control window for receiving data from the | ||||||
|  |     /// remote. | ||||||
|  |     local_flow_controller: FlowController, | ||||||
|  |  | ||||||
|  |     /// Tracks the onnection-level flow control window for receiving data from the remote. | ||||||
|  |     remote_flow_controller: FlowController, | ||||||
|  |  | ||||||
|  |     /// When `poll_window_update` is not ready, then the calling task is saved to be | ||||||
|  |     /// notified later. Access to poll_window_update must not be shared across tasks. | ||||||
|  |     blocked_window_update: Option<task::Task>, | ||||||
|  |  | ||||||
|  |     sending_window_update: Option<frame::WindowUpdate>, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T, U> FlowControl<T> | impl<T, U> FlowControl<T> | ||||||
| @@ -25,10 +39,71 @@ 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: FlowController::new(initial_local_window_size), | ||||||
|  |             remote_flow_controller: FlowController::new(initial_remote_window_size), | ||||||
|  |             blocked_window_update: None, | ||||||
|  |             sending_window_update: None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl<T> FlowControl<T> { | ||||||
|  |     #[inline] | ||||||
|  |     fn claim_local_window(&mut self, len: WindowSize) -> Result<(), ConnectionError> { | ||||||
|  |         self.local_flow_controller.claim_window(len) | ||||||
|  |             .map_err(|_| error::Reason::FlowControlError.into()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[inline] | ||||||
|  |     fn claim_remote_window(&mut self, len: WindowSize) -> Result<(), ConnectionError> { | ||||||
|  |         self.remote_flow_controller.claim_window(len) | ||||||
|  |             .map_err(|_| error::User::FlowControlViolation.into()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T: StreamTransporter> FlowControl<T> { | ||||||
|  |     /// 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; | ||||||
|  |         } | ||||||
|  |         let added = if id.is_zero() { | ||||||
|  |             self.remote_flow_controller.grow_window(incr); | ||||||
|  |             true | ||||||
|  |         } else if let Some(mut s) = self.streams_mut().get_mut(&id) { | ||||||
|  |             s.grow_send_window(incr); | ||||||
|  |             true | ||||||
|  |         } else { | ||||||
|  |             false | ||||||
|  |         }; | ||||||
|  |         if added { | ||||||
|  |             if let Some(task) = self.blocked_window_update.take() { | ||||||
|  |                 task.notify(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T, U> FlowControl<T> | ||||||
|  |     where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>, | ||||||
|  | { | ||||||
|  |     /// Attempts to send a window update to the remote, if one is pending. | ||||||
|  |     fn poll_sending_window_update(&mut self) -> Poll<(), ConnectionError> { | ||||||
|  |         if let Some(f) = self.sending_window_update.take() { | ||||||
|  |             if self.inner.start_send(f.into())?.is_not_ready() { | ||||||
|  |                 self.sending_window_update = Some(f); | ||||||
|  |                 return Ok(Async::NotReady); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(Async::Ready(())) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Applies an update to an endpoint's initial window size. | /// Applies an update to an endpoint's initial window size. | ||||||
| /// | /// | ||||||
| /// Per RFC 7540 §6.9.2 | /// Per RFC 7540 §6.9.2 | ||||||
| @@ -116,7 +191,23 @@ impl<T> Stream for FlowControl<T> | |||||||
|     type Error = T::Error; |     type Error = T::Error; | ||||||
|  |  | ||||||
|     fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> { |     fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> { | ||||||
|         self.inner.poll() |         use frame::Frame::*; | ||||||
|  |         trace!("poll"); | ||||||
|  |  | ||||||
|  |         loop { | ||||||
|  |             match try_ready!(self.inner.poll()) { | ||||||
|  |                 Some(WindowUpdate(v)) => { | ||||||
|  |                     self.grow_remote_window(v.stream_id(), v.size_increment()); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 Some(Data(v)) => { | ||||||
|  |                     self.claim_local_window(v.len())?; | ||||||
|  |                     return Ok(Async::Ready(Some(Data(v)))); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 v => return Ok(Async::Ready(v)), | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -129,6 +220,12 @@ impl<T, U> Sink for FlowControl<T> | |||||||
|     type SinkError = T::SinkError; |     type SinkError = T::SinkError; | ||||||
|  |  | ||||||
|     fn start_send(&mut self, item: Frame<U>) -> StartSend<T::SinkItem, T::SinkError> { |     fn start_send(&mut self, item: Frame<U>) -> StartSend<T::SinkItem, T::SinkError> { | ||||||
|  |         use frame::Frame::*; | ||||||
|  |  | ||||||
|  |         if let &Data(ref v) = &item { | ||||||
|  |             self.claim_remote_window(v.len())?; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         self.inner.start_send(item) |         self.inner.start_send(item) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ use tokio_io::codec::length_delimited; | |||||||
|  |  | ||||||
| use bytes::{Buf, IntoBuf}; | use bytes::{Buf, IntoBuf}; | ||||||
|  |  | ||||||
| use ordermap::OrderMap; | use ordermap::{Entry, OrderMap}; | ||||||
| use fnv::FnvHasher; | use fnv::FnvHasher; | ||||||
| use std::hash::BuildHasherDefault; | use std::hash::BuildHasherDefault; | ||||||
|  |  | ||||||
| @@ -45,13 +45,14 @@ use std::hash::BuildHasherDefault; | |||||||
| /// | /// | ||||||
| /// All transporters below Settings must apply relevant settings before passing a frame on | /// All transporters below Settings must apply relevant settings before passing a frame on | ||||||
| /// to another level.  For example, if the frame writer n | /// to another level.  For example, if the frame writer n | ||||||
| type Transport<T, B> = | type Transport<T, P, B> = | ||||||
|     Settings< |     Settings< | ||||||
|         FlowControl< |         FlowControl< | ||||||
|             StreamTracker< |             StreamTracker< | ||||||
|                 PingPong< |                 PingPong< | ||||||
|                     Framer<T, B>, |                     Framer<T, B>, | ||||||
|                     B>>>>; |                     B>, | ||||||
|  |                 P>>>; | ||||||
|  |  | ||||||
| type Framer<T, B> = | type Framer<T, B> = | ||||||
|     FramedRead< |     FramedRead< | ||||||
| @@ -65,6 +66,14 @@ pub struct StreamMap { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl StreamMap { | impl StreamMap { | ||||||
|  |     fn get_mut(&mut self, id: &StreamId) -> Option<&mut StreamState> { | ||||||
|  |         self.inner.get_mut(id) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn entry(&mut self, id: StreamId) -> Entry<StreamId, StreamState, BuildHasherDefault<FnvHasher>> { | ||||||
|  |         self.inner.entry(id) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     fn shrink_local_window(&mut self, decr: u32) { |     fn shrink_local_window(&mut self, decr: u32) { | ||||||
|         for (_, mut s) in &mut self.inner { |         for (_, mut s) in &mut self.inner { | ||||||
|             s.shrink_recv_window(decr) |             s.shrink_recv_window(decr) | ||||||
| @@ -159,10 +168,17 @@ pub fn from_server_handshaker<T, P, B>(settings: Settings<FramedWrite<T, B::Buf> | |||||||
|             .num_skip(0) // Don't skip the header |             .num_skip(0) // Don't skip the header | ||||||
|             .new_read(io); |             .new_read(io); | ||||||
|  |  | ||||||
|         FlowControl::new(initial_local_window_size, initial_remote_window_size, |         FlowControl::new( | ||||||
|             StreamTracker::new(local_max_concurrency, remote_max_concurrency, |             initial_local_window_size, | ||||||
|                 PingPong::new( |             initial_remote_window_size, | ||||||
|                     FramedRead::new(framer)))) |             StreamTracker::new( | ||||||
|  |                 initial_local_window_size, | ||||||
|  |                 initial_remote_window_size, | ||||||
|  |                 local_max_concurrency, | ||||||
|  |                 remote_max_concurrency, | ||||||
|  |                 PingPong::new(FramedRead::new(framer)) | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     connection::new(transport) |     connection::new(transport) | ||||||
|   | |||||||
| @@ -1,36 +1,48 @@ | |||||||
| use ConnectionError; | use ConnectionError; | ||||||
|  | use error::User::*; | ||||||
| use frame::{self, Frame}; | use frame::{self, Frame}; | ||||||
| use proto::{ReadySink, StreamMap, ConnectionTransporter, StreamTransporter}; | use proto::*; | ||||||
|  |  | ||||||
| use futures::*; | use futures::*; | ||||||
|  |  | ||||||
|  | use std::marker::PhantomData; | ||||||
|  |  | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct StreamTracker<T> { | pub struct StreamTracker<T, P> { | ||||||
|     inner: T, |     inner: T, | ||||||
|  |     peer: PhantomData<P>, | ||||||
|     streams: StreamMap, |     streams: StreamMap, | ||||||
|     local_max_concurrency: Option<u32>, |     local_max_concurrency: Option<u32>, | ||||||
|     remote_max_concurrency: Option<u32>, |     remote_max_concurrency: Option<u32>, | ||||||
|  |     initial_local_window_size: WindowSize, | ||||||
|  |     initial_remote_window_size: WindowSize, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T, U> StreamTracker<T> | impl<T, P, U> StreamTracker<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 | ||||||
| { | { | ||||||
|     pub fn new(local_max_concurrency: Option<u32>, |     pub fn new(initial_local_window_size: WindowSize, | ||||||
|  |                initial_remote_window_size: WindowSize, | ||||||
|  |                local_max_concurrency: Option<u32>, | ||||||
|                remote_max_concurrency: Option<u32>, |                remote_max_concurrency: Option<u32>, | ||||||
|                inner: T) |                inner: T) | ||||||
|         -> StreamTracker<T> |         -> StreamTracker<T, P> | ||||||
|     { |     { | ||||||
|         StreamTracker { |         StreamTracker { | ||||||
|             inner, |             inner, | ||||||
|  |             peer: PhantomData, | ||||||
|             streams: StreamMap::default(), |             streams: StreamMap::default(), | ||||||
|             local_max_concurrency, |             local_max_concurrency, | ||||||
|             remote_max_concurrency, |             remote_max_concurrency, | ||||||
|  |             initial_local_window_size, | ||||||
|  |             initial_remote_window_size, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T> StreamTransporter for StreamTracker<T> { | impl<T, P> StreamTransporter for StreamTracker<T, P> { | ||||||
|     fn streams(&self) -> &StreamMap { |     fn streams(&self) -> &StreamMap { | ||||||
|         &self.streams |         &self.streams | ||||||
|     } |     } | ||||||
| @@ -58,38 +70,101 @@ impl<T> StreamTransporter for StreamTracker<T> { | |||||||
| /// > 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: ConnectionTransporter> ConnectionTransporter for StreamTracker<T> { | impl<T, P> ConnectionTransporter for StreamTracker<T, P> | ||||||
|  |     where T: ConnectionTransporter | ||||||
|  | { | ||||||
|     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.local_max_concurrency = set.max_concurrent_streams(); | ||||||
|  |         self.initial_local_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.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, U> Stream for StreamTracker<T> | impl<T, P> Stream for StreamTracker<T, P> | ||||||
|     where T: Stream<Item = Frame<U>, Error = ConnectionError>, |     where T: Stream<Item = Frame, Error = ConnectionError>, | ||||||
|  |           P: Peer, | ||||||
| { | { | ||||||
|     type Item = T::Item; |     type Item = T::Item; | ||||||
|     type Error = T::Error; |     type Error = T::Error; | ||||||
|  |  | ||||||
|     fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> { |     fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> { | ||||||
|         self.inner.poll() |         use frame::Frame::*; | ||||||
|  |  | ||||||
|  |         match try_ready!(self.inner.poll()) { | ||||||
|  |             Some(Headers(v)) => { | ||||||
|  |                 let id = v.stream_id(); | ||||||
|  |                 let eos = v.is_end_stream(); | ||||||
|  |  | ||||||
|  |                 let initialized = self.streams | ||||||
|  |                     .entry(id) | ||||||
|  |                     .or_insert_with(|| StreamState::default()) | ||||||
|  |                     .recv_headers::<P>(eos, self.initial_local_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_remote_stream_id(id) { | ||||||
|  |                         unimplemented!(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 Ok(Async::Ready(Some(Headers(v)))) | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             f => Ok(Async::Ready(f)) | ||||||
|  |  | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| impl<T, U> Sink for StreamTracker<T> | impl<T, P, U> Sink for StreamTracker<T, P> | ||||||
|     where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>, |     where T: Sink<SinkItem = Frame<U>, SinkError = ConnectionError>, | ||||||
|  |           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, item: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> { | ||||||
|  |         use frame::Frame::*; | ||||||
|  |  | ||||||
|  |         if let &Headers(ref v) = &item { | ||||||
|  |             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 | ||||||
|  |             let initialized = self.streams | ||||||
|  |                 .entry(id) | ||||||
|  |                 .or_insert_with(|| StreamState::default()) | ||||||
|  |                 .send_headers::<P>(eos, self.initial_remote_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(InvalidStreamId.into()); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         self.inner.start_send(item) |         self.inner.start_send(item) | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn poll_complete(&mut self) -> Poll<(), T::SinkError> { |     fn poll_complete(&mut self) -> Poll<(), T::SinkError> { | ||||||
| @@ -98,10 +173,11 @@ impl<T, U> Sink for StreamTracker<T> | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| impl<T, U> ReadySink for StreamTracker<T> | impl<T, P, U> ReadySink for StreamTracker<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, | ||||||
| { | { | ||||||
|     fn poll_ready(&mut self) -> Poll<(), ConnectionError> { |     fn poll_ready(&mut self) -> Poll<(), ConnectionError> { | ||||||
|         self.inner.poll_ready() |         self.inner.poll_ready() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user