test flow control state
This commit is contained in:
		| @@ -65,12 +65,16 @@ impl<T> Data<T> { | ||||
| } | ||||
|  | ||||
| impl<T: Buf> Data<T> { | ||||
|     pub fn from_buf(stream_id: StreamId, data: T) -> Self { | ||||
|     pub fn from_buf(stream_id: StreamId, data: T, eos: bool) -> Self { | ||||
|         let mut flags = DataFlag::default(); | ||||
|         if eos { | ||||
|             flags.set_end_stream(); | ||||
|         } | ||||
|         Data { | ||||
|             stream_id, | ||||
|             data_len: data.remaining() as FrameSize, | ||||
|             data, | ||||
|             flags: DataFlag::default(), | ||||
|             flags, | ||||
|             pad_len: None, | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -148,29 +148,18 @@ impl<T, P, B> Stream for Connection<T, P, B> | ||||
|  | ||||
|             trace!("poll; frame={:?}", frame); | ||||
|             let frame = match frame { | ||||
|                 Some(Headers(v)) => { | ||||
|                     // TODO: Update stream state | ||||
|                     let stream_id = v.stream_id(); | ||||
|                     let end_of_stream = v.is_end_stream(); | ||||
|                 Some(Headers(v)) => Frame::Headers { | ||||
|                     id: v.stream_id(), | ||||
|                     end_of_stream: v.is_end_stream(), | ||||
|                     headers: P::convert_poll_message(v), | ||||
|                 }, | ||||
|  | ||||
|                     Frame::Headers { | ||||
|                         id: stream_id, | ||||
|                         headers: P::convert_poll_message(v), | ||||
|                         end_of_stream: end_of_stream, | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 Some(Data(v)) => { | ||||
|                     let id = v.stream_id(); | ||||
|                     let end_of_stream = v.is_end_stream(); | ||||
|  | ||||
|                     Frame::Data { | ||||
|                         id, | ||||
|                         end_of_stream, | ||||
|                         data_len: v.len(), | ||||
|                         data: v.into_payload(), | ||||
|                     } | ||||
|                 } | ||||
|                 Some(Data(v)) => Frame::Data { | ||||
|                     id: v.stream_id(), | ||||
|                     end_of_stream: v.is_end_stream(), | ||||
|                     data_len: v.len(), | ||||
|                     data: v.into_payload(), | ||||
|                 }, | ||||
|  | ||||
|                 Some(frame) => panic!("unexpected frame; frame={:?}", frame), | ||||
|                 None => return Ok(Async::Ready(None)), | ||||
| @@ -193,12 +182,11 @@ impl<T, P, B> Sink for Connection<T, P, B> | ||||
|     fn start_send(&mut self, item: Self::SinkItem) | ||||
|         -> StartSend<Self::SinkItem, Self::SinkError> | ||||
|     { | ||||
|         use frame::Frame::Headers; | ||||
|         trace!("start_send"); | ||||
|  | ||||
|         // Ensure that a pending window update is sent before doing anything further and | ||||
|         // ensure that the inner sink will actually receive a frame. | ||||
|         if self.poll_ready()? == Async::NotReady { | ||||
|         // Ensure the transport is ready to send a frame before we transform the external | ||||
|         // `Frame` into an internal `frame::Framme`. | ||||
|         if self.inner.poll_ready()? == Async::NotReady { | ||||
|             return Ok(AsyncSink::NotReady(item)); | ||||
|         } | ||||
|  | ||||
| @@ -208,17 +196,13 @@ impl<T, P, B> Sink for Connection<T, P, B> | ||||
|                 // it's already been determined that the inner `Sink` can accept the item. | ||||
|                 // If the item is rejected, then there is a bug. | ||||
|                 let frame = P::convert_send_message(id, headers, end_of_stream); | ||||
|                 let res = self.inner.start_send(Headers(frame))?; | ||||
|                 let res = self.inner.start_send(frame::Frame::Headers(frame))?; | ||||
|                 assert!(res.is_ready()); | ||||
|                 Ok(AsyncSink::Ready) | ||||
|             } | ||||
|  | ||||
|             Frame::Data { id, data, end_of_stream, .. } => { | ||||
|                 let mut frame = frame::Data::from_buf(id, data.into_buf()); | ||||
|                 if end_of_stream { | ||||
|                     frame.set_end_stream(); | ||||
|                 } | ||||
|  | ||||
|                 let frame = frame::Data::from_buf(id, data.into_buf(), end_of_stream); | ||||
|                 let res = try!(self.inner.start_send(frame.into())); | ||||
|                 assert!(res.is_ready()); | ||||
|                 Ok(AsyncSink::Ready) | ||||
|   | ||||
| @@ -45,8 +45,8 @@ impl<T, U> FlowControl<T> | ||||
|             inner, | ||||
|             initial_local_window_size, | ||||
|             initial_remote_window_size, | ||||
|             local_flow_controller: FlowControlState::new(initial_local_window_size), | ||||
|             remote_flow_controller: FlowControlState::new(initial_remote_window_size), | ||||
|             local_flow_controller: FlowControlState::with_initial_size(initial_local_window_size), | ||||
|             remote_flow_controller: FlowControlState::with_next_update(initial_remote_window_size), | ||||
|             blocked_remote_window_update: None, | ||||
|             sending_local_window_update: None, | ||||
|             pending_local_window_updates: VecDeque::new(), | ||||
| @@ -54,6 +54,7 @@ impl<T, U> FlowControl<T> | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Flow control utitlities. | ||||
| impl<T: ControlStreams> FlowControl<T> { | ||||
|     fn claim_local_window(&mut self, id: &StreamId, len: WindowSize) -> Result<(), ConnectionError> { | ||||
|         let res = if id.is_zero() { | ||||
| @@ -106,6 +107,7 @@ impl<T: ControlStreams> FlowControl<T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Exposes a public upward API for flow control. | ||||
| impl<T: ControlStreams> ControlFlow for FlowControl<T> { | ||||
|     fn poll_remote_window_update(&mut self, id: StreamId) -> Poll<WindowSize, ConnectionError> { | ||||
|         if id.is_zero() { | ||||
| @@ -139,6 +141,7 @@ impl<T: ControlStreams> ControlFlow for FlowControl<T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Proxies access to streams. | ||||
| impl<T: ControlStreams> ControlStreams for FlowControl<T> { | ||||
|     #[inline] | ||||
|     fn streams(&self) -> &StreamMap { | ||||
| @@ -183,7 +186,7 @@ impl<T, U> FlowControl<T> | ||||
|  | ||||
| /// Applies an update to an endpoint's initial window size. | ||||
| /// | ||||
| /// Per RFC 7540 §6.9.2 | ||||
| /// Per RFC 7540 §6.9.2: | ||||
| /// | ||||
| /// > In addition to changing the flow-control window for streams that are not yet | ||||
| /// > active, a SETTINGS frame can alter the initial flow-control window size for | ||||
|   | ||||
| @@ -18,12 +18,12 @@ pub struct FlowControlState { | ||||
|  | ||||
| impl Default for FlowControlState { | ||||
|     fn default() -> Self { | ||||
|         Self::new(DEFAULT_INITIAL_WINDOW_SIZE) | ||||
|         Self::with_initial_size(DEFAULT_INITIAL_WINDOW_SIZE) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl FlowControlState { | ||||
|     pub fn new(window_size: WindowSize) -> FlowControlState { | ||||
|     pub fn with_initial_size(window_size: WindowSize) -> FlowControlState { | ||||
|         FlowControlState { | ||||
|             window_size, | ||||
|             underflow: 0, | ||||
| @@ -31,11 +31,24 @@ impl FlowControlState { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn with_next_update(next_window_update: WindowSize) -> FlowControlState { | ||||
|         FlowControlState { | ||||
|             window_size: 0, | ||||
|             underflow: 0, | ||||
|             next_window_update, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// 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; | ||||
|         if decr < self.next_window_update { | ||||
|             self.next_window_update -= decr | ||||
|         } else { | ||||
|             self.underflow += decr - self.next_window_update; | ||||
|             self.next_window_update = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Claims the provided amount from the window, if there is enough space. | ||||
| @@ -51,7 +64,7 @@ impl FlowControlState { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Applies a window increment immediately. | ||||
|     /// Increase the _unadvertised_ window capacity. | ||||
|     pub fn grow_window(&mut self, sz: WindowSize) { | ||||
|         if sz <= self.underflow { | ||||
|             self.underflow -= sz; | ||||
| @@ -59,12 +72,11 @@ impl FlowControlState { | ||||
|         } | ||||
|  | ||||
|         let added = sz - self.underflow; | ||||
|         self.window_size += added; | ||||
|         self.next_window_update += added; | ||||
|         self.underflow = 0; | ||||
|     } | ||||
|  | ||||
|     /// Obtains and clears an unadvertised window update. | ||||
|     /// Obtains and applies an unadvertised window update. | ||||
|     pub fn take_window_update(&mut self) -> Option<WindowSize> { | ||||
|         if self.next_window_update == 0 { | ||||
|             return None; | ||||
| @@ -72,12 +84,93 @@ impl FlowControlState { | ||||
|  | ||||
|         let incr = self.next_window_update; | ||||
|         self.next_window_update = 0; | ||||
|         self.window_size += incr; | ||||
|         Some(incr) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test() { | ||||
|     let mut fc = FlowControlState::new(65_535); | ||||
| fn test_with_initial_size() { | ||||
|     let mut fc = FlowControlState::with_initial_size(10); | ||||
|  | ||||
|     fc.grow_window(8); | ||||
|     assert_eq!(fc.window_size, 10); | ||||
|     assert_eq!(fc.next_window_update, 8); | ||||
|  | ||||
|     assert_eq!(fc.take_window_update(), Some(8)); | ||||
|     assert_eq!(fc.window_size, 18); | ||||
|     assert_eq!(fc.next_window_update, 0); | ||||
|  | ||||
|     assert!(fc.claim_window(13).is_ok()); | ||||
|     assert_eq!(fc.window_size, 5); | ||||
|     assert_eq!(fc.next_window_update, 0); | ||||
|     assert!(fc.take_window_update().is_none()); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_with_next_update() { | ||||
|     let mut fc = FlowControlState::with_next_update(10); | ||||
|  | ||||
|     fc.grow_window(8); | ||||
|     assert_eq!(fc.window_size, 0); | ||||
|     assert_eq!(fc.next_window_update, 18); | ||||
|  | ||||
|     assert_eq!(fc.take_window_update(), Some(18)); | ||||
|     assert_eq!(fc.window_size, 18); | ||||
|     assert_eq!(fc.next_window_update, 0); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_grow_accumulates() { | ||||
|     let mut fc = FlowControlState::with_initial_size(5); | ||||
|  | ||||
|     // Updates accumulate, though the window is not made immediately available.  Trying to | ||||
|     // claim data not returned by take_window_update results in an underflow. | ||||
|  | ||||
|     fc.grow_window(2); | ||||
|     assert_eq!(fc.window_size, 5); | ||||
|     assert_eq!(fc.next_window_update, 2); | ||||
|  | ||||
|     fc.grow_window(6); | ||||
|     assert_eq!(fc.window_size, 5); | ||||
|     assert_eq!(fc.next_window_update, 8); | ||||
|  | ||||
|     assert!(fc.claim_window(13).is_err()); | ||||
|     assert_eq!(fc.window_size, 5); | ||||
|     assert_eq!(fc.next_window_update, 8); | ||||
|  | ||||
|     assert_eq!(fc.take_window_update(), Some(8)); | ||||
|     assert_eq!(fc.window_size, 13); | ||||
|     assert_eq!(fc.next_window_update, 0); | ||||
|  | ||||
|     assert!(fc.claim_window(13).is_ok()); | ||||
|     assert_eq!(fc.window_size, 0); | ||||
|     assert_eq!(fc.next_window_update, 0); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_shrink() { | ||||
|     let mut fc = FlowControlState::with_initial_size(5); | ||||
|     assert_eq!(fc.window_size, 5); | ||||
|     assert_eq!(fc.next_window_update, 0); | ||||
|  | ||||
|     fc.grow_window(3); | ||||
|     assert_eq!(fc.window_size, 5); | ||||
|     assert_eq!(fc.next_window_update, 3); | ||||
|     assert_eq!(fc.underflow, 0); | ||||
|  | ||||
|     fc.shrink_window(8); | ||||
|     assert_eq!(fc.window_size, 5); | ||||
|     assert_eq!(fc.next_window_update, 0); | ||||
|     assert_eq!(fc.underflow, 5); | ||||
|  | ||||
|     assert!(fc.claim_window(5).is_ok()); | ||||
|     assert_eq!(fc.window_size, 0); | ||||
|     assert_eq!(fc.next_window_update, 0); | ||||
|     assert_eq!(fc.underflow, 5); | ||||
|  | ||||
|     fc.grow_window(8); | ||||
|     assert_eq!(fc.window_size, 0); | ||||
|     assert_eq!(fc.next_window_update, 3); | ||||
|     assert_eq!(fc.underflow, 0); | ||||
| } | ||||
|   | ||||
| @@ -44,7 +44,7 @@ use self::state::StreamState; | ||||
| /// | ||||
| /// All transporters below Settings must apply relevant settings before passing a frame on | ||||
| /// to another level.  For example, if the frame writer n | ||||
| type Transport<T, P, B> = | ||||
| type Transport<T, P, B>= | ||||
|     Settings< | ||||
|         FlowControl< | ||||
|             StreamTracker< | ||||
| @@ -98,20 +98,23 @@ impl StreamMap { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Allows settings updates to be pushed "down" the transport (i.e. below Settings). | ||||
| /// Allows settings updates to be pushed "down" the transport (i.e. from Settings down to | ||||
| /// FramedWrite). | ||||
| pub trait ApplySettings { | ||||
|     fn apply_local_settings(&mut self, set: &frame::SettingSet) -> Result<(), ConnectionError>; | ||||
|     fn apply_remote_settings(&mut self, set: &frame::SettingSet) -> Result<(), ConnectionError>; | ||||
| } | ||||
|  | ||||
| /// Exposes settings to "upper" layers of the transport (i.e. above Settings). | ||||
| /// Exposes settings to "upper" layers of the transport (i.e. from Settings up to---and | ||||
| /// above---Connection). | ||||
| pub trait ControlSettings { | ||||
|     fn update_local_settings(&mut self, set: frame::SettingSet) -> Result<(), ConnectionError>; | ||||
|     fn local_settings(&self) -> &SettingSet; | ||||
|     fn remote_settings(&self) -> &SettingSet; | ||||
| } | ||||
|  | ||||
| /// Exposes stream states to "upper" layers of the transport (i.e. above StreamTracker). | ||||
| /// Exposes stream states to "upper" layers of the transport (i.e. from StreamTracker up | ||||
| /// to Connection). | ||||
| pub trait ControlStreams { | ||||
|     fn streams(&self)-> &StreamMap; | ||||
|     fn streams_mut(&mut self) -> &mut StreamMap; | ||||
|   | ||||
| @@ -78,7 +78,7 @@ impl StreamState { | ||||
|                 if eos { | ||||
|                     *self = HalfClosedRemote(local); | ||||
|                 } else { | ||||
|                     *self = Open { local, remote: Data(FlowControlState::new(initial_recv_window_size)) }; | ||||
|                     *self = Open { local, remote: Data(FlowControlState::with_initial_size(initial_recv_window_size)) }; | ||||
|                 } | ||||
|                 Ok(true) | ||||
|             } | ||||
| @@ -98,7 +98,7 @@ impl StreamState { | ||||
|                 if eos { | ||||
|                     *self = Closed; | ||||
|                 } else { | ||||
|                     *self = HalfClosedLocal(Data(FlowControlState::new(initial_recv_window_size))); | ||||
|                     *self = HalfClosedLocal(Data(FlowControlState::with_initial_size(initial_recv_window_size))); | ||||
|                 }; | ||||
|                 Ok(false) | ||||
|             } | ||||
| @@ -155,7 +155,7 @@ impl StreamState { | ||||
|                     HalfClosedLocal(Headers) | ||||
|                 } else { | ||||
|                     Open { | ||||
|                         local: Data(FlowControlState::new(initial_window_size)), | ||||
|                         local: Data(FlowControlState::with_initial_size(initial_window_size)), | ||||
|                         remote: Headers, | ||||
|                     } | ||||
|                 }; | ||||
| @@ -169,7 +169,8 @@ impl StreamState { | ||||
|                 *self = if eos { | ||||
|                     HalfClosedLocal(remote) | ||||
|                 } else { | ||||
|                     let local = Data(FlowControlState::new(initial_window_size)); | ||||
|                     let fc = FlowControlState::with_initial_size(initial_window_size); | ||||
|                     let local = Data(fc); | ||||
|                     Open { local, remote } | ||||
|                 }; | ||||
|  | ||||
| @@ -182,7 +183,8 @@ impl StreamState { | ||||
|                 *self = if eos { | ||||
|                     Closed | ||||
|                 } else { | ||||
|                     HalfClosedRemote(Data(FlowControlState::new(initial_window_size))) | ||||
|                     let fc = FlowControlState::with_initial_size(initial_window_size); | ||||
|                     HalfClosedRemote(Data(fc)) | ||||
|                 }; | ||||
|  | ||||
|                 Ok(false) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user