Gracefully handle crypto frame outstanding checks after crypto substreams are reset. When multiple packets carry the same crypto data (e.g., due to retransmissions), one packet can be acked and trigger resetting the crypto substreams while the other packets remain in flight. If those other packets subsequently trigger an outstanding frame check, QuicCryptoStream should handle them without reporting a bug. PiperOrigin-RevId: 991160397
diff --git a/quiche/quic/core/quic_crypto_stream.cc b/quiche/quic/core/quic_crypto_stream.cc index 16a9484..867b934 100644 --- a/quiche/quic/core/quic_crypto_stream.cc +++ b/quiche/quic/core/quic_crypto_stream.cc
@@ -578,7 +578,12 @@ return false; } if (substreams_.empty()) { - ReportCryptoSubStreamResetBug(); + // Due to retransmission, there can be multiple packets containing the last + // unacked crypto data sent to peer. After one of these packets is acked, + // the substreams can be reset, while other packets containing the same + // crypto data are still in flight. + QUIC_LOG(INFO) + << "IsFrameOutstanding is called after substreams are reset."; return false; } return substreams_[QuicUtils::GetPacketNumberSpace(level)]
diff --git a/quiche/quic/core/quic_crypto_stream_test.cc b/quiche/quic/core/quic_crypto_stream_test.cc index 6da190f..fdf9109 100644 --- a/quiche/quic/core/quic_crypto_stream_test.cc +++ b/quiche/quic/core/quic_crypto_stream_test.cc
@@ -122,6 +122,8 @@ } } + using QuicCryptoStream::ResetCryptoSubstreams; + private: quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; std::vector<CryptoHandshakeMessage> messages_; @@ -812,6 +814,29 @@ stream_->OnCryptoFrame(empty_crypto_frame); } +// Regression test for b/566549350 +TEST_F(QuicCryptoStreamTest, OperationsAfterResetCryptoSubstreams) { + if (!VersionIsIetfQuic(connection_->transport_version())) { + return; + } + std::string data(1350, 'a'); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + stream_->WriteCryptoData(ENCRYPTION_INITIAL, data); + EXPECT_TRUE(stream_->IsFrameOutstanding(ENCRYPTION_INITIAL, 0, 1350)); + + QuicCryptoFrame frame(ENCRYPTION_INITIAL, 0, 1350); + EXPECT_TRUE(stream_->OnCryptoFrameAcked(frame, QuicTime::Delta::Zero())); + EXPECT_FALSE(stream_->IsFrameOutstanding(ENCRYPTION_INITIAL, 0, 1350)); + + stream_->ResetCryptoSubstreams(); + + EXPECT_FALSE(stream_->IsFrameOutstanding(ENCRYPTION_INITIAL, 0, 1350)); + EXPECT_FALSE(stream_->OnCryptoFrameAcked(frame, QuicTime::Delta::Zero())); + stream_->OnCryptoFrameLost(&frame); +} + } // namespace } // namespace test } // namespace quic