gfe-relnote: In QUIC, add SetSoftMaxPacketLength to set a soft max packet length in creator. If a single frame is failed to be serialized, restore the actual max packet length. Not used yet. Not protected.
PiperOrigin-RevId: 278424350
Change-Id: Ie89ba741b6798dee0683b4678edc9242f37f7672
diff --git a/quic/core/crypto/aead_base_encrypter.cc b/quic/core/crypto/aead_base_encrypter.cc
index 405292e..a3173f5 100644
--- a/quic/core/crypto/aead_base_encrypter.cc
+++ b/quic/core/crypto/aead_base_encrypter.cc
@@ -168,7 +168,7 @@
}
size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
- return ciphertext_size - auth_tag_size_;
+ return ciphertext_size - std::min(ciphertext_size, auth_tag_size_);
}
size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
diff --git a/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc b/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
index 5ccc44d..d529d5d 100644
--- a/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
+++ b/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
@@ -228,6 +228,7 @@
EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+ EXPECT_EQ(0u, encrypter.GetMaxPlaintextSize(11));
}
TEST_F(Aes128Gcm12EncrypterTest, GetCiphertextSize) {
diff --git a/quic/core/crypto/null_encrypter.cc b/quic/core/crypto/null_encrypter.cc
index 4ad9b2a..1fe0fdf 100644
--- a/quic/core/crypto/null_encrypter.cc
+++ b/quic/core/crypto/null_encrypter.cc
@@ -75,7 +75,7 @@
}
size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
- return ciphertext_size - GetHashLength();
+ return ciphertext_size - std::min(ciphertext_size, GetHashLength());
}
size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) const {
diff --git a/quic/core/crypto/null_encrypter_test.cc b/quic/core/crypto/null_encrypter_test.cc
index fd95cc6..c6a89ef 100644
--- a/quic/core/crypto/null_encrypter_test.cc
+++ b/quic/core/crypto/null_encrypter_test.cc
@@ -87,6 +87,7 @@
EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+ EXPECT_EQ(0u, encrypter.GetMaxPlaintextSize(11));
}
TEST_F(NullEncrypterTest, GetCiphertextSize) {
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index fa45cf9..9f7f160 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -124,6 +124,7 @@
next_transmission_type_(NOT_RETRANSMISSION),
flusher_attached_(false),
fully_pad_crypto_handshake_packets_(true),
+ latched_hard_max_packet_length_(0),
combine_generator_and_creator_(
GetQuicReloadableFlag(quic_combine_generator_and_creator)),
populate_nonretransmittable_frames_(
@@ -168,6 +169,25 @@
<< "Attempted to set max packet length too small";
}
+void QuicPacketCreator::SetSoftMaxPacketLength(QuicByteCount length) {
+ DCHECK(CanSetMaxPacketLength());
+ if (length > max_packet_length_) {
+ QUIC_BUG << ENDPOINT
+ << "Try to increase max_packet_length_ in "
+ "SetSoftMaxPacketLength, use SetMaxPacketLength instead.";
+ return;
+ }
+ if (framer_->GetMaxPlaintextSize(length) <
+ PacketHeaderSize() + MinPlaintextPacketSize(framer_->version())) {
+ QUIC_DLOG(INFO) << length << " is too small to fit packet header";
+ return;
+ }
+ QUIC_DVLOG(1) << "Setting soft max packet length to: " << length;
+ latched_hard_max_packet_length_ = max_packet_length_;
+ max_packet_length_ = length;
+ max_plaintext_size_ = framer_->GetMaxPlaintextSize(length);
+}
+
// Stops serializing version of the protocol in packets sent after this call.
// A packet that is already open might send kQuicVersionSize bytes less than the
// maximum packet size if we stop sending version before it is serialized.
@@ -295,14 +315,28 @@
bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id,
QuicStreamOffset offset,
size_t data_size) {
- return BytesFree() >
- QuicFramer::GetMinStreamFrameSize(framer_->transport_version(), id,
- offset, true, data_size);
+ const size_t min_stream_frame_size = QuicFramer::GetMinStreamFrameSize(
+ framer_->transport_version(), id, offset, /*last_frame_in_packet=*/true,
+ data_size);
+ if (BytesFree() > min_stream_frame_size) {
+ return true;
+ }
+ if (!RemoveSoftMaxPacketLength()) {
+ return false;
+ }
+ return BytesFree() > min_stream_frame_size;
}
bool QuicPacketCreator::HasRoomForMessageFrame(QuicByteCount length) {
- return BytesFree() >= QuicFramer::GetMessageFrameSize(
- framer_->transport_version(), true, length);
+ const size_t message_frame_size = QuicFramer::GetMessageFrameSize(
+ framer_->transport_version(), /*last_frame_in_packet=*/true, length);
+ if (BytesFree() >= message_frame_size) {
+ return true;
+ }
+ if (!RemoveSoftMaxPacketLength()) {
+ return false;
+ }
+ return BytesFree() >= message_frame_size;
}
// TODO(fkastenholz): this method should not use constant values for
@@ -388,7 +422,8 @@
QuicFrame* frame) {
size_t min_frame_size =
QuicFramer::GetMinCryptoFrameSize(write_length, offset);
- if (BytesFree() <= min_frame_size) {
+ if (BytesFree() <= min_frame_size &&
+ (!RemoveSoftMaxPacketLength() || BytesFree() <= min_frame_size)) {
return false;
}
size_t max_write_length = BytesFree() - min_frame_size;
@@ -423,6 +458,7 @@
SerializedPacket packet(std::move(packet_));
ClearPacket();
+ RemoveSoftMaxPacketLength();
delegate_->OnSerializedPacket(&packet);
}
@@ -730,6 +766,7 @@
QuicPacketCreator::SerializeConnectivityProbingPacket() {
QUIC_BUG_IF(VersionHasIetfQuicFrames(framer_->transport_version()))
<< "Must not be version 99 to serialize padded ping connectivity probe";
+ RemoveSoftMaxPacketLength();
QuicPacketHeader header;
// FillPacketHeader increments packet_number_.
FillPacketHeader(&header);
@@ -765,6 +802,7 @@
<< "Must be version 99 to serialize path challenge connectivity probe, "
"is version "
<< framer_->transport_version();
+ RemoveSoftMaxPacketLength();
QuicPacketHeader header;
// FillPacketHeader increments packet_number_.
FillPacketHeader(&header);
@@ -801,6 +839,7 @@
<< "Must be version 99 to serialize path response connectivity probe, is "
"version "
<< framer_->transport_version();
+ RemoveSoftMaxPacketLength();
QuicPacketHeader header;
// FillPacketHeader increments packet_number_.
FillPacketHeader(&header);
@@ -1115,7 +1154,8 @@
// the slow path loop.
bool run_fast_path =
!has_handshake && state != FIN_AND_PADDING && !HasPendingFrames() &&
- write_length - total_bytes_consumed > kMaxOutgoingPacketSize;
+ write_length - total_bytes_consumed > kMaxOutgoingPacketSize &&
+ latched_hard_max_packet_length_ == 0;
while (!run_fast_path && delegate_->ShouldGeneratePacket(
HAS_RETRANSMITTABLE_DATA,
@@ -1154,7 +1194,8 @@
run_fast_path =
!has_handshake && state != FIN_AND_PADDING && !HasPendingFrames() &&
- write_length - total_bytes_consumed > kMaxOutgoingPacketSize;
+ write_length - total_bytes_consumed > kMaxOutgoingPacketSize &&
+ latched_hard_max_packet_length_ == 0;
}
if (run_fast_path) {
@@ -1449,6 +1490,12 @@
size_t frame_len = framer_->GetSerializedFrameLength(
frame, BytesFree(), queued_frames_.empty(),
/* last_frame_in_packet= */ true, GetPacketNumberLength());
+ if (frame_len == 0 && RemoveSoftMaxPacketLength()) {
+ // Remove soft max_packet_length and retry.
+ frame_len = framer_->GetSerializedFrameLength(
+ frame, BytesFree(), queued_frames_.empty(),
+ /* last_frame_in_packet= */ true, GetPacketNumberLength());
+ }
if (frame_len == 0) {
// Current open packet is full.
FlushCurrentPacket();
@@ -1528,6 +1575,21 @@
return true;
}
+bool QuicPacketCreator::RemoveSoftMaxPacketLength() {
+ if (latched_hard_max_packet_length_ == 0) {
+ return false;
+ }
+ if (!CanSetMaxPacketLength()) {
+ return false;
+ }
+ QUIC_DVLOG(1) << "Restoring max packet length to: "
+ << latched_hard_max_packet_length_;
+ SetMaxPacketLength(latched_hard_max_packet_length_);
+ // Reset latched_max_packet_length_.
+ latched_hard_max_packet_length_ = 0;
+ return true;
+}
+
void QuicPacketCreator::MaybeAddPadding() {
// The current packet should have no padding bytes because padding is only
// added when this method is called just before the packet is serialized.
@@ -1639,8 +1701,12 @@
VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, GetLengthLength());
// This is the largest possible message payload when the length field is
// omitted.
- return max_plaintext_size_ -
- std::min(max_plaintext_size_, packet_header_size + kQuicFrameTypeSize);
+ size_t max_plaintext_size =
+ latched_hard_max_packet_length_ == 0
+ ? max_plaintext_size_
+ : framer_->GetMaxPlaintextSize(latched_hard_max_packet_length_);
+ return max_plaintext_size -
+ std::min(max_plaintext_size, packet_header_size + kQuicFrameTypeSize);
}
QuicPacketLength QuicPacketCreator::GetGuaranteedLargestMessagePayload() const {
@@ -1669,9 +1735,13 @@
VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, length_length);
// This is the largest possible message payload when the length field is
// omitted.
+ size_t max_plaintext_size =
+ latched_hard_max_packet_length_ == 0
+ ? max_plaintext_size_
+ : framer_->GetMaxPlaintextSize(latched_hard_max_packet_length_);
const QuicPacketLength largest_payload =
- max_plaintext_size_ -
- std::min(max_plaintext_size_, packet_header_size + kQuicFrameTypeSize);
+ max_plaintext_size -
+ std::min(max_plaintext_size, packet_header_size + kQuicFrameTypeSize);
// This must always be less than or equal to GetCurrentLargestMessagePayload.
DCHECK_LE(largest_payload, GetCurrentLargestMessagePayload());
return largest_payload;
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index 73e69c1..b8240c1 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -270,6 +270,11 @@
// Sets the maximum packet length.
void SetMaxPacketLength(QuicByteCount length);
+ // Set a soft maximum packet length in the creator. If a packet cannot be
+ // successfully created, creator will remove the soft limit and use the actual
+ // max packet length.
+ void SetSoftMaxPacketLength(QuicByteCount length);
+
// Increases pending_padding_bytes by |size|. Pending padding will be sent by
// MaybeAddPadding().
void AddPendingPadding(QuicByteCount size);
@@ -476,6 +481,13 @@
// Returns true on success.
bool MaybeCoalesceStreamFrame(const QuicStreamFrame& frame);
+ // Called to remove the soft max_packet_length and restores
+ // latched_hard_max_packet_length_ if the packet cannot accommodate a single
+ // frame. Returns true if the soft limit is successfully removed. Returns
+ // false if either there is no current soft limit or there are queued frames
+ // (such that the packet length cannot be changed).
+ bool RemoveSoftMaxPacketLength();
+
// Returns true if a diversification nonce should be included in the current
// packet's header.
bool IncludeNonceInPublicHeader() const;
@@ -575,6 +587,11 @@
// flusher detaches.
QuicPacketNumber write_start_packet_number_;
+ // If not 0, this latches the actual max_packet_length when
+ // SetSoftMaxPacketLength is called and max_packet_length_ gets
+ // set to a soft value.
+ QuicByteCount latched_hard_max_packet_length_;
+
// Latched value of quic_combine_generator_and_creator.
const bool combine_generator_and_creator_;
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index 5a0c9db..0ba7bd1 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -2167,6 +2167,75 @@
}
}
+TEST_P(QuicPacketCreatorTest, SoftMaxPacketLength) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ QuicByteCount previous_max_packet_length = creator_.max_packet_length();
+ const size_t overhead =
+ GetPacketHeaderOverhead(client_framer_.transport_version()) +
+ QuicPacketCreator::MinPlaintextPacketSize(client_framer_.version()) +
+ GetEncryptionOverhead();
+ // Make sure a length which cannot accommodate header (includes header
+ // protection minimal length) gets rejected.
+ creator_.SetSoftMaxPacketLength(overhead - 1);
+ EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length());
+
+ creator_.SetSoftMaxPacketLength(overhead);
+ EXPECT_EQ(overhead, creator_.max_packet_length());
+
+ // Verify creator has room for stream frame because max_packet_length_ gets
+ // restored.
+ ASSERT_TRUE(creator_.HasRoomForStreamFrame(GetNthClientInitiatedStreamId(1),
+ kMaxIetfVarInt, kMaxIetfVarInt));
+ EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length());
+
+ // Same for message frame.
+ if (VersionSupportsMessageFrames(client_framer_.transport_version())) {
+ creator_.SetSoftMaxPacketLength(overhead);
+ // Verify GetCurrentLargestMessagePayload is based on the actual
+ // max_packet_length.
+ EXPECT_LT(1u, creator_.GetCurrentLargestMessagePayload());
+ EXPECT_EQ(overhead, creator_.max_packet_length());
+ ASSERT_TRUE(creator_.HasRoomForMessageFrame(
+ creator_.GetCurrentLargestMessagePayload()));
+ EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length());
+ }
+
+ // Verify creator can consume crypto data because max_packet_length_ gets
+ // restored.
+ creator_.SetSoftMaxPacketLength(overhead);
+ EXPECT_EQ(overhead, creator_.max_packet_length());
+ std::string data = "crypto data";
+ MakeIOVector(data, &iov_);
+ QuicFrame frame;
+ if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_,
+ 1u, iov_.iov_len, 0u, kOffset, false, true, NOT_RETRANSMISSION,
+ &frame));
+ size_t bytes_consumed = frame.stream_frame.data_length;
+ EXPECT_LT(0u, bytes_consumed);
+ } else {
+ producer_.SaveCryptoData(ENCRYPTION_INITIAL, kOffset, data);
+ ASSERT_TRUE(creator_.ConsumeCryptoDataToFillCurrentPacket(
+ ENCRYPTION_INITIAL, data.length(), kOffset,
+ /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame));
+ size_t bytes_consumed = frame.crypto_frame->data_length;
+ EXPECT_LT(0u, bytes_consumed);
+ }
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.FlushCurrentPacket();
+
+ // Verify ACK frame can be consumed.
+ creator_.SetSoftMaxPacketLength(overhead);
+ EXPECT_EQ(overhead, creator_.max_packet_length());
+ QuicAckFrame ack_frame(InitAckFrame(10u));
+ EXPECT_TRUE(
+ creator_.AddSavedFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+}
+
} // namespace
} // namespace test
} // namespace quic