gfe-relnote: In QUIC, add connected_ check in OnCanWrite and flusher's destructor. Protected by gfe2_reloadable_flag_quic_check_connected_before_flush.
PiperOrigin-RevId: 251653969
Change-Id: I93d9204391c7b900d0abb73aab9cab70f7ffabe9
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 1c589d6..83e6f04 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -2094,6 +2094,10 @@
}
void QuicConnection::OnCanWrite() {
+ if (GetQuicReloadableFlag(quic_check_connected_before_flush) && !connected_) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_check_connected_before_flush, 2, 2);
+ return;
+ }
DCHECK(!writer_->IsWriteBlocked());
// Add a flusher to ensure the connection is marked app-limited.
@@ -3459,6 +3463,11 @@
if (connection_ == nullptr) {
return;
}
+ if (GetQuicReloadableFlag(quic_check_connected_before_flush) &&
+ !connection_->connected()) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_check_connected_before_flush, 1, 2);
+ return;
+ }
if (flush_and_set_pending_retransmission_alarm_on_delete_) {
if (connection_->packet_generator_.deprecate_ack_bundling_mode()) {
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 9cca434..ba40e7a 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1943,7 +1943,11 @@
EXPECT_EQ(0u, connection_.GetStats().packets_discarded);
connection_.OnCanWrite();
- EXPECT_EQ(1u, connection_.GetStats().packets_discarded);
+ if (GetQuicReloadableFlag(quic_check_connected_before_flush)) {
+ EXPECT_EQ(0u, connection_.GetStats().packets_discarded);
+ } else {
+ EXPECT_EQ(1u, connection_.GetStats().packets_discarded);
+ }
}
TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtServer) {
@@ -8505,6 +8509,44 @@
EXPECT_EQ(TestConnectionId(0x33), connection_.client_connection_id());
}
+// Regression test for b/134416344.
+TEST_P(QuicConnectionTest, CheckConnectedBeforeFlush) {
+ // This test mimics a scenario where a connection processes 2 packets and the
+ // 2nd packet contains connection close frame. When the 2nd flusher goes out
+ // of scope, a delayed ACK is pending, and ACK alarm should not be scheduled
+ // because connection is disconnected.
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _));
+ EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
+ std::unique_ptr<QuicConnectionCloseFrame> connection_close_frame(
+ new QuicConnectionCloseFrame(QUIC_INTERNAL_ERROR));
+ if (connection_.transport_version() == QUIC_VERSION_99) {
+ connection_close_frame->close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ }
+ // Received 2 packets.
+ QuicFrame frame;
+ if (QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+ frame = QuicFrame(&crypto_frame_);
+ EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber());
+ } else {
+ frame = QuicFrame(QuicStreamFrame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false,
+ 0u, QuicStringPiece()));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ }
+ ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress);
+ QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
+ EXPECT_TRUE(ack_alarm->IsSet());
+ ProcessFramePacketWithAddresses(QuicFrame(connection_close_frame.get()),
+ kSelfAddress, kPeerAddress);
+ if (GetQuicReloadableFlag(quic_check_connected_before_flush)) {
+ // Verify ack alarm is not set.
+ EXPECT_FALSE(ack_alarm->IsSet());
+ } else {
+ EXPECT_TRUE(ack_alarm->IsSet());
+ }
+}
+
} // namespace
} // namespace test
} // namespace quic