diff --git a/quic/api/QuicPacketScheduler.cpp b/quic/api/QuicPacketScheduler.cpp index f7756a5d2..039e5ddb7 100644 --- a/quic/api/QuicPacketScheduler.cpp +++ b/quic/api/QuicPacketScheduler.cpp @@ -648,6 +648,8 @@ bool RstStreamScheduler::writeRsts(PacketBuilderInterface& builder) { for (const auto& resetStream : conn_.pendingEvents.resets) { auto streamId = resetStream.first; QuicStreamState* streamState = conn_.streamManager->getStream(streamId); + CHECK(streamState) << "Stream " << streamId + << " not found when going through resets"; if (streamState->pendingWrites.empty() && streamState->writeBufMeta.length == 0) { // We only write a RESET_STREAM or RESET_STREAM_AT frame for a stream diff --git a/quic/state/QuicStreamManager.cpp b/quic/state/QuicStreamManager.cpp index cae4fb10d..ea25a974d 100644 --- a/quic/state/QuicStreamManager.cpp +++ b/quic/state/QuicStreamManager.cpp @@ -555,6 +555,11 @@ void QuicStreamManager::removeClosedStream(StreamId streamId) { } VLOG(10) << "Removing closed stream=" << streamId; DCHECK(it->second.inTerminalStates()); + if (conn_.pendingEvents.resets.contains(streamId)) { + // This can happen when we send two reliable resets, one of which is + // egressed and ACKed. + conn_.pendingEvents.resets.erase(streamId); + } if (conn_.transportSettings.unidirectionalStreamsReadCallbacksFirst && isUnidirectionalStream(streamId)) { unidirectionalReadableStreams_.erase(streamId); diff --git a/quic/state/test/QuicStreamManagerTest.cpp b/quic/state/test/QuicStreamManagerTest.cpp index 64b5d4785..3eac7e1b2 100644 --- a/quic/state/test/QuicStreamManagerTest.cpp +++ b/quic/state/test/QuicStreamManagerTest.cpp @@ -826,6 +826,21 @@ TEST_P(QuicStreamManagerTest, StreamPriorityExcludesControl) { manager.removeClosedStream(stream->id); } +TEST_P(QuicStreamManagerTest, RemoveResetsUponClosure) { + MockQuicStreamPrioritiesObserver mObserver; + + auto& manager = *conn.streamManager; + auto stream = manager.createNextBidirectionalStream().value(); + conn.pendingEvents.resets.emplace( + stream->id, RstStreamFrame(stream->id, 0, 0)); + stream->sendState = StreamSendState::Closed; + stream->recvState = StreamRecvState::Closed; + + EXPECT_TRUE(conn.pendingEvents.resets.contains(stream->id)); + manager.removeClosedStream(stream->id); + EXPECT_FALSE(conn.pendingEvents.resets.contains(stream->id)); +} + Buf createBuffer(uint32_t len) { auto buf = folly::IOBuf::create(len); buf->append(len);