gfe-relnote: Add QuicCoalescedPacket class. Not used in prod, not protected.

PiperOrigin-RevId: 276269515
Change-Id: I66c08eedac83bf190f871be093bfea9ec99967c9
diff --git a/quic/core/quic_coalesced_packet.cc b/quic/core/quic_coalesced_packet.cc
new file mode 100644
index 0000000..62eee42
--- /dev/null
+++ b/quic/core/quic_coalesced_packet.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_coalesced_packet.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+QuicCoalescedPacket::QuicCoalescedPacket()
+    : length_(0), max_packet_length_(0) {}
+
+QuicCoalescedPacket::~QuicCoalescedPacket() {
+  Clear();
+}
+
+bool QuicCoalescedPacket::MaybeCoalescePacket(
+    const SerializedPacket& packet,
+    const QuicSocketAddress& self_address,
+    const QuicSocketAddress& peer_address,
+    QuicBufferAllocator* allocator,
+    QuicPacketLength current_max_packet_length) {
+  if (packet.encrypted_length == 0) {
+    QUIC_BUG << "Trying to coalesce an empty packet";
+    return true;
+  }
+  if (length_ == 0) {
+#ifndef NDEBUG
+    for (const auto& buffer : encrypted_buffers_) {
+      DCHECK(buffer.empty());
+    }
+#endif
+    DCHECK(initial_packet_ == nullptr);
+    // This is the first packet, set max_packet_length and self/peer
+    // addresses.
+    max_packet_length_ = current_max_packet_length;
+    self_address_ = self_address;
+    peer_address_ = peer_address;
+  } else {
+    if (self_address_ != self_address || peer_address_ != peer_address) {
+      // Do not coalesce packet with different self/peer addresses.
+      QUIC_DLOG(INFO)
+          << "Cannot coalesce packet because self/peer address changed";
+      return false;
+    }
+    if (max_packet_length_ != current_max_packet_length) {
+      QUIC_DLOG(INFO)
+          << "Do not try to coalesce packet when max packet length changed.";
+      return false;
+    }
+    if (!encrypted_buffers_[packet.encryption_level].empty() ||
+        (packet.encryption_level == ENCRYPTION_INITIAL &&
+         initial_packet_ != nullptr)) {
+      // Do not coalesce packets of the same encryption level.
+      return false;
+    }
+  }
+
+  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;
+  }
+
+  length_ += packet.encrypted_length;
+  if (packet.encryption_level == ENCRYPTION_INITIAL) {
+    // Save a copy of ENCRYPTION_INITIAL packet (excluding encrypted buffer, as
+    // the packet will be re-serialized later).
+    initial_packet_ = QuicWrapUnique<SerializedPacket>(
+        CopySerializedPacket(packet, allocator, /*copy_buffer=*/false));
+    return true;
+  }
+  // Copy encrypted buffer of packets with other encryption levels.
+  encrypted_buffers_[packet.encryption_level] =
+      std::string(packet.encrypted_buffer, packet.encrypted_length);
+  return true;
+}
+
+void QuicCoalescedPacket::Clear() {
+  self_address_ = QuicSocketAddress();
+  peer_address_ = QuicSocketAddress();
+  length_ = 0;
+  max_packet_length_ = 0;
+  for (auto& packet : encrypted_buffers_) {
+    packet.clear();
+  }
+  if (initial_packet_ != nullptr) {
+    ClearSerializedPacket(initial_packet_.get());
+  }
+  initial_packet_ = nullptr;
+}
+
+}  // namespace quic
diff --git a/quic/core/quic_coalesced_packet.h b/quic/core/quic_coalesced_packet.h
new file mode 100644
index 0000000..9fbdff2
--- /dev/null
+++ b/quic/core/quic_coalesced_packet.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_COALESCED_PACKET_H_
+#define QUICHE_QUIC_CORE_QUIC_COALESCED_PACKET_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+// QuicCoalescedPacket is used to buffer multiple packets which can be coalesced
+// into the same UDP datagram.
+class QUIC_EXPORT_PRIVATE QuicCoalescedPacket {
+ public:
+  QuicCoalescedPacket();
+  ~QuicCoalescedPacket();
+
+  // Returns true if |packet| is successfully coalesced with existing packets.
+  // Returns false otherwise.
+  bool MaybeCoalescePacket(const SerializedPacket& packet,
+                           const QuicSocketAddress& self_address,
+                           const QuicSocketAddress& peer_address,
+                           QuicBufferAllocator* allocator,
+                           QuicPacketLength current_max_packet_length);
+
+  // Clears this coalesced packet.
+  void Clear();
+
+  QuicPacketLength length() const { return length_; }
+
+  QuicPacketLength max_packet_length() const { return max_packet_length_; }
+
+ private:
+  // self/peer addresses are set when trying to coalesce the first packet.
+  // Packets with different self/peer addresses cannot be coalesced.
+  QuicSocketAddress self_address_;
+  QuicSocketAddress peer_address_;
+  // Length of this coalesced packet.
+  QuicPacketLength length_;
+  // Max packet length. Do not try to coalesce packet when max packet length
+  // changes (e.g., with MTU discovery).
+  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.
+  // Null otherwise. Please note, the encrypted_buffer field is not copied. The
+  // frames are copied to allow it be re-serialized when this coalesced packet
+  // gets sent.
+  std::unique_ptr<SerializedPacket> initial_packet_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_QUIC_COALESCED_PACKET_H_
diff --git a/quic/core/quic_coalesced_packet_test.cc b/quic/core/quic_coalesced_packet_test.cc
new file mode 100644
index 0000000..aa6b781
--- /dev/null
+++ b/quic/core/quic_coalesced_packet_test.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_coalesced_packet.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+TEST(QuicCoalescedPacketTest, MaybeCoalescePacket) {
+  QuicCoalescedPacket coalesced;
+  SimpleBufferAllocator allocator;
+  EXPECT_EQ(0u, coalesced.length());
+  char buffer[1000];
+  QuicSocketAddress self_address(QuicIpAddress::Loopback4(), 1);
+  QuicSocketAddress peer_address(QuicIpAddress::Loopback4(), 2);
+  SerializedPacket packet1(QuicPacketNumber(1), PACKET_4BYTE_PACKET_NUMBER,
+                           buffer, 500, false, false);
+  QuicAckFrame ack_frame(InitAckFrame(1));
+  packet1.nonretransmittable_frames.push_back(QuicFrame(&ack_frame));
+  packet1.retransmittable_frames.push_back(
+      QuicFrame(QuicStreamFrame(1, true, 0, 100)));
+  ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet1, self_address, peer_address,
+                                            &allocator, 1500));
+  EXPECT_EQ(1500u, coalesced.max_packet_length());
+  EXPECT_EQ(500u, coalesced.length());
+
+  // Cannot coalesce packet of the same encryption level.
+  SerializedPacket packet2(QuicPacketNumber(2), PACKET_4BYTE_PACKET_NUMBER,
+                           buffer, 500, false, false);
+  EXPECT_FALSE(coalesced.MaybeCoalescePacket(packet2, self_address,
+                                             peer_address, &allocator, 1500));
+
+  SerializedPacket packet3(QuicPacketNumber(3), PACKET_4BYTE_PACKET_NUMBER,
+                           buffer, 500, false, false);
+  packet3.nonretransmittable_frames.push_back(QuicFrame(QuicPaddingFrame(100)));
+  packet3.encryption_level = ENCRYPTION_ZERO_RTT;
+  ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet3, self_address, peer_address,
+                                            &allocator, 1500));
+  EXPECT_EQ(1500u, coalesced.max_packet_length());
+  EXPECT_EQ(1000u, coalesced.length());
+
+  SerializedPacket packet4(QuicPacketNumber(4), PACKET_4BYTE_PACKET_NUMBER,
+                           buffer, 500, false, false);
+  packet4.encryption_level = ENCRYPTION_FORWARD_SECURE;
+  // Cannot coalesce packet of changed self/peer address.
+  EXPECT_FALSE(coalesced.MaybeCoalescePacket(
+      packet4, QuicSocketAddress(QuicIpAddress::Loopback4(), 3), peer_address,
+      &allocator, 1500));
+
+  // Packet does not fit.
+  SerializedPacket packet5(QuicPacketNumber(5), PACKET_4BYTE_PACKET_NUMBER,
+                           buffer, 501, false, false);
+  packet5.encryption_level = ENCRYPTION_FORWARD_SECURE;
+  EXPECT_FALSE(coalesced.MaybeCoalescePacket(packet5, self_address,
+                                             peer_address, &allocator, 1500));
+  EXPECT_EQ(1500u, coalesced.max_packet_length());
+  EXPECT_EQ(1000u, coalesced.length());
+
+  // Max packet number length changed.
+  SerializedPacket packet6(QuicPacketNumber(6), PACKET_4BYTE_PACKET_NUMBER,
+                           buffer, 100, false, false);
+  packet6.encryption_level = ENCRYPTION_FORWARD_SECURE;
+  EXPECT_FALSE(coalesced.MaybeCoalescePacket(packet6, self_address,
+                                             peer_address, &allocator, 1000));
+  EXPECT_EQ(1500u, coalesced.max_packet_length());
+  EXPECT_EQ(1000u, coalesced.length());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quic