Skip to content

Commit 3d558a6

Browse files
noxseanmonstar
authored andcommitted
Ignore Error::GoAway in State::is_remote_reset
When Streams::recv_go_away is called, Recv::handle_error is called on every stream whose stream id is past the GO_AWAY's last stream id, and those streams may have been pending-accepting. If the stream had not encountered an error before, Recv::handle_error then sets its state to State::Closed(Error::GoAway(_, _, Initiator::Remote)) which makes State::is_remote_reset return true in Streams::next_incoming, which leads to Counts::dec_remote_reset_streams being called even though Counts::inc_remote_reset_streams was never called for that stream, causing a panic about the counter being 0.
1 parent 7a77f93 commit 3d558a6

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

src/proto/streams/state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ impl State {
362362

363363
pub fn is_remote_reset(&self) -> bool {
364364
match self.inner {
365-
Closed(Cause::Error(ref e)) => !e.is_local(),
365+
Closed(Cause::Error(Error::Reset(_, _, Initiator::Remote))) => true,
366366
_ => false,
367367
}
368368
}

tests/h2-tests/tests/stream_states.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,53 @@ async fn reset_streams_dont_grow_memory_continuously() {
240240
join(srv, client).await;
241241
}
242242

243+
#[tokio::test]
244+
async fn go_away_with_pending_accepting() {
245+
// h2_support::trace_init!();
246+
let (io, mut client) = mock::new();
247+
248+
let (sent_go_away_tx, sent_go_away_rx) = oneshot::channel();
249+
let (recv_go_away_tx, recv_go_away_rx) = oneshot::channel();
250+
251+
let client = async move {
252+
let settings = client.assert_server_handshake().await;
253+
assert_default_settings!(settings);
254+
255+
client
256+
.send_frame(frames::headers(1).request("GET", "https://baguette/").eos())
257+
.await;
258+
259+
client
260+
.send_frame(frames::headers(3).request("GET", "https://campagne/").eos())
261+
.await;
262+
client.send_frame(frames::go_away(1).protocol_error()).await;
263+
264+
sent_go_away_tx.send(()).unwrap();
265+
266+
recv_go_away_rx.await.unwrap();
267+
};
268+
269+
let srv = async move {
270+
let mut srv = server::Builder::new()
271+
.max_pending_accept_reset_streams(1)
272+
.handshake::<_, Bytes>(io)
273+
.await
274+
.expect("handshake");
275+
276+
let (_req_1, _send_response_1) = srv.accept().await.unwrap().unwrap();
277+
278+
poll_fn(|cx| srv.poll_closed(cx))
279+
.drive(sent_go_away_rx)
280+
.await
281+
.unwrap();
282+
283+
let (_req_2, _send_response_2) = srv.accept().await.unwrap().unwrap();
284+
285+
recv_go_away_tx.send(()).unwrap();
286+
};
287+
join(srv, client).await;
288+
}
289+
243290
#[tokio::test]
244291
async fn pending_accept_reset_streams_decrement_too() {
245292
h2_support::trace_init!();

0 commit comments

Comments
 (0)