When Streams are dropped, close Connection (#221) (#222)

When all Streams are dropped / finished, the Connection was held
open until the peer hangs up. Instead, the Connection should hang up
once it knows that nothing more will be sent.

To fix this, we notify the Connection when a stream is no longer
referenced. On the Connection poll(), we check that there are no
active, held, reset streams or any references to the Streams
and transition to sending a GOAWAY if that is case.

The specific behavior depends on if running as a client or server.
This commit is contained in:
Darren Tsung
2018-02-15 13:14:18 -08:00
committed by Carl Lerche
parent 73b4c03b55
commit 0c59957d88
14 changed files with 193 additions and 45 deletions

View File

@@ -122,6 +122,27 @@ where
Ok(().into())
}
fn transition_to_go_away(&mut self, id: StreamId, e: Reason) {
let goaway = frame::GoAway::new(id, e);
self.state = State::GoAway(goaway);
}
/// Closes the connection by transitioning to a GOAWAY state
/// iff there are no streams or references
pub fn maybe_close_connection_if_no_streams(&mut self) {
// If we poll() and realize that there are no streams or references
// then we can close the connection by transitioning to GOAWAY
if self.streams.num_active_streams() == 0 && !self.streams.has_streams_or_other_references() {
self.close_connection();
}
}
/// Closes the connection by transitioning to a GOAWAY state
pub fn close_connection(&mut self) {
let last_processed_id = self.streams.last_processed_id();
self.transition_to_go_away(last_processed_id, Reason::NO_ERROR);
}
/// Advances the internal state of the connection.
pub fn poll(&mut self) -> Poll<(), proto::Error> {
use codec::RecvError::*;
@@ -143,9 +164,8 @@ where
if self.error.is_some() {
if self.streams.num_active_streams() == 0 {
let id = self.streams.last_processed_id();
let goaway = frame::GoAway::new(id, Reason::NO_ERROR);
self.state = State::GoAway(goaway);
let last_processed_id = self.streams.last_processed_id();
self.transition_to_go_away(last_processed_id, Reason::NO_ERROR);
continue;
}
}
@@ -160,12 +180,7 @@ where
// Reset all active streams
let last_processed_id = self.streams.recv_err(&e.into());
// Create the GO_AWAY frame with the last_processed_id
let frame = frame::GoAway::new(last_processed_id, e);
// Transition to the going away state.
self.state = State::GoAway(frame);
self.transition_to_go_away(last_processed_id, e);
},
// Attempting to read a frame resulted in a stream level error.
// This is handled by resetting the frame then trying to read