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