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