Send RST_STREAM of STREAM_CLOSED instead of GOAWAY if stream may have been forgotten

This commit is contained in:
Sean McArthur
2019-06-27 11:01:32 -07:00
parent 19936721b8
commit ab52cf9b30
6 changed files with 173 additions and 110 deletions

View File

@@ -140,17 +140,39 @@ where
let key = match me.store.find_entry(id) {
Entry::Occupied(e) => e.key(),
Entry::Vacant(e) => match me.actions.recv.open(id, Open::Headers, &mut me.counts)? {
Some(stream_id) => {
let stream = Stream::new(
stream_id,
me.actions.send.init_window_sz(),
me.actions.recv.init_window_sz(),
);
Entry::Vacant(e) => {
// Client: it's possible to send a request, and then send
// a RST_STREAM while the response HEADERS were in transit.
//
// Server: we can't reset a stream before having received
// the request headers, so don't allow.
if !P::is_server() {
// This may be response headers for a stream we've already
// forgotten about...
if me.actions.may_have_forgotten_stream::<P>(id) {
debug!(
"recv_headers for old stream={:?}, sending STREAM_CLOSED",
id,
);
return Err(RecvError::Stream {
id,
reason: Reason::STREAM_CLOSED,
});
}
}
e.insert(stream)
},
None => return Ok(()),
match me.actions.recv.open(id, Open::Headers, &mut me.counts)? {
Some(stream_id) => {
let stream = Stream::new(
stream_id,
me.actions.send.init_window_sz(),
me.actions.recv.init_window_sz(),
);
e.insert(stream)
},
None => return Ok(()),
}
},
};
@@ -236,6 +258,25 @@ where
return Ok(());
}
if me.actions.may_have_forgotten_stream::<P>(id) {
debug!(
"recv_data for old stream={:?}, sending STREAM_CLOSED",
id,
);
let sz = frame.payload().len();
// This should have been enforced at the codec::FramedRead layer, so
// this is just a sanity check.
assert!(sz <= super::MAX_WINDOW_SIZE as usize);
let sz = sz as WindowSize;
me.actions.recv.ignore_data(sz)?;
return Err(RecvError::Stream {
id,
reason: Reason::STREAM_CLOSED,
});
}
proto_err!(conn: "recv_data: stream not found; id={:?}", id);
return Err(RecvError::Connection(Reason::PROTOCOL_ERROR));
},
@@ -674,13 +715,10 @@ where
let key = match me.store.find_entry(id) {
Entry::Occupied(e) => e.key(),
Entry::Vacant(e) => match me.actions.recv.open(id, Open::Headers, &mut me.counts) {
Ok(Some(stream_id)) => {
let stream = Stream::new(stream_id, 0, 0);
Entry::Vacant(e) => {
let stream = Stream::new(id, 0, 0);
e.insert(stream)
},
_ => return,
e.insert(stream)
},
};
@@ -1250,6 +1288,26 @@ impl Actions {
}
}
/// Check if we possibly could have processed and since forgotten this stream.
///
/// If we send a RST_STREAM for a stream, we will eventually "forget" about
/// the stream to free up memory. It's possible that the remote peer had
/// frames in-flight, and by the time we receive them, our own state is
/// gone. We *could* tear everything down by sending a GOAWAY, but it
/// is more likely to be latency/memory constraints that caused this,
/// and not a bad actor. So be less catastrophic, the spec allows
/// us to send another RST_STREAM of STREAM_CLOSED.
fn may_have_forgotten_stream<P: Peer>(&self, id: StreamId) -> bool {
if id.is_zero() {
return false;
}
if P::is_local_init(id) {
self.send.may_have_created_stream(id)
} else {
self.recv.may_have_created_stream(id)
}
}
fn clear_queues(&mut self,
clear_pending_accept: bool,
store: &mut Store,