Add GetLargestGuaranteedMessagePayload to QuicSession/Connection/Generator/Creator to expose the largest message payload that's guaranteed to fit in any packet containing application data. gfe-relnote: n/a (Only used in Quartc) PiperOrigin-RevId: 242135946 Change-Id: I52cc763af88d22fc240210e00cf4d4062d0b9c59
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc index c3f50ef..6935dee 100644 --- a/quic/core/http/end_to_end_test.cc +++ b/quic/core/http/end_to_end_test.cc
@@ -3552,8 +3552,8 @@ } SetPacketLossPercentage(30); - ASSERT_GT(kMaxPacketSize, client_session->GetLargestMessagePayload()); - ASSERT_LT(0, client_session->GetLargestMessagePayload()); + ASSERT_GT(kMaxPacketSize, client_session->GetCurrentLargestMessagePayload()); + ASSERT_LT(0, client_session->GetCurrentLargestMessagePayload()); std::string message_string(kMaxPacketSize, 'a'); QuicStringPiece message_buffer(message_string); @@ -3564,20 +3564,23 @@ QuicConnection::ScopedPacketFlusher flusher( client_session->connection(), QuicConnection::SEND_ACK_IF_PENDING); // Verify the largest message gets successfully sent. - EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 1), - client_session->SendMessage(MakeSpan( - client_session->connection() - ->helper() - ->GetStreamSendBufferAllocator(), - QuicStringPiece(message_buffer.data(), - client_session->GetLargestMessagePayload()), - &storage))); + EXPECT_EQ( + MessageResult(MESSAGE_STATUS_SUCCESS, 1), + client_session->SendMessage(MakeSpan( + client_session->connection() + ->helper() + ->GetStreamSendBufferAllocator(), + QuicStringPiece(message_buffer.data(), + client_session->GetCurrentLargestMessagePayload()), + &storage))); // Send more messages with size (0, largest_payload] until connection is // write blocked. const int kTestMaxNumberOfMessages = 100; for (size_t i = 2; i <= kTestMaxNumberOfMessages; ++i) { size_t message_length = - random->RandUint64() % client_session->GetLargestMessagePayload() + 1; + random->RandUint64() % + client_session->GetCurrentLargestMessagePayload() + + 1; MessageResult result = client_session->SendMessage(MakeSpan( client_session->connection() ->helper() @@ -3592,17 +3595,17 @@ } client_->WaitForDelayedAcks(); - EXPECT_EQ( - MESSAGE_STATUS_TOO_LARGE, - client_session - ->SendMessage(MakeSpan( - client_session->connection() - ->helper() - ->GetStreamSendBufferAllocator(), - QuicStringPiece(message_buffer.data(), - client_session->GetLargestMessagePayload() + 1), - &storage)) - .status); + EXPECT_EQ(MESSAGE_STATUS_TOO_LARGE, + client_session + ->SendMessage(MakeSpan( + client_session->connection() + ->helper() + ->GetStreamSendBufferAllocator(), + QuicStringPiece( + message_buffer.data(), + client_session->GetCurrentLargestMessagePayload() + 1), + &storage)) + .status); EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); }
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index 68567b7..c6f939e 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -3875,7 +3875,7 @@ << transport_version(); return MESSAGE_STATUS_UNSUPPORTED; } - if (message.total_length() > GetLargestMessagePayload()) { + if (message.total_length() > GetCurrentLargestMessagePayload()) { return MESSAGE_STATUS_TOO_LARGE; } if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) { @@ -3885,8 +3885,12 @@ return packet_generator_.AddMessageFrame(message_id, message); } -QuicPacketLength QuicConnection::GetLargestMessagePayload() const { - return packet_generator_.GetLargestMessagePayload(); +QuicPacketLength QuicConnection::GetCurrentLargestMessagePayload() const { + return packet_generator_.GetCurrentLargestMessagePayload(); +} + +QuicPacketLength QuicConnection::GetGuaranteedLargestMessagePayload() const { + return packet_generator_.GetGuaranteedLargestMessagePayload(); } bool QuicConnection::ShouldSetAckAlarm() const {
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h index edd132b..8176934 100644 --- a/quic/core/quic_connection.h +++ b/quic/core/quic_connection.h
@@ -757,7 +757,13 @@ QuicMemSliceSpan message); // Returns the largest payload that will fit into a single MESSAGE frame. - QuicPacketLength GetLargestMessagePayload() const; + // Because overhead can vary during a connection, this method should be + // checked for every message. + QuicPacketLength GetCurrentLargestMessagePayload() const; + // Returns the largest payload that will fit into a single MESSAGE frame at + // any point during the connection. This assumes the version and + // connection ID lengths do not change. + QuicPacketLength GetGuaranteedLargestMessagePayload() const; // Return the id of the cipher of the primary decrypter of the framer. uint32_t cipher_id() const { return framer_.decrypter()->cipher_id(); }
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index 3d1e483..f5a840a 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -8257,7 +8257,7 @@ connection_.SupportsMultiplePacketNumberSpaces()) { return; } - std::string message(connection_.GetLargestMessagePayload() * 2, 'a'); + std::string message(connection_.GetCurrentLargestMessagePayload() * 2, 'a'); QuicStringPiece message_data(message); QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); { @@ -8272,8 +8272,9 @@ MESSAGE_STATUS_SUCCESS, connection_.SendMessage( 1, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(), - QuicStringPiece(message_data.data(), - connection_.GetLargestMessagePayload()), + QuicStringPiece( + message_data.data(), + connection_.GetCurrentLargestMessagePayload()), &storage))); } // Fail to send a message if connection is congestion control blocked. @@ -8289,11 +8290,11 @@ EXPECT_EQ( MESSAGE_STATUS_TOO_LARGE, connection_.SendMessage( - 3, - MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(), - QuicStringPiece(message_data.data(), - connection_.GetLargestMessagePayload() + 1), - &storage))); + 3, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(), + QuicStringPiece( + message_data.data(), + connection_.GetCurrentLargestMessagePayload() + 1), + &storage))); } // Test to check that the path challenge/path response logic works
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc index 224aadf..4d5bd14 100644 --- a/quic/core/quic_packet_creator.cc +++ b/quic/core/quic_packet_creator.cc
@@ -10,9 +10,11 @@ #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h" #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" @@ -984,7 +986,7 @@ } } -QuicPacketLength QuicPacketCreator::GetLargestMessagePayload() const { +QuicPacketLength QuicPacketCreator::GetCurrentLargestMessagePayload() const { if (framer_->transport_version() <= QUIC_VERSION_44) { return 0; } @@ -992,13 +994,44 @@ framer_->transport_version(), GetDestinationConnectionIdLength(), GetSourceConnectionIdLength(), IncludeVersionInHeader(), IncludeNonceInPublicHeader(), GetPacketNumberLength(), - GetRetryTokenLengthLength(), GetRetryToken().length(), GetLengthLength()); + // No Retry token on packets containing application data. + 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); } +QuicPacketLength QuicPacketCreator::GetGuaranteedLargestMessagePayload() const { + if (framer_->transport_version() <= QUIC_VERSION_44) { + return 0; + } + // QUIC Crypto server packets may include a diversification nonce. + const bool may_include_nonce = + framer_->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO && + framer_->perspective() == Perspective::IS_SERVER; + // IETF QUIC long headers include a length on client 0RTT packets. + QuicVariableLengthIntegerLength length_length = + framer_->perspective() == Perspective::IS_CLIENT + ? VARIABLE_LENGTH_INTEGER_LENGTH_2 + : VARIABLE_LENGTH_INTEGER_LENGTH_0; + const size_t packet_header_size = GetPacketHeaderSize( + framer_->transport_version(), GetDestinationConnectionIdLength(), + // Assume CID lengths don't change, but version may be present. + GetSourceConnectionIdLength(), kIncludeVersion, may_include_nonce, + PACKET_4BYTE_PACKET_NUMBER, + // No Retry token on packets containing application data. + VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, length_length); + // This is the largest possible message payload when the length field is + // omitted. + const QuicPacketLength largest_payload = + 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; +} + bool QuicPacketCreator::HasIetfLongHeader() const { return framer_->transport_version() > QUIC_VERSION_43 && packet_.encryption_level < ENCRYPTION_FORWARD_SECURE;
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h index 7c8f496..ea93552 100644 --- a/quic/core/quic_packet_creator.h +++ b/quic/core/quic_packet_creator.h
@@ -255,7 +255,11 @@ void SetRetryToken(QuicStringPiece retry_token); // Returns the largest payload that will fit into a single MESSAGE frame. - QuicPacketLength GetLargestMessagePayload() const; + QuicPacketLength GetCurrentLargestMessagePayload() const; + // Returns the largest payload that will fit into a single MESSAGE frame at + // any point during the connection. This assumes the version and + // connection ID lengths do not change. + QuicPacketLength GetGuaranteedLargestMessagePayload() const; void set_debug_delegate(DebugDelegate* debug_delegate) { debug_delegate_ = debug_delegate;
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc index aea0540..aa2f6f7 100644 --- a/quic/core/quic_packet_creator_test.cc +++ b/quic/core/quic_packet_creator_test.cc
@@ -1606,9 +1606,9 @@ Invoke(this, &QuicPacketCreatorTest::ClearSerializedPacketForTests)); QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); // Verify that there is enough room for the largest message payload. - EXPECT_TRUE( - creator_.HasRoomForMessageFrame(creator_.GetLargestMessagePayload())); - std::string message(creator_.GetLargestMessagePayload(), 'a'); + EXPECT_TRUE(creator_.HasRoomForMessageFrame( + creator_.GetCurrentLargestMessagePayload())); + std::string message(creator_.GetCurrentLargestMessagePayload(), 'a'); QuicMessageFrame* message_frame = new QuicMessageFrame(1, MakeSpan(&allocator_, message, &storage)); EXPECT_TRUE( @@ -1639,8 +1639,8 @@ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame4), NOT_RETRANSMISSION)); EXPECT_TRUE(creator_.HasPendingFrames()); // Verify there is not enough room for largest payload. - EXPECT_FALSE( - creator_.HasRoomForMessageFrame(creator_.GetLargestMessagePayload())); + EXPECT_FALSE(creator_.HasRoomForMessageFrame( + creator_.GetCurrentLargestMessagePayload())); // Add largest message will causes the flush of the stream frame. QuicMessageFrame frame5(5, MakeSpan(&allocator_, message, &storage)); EXPECT_FALSE(creator_.AddSavedFrame(QuicFrame(&frame5), NOT_RETRANSMISSION)); @@ -1654,31 +1654,40 @@ std::string message_data(kDefaultMaxPacketSize, 'a'); QuicStringPiece message_buffer(message_data); QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); - // Test all possible size of message frames. - for (size_t message_size = 0; - message_size <= creator_.GetLargestMessagePayload(); ++message_size) { - QuicMessageFrame* frame = new QuicMessageFrame( - 0, MakeSpan(&allocator_, - QuicStringPiece(message_buffer.data(), message_size), - &storage)); - EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION)); - EXPECT_TRUE(creator_.HasPendingFrames()); + // Test all possible encryption levels of message frames. + for (EncryptionLevel level : + {ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) { + creator_.set_encryption_level(level); + // Test all possible sizes of message frames. + for (size_t message_size = 0; + message_size <= creator_.GetCurrentLargestMessagePayload(); + ++message_size) { + QuicMessageFrame* frame = new QuicMessageFrame( + 0, MakeSpan(&allocator_, + QuicStringPiece(message_buffer.data(), message_size), + &storage)); + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION)); + EXPECT_TRUE(creator_.HasPendingFrames()); - size_t expansion_bytes = message_size >= 64 ? 2 : 1; - EXPECT_EQ(expansion_bytes, creator_.ExpansionOnNewFrame()); - // Verify BytesFree returns bytes available for the next frame, which should - // subtract the message length. - size_t expected_bytes_free = - creator_.GetLargestMessagePayload() - message_size < expansion_bytes - ? 0 - : creator_.GetLargestMessagePayload() - expansion_bytes - - message_size; - EXPECT_EQ(expected_bytes_free, creator_.BytesFree()); - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); - creator_.Flush(); - ASSERT_TRUE(serialized_packet_.encrypted_buffer); - DeleteSerializedPacket(); + size_t expansion_bytes = message_size >= 64 ? 2 : 1; + EXPECT_EQ(expansion_bytes, creator_.ExpansionOnNewFrame()); + // Verify BytesFree returns bytes available for the next frame, which + // should subtract the message length. + size_t expected_bytes_free = + creator_.GetCurrentLargestMessagePayload() - message_size < + expansion_bytes + ? 0 + : creator_.GetCurrentLargestMessagePayload() - expansion_bytes - + message_size; + EXPECT_EQ(expected_bytes_free, creator_.BytesFree()); + EXPECT_LE(creator_.GetGuaranteedLargestMessagePayload(), + creator_.GetCurrentLargestMessagePayload()); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.Flush(); + ASSERT_TRUE(serialized_packet_.encrypted_buffer); + DeleteSerializedPacket(); + } } }
diff --git a/quic/core/quic_packet_generator.cc b/quic/core/quic_packet_generator.cc index 8267ade..831689b 100644 --- a/quic/core/quic_packet_generator.cc +++ b/quic/core/quic_packet_generator.cc
@@ -472,7 +472,7 @@ MaybeBundleAckOpportunistically(); } const QuicByteCount message_length = message.total_length(); - if (message_length > GetLargestMessagePayload()) { + if (message_length > GetCurrentLargestMessagePayload()) { return MESSAGE_STATUS_TOO_LARGE; } SendQueuedFrames(/*flush=*/false); @@ -530,8 +530,13 @@ return true; } -QuicPacketLength QuicPacketGenerator::GetLargestMessagePayload() const { - return packet_creator_.GetLargestMessagePayload(); +QuicPacketLength QuicPacketGenerator::GetCurrentLargestMessagePayload() const { + return packet_creator_.GetCurrentLargestMessagePayload(); +} + +QuicPacketLength QuicPacketGenerator::GetGuaranteedLargestMessagePayload() + const { + return packet_creator_.GetGuaranteedLargestMessagePayload(); } void QuicPacketGenerator::SetConnectionId(QuicConnectionId connection_id) {
diff --git a/quic/core/quic_packet_generator.h b/quic/core/quic_packet_generator.h index 4c780f5..dc191a0 100644 --- a/quic/core/quic_packet_generator.h +++ b/quic/core/quic_packet_generator.h
@@ -227,7 +227,8 @@ bool FlushAckFrame(const QuicFrames& frames); // Returns the largest payload that will fit into a single MESSAGE frame. - QuicPacketLength GetLargestMessagePayload() const; + QuicPacketLength GetCurrentLargestMessagePayload() const; + QuicPacketLength GetGuaranteedLargestMessagePayload() const; // Update the connection ID used in outgoing packets. void SetConnectionId(QuicConnectionId connection_id);
diff --git a/quic/core/quic_packet_generator_test.cc b/quic/core/quic_packet_generator_test.cc index bc20bfe..09abb31 100644 --- a/quic/core/quic_packet_generator_test.cc +++ b/quic/core/quic_packet_generator_test.cc
@@ -1531,9 +1531,10 @@ EXPECT_EQ( MESSAGE_STATUS_SUCCESS, generator_.AddMessageFrame( - 2, MakeSpan(&allocator_, - std::string(generator_.GetLargestMessagePayload(), 'a'), - &storage))); + 2, MakeSpan( + &allocator_, + std::string(generator_.GetCurrentLargestMessagePayload(), 'a'), + &storage))); EXPECT_TRUE(generator_.HasRetransmittableFrames()); // Failed to send messages which cannot fit into one packet. @@ -1542,7 +1543,8 @@ generator_.AddMessageFrame( 3, MakeSpan(&allocator_, - std::string(generator_.GetLargestMessagePayload() + 10, 'a'), + std::string( + generator_.GetCurrentLargestMessagePayload() + 10, 'a'), &storage))); }
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc index 763d208..01d6feb 100644 --- a/quic/core/quic_session.cc +++ b/quic/core/quic_session.cc
@@ -1623,8 +1623,12 @@ return connection_->session_decides_what_to_write(); } -QuicPacketLength QuicSession::GetLargestMessagePayload() const { - return connection_->GetLargestMessagePayload(); +QuicPacketLength QuicSession::GetCurrentLargestMessagePayload() const { + return connection_->GetCurrentLargestMessagePayload(); +} + +QuicPacketLength QuicSession::GetGuaranteedLargestMessagePayload() const { + return connection_->GetGuaranteedLargestMessagePayload(); } void QuicSession::SendStopSending(uint16_t code, QuicStreamId stream_id) {
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h index 776da8e..b13f627 100644 --- a/quic/core/quic_session.h +++ b/quic/core/quic_session.h
@@ -320,7 +320,12 @@ // Returns the largest payload that will fit into a single MESSAGE frame. // Because overhead can vary during a connection, this method should be // checked for every message. - QuicPacketLength GetLargestMessagePayload() const; + QuicPacketLength GetCurrentLargestMessagePayload() const; + + // Returns the largest payload that will fit into a single MESSAGE frame at + // any point during the connection. This assumes the version and + // connection ID lengths do not change. + QuicPacketLength GetGuaranteedLargestMessagePayload() const; bool goaway_sent() const { return goaway_sent_; }
diff --git a/quic/quartc/quartc_session.cc b/quic/quartc/quartc_session.cc index e5b6952..80031dd 100644 --- a/quic/quartc/quartc_session.cc +++ b/quic/quartc/quartc_session.cc
@@ -47,10 +47,10 @@ return false; } - if (message.size() > GetLargestMessagePayload()) { + if (message.size() > GetCurrentLargestMessagePayload()) { QUIC_LOG(ERROR) << "Message is too big, message_size=" << message.size() - << ", GetLargestMessagePayload=" - << GetLargestMessagePayload(); + << ", GetCurrentLargestMessagePayload=" + << GetCurrentLargestMessagePayload(); return false; }
diff --git a/quic/quartc/quartc_session.h b/quic/quartc/quartc_session.h index 35df8b6..29e6306 100644 --- a/quic/quartc/quartc_session.h +++ b/quic/quartc/quartc_session.h
@@ -51,8 +51,8 @@ bool SendOrQueueMessage(std::string message); // Returns largest message payload acceptable in SendQuartcMessage. - QuicPacketLength GetLargestMessagePayload() const { - return connection()->GetLargestMessagePayload(); + QuicPacketLength GetCurrentLargestMessagePayload() const { + return connection()->GetCurrentLargestMessagePayload(); } // Return true if transport support message frame.
diff --git a/quic/quartc/quartc_session_test.cc b/quic/quartc/quartc_session_test.cc index fc80c8b..31abd57 100644 --- a/quic/quartc/quartc_session_test.cc +++ b/quic/quartc/quartc_session_test.cc
@@ -230,12 +230,12 @@ // Send message of maximum allowed length. std::string message_max_long = - std::string(server_peer_->GetLargestMessagePayload(), 'A'); + std::string(server_peer_->GetCurrentLargestMessagePayload(), 'A'); ASSERT_TRUE(server_peer_->SendOrQueueMessage(message_max_long)); // Send long message which should fail. std::string message_too_long = - std::string(server_peer_->GetLargestMessagePayload() + 1, 'B'); + std::string(server_peer_->GetCurrentLargestMessagePayload() + 1, 'B'); ASSERT_FALSE(server_peer_->SendOrQueueMessage(message_too_long)); // Wait for peer 2 to receive message.