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