Allow QUIC control frames to be sent while a packet is being processed. Removes the restriction added in cr/574987377. Instead, changes the behavior of QuicPacketCreator::SetMaxPacketLength() to allow it to be called while frames are queued. In this case, the new max packet length is applied to the *next* packet. Note: uses a restart flag instead of a reloadable flag to avoid needing to coordinate the change in QuicSession with the change in QuicPacketCreator. Protected by FLAGS_quic_restart_flag_quic_allow_control_frames_while_procesing. PiperOrigin-RevId: 588249854
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h index dc53509..d2a4308 100644 --- a/quiche/quic/core/quic_flags_list.h +++ b/quiche/quic/core/quic_flags_list.h
@@ -47,6 +47,8 @@ QUIC_FLAG(quic_reloadable_flag_quic_allow_client_enabled_bbr_v2, true) // If true, allow quic to use new ALPS codepoint to negotiate during handshake for H3 if client sends new ALPS codepoint. QUIC_FLAG(quic_reloadable_flag_quic_gfe_allow_alps_new_codepoint, false) +// If true, allows QUIC control frames to be written while a packet is being processed. +QUIC_FLAG(quic_restart_flag_quic_allow_control_frames_while_procesing, false) // If true, always bundle qpack decoder data with other frames opportunistically. QUIC_FLAG(quic_restart_flag_quic_opport_bundle_qpack_decoder_data2, false) // If true, an endpoint does not detect path degrading or blackholing until handshake gets confirmed.
diff --git a/quiche/quic/core/quic_packet_creator.cc b/quiche/quic/core/quic_packet_creator.cc index 793f417..ed6e94b 100644 --- a/quiche/quic/core/quic_packet_creator.cc +++ b/quiche/quic/core/quic_packet_creator.cc
@@ -117,6 +117,7 @@ random_(random), have_diversification_nonce_(false), max_packet_length_(0), + next_max_packet_length_(0), server_connection_id_included_(CONNECTION_ID_PRESENT), packet_size_(0), server_connection_id_(server_connection_id), @@ -155,8 +156,18 @@ } void QuicPacketCreator::SetMaxPacketLength(QuicByteCount length) { - QUICHE_DCHECK(CanSetMaxPacketLength()) << ENDPOINT; - + if (!GetQuicRestartFlag(quic_allow_control_frames_while_procesing)) { + QUICHE_DCHECK(CanSetMaxPacketLength()) << ENDPOINT; + } else { + QUIC_RESTART_FLAG_COUNT_N(quic_allow_control_frames_while_procesing, 2, 3); + if (!CanSetMaxPacketLength()) { + QUIC_RESTART_FLAG_COUNT_N(quic_allow_control_frames_while_procesing, 3, + 3); + // The new max packet length will be applied to the next packet. + next_max_packet_length_ = length; + return; + } + } // Avoid recomputing |max_plaintext_size_| if the length does not actually // change. if (length == max_packet_length_) { @@ -473,6 +484,11 @@ ClearPacket(); RemoveSoftMaxPacketLength(); delegate_->OnSerializedPacket(std::move(packet)); + if (next_max_packet_length_ != 0) { + QUICHE_DCHECK(CanSetMaxPacketLength()) << ENDPOINT; + SetMaxPacketLength(next_max_packet_length_); + next_max_packet_length_ = 0; + } } void QuicPacketCreator::ClearPacket() {
diff --git a/quiche/quic/core/quic_packet_creator.h b/quiche/quic/core/quic_packet_creator.h index bb2303e..1a4e725 100644 --- a/quiche/quic/core/quic_packet_creator.h +++ b/quiche/quic/core/quic_packet_creator.h
@@ -623,6 +623,9 @@ DiversificationNonce diversification_nonce_; // Maximum length including headers and encryption (UDP payload length.) QuicByteCount max_packet_length_; + // Value of max_packet_length_ to be applied for the next packet, if not 0. + QuicByteCount next_max_packet_length_; + size_t max_plaintext_size_; // Whether the server_connection_id is sent over the wire. QuicConnectionIdIncluded server_connection_id_included_;
diff --git a/quiche/quic/core/quic_packet_creator_test.cc b/quiche/quic/core/quic_packet_creator_test.cc index e1510f5..573e4ce 100644 --- a/quiche/quic/core/quic_packet_creator_test.cc +++ b/quiche/quic/core/quic_packet_creator_test.cc
@@ -356,6 +356,76 @@ ProcessPacket(serialized); } +TEST_P(QuicPacketCreatorTest, SerializePacketWithPadding) { + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + + creator_.AddFrame(QuicFrame(QuicWindowUpdateFrame()), NOT_RETRANSMISSION); + creator_.AddFrame(QuicFrame(QuicPaddingFrame()), NOT_RETRANSMISSION); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.FlushCurrentPacket(); + ASSERT_TRUE(serialized_packet_->encrypted_buffer); + + EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_->encrypted_length); + + DeleteSerializedPacket(); +} + +TEST_P(QuicPacketCreatorTest, SerializeLargerPacketWithPadding) { + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + const QuicByteCount packet_size = 100 + kDefaultMaxPacketSize; + creator_.SetMaxPacketLength(packet_size); + + creator_.AddFrame(QuicFrame(QuicWindowUpdateFrame()), NOT_RETRANSMISSION); + creator_.AddFrame(QuicFrame(QuicPaddingFrame()), NOT_RETRANSMISSION); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.FlushCurrentPacket(); + ASSERT_TRUE(serialized_packet_->encrypted_buffer); + + EXPECT_EQ(packet_size, serialized_packet_->encrypted_length); + + DeleteSerializedPacket(); +} + +TEST_P(QuicPacketCreatorTest, IncreaseMaxPacketLengthWithFramesPending) { + if (!GetQuicRestartFlag(quic_allow_control_frames_while_procesing)) { + // When this flag is not set, the call to SetMaxPacketLength() + // is an error which triggers a QUICHE_DCHECK. + return; + } + + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + const QuicByteCount packet_size = 100 + kDefaultMaxPacketSize; + + // Since the creator has a frame queued, the packet size will not change. + creator_.AddFrame(QuicFrame(QuicWindowUpdateFrame()), NOT_RETRANSMISSION); + creator_.SetMaxPacketLength(packet_size); + creator_.AddFrame(QuicFrame(QuicPaddingFrame()), NOT_RETRANSMISSION); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.FlushCurrentPacket(); + ASSERT_TRUE(serialized_packet_->encrypted_buffer); + + EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_->encrypted_length); + + DeleteSerializedPacket(); + + // Now that the previous packet was generated, the next on will use + // the new larger size. + creator_.AddFrame(QuicFrame(QuicWindowUpdateFrame()), NOT_RETRANSMISSION); + creator_.AddFrame(QuicFrame(QuicPaddingFrame()), NOT_RETRANSMISSION); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.FlushCurrentPacket(); + ASSERT_TRUE(serialized_packet_->encrypted_buffer); + EXPECT_EQ(packet_size, serialized_packet_->encrypted_length); + + EXPECT_EQ(packet_size, serialized_packet_->encrypted_length); + + DeleteSerializedPacket(); +} + TEST_P(QuicPacketCreatorTest, ConsumeCryptoDataToFillCurrentPacket) { std::string data = "crypto data"; QuicFrame frame;
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc index a6e8ed5..4a805aa 100644 --- a/quiche/quic/core/quic_session.cc +++ b/quiche/quic/core/quic_session.cc
@@ -880,11 +880,15 @@ // Suppress the write before encryption gets established. return false; } - if (limit_sending_max_streams_ && - connection_->framer().is_processing_packet()) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_limit_sending_max_streams2, 3, 3); - // The frame will be sent when OnCanWrite() is called. - return false; + if (GetQuicRestartFlag(quic_allow_control_frames_while_procesing)) { + QUIC_RESTART_FLAG_COUNT_N(quic_allow_control_frames_while_procesing, 1, 3); + } else { + if (limit_sending_max_streams_ && + connection_->framer().is_processing_packet()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_limit_sending_max_streams2, 3, 3); + // The frame will be sent when OnCanWrite() is called. + return false; + } } SetTransmissionType(type); QuicConnection::ScopedEncryptionLevelContext context(