gfe-relnote: In QUIC, always bundle ACK with connection close with TLS handshake for debugging purpose. Protected by blocked gfe2_reloadable_flag_quic_enable_version_t*. PiperOrigin-RevId: 294942214 Change-Id: Ided5cdb8d4c8a50325e63f4c9676da0d7716c7c6
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index fe0ea7b..0bfb6ea 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -2917,10 +2917,8 @@ ClearQueuedPackets(); // If there was a packet write error, write the smallest close possible. ScopedPacketFlusher flusher(this); - // When multiple packet number spaces is supported, an ACK frame will be - // bundled when connection is not write blocked. - if (!SupportsMultiplePacketNumberSpaces() && - error != QUIC_PACKET_WRITE_ERROR && + // Always bundle an ACK with connection close for debugging purpose. + if (error != QUIC_PACKET_WRITE_ERROR && !GetUpdatedAckFrame().ack_frame->packets.Empty()) { SendAck(); } @@ -2955,15 +2953,17 @@ QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet at level: " << EncryptionLevelToString(level); SetDefaultEncryptionLevel(level); - // If there was a packet write error, write the smallest close possible. - // When multiple packet number spaces are supported, an ACK frame will - // be bundled by the ScopedPacketFlusher. Otherwise, an ACK must be sent - // explicitly. - if (!SupportsMultiplePacketNumberSpaces() && - error != QUIC_PACKET_WRITE_ERROR && - !GetUpdatedAckFrame().ack_frame->packets.Empty()) { - SendAck(); + // Bundle an ACK of the corresponding packet number space for debugging + // purpose. + if (error != QUIC_PACKET_WRITE_ERROR) { + const QuicFrame ack_frame = GetUpdatedAckFrame(); + if (!ack_frame.ack_frame->packets.Empty()) { + QuicFrames frames; + frames.push_back(ack_frame); + packet_creator_.FlushAckFrame(frames); + } } + auto* frame = new QuicConnectionCloseFrame(transport_version(), error, details, framer_.current_received_frame_type());
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index 704241c..67e2ab0 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -9973,6 +9973,58 @@ ProcessFramesPacketAtLevel(1, frames, ENCRYPTION_FORWARD_SECURE); } +TEST_P(QuicConnectionTest, + BundleAckWithConnectionCloseMultiplePacketNumberSpace) { + if (!connection_.SupportsMultiplePacketNumberSpaces()) { + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + // Receives packet 1000 in initial data. + ProcessCryptoPacketAtLevel(1000, ENCRYPTION_INITIAL); + // Receives packet 2000 in application data. + ProcessDataPacketAtLevel(2000, false, ENCRYPTION_FORWARD_SECURE); + EXPECT_CALL(visitor_, OnConnectionClosed(_, _)); + const QuicErrorCode kQuicErrorCode = QUIC_INTERNAL_ERROR; + connection_.CloseConnection( + kQuicErrorCode, "Some random error message", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + + EXPECT_EQ(2u, QuicConnectionPeer::GetNumEncryptionLevels(&connection_)); + + TestConnectionCloseQuicErrorCode(kQuicErrorCode); + EXPECT_EQ(1u, writer_->connection_close_frames().size()); + // Verify ack is bundled. + EXPECT_EQ(1u, writer_->ack_frames().size()); + + if (!connection_.version().CanSendCoalescedPackets()) { + // Each connection close packet should be sent in distinct UDP packets. + EXPECT_EQ(QuicConnectionPeer::GetNumEncryptionLevels(&connection_), + writer_->connection_close_packets()); + EXPECT_EQ(QuicConnectionPeer::GetNumEncryptionLevels(&connection_), + writer_->packets_write_attempts()); + return; + } + + // A single UDP packet should be sent with multiple connection close packets + // coalesced together. + EXPECT_EQ(1u, writer_->packets_write_attempts()); + + // Only the first packet has been processed yet. + EXPECT_EQ(1u, writer_->connection_close_packets()); + + // ProcessPacket resets the visitor and frees the coalesced packet. + ASSERT_TRUE(writer_->coalesced_packet() != nullptr); + auto packet = writer_->coalesced_packet()->Clone(); + writer_->framer()->ProcessPacket(*packet); + EXPECT_EQ(1u, writer_->connection_close_packets()); + EXPECT_EQ(1u, writer_->connection_close_frames().size()); + // Verify ack is bundled. + EXPECT_EQ(1u, writer_->ack_frames().size()); + ASSERT_TRUE(writer_->coalesced_packet() == nullptr); +} + } // namespace } // namespace test } // namespace quic