gfe-relnote: Add QuicPacketCreator::SerializeCoalescedPacket function. Not used yet. Not protected. PiperOrigin-RevId: 276529504 Change-Id: Id9ddd1c7af0b52e12e75c754295e10d23058ee62
diff --git a/quic/core/quic_coalesced_packet.cc b/quic/core/quic_coalesced_packet.cc index 62eee42..6d7f872 100644 --- a/quic/core/quic_coalesced_packet.cc +++ b/quic/core/quic_coalesced_packet.cc
@@ -58,20 +58,15 @@ } } - if (packet.encryption_level == ENCRYPTION_INITIAL) { - for (const auto& frame : packet.nonretransmittable_frames) { - if (frame.type == PADDING_FRAME) { - QUIC_BUG << "ENCRYPTION_INITIAL packet should not contain padding " - "frame if trying to send coalesced packet"; - return false; - } - } - } if (length_ + packet.encrypted_length > max_packet_length_) { // Packet does not fit. return false; } - + QUIC_DVLOG(1) << "Successfully coalesced packet: encryption_level: " + << EncryptionLevelToString(packet.encryption_level) + << ", encrypted_length: " << packet.encrypted_length + << ", current length: " << length_ + << ", max_packet_length: " << max_packet_length_; length_ += packet.encrypted_length; if (packet.encryption_level == ENCRYPTION_INITIAL) { // Save a copy of ENCRYPTION_INITIAL packet (excluding encrypted buffer, as @@ -100,4 +95,23 @@ initial_packet_ = nullptr; } +bool QuicCoalescedPacket::CopyEncryptedBuffers(char* buffer, + size_t buffer_len, + size_t* length_copied) const { + *length_copied = 0; + for (const auto& packet : encrypted_buffers_) { + if (packet.empty()) { + continue; + } + if (packet.length() > buffer_len) { + return false; + } + memcpy(buffer, packet.data(), packet.length()); + buffer += packet.length(); + buffer_len -= packet.length(); + *length_copied += packet.length(); + } + return true; +} + } // namespace quic
diff --git a/quic/core/quic_coalesced_packet.h b/quic/core/quic_coalesced_packet.h index 9fbdff2..cb4825a 100644 --- a/quic/core/quic_coalesced_packet.h +++ b/quic/core/quic_coalesced_packet.h
@@ -27,6 +27,17 @@ // Clears this coalesced packet. void Clear(); + // Copies encrypted_buffers_ to |buffer| and sets |length_copied| to the + // copied amount. Returns false if copy fails (i.e., |buffer_len| is not + // enough). + bool CopyEncryptedBuffers(char* buffer, + size_t buffer_len, + size_t* length_copied) const; + + const SerializedPacket* initial_packet() const { + return initial_packet_.get(); + } + QuicPacketLength length() const { return length_; } QuicPacketLength max_packet_length() const { return max_packet_length_; } @@ -43,8 +54,6 @@ QuicPacketLength max_packet_length_; // Copies of packets' encrypted buffers according to different encryption // levels. - // TODO(fayang): Test serialization when implementing - // QuicPacketCreator::SerializeCoalescedPacket. std::string encrypted_buffers_[NUM_ENCRYPTION_LEVELS]; // A copy of ENCRYPTION_INITIAL packet if this coalesced packet contains one.
diff --git a/quic/core/quic_coalesced_packet_test.cc b/quic/core/quic_coalesced_packet_test.cc index aa6b781..6eb5a61 100644 --- a/quic/core/quic_coalesced_packet_test.cc +++ b/quic/core/quic_coalesced_packet_test.cc
@@ -71,6 +71,42 @@ EXPECT_EQ(1000u, coalesced.length()); } +TEST(QuicCoalescedPacketTest, CopyEncryptedBuffers) { + QuicCoalescedPacket coalesced; + SimpleBufferAllocator allocator; + QuicSocketAddress self_address(QuicIpAddress::Loopback4(), 1); + QuicSocketAddress peer_address(QuicIpAddress::Loopback4(), 2); + std::string buffer(500, 'a'); + std::string buffer2(500, 'b'); + SerializedPacket packet1(QuicPacketNumber(1), PACKET_4BYTE_PACKET_NUMBER, + buffer.data(), 500, + /*has_ack=*/false, /*has_stop_waiting=*/false); + packet1.encryption_level = ENCRYPTION_ZERO_RTT; + SerializedPacket packet2(QuicPacketNumber(2), PACKET_4BYTE_PACKET_NUMBER, + buffer2.data(), 500, + /*has_ack=*/false, /*has_stop_waiting=*/false); + packet2.encryption_level = ENCRYPTION_FORWARD_SECURE; + + ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet1, self_address, peer_address, + &allocator, 1500)); + ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet2, self_address, peer_address, + &allocator, 1500)); + EXPECT_EQ(1000u, coalesced.length()); + + char copy_buffer[1000]; + size_t length_copied = 0; + EXPECT_FALSE( + coalesced.CopyEncryptedBuffers(copy_buffer, 900, &length_copied)); + ASSERT_TRUE( + coalesced.CopyEncryptedBuffers(copy_buffer, 1000, &length_copied)); + EXPECT_EQ(1000u, length_copied); + char expected[1000]; + memset(expected, 'a', 500); + memset(expected + 500, 'b', 500); + test::CompareCharArraysWithHexError("copied buffers", copy_buffer, + length_copied, expected, 1000); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc index 0dc2921..fa45cf9 100644 --- a/quic/core/quic_packet_creator.cc +++ b/quic/core/quic_packet_creator.cc
@@ -53,6 +53,38 @@ } } +// ScopedPacketContextSwitcher saves |packet|'s states and change states +// during its construction. When the switcher goes out of scope, it restores +// saved states. +class ScopedPacketContextSwitcher { + public: + ScopedPacketContextSwitcher(QuicPacketNumber packet_number, + QuicPacketNumberLength packet_number_length, + EncryptionLevel encryption_level, + SerializedPacket* packet) + + : saved_packet_number_(packet->packet_number), + saved_packet_number_length_(packet->packet_number_length), + saved_encryption_level_(packet->encryption_level), + packet_(packet) { + packet_->packet_number = packet_number, + packet_->packet_number_length = packet_number_length; + packet_->encryption_level = encryption_level; + } + + ~ScopedPacketContextSwitcher() { + packet_->packet_number = saved_packet_number_; + packet_->packet_number_length = saved_packet_number_length_; + packet_->encryption_level = saved_encryption_level_; + } + + private: + const QuicPacketNumber saved_packet_number_; + const QuicPacketNumberLength saved_packet_number_length_; + const EncryptionLevel saved_encryption_level_; + SerializedPacket* packet_; +}; + } // namespace #define ENDPOINT \ @@ -408,6 +440,53 @@ needs_full_padding_ = false; } +size_t QuicPacketCreator::ReserializeInitialPacketInCoalescedPacket( + const SerializedPacket& packet, + size_t padding_size, + char* buffer, + size_t buffer_len) { + QUIC_BUG_IF(packet.encryption_level != ENCRYPTION_INITIAL); + QUIC_BUG_IF(packet.nonretransmittable_frames.empty() && + packet.retransmittable_frames.empty()) + << "Attempt to serialize empty ENCRYPTION_INITIAL packet in coalesced " + "packet"; + ScopedPacketContextSwitcher switcher( + packet.packet_number - + 1, // -1 because serialize packet increase packet number. + packet.packet_number_length, packet.encryption_level, &packet_); + for (const QuicFrame& frame : packet.nonretransmittable_frames) { + if (!AddFrame(frame, packet.transmission_type)) { + QUIC_BUG << "Failed to serialize frame: " << frame; + return 0; + } + } + for (const QuicFrame& frame : packet.retransmittable_frames) { + if (!AddFrame(frame, packet.transmission_type)) { + QUIC_BUG << "Failed to serialize frame: " << frame; + return 0; + } + } + // Add necessary padding. + if (padding_size > 0) { + QUIC_DVLOG(2) << ENDPOINT << "Add padding of size: " << padding_size; + if (!AddFrame(QuicFrame(QuicPaddingFrame(padding_size)), + packet.transmission_type)) { + QUIC_BUG << "Failed to add padding of size " << padding_size + << " when serializing ENCRYPTION_INITIAL " + "packet in coalesced packet"; + return 0; + } + } + SerializePacket(buffer, buffer_len); + const size_t encrypted_length = packet_.encrypted_length; + // Clear frames in packet_. No need to DeleteFrames since frames are owned by + // initial_packet. + packet_.retransmittable_frames.clear(); + packet_.nonretransmittable_frames.clear(); + ClearPacket(); + return encrypted_length; +} + void QuicPacketCreator::CreateAndSerializeStreamFrame( QuicStreamId id, size_t write_length, @@ -836,6 +915,43 @@ return framer_->BuildDataPacket(header, frames, buffer, packet_length, level); } +size_t QuicPacketCreator::SerializeCoalescedPacket( + const QuicCoalescedPacket& coalesced, + char* buffer, + size_t buffer_len) { + QUIC_BUG_IF(packet_.num_padding_bytes != 0); + if (HasPendingFrames()) { + QUIC_BUG << "Try to serialize coalesced packet with pending frames"; + return 0; + } + QUIC_BUG_IF(coalesced.length() == 0) + << "Attempt to serialize empty coalesced packet"; + size_t packet_length = 0; + if (coalesced.initial_packet() != nullptr) { + size_t initial_length = ReserializeInitialPacketInCoalescedPacket( + *coalesced.initial_packet(), + /*padding_size=*/coalesced.max_packet_length() - coalesced.length(), + buffer, buffer_len); + if (initial_length == 0) { + QUIC_BUG << "Failed to reserialize ENCRYPTION_INITIAL packet in " + "coalesced packet"; + return 0; + } + buffer += initial_length; + buffer_len -= initial_length; + packet_length += initial_length; + } + size_t length_copied = 0; + if (!coalesced.CopyEncryptedBuffers(buffer, buffer_len, &length_copied)) { + return 0; + } + packet_length += length_copied; + QUIC_DVLOG(1) << ENDPOINT + << "Successfully serialized coalesced packet of length: " + << packet_length; + return packet_length; +} + // TODO(b/74062209): Make this a public method of framer? SerializedPacket QuicPacketCreator::NoPacket() { return SerializedPacket(QuicPacketNumber(), PACKET_1BYTE_PACKET_NUMBER,
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h index 518e71d..73e69c1 100644 --- a/quic/core/quic_packet_creator.h +++ b/quic/core/quic_packet_creator.h
@@ -14,6 +14,7 @@ #include <vector> #include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_coalesced_packet.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" @@ -411,6 +412,12 @@ size_t packet_length, EncryptionLevel level); + // Serializes |coalesced| to provided |buffer|, returns coalesced packet + // length if serialization succeeds. Otherwise, returns 0. + size_t SerializeCoalescedPacket(const QuicCoalescedPacket& coalesced, + char* buffer, + size_t buffer_len); + private: friend class test::QuicPacketCreatorPeer; @@ -455,6 +462,16 @@ // Clears all fields of packet_ that should be cleared between serializations. void ClearPacket(); + // Re-serialzes frames of ENCRYPTION_INITIAL packet in coalesced packet with + // the original packet's packet number and packet number length. + // |padding_size| indicates the size of necessary padding. Returns 0 if + // serialization fails. + size_t ReserializeInitialPacketInCoalescedPacket( + const SerializedPacket& packet, + size_t padding_size, + char* buffer, + size_t buffer_len); + // Tries to coalesce |frame| with the back of |queued_frames_|. // Returns true on success. bool MaybeCoalesceStreamFrame(const QuicStreamFrame& frame);
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc index 437f76c..f13805c 100644 --- a/quic/core/quic_packet_creator_test.cc +++ b/quic/core/quic_packet_creator_test.cc
@@ -22,6 +22,7 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" @@ -2096,6 +2097,76 @@ EXPECT_EQ(serialized.encrypted_length, packet.encrypted_length); } +TEST_P(QuicPacketCreatorTest, SerializeCoalescedPacket) { + if (!GetQuicReloadableFlag(quic_populate_nonretransmittable_frames)) { + return; + } + QuicCoalescedPacket coalesced; + SimpleBufferAllocator allocator; + QuicSocketAddress self_address(QuicIpAddress::Loopback4(), 1); + QuicSocketAddress peer_address(QuicIpAddress::Loopback4(), 2); + for (size_t i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { + EncryptionLevel level = static_cast<EncryptionLevel>(i); + creator_.set_encryption_level(level); + QuicAckFrame ack_frame(InitAckFrame(1)); + frames_.push_back(QuicFrame(&ack_frame)); + if (level != ENCRYPTION_INITIAL && level != ENCRYPTION_HANDSHAKE) { + frames_.push_back( + QuicFrame(QuicStreamFrame(1, false, 0u, QuicStringPiece()))); + } + SerializedPacket serialized = SerializeAllFrames(frames_); + EXPECT_EQ(level, serialized.encryption_level); + frames_.clear(); + ASSERT_TRUE(coalesced.MaybeCoalescePacket(serialized, self_address, + peer_address, &allocator, + creator_.max_packet_length())); + } + char buffer[kMaxOutgoingPacketSize]; + size_t coalesced_length = creator_.SerializeCoalescedPacket( + coalesced, buffer, kMaxOutgoingPacketSize); + // Verify packet is padded to full. + ASSERT_EQ(coalesced.max_packet_length(), coalesced_length); + if (!QuicVersionHasLongHeaderLengths(server_framer_.transport_version())) { + return; + } + // Verify packet process. + std::unique_ptr<QuicEncryptedPacket> packets[NUM_ENCRYPTION_LEVELS]; + packets[ENCRYPTION_INITIAL] = + QuicMakeUnique<QuicEncryptedPacket>(buffer, coalesced_length); + for (size_t i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + if (i < ENCRYPTION_FORWARD_SECURE) { + // Save coalesced packet. + EXPECT_CALL(framer_visitor_, OnCoalescedPacket(_)) + .WillOnce(Invoke([i, &packets](const QuicEncryptedPacket& packet) { + packets[i + 1] = packet.Clone(); + })); + } + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnAckFrameStart(_, _)).WillOnce(Return(true)); + EXPECT_CALL(framer_visitor_, + OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2))) + .WillOnce(Return(true)); + EXPECT_CALL(framer_visitor_, OnAckFrameEnd(_)).WillOnce(Return(true)); + if (i == ENCRYPTION_INITIAL) { + // Verify padding is added. + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + } else { + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(testing::AtMost(1)); + } + if (i != ENCRYPTION_INITIAL && i != ENCRYPTION_HANDSHAKE) { + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + } + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + server_framer_.ProcessPacket(*packets[i]); + } +} + } // namespace } // namespace test } // namespace quic