You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by ma...@apache.org on 2017/12/13 23:44:37 UTC

[trafficserver] 05/08: Parse and create ack block in draft-08 way

This is an automated email from the ASF dual-hosted git repository.

masaori pushed a commit to branch quic-latest
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit af62ebda49bc8511b61209f59d3fb3f6f6364615
Author: Masakazu Kitajo <ma...@apache.org>
AuthorDate: Wed Dec 13 12:10:50 2017 +0900

    Parse and create ack block in draft-08 way
---
 iocore/net/P_QUICNetVConnection.h                |   8 +-
 iocore/net/QUICNetVConnection.cc                 |  10 +-
 iocore/net/quic/Mock.h                           |  60 +++++----
 iocore/net/quic/QUICAckFrameCreator.cc           |   8 +-
 iocore/net/quic/QUICCongestionController.cc      |  20 +++
 iocore/net/quic/QUICCongestionController.h       |   9 +-
 iocore/net/quic/QUICFrame.cc                     |   1 +
 iocore/net/quic/QUICLossDetector.cc              |  17 ++-
 iocore/net/quic/QUICLossDetector.h               |   4 +-
 iocore/net/quic/QUICPacketTransmitter.h          |   6 +-
 iocore/net/quic/test/test_QUICAckFrameCreator.cc |  16 +--
 iocore/net/quic/test/test_QUICLossDetector.cc    | 150 ++++++++++++++++++-----
 12 files changed, 229 insertions(+), 80 deletions(-)

diff --git a/iocore/net/P_QUICNetVConnection.h b/iocore/net/P_QUICNetVConnection.h
index 6d3a12a..4f4ba37 100644
--- a/iocore/net/P_QUICNetVConnection.h
+++ b/iocore/net/P_QUICNetVConnection.h
@@ -177,7 +177,7 @@ public:
   QUICPacketNumber largest_acked_packet_number() override;
 
   // QUICConnection (QUICPacketTransmitter)
-  virtual void transmit_packet(QUICPacketUPtr packet) override;
+  virtual uint32_t transmit_packet(QUICPacketUPtr packet) override;
   virtual void retransmit_packet(const QUICPacket &packet) override;
   virtual Ptr<ProxyMutex> get_packet_transmitter_mutex() override;
 
@@ -229,8 +229,8 @@ private:
   QUICRemoteFlowController *_remote_flow_controller = nullptr;
   QUICLocalFlowController *_local_flow_controller   = nullptr;
 
-  Queue<UDPPacket> _packet_recv_queue;
-  Queue<QUICPacket> _packet_send_queue;
+  CountQueue<UDPPacket> _packet_recv_queue;
+  CountQueue<QUICPacket> _packet_send_queue;
   std::queue<QUICPacketUPtr> _quic_packet_recv_queue;
   // `_frame_send_queue` is the queue for any type of frame except STREAM frame.
   // The flow contorl doesn't blcok frames in this queue.
@@ -243,7 +243,7 @@ private:
   void _close_packet_write_ready(Event *data);
   Event *_packet_write_ready = nullptr;
 
-  void _transmit_packet(QUICPacketUPtr);
+  uint32_t _transmit_packet(QUICPacketUPtr);
   void _transmit_frame(QUICFrameUPtr);
 
   void _store_frame(ats_unique_buf &buf, size_t &len, bool &retransmittable, QUICPacketType &current_packet_type,
diff --git a/iocore/net/QUICNetVConnection.cc b/iocore/net/QUICNetVConnection.cc
index 013d1ee..228134c 100644
--- a/iocore/net/QUICNetVConnection.cc
+++ b/iocore/net/QUICNetVConnection.cc
@@ -110,7 +110,7 @@ QUICNetVConnection::start(SSL_CTX *ssl_ctx)
   // Create frame handlers
   this->_stream_manager         = new QUICStreamManager(this->connection_id(), this, this->_application_map);
   this->_congestion_controller  = new QUICCongestionController();
-  this->_loss_detector          = new QUICLossDetector(this);
+  this->_loss_detector          = new QUICLossDetector(this, this->_congestion_controller);
   this->_remote_flow_controller = new QUICRemoteConnectionFlowController(0, this);
   this->_local_flow_controller  = new QUICLocalConnectionFlowController(0, this);
 
@@ -216,7 +216,7 @@ QUICNetVConnection::stream_manager()
   return this->_stream_manager;
 }
 
-void
+uint32_t
 QUICNetVConnection::_transmit_packet(QUICPacketUPtr packet)
 {
   SCOPED_MUTEX_LOCK(packet_transmitter_lock, this->_packet_transmitter_mutex, this_ethread());
@@ -225,13 +225,15 @@ QUICNetVConnection::_transmit_packet(QUICPacketUPtr packet)
                packet->size());
   // TODO Remove const_cast
   this->_packet_send_queue.enqueue(const_cast<QUICPacket *>(packet.release()));
+  return this->_packet_send_queue.size;
 }
 
-void
+uint32_t
 QUICNetVConnection::transmit_packet(QUICPacketUPtr packet)
 {
-  this->_transmit_packet(std::move(packet));
+  uint32_t npackets = this->_transmit_packet(std::move(packet));
   this->_schedule_packet_write_ready();
+  return npackets;
 }
 
 void
diff --git a/iocore/net/quic/Mock.h b/iocore/net/quic/Mock.h
index 3050a43..363de99 100644
--- a/iocore/net/quic/Mock.h
+++ b/iocore/net/quic/Mock.h
@@ -157,10 +157,11 @@ public:
     return 0;
   }
 
-  void
+  uint32_t
   transmit_packet(QUICPacketUPtr packet) override
   {
     ++_transmit_count;
+    return 1;
   }
 
   void
@@ -275,16 +276,21 @@ class MockQUICPacketTransmitter : public QUICPacketTransmitter
 {
 public:
   MockQUICPacketTransmitter() : QUICPacketTransmitter() { this->_mutex = new_ProxyMutex(); };
-  void
+
+  uint32_t
   transmit_packet(QUICPacketUPtr packet) override
   {
-    ++_transmit_count;
+    if (packet) {
+      this->transmitted.insert(packet->packet_number());
+      return 1;
+    }
+    return 0;
   }
 
   void
   retransmit_packet(const QUICPacket &packet) override
   {
-    ++_retransmit_count;
+    this->retransmitted.insert(packet.packet_number());
   }
 
   Ptr<ProxyMutex>
@@ -293,9 +299,10 @@ public:
     return this->_mutex;
   }
 
-  int _transmit_count   = 0;
-  int _retransmit_count = 0;
   Ptr<ProxyMutex> _mutex;
+
+  std::set<QUICPacketNumber> transmitted;
+  std::set<QUICPacketNumber> retransmitted;
 };
 
 class MockQUICFrameTransmitter : public QUICFrameTransmitter
@@ -316,25 +323,9 @@ public:
   int frameCount[256] = {0};
 };
 
-class MockQUICLossDetector : public QUICLossDetector
-{
-public:
-  MockQUICLossDetector() : QUICLossDetector(new MockQUICPacketTransmitter()) {}
-  void
-  rcv_frame(std::shared_ptr<const QUICFrame>)
-  {
-  }
-
-  void
-  on_packet_sent(QUICPacketUPtr packet)
-  {
-  }
-};
-
 class MockQUICCongestionController : public QUICCongestionController
 {
 public:
-  MockQUICCongestionController() : QUICCongestionController() {}
   // Override
   virtual QUICErrorUPtr
   handle_frame(std::shared_ptr<const QUICFrame> f) override
@@ -345,6 +336,14 @@ public:
     return QUICErrorUPtr(new QUICNoError());
   }
 
+  virtual void
+  on_packets_lost(std::set<QUICPacketNumber> packets) override
+  {
+    for (auto pn : packets) {
+      lost_packets.insert(pn);
+    }
+  }
+
   // for Test
   int
   getStreamFrameCount()
@@ -370,11 +369,28 @@ public:
     return _totalFrameCount;
   }
 
+  std::set<QUICPacketNumber> lost_packets;
+
 private:
   int _totalFrameCount = 0;
   int _frameCount[256] = {0};
 };
 
+class MockQUICLossDetector : public QUICLossDetector
+{
+public:
+  MockQUICLossDetector() : QUICLossDetector(new MockQUICPacketTransmitter(), new MockQUICCongestionController()) {}
+  void
+  rcv_frame(std::shared_ptr<const QUICFrame>)
+  {
+  }
+
+  void
+  on_packet_sent(QUICPacketUPtr packet)
+  {
+  }
+};
+
 class MockQUICApplication : public QUICApplication
 {
 public:
diff --git a/iocore/net/quic/QUICAckFrameCreator.cc b/iocore/net/quic/QUICAckFrameCreator.cc
index eb58c7f..8c1dafb 100644
--- a/iocore/net/quic/QUICAckFrameCreator.cc
+++ b/iocore/net/quic/QUICAckFrameCreator.cc
@@ -87,10 +87,10 @@ QUICAckFrameCreator::_create_ack_frame()
     }
 
     if (ack_frame) {
-      ack_frame->ack_block_section()->add_ack_block({gap, length});
+      ack_frame->ack_block_section()->add_ack_block({static_cast<uint8_t>(gap - 1), length - 1});
     } else {
       uint16_t delay = (Thread::get_hrtime() - this->_packet_numbers.largest_ack_received_time()) / 1000; // TODO Milliseconds?
-      ack_frame      = QUICFrameFactory::create_ack_frame(largest_ack_number, delay, length);
+      ack_frame      = QUICFrameFactory::create_ack_frame(largest_ack_number, delay, length - 1);
     }
 
     gap             = last_ack_number - this->_packet_numbers[i];
@@ -99,10 +99,10 @@ QUICAckFrameCreator::_create_ack_frame()
   }
 
   if (ack_frame) {
-    ack_frame->ack_block_section()->add_ack_block({gap, length});
+    ack_frame->ack_block_section()->add_ack_block({static_cast<uint8_t>(gap - 1), length - 1});
   } else {
     uint16_t delay = (Thread::get_hrtime() - this->_packet_numbers.largest_ack_received_time()) / 1000; // TODO Milliseconds?
-    ack_frame      = QUICFrameFactory::create_ack_frame(largest_ack_number, delay, length);
+    ack_frame      = QUICFrameFactory::create_ack_frame(largest_ack_number, delay, length - 1);
   }
   return ack_frame;
 }
diff --git a/iocore/net/quic/QUICCongestionController.cc b/iocore/net/quic/QUICCongestionController.cc
index d06902b..96c2b55 100644
--- a/iocore/net/quic/QUICCongestionController.cc
+++ b/iocore/net/quic/QUICCongestionController.cc
@@ -49,3 +49,23 @@ QUICCongestionController::handle_frame(std::shared_ptr<const QUICFrame> frame)
 
   return error;
 }
+
+void
+QUICCongestionController::on_packet_sent()
+{
+}
+
+void
+QUICCongestionController::on_packet_acked()
+{
+}
+
+void
+QUICCongestionController::on_packets_lost(std::set<QUICPacketNumber> packets)
+{
+}
+
+void
+QUICCongestionController::on_rto_verified()
+{
+}
diff --git a/iocore/net/quic/QUICCongestionController.h b/iocore/net/quic/QUICCongestionController.h
index d44b9bf..1f76343 100644
--- a/iocore/net/quic/QUICCongestionController.h
+++ b/iocore/net/quic/QUICCongestionController.h
@@ -23,7 +23,9 @@
 
 #pragma once
 
-#include <QUICFrameHandler.h>
+#include <set>
+#include "QUICTypes.h"
+#include "QUICFrameHandler.h"
 
 // TODO Implement congestion controll.
 // Congestion controller will be required after the 2nd implementation draft.
@@ -33,5 +35,10 @@ public:
   virtual std::vector<QUICFrameType> interests() override;
   virtual QUICErrorUPtr handle_frame(std::shared_ptr<const QUICFrame>) override;
 
+  void on_packet_sent();
+  void on_packet_acked();
+  virtual void on_packets_lost(std::set<QUICPacketNumber> packets);
+  void on_rto_verified();
+
 private:
 };
diff --git a/iocore/net/quic/QUICFrame.cc b/iocore/net/quic/QUICFrame.cc
index a91c78f..12b09f7 100644
--- a/iocore/net/quic/QUICFrame.cc
+++ b/iocore/net/quic/QUICFrame.cc
@@ -607,6 +607,7 @@ QUICAckFrame::AckBlockSection::const_iterator::const_iterator(uint8_t index, con
 QUICAckFrame::AckBlockSection::const_iterator::const_iterator(uint8_t index, const std::vector<QUICAckFrame::AckBlock> *ack_block)
 {
   this->_index      = index;
+  this->_buf        = nullptr;
   this->_ack_blocks = ack_block;
   if (this->_ack_blocks->size()) {
     if (this->_ack_blocks->size() == this->_index) {
diff --git a/iocore/net/quic/QUICLossDetector.cc b/iocore/net/quic/QUICLossDetector.cc
index 31cae5a..d735ae4 100644
--- a/iocore/net/quic/QUICLossDetector.cc
+++ b/iocore/net/quic/QUICLossDetector.cc
@@ -28,7 +28,8 @@
 #define QUICLDDebug(fmt, ...) \
   Debug("quic_loss_detector", "[%" PRIx64 "] " fmt, static_cast<uint64_t>(this->_connection_id), ##__VA_ARGS__)
 
-QUICLossDetector::QUICLossDetector(QUICPacketTransmitter *transmitter) : _transmitter(transmitter)
+QUICLossDetector::QUICLossDetector(QUICPacketTransmitter *transmitter, QUICCongestionController *cc)
+  : _transmitter(transmitter), _cc(cc)
 {
   this->mutex = new_ProxyMutex();
 
@@ -133,7 +134,7 @@ QUICLossDetector::_detect_lost_packets(QUICPacketNumber largest_acked_packet_num
   // Inform the congestion controller of lost packets and
   // lets it decide whether to retransmit immediately.
   if (!lost_packets.empty()) {
-    // TODO cc->on_packets_lost(lost_packets);
+    this->_cc->on_packets_lost(lost_packets);
     for (auto packet_number : lost_packets) {
       this->_decrement_packet_count(packet_number);
       this->_sent_packets.erase(packet_number);
@@ -350,12 +351,16 @@ QUICLossDetector::_determine_newly_acked_packets(const QUICAckFrame &ack_frame)
 {
   std::set<QUICPacketNumber> packets;
   QUICPacketNumber x = ack_frame.largest_acknowledged();
-  packets.insert(x);
+  for (uint64_t i = 0; i <= ack_frame.ack_block_section()->first_ack_block_length(); ++i) {
+    packets.insert(x--);
+  }
   for (auto &&block : *(ack_frame.ack_block_section())) {
-    for (int i = 0; i < block.gap(); ++i) {
-      packets.insert(++x);
+    for (uint64_t i = 0; i <= block.gap(); ++i) {
+      x--;
+    }
+    for (uint64_t i = 0; i <= block.length(); ++i) {
+      packets.insert(x--);
     }
-    x += block.length();
   }
 
   return packets;
diff --git a/iocore/net/quic/QUICLossDetector.h b/iocore/net/quic/QUICLossDetector.h
index b7071fc..d72785c 100644
--- a/iocore/net/quic/QUICLossDetector.h
+++ b/iocore/net/quic/QUICLossDetector.h
@@ -37,11 +37,12 @@
 #include "QUICFrame.h"
 #include "QUICFrameHandler.h"
 #include "QUICPacketTransmitter.h"
+#include "QUICCongestionController.h"
 
 class QUICLossDetector : public Continuation, public QUICFrameHandler
 {
 public:
-  QUICLossDetector(QUICPacketTransmitter *transmitter);
+  QUICLossDetector(QUICPacketTransmitter *transmitter, QUICCongestionController *cc);
 
   int event_handler(int event, Event *edata);
 
@@ -116,4 +117,5 @@ private:
   void _retransmit_handshake_packets();
 
   QUICPacketTransmitter *_transmitter = nullptr;
+  QUICCongestionController *_cc       = nullptr;
 };
diff --git a/iocore/net/quic/QUICPacketTransmitter.h b/iocore/net/quic/QUICPacketTransmitter.h
index 5396a6a..d562257 100644
--- a/iocore/net/quic/QUICPacketTransmitter.h
+++ b/iocore/net/quic/QUICPacketTransmitter.h
@@ -30,11 +30,13 @@ class QUICPacketTransmitter
 {
 public:
   /*
-   * Enqueue a packetfor transmission
+   * Enqueue a packet for transmission
    *
+   * If packet parameter is not passed, it just sends an event without queuing a new packet.
    * This sends QUIC_PACKET_WRITE_READY event.
+   * This return number of packets currently in queue
    */
-  virtual void transmit_packet(QUICPacketUPtr packet) = 0;
+  virtual uint32_t transmit_packet(QUICPacketUPtr packet = QUICPacketUPtr(nullptr, &QUICPacketDeleter::delete_packet)) = 0;
 
   /*
    * Enqueue a packet for retransmission
diff --git a/iocore/net/quic/test/test_QUICAckFrameCreator.cc b/iocore/net/quic/test/test_QUICAckFrameCreator.cc
index 025d90c..09e742d 100644
--- a/iocore/net/quic/test/test_QUICAckFrameCreator.cc
+++ b/iocore/net/quic/test/test_QUICAckFrameCreator.cc
@@ -41,7 +41,7 @@ TEST_CASE("QUICAckFrameCreator", "[quic]")
   CHECK(frame != nullptr);
   CHECK(frame->num_blocks() == 0);
   CHECK(frame->largest_acknowledged() == 1);
-  CHECK(frame->ack_block_section()->first_ack_block_length() == 1);
+  CHECK(frame->ack_block_section()->first_ack_block_length() == 0);
 
   frame = creator.create();
   CHECK(frame == nullptr);
@@ -55,7 +55,7 @@ TEST_CASE("QUICAckFrameCreator", "[quic]")
   CHECK(frame != nullptr);
   CHECK(frame->num_blocks() == 0);
   CHECK(frame->largest_acknowledged() == 5);
-  CHECK(frame->ack_block_section()->first_ack_block_length() == 4);
+  CHECK(frame->ack_block_section()->first_ack_block_length() == 3);
 
   // Loss
   creator.update(6, true);
@@ -65,8 +65,8 @@ TEST_CASE("QUICAckFrameCreator", "[quic]")
   CHECK(frame != nullptr);
   CHECK(frame->num_blocks() == 1);
   CHECK(frame->largest_acknowledged() == 10);
-  CHECK(frame->ack_block_section()->first_ack_block_length() == 1);
-  CHECK(frame->ack_block_section()->begin()->gap() == 2);
+  CHECK(frame->ack_block_section()->first_ack_block_length() == 0);
+  CHECK(frame->ack_block_section()->begin()->gap() == 1);
 }
 
 TEST_CASE("QUICAckFrameCreator_loss_recover", "[quic]")
@@ -88,8 +88,8 @@ TEST_CASE("QUICAckFrameCreator_loss_recover", "[quic]")
   CHECK(frame != nullptr);
   CHECK(frame->num_blocks() == 2);
   CHECK(frame->largest_acknowledged() == 9);
-  CHECK(frame->ack_block_section()->first_ack_block_length() == 2);
-  CHECK(frame->ack_block_section()->begin()->gap() == 1);
+  CHECK(frame->ack_block_section()->first_ack_block_length() == 1);
+  CHECK(frame->ack_block_section()->begin()->gap() == 0);
 
   frame = creator.create();
   CHECK(frame == nullptr);
@@ -100,8 +100,8 @@ TEST_CASE("QUICAckFrameCreator_loss_recover", "[quic]")
   CHECK(frame != nullptr);
   CHECK(frame->num_blocks() == 1);
   CHECK(frame->largest_acknowledged() == 7);
-  CHECK(frame->ack_block_section()->first_ack_block_length() == 1);
-  CHECK(frame->ack_block_section()->begin()->gap() == 2);
+  CHECK(frame->ack_block_section()->first_ack_block_length() == 0);
+  CHECK(frame->ack_block_section()->begin()->gap() == 1);
 }
 
 TEST_CASE("QUICAckFrameCreator_QUICAckPacketNumbers", "[quic]")
diff --git a/iocore/net/quic/test/test_QUICLossDetector.cc b/iocore/net/quic/test/test_QUICLossDetector.cc
index af39c41..17f6029 100644
--- a/iocore/net/quic/test/test_QUICLossDetector.cc
+++ b/iocore/net/quic/test/test_QUICLossDetector.cc
@@ -27,33 +27,127 @@
 #include "QUICEvents.h"
 #include "Mock.h"
 
-TEST_CASE("QUICLossDetector_Loss_in_Handshake", "[quic]")
+TEST_CASE("QUICLossDetector_Loss", "[quic]")
 {
-  MockQUICPacketTransmitter *tx = new MockQUICPacketTransmitter();
-  QUICLossDetector detector(tx);
-
-  // Check initial state
-  CHECK(tx->_retransmit_count == 0);
-
-  // Send SERVER_CLEARTEXT (Handshake message)
-  uint8_t raw[4]         = {0};
-  ats_unique_buf payload = ats_unique_malloc(sizeof(raw));
-  memcpy(payload.get(), raw, sizeof(raw));
-
-  QUICPacketHeader *header = QUICPacketHeader::build(QUICPacketType::HANDSHAKE, 0xffddbb9977553311ULL, 0x00000001, 0, 0x00112233,
-                                                     std::move(payload), sizeof(raw));
-  QUICPacketUPtr packet =
-    QUICPacketUPtr(new QUICPacket(header, std::move(payload), sizeof(raw), true), [](QUICPacket *p) { delete p; });
-  detector.on_packet_sent(std::move(packet));
-  ink_hrtime_sleep(HRTIME_MSECONDS(1000));
-  CHECK(tx->_retransmit_count > 0);
-
-  // Receive ACK
-  std::shared_ptr<QUICAckFrame> frame = std::make_shared<QUICAckFrame>(0x01, 20, 0);
-  frame->ack_block_section()->add_ack_block({0, 1ULL});
-  detector.handle_frame(frame);
-  ink_hrtime_sleep(HRTIME_MSECONDS(1500));
-  int retransmit_count = tx->_retransmit_count;
-  ink_hrtime_sleep(HRTIME_MSECONDS(1500));
-  CHECK(tx->_retransmit_count == retransmit_count);
+  MockQUICCrypto crypto;
+  QUICPacketFactory pf;
+  pf.set_crypto_module(&crypto);
+
+  QUICAckFrameCreator *afc         = new QUICAckFrameCreator();
+  QUICConnectionId connection_id   = 1;
+  MockQUICPacketTransmitter *tx    = new MockQUICPacketTransmitter();
+  MockQUICCongestionController *cc = new MockQUICCongestionController();
+  QUICLossDetector detector(tx, cc);
+  ats_unique_buf payload              = ats_unique_malloc(16);
+  size_t payload_len                  = 16;
+  QUICPacketUPtr packet               = QUICPacketFactory::create_null_packet();
+  std::shared_ptr<QUICAckFrame> frame = QUICFrameFactory::create_null_ack_frame();
+  uint16_t ack_delay                  = 50;
+
+  SECTION("Handshake")
+  {
+    // Check initial state
+    CHECK(tx->retransmitted.size() == 0);
+
+    // Send SERVER_CLEARTEXT (Handshake message)
+    uint8_t raw[4]         = {0};
+    ats_unique_buf payload = ats_unique_malloc(sizeof(raw));
+    memcpy(payload.get(), raw, sizeof(raw));
+
+    QUICPacketHeader *header = QUICPacketHeader::build(QUICPacketType::HANDSHAKE, 0xffddbb9977553311ULL, 0x00000001, 0, 0x00112233,
+                                                       std::move(payload), sizeof(raw));
+    QUICPacketUPtr packet =
+      QUICPacketUPtr(new QUICPacket(header, std::move(payload), sizeof(raw), true), [](QUICPacket *p) { delete p; });
+    detector.on_packet_sent(std::move(packet));
+    ink_hrtime_sleep(HRTIME_MSECONDS(1000));
+    CHECK(tx->retransmitted.size() > 0);
+
+    // Receive ACK
+    std::shared_ptr<QUICAckFrame> frame = std::make_shared<QUICAckFrame>(0x01, 20, 0);
+    frame->ack_block_section()->add_ack_block({0, 1ULL});
+    detector.handle_frame(frame);
+    ink_hrtime_sleep(HRTIME_MSECONDS(1500));
+    int retransmit_count = tx->retransmitted.size();
+    ink_hrtime_sleep(HRTIME_MSECONDS(1500));
+    CHECK(tx->retransmitted.size() == retransmit_count);
+  }
+
+  SECTION("1-RTT")
+  {
+    // Check initial state
+    CHECK(tx->retransmitted.size() == 0);
+
+    // Send packet (1) to (7)
+    payload                = ats_unique_malloc(16);
+    QUICPacketUPtr packet1 = pf.create_server_protected_packet(connection_id, detector.largest_acked_packet_number(),
+                                                               std::move(payload), payload_len, true);
+    payload                = ats_unique_malloc(16);
+    QUICPacketUPtr packet2 = pf.create_server_protected_packet(connection_id, detector.largest_acked_packet_number(),
+                                                               std::move(payload), payload_len, true);
+    payload                = ats_unique_malloc(16);
+    QUICPacketUPtr packet3 = pf.create_server_protected_packet(connection_id, detector.largest_acked_packet_number(),
+                                                               std::move(payload), payload_len, true);
+    payload                = ats_unique_malloc(16);
+    QUICPacketUPtr packet4 = pf.create_server_protected_packet(connection_id, detector.largest_acked_packet_number(),
+                                                               std::move(payload), payload_len, true);
+    payload                = ats_unique_malloc(16);
+    QUICPacketUPtr packet5 = pf.create_server_protected_packet(connection_id, detector.largest_acked_packet_number(),
+                                                               std::move(payload), payload_len, true);
+    payload                = ats_unique_malloc(16);
+    QUICPacketUPtr packet6 = pf.create_server_protected_packet(connection_id, detector.largest_acked_packet_number(),
+                                                               std::move(payload), payload_len, true);
+    payload                = ats_unique_malloc(16);
+    QUICPacketUPtr packet7 = pf.create_server_protected_packet(connection_id, detector.largest_acked_packet_number(),
+                                                               std::move(payload), payload_len, true);
+    payload                = ats_unique_malloc(16);
+    QUICPacketUPtr packet8 = pf.create_server_protected_packet(connection_id, detector.largest_acked_packet_number(),
+                                                               std::move(payload), payload_len, true);
+    payload                = ats_unique_malloc(16);
+    QUICPacketUPtr packet9 = pf.create_server_protected_packet(connection_id, detector.largest_acked_packet_number(),
+                                                               std::move(payload), payload_len, true);
+
+    QUICPacketNumber pn1 = packet1->packet_number();
+    QUICPacketNumber pn2 = packet2->packet_number();
+    QUICPacketNumber pn3 = packet3->packet_number();
+    QUICPacketNumber pn4 = packet4->packet_number();
+    QUICPacketNumber pn5 = packet5->packet_number();
+    QUICPacketNumber pn6 = packet6->packet_number();
+    QUICPacketNumber pn7 = packet7->packet_number();
+    QUICPacketNumber pn8 = packet8->packet_number();
+    QUICPacketNumber pn9 = packet9->packet_number();
+
+    detector.on_packet_sent(std::move(packet1));
+    detector.on_packet_sent(std::move(packet2));
+    detector.on_packet_sent(std::move(packet3));
+    detector.on_packet_sent(std::move(packet4));
+    detector.on_packet_sent(std::move(packet5));
+    detector.on_packet_sent(std::move(packet6));
+    detector.on_packet_sent(std::move(packet7));
+    detector.on_packet_sent(std::move(packet8));
+    detector.on_packet_sent(std::move(packet9));
+
+    ink_hrtime_sleep(HRTIME_MSECONDS(10000));
+
+    // Receive an ACK for (1) (4) (5) (7) (8) (9)
+    afc->update(pn1, true);
+    afc->update(pn4, true);
+    afc->update(pn5, true);
+    afc->update(pn7, true);
+    afc->update(pn8, true);
+    afc->update(pn9, true);
+    frame = afc->create();
+    detector.handle_frame(frame);
+
+    CHECK(cc->lost_packets.size() == 3);
+
+    CHECK(cc->lost_packets.find(pn1) == cc->lost_packets.end());
+    CHECK(cc->lost_packets.find(pn2) != cc->lost_packets.end());
+    CHECK(cc->lost_packets.find(pn3) != cc->lost_packets.end());
+    CHECK(cc->lost_packets.find(pn4) == cc->lost_packets.end());
+    CHECK(cc->lost_packets.find(pn5) == cc->lost_packets.end());
+    CHECK(cc->lost_packets.find(pn6) != cc->lost_packets.end());
+    CHECK(cc->lost_packets.find(pn7) == cc->lost_packets.end());
+    CHECK(cc->lost_packets.find(pn8) == cc->lost_packets.end());
+    CHECK(cc->lost_packets.find(pn9) == cc->lost_packets.end());
+  }
 }

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.