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 2022/09/07 23:29:48 UTC

[trafficserver] branch 10-Dev updated: Fix ATS original QUIC impl (#9079)

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

maskit pushed a commit to branch 10-Dev
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/10-Dev by this push:
     new c9b046c04 Fix ATS original QUIC impl (#9079)
c9b046c04 is described below

commit c9b046c0456a7293e45b098ac1727fc5872df7d6
Author: Masakazu Kitajo <ma...@apache.org>
AuthorDate: Thu Sep 8 08:29:42 2022 +0900

    Fix ATS original QUIC impl (#9079)
    
    * Fix UDP socket initialization on QUIC original impl
    
    * Fix a use-after-free issue in H3 native impl
    
    * Update unit tests
    
    * Add back a test for H3
---
 iocore/net/QUICNetProcessor.cc                   |   3 -
 iocore/net/QUICNetVConnection.cc                 |   2 +
 iocore/net/QUICPacketHandler.cc                  |  17 +++-
 iocore/net/quic/Mock.h                           |   2 +-
 iocore/net/quic/QUICAckFrameCreator.cc           |   3 +-
 iocore/net/quic/QUICAckFrameCreator.h            |   2 +-
 iocore/net/quic/QUICAltConnectionManager.cc      |   3 +-
 iocore/net/quic/QUICAltConnectionManager.h       |   2 +-
 iocore/net/quic/QUICBidirectionalStream.cc       |  32 +++++--
 iocore/net/quic/QUICBidirectionalStream.h        |   2 +-
 iocore/net/quic/QUICCryptoStream.cc              |   7 +-
 iocore/net/quic/QUICCryptoStream.h               |   2 +-
 iocore/net/quic/QUICFlowController.cc            |  36 +++++---
 iocore/net/quic/QUICFlowController.h             |  15 ++--
 iocore/net/quic/QUICFrameGenerator.h             |   5 +-
 iocore/net/quic/QUICFrameRetransmitter.h         |   1 +
 iocore/net/quic/QUICHandshake.cc                 |   4 +-
 iocore/net/quic/QUICHandshake.h                  |   2 +-
 iocore/net/quic/QUICPathValidator.cc             |   3 +-
 iocore/net/quic/QUICPathValidator.h              |   2 +-
 iocore/net/quic/QUICStreamBase.cc                |  16 ++++
 iocore/net/quic/QUICStreamManager_native.cc      |  26 +++++-
 iocore/net/quic/QUICStreamManager_native.h       |   4 +-
 iocore/net/quic/QUICStreamVCAdapter.cc           |  14 +--
 iocore/net/quic/QUICStreamVCAdapter.h            |   1 +
 iocore/net/quic/QUICStream_native.h              |   3 +
 iocore/net/quic/QUICTokenCreator.cc              |   2 +-
 iocore/net/quic/QUICTokenCreator.h               |   2 +-
 iocore/net/quic/QUICUnidirectionalStream.cc      |  29 ++++---
 iocore/net/quic/QUICUnidirectionalStream.h       |   4 +-
 iocore/net/quic/test/test_QUICAckFrameCreator.cc |  46 +++++-----
 iocore/net/quic/test/test_QUICFlowController.cc  |  72 +++++++--------
 iocore/net/quic/test/test_QUICLossDetector.cc    |   4 +-
 iocore/net/quic/test/test_QUICPathValidator.cc   |   4 +-
 iocore/net/quic/test/test_QUICStream.cc          | 106 +++++++++++------------
 iocore/net/quic/test/test_QUICStreamManager.cc   |   4 +-
 proxy/http3/Http3App.cc                          |   1 +
 tests/gold_tests/timeout/active_timeout.test.py  |  16 ++--
 38 files changed, 301 insertions(+), 198 deletions(-)

diff --git a/iocore/net/QUICNetProcessor.cc b/iocore/net/QUICNetProcessor.cc
index 93d715230..c5b78222c 100644
--- a/iocore/net/QUICNetProcessor.cc
+++ b/iocore/net/QUICNetProcessor.cc
@@ -220,8 +220,5 @@ QUICNetProcessor::main_accept(Continuation *cont, SOCKET fd, AcceptOptions const
   na->action_->server = &na->server;
   na->init_accept();
 
-  SCOPED_MUTEX_LOCK(lock, na->mutex, this_ethread());
-  udpNet.UDPBind((Continuation *)na, &na->server.accept_addr.sa, fd, 1048576, 1048576);
-
   return na->action_.get();
 }
diff --git a/iocore/net/QUICNetVConnection.cc b/iocore/net/QUICNetVConnection.cc
index 18d087a93..07638e276 100644
--- a/iocore/net/QUICNetVConnection.cc
+++ b/iocore/net/QUICNetVConnection.cc
@@ -1678,6 +1678,8 @@ QUICNetVConnection::_packetize_frames(uint8_t *packet_buf, QUICEncryptionLevel l
       frame =
         g->generate_frame(frame_instance_buffer, level, this->_remote_flow_controller->credit(), max_frame_size, len, seq_num);
       if (frame) {
+        ink_release_assert(dynamic_cast<QUICStreamBase *>(frame->generated_by()) == nullptr);
+        ink_release_assert(dynamic_cast<QUICBidirectionalStream *>(frame->generated_by()) == nullptr);
         this->_context->trigger(QUICContext::CallbackEvent::FRAME_PACKETIZE, *frame);
         // Some frame types must not be sent on Initial and Handshake packets
         switch (auto t = frame->type(); level) {
diff --git a/iocore/net/QUICPacketHandler.cc b/iocore/net/QUICPacketHandler.cc
index a30e9beff..d555b74ae 100644
--- a/iocore/net/QUICPacketHandler.cc
+++ b/iocore/net/QUICPacketHandler.cc
@@ -167,7 +167,7 @@ int
 QUICPacketHandlerIn::acceptEvent(int event, void *data)
 {
   // NetVConnection *netvc;
-  ink_release_assert(event == NET_EVENT_DATAGRAM_OPEN || event == NET_EVENT_DATAGRAM_READ_READY ||
+  ink_release_assert(event == EVENT_IMMEDIATE || event == NET_EVENT_DATAGRAM_OPEN || event == NET_EVENT_DATAGRAM_READ_READY ||
                      event == NET_EVENT_DATAGRAM_ERROR);
   ink_release_assert((event == NET_EVENT_DATAGRAM_OPEN) ? (data != nullptr) : (1));
   ink_release_assert((event == NET_EVENT_DATAGRAM_READ_READY) ? (data != nullptr) : (1));
@@ -186,6 +186,11 @@ QUICPacketHandlerIn::acceptEvent(int event, void *data)
       this->_recv_packet(event, packet_r);
     }
     return EVENT_CONT;
+  } else if (event == EVENT_IMMEDIATE) {
+    this->setThreadAffinity(this_ethread());
+    SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
+    udpNet.UDPBind((Continuation *)this, &this->server.accept_addr.sa, -1, 1048576, 1048576);
+    return EVENT_CONT;
   }
 
   /////////////////
@@ -202,7 +207,17 @@ QUICPacketHandlerIn::acceptEvent(int event, void *data)
 void
 QUICPacketHandlerIn::init_accept(EThread *t = nullptr)
 {
+  int i, n;
+
   SET_HANDLER(&QUICPacketHandlerIn::acceptEvent);
+
+  n = eventProcessor.thread_group[ET_UDP]._count;
+  for (i = 0; i < n; i++) {
+    NetAccept *a = (i < n - 1) ? clone() : this;
+    EThread *t   = eventProcessor.thread_group[ET_UDP]._thread[i];
+    a->mutex     = get_NetHandler(t)->mutex;
+    t->schedule_imm(a);
+  }
 }
 
 Continuation *
diff --git a/iocore/net/quic/Mock.h b/iocore/net/quic/Mock.h
index 442c8eab0..a7c178619 100644
--- a/iocore/net/quic/Mock.h
+++ b/iocore/net/quic/Mock.h
@@ -1071,7 +1071,7 @@ public:
 
   QUICFrame *
   generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                 size_t current_packet_size, uint32_t seq_num) override
+                 size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override
   {
     QUICFrame *frame              = QUICFrameFactory::create_ping_frame(buf, 0, this);
     QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc());
diff --git a/iocore/net/quic/QUICAckFrameCreator.cc b/iocore/net/quic/QUICAckFrameCreator.cc
index bee2b76bc..2b494508d 100644
--- a/iocore/net/quic/QUICAckFrameCreator.cc
+++ b/iocore/net/quic/QUICAckFrameCreator.cc
@@ -61,7 +61,8 @@ QUICAckFrameManager::update(QUICEncryptionLevel level, QUICPacketNumber packet_n
  */
 QUICFrame *
 QUICAckFrameManager::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t /* connection_credit */,
-                                    uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num)
+                                    uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num,
+                                    QUICFrameGenerator *owner)
 {
   QUICAckFrame *ack_frame = nullptr;
 
diff --git a/iocore/net/quic/QUICAckFrameCreator.h b/iocore/net/quic/QUICAckFrameCreator.h
index 55e20f254..ab1fabd83 100644
--- a/iocore/net/quic/QUICAckFrameCreator.h
+++ b/iocore/net/quic/QUICAckFrameCreator.h
@@ -106,7 +106,7 @@ public:
    * Calls create directly.
    */
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t seq_num) override;
+                            size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override;
 
   QUICFrameId issue_frame_id();
   uint8_t ack_delay_exponent() const;
diff --git a/iocore/net/quic/QUICAltConnectionManager.cc b/iocore/net/quic/QUICAltConnectionManager.cc
index 5226a1dee..10f6ab54e 100644
--- a/iocore/net/quic/QUICAltConnectionManager.cc
+++ b/iocore/net/quic/QUICAltConnectionManager.cc
@@ -299,7 +299,8 @@ QUICAltConnectionManager::will_generate_frame(QUICEncryptionLevel level, size_t
  */
 QUICFrame *
 QUICAltConnectionManager::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t /* connection_credit */,
-                                         uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num)
+                                         uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num,
+                                         QUICFrameGenerator *owner)
 {
   QUICFrame *frame = nullptr;
   if (!this->_is_level_matched(level)) {
diff --git a/iocore/net/quic/QUICAltConnectionManager.h b/iocore/net/quic/QUICAltConnectionManager.h
index ade5f8942..37d683788 100644
--- a/iocore/net/quic/QUICAltConnectionManager.h
+++ b/iocore/net/quic/QUICAltConnectionManager.h
@@ -82,7 +82,7 @@ public:
   // QUICFrameGenerator
   bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override;
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t seq_num) override;
+                            size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override;
 
 private:
   struct AltConnectionInfo {
diff --git a/iocore/net/quic/QUICBidirectionalStream.cc b/iocore/net/quic/QUICBidirectionalStream.cc
index a8dccbf20..18c54496c 100644
--- a/iocore/net/quic/QUICBidirectionalStream.cc
+++ b/iocore/net/quic/QUICBidirectionalStream.cc
@@ -190,9 +190,10 @@ QUICBidirectionalStream::will_generate_frame(QUICEncryptionLevel level, size_t c
 
 QUICFrame *
 QUICBidirectionalStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit,
-                                        uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num)
+                                        uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num,
+                                        QUICFrameGenerator *owner)
 {
-  QUICFrame *frame = this->create_retransmitted_frame(buf, level, maximum_frame_size, this->_issue_frame_id(), this);
+  QUICFrame *frame = this->create_retransmitted_frame(buf, level, maximum_frame_size, this->_issue_frame_id(), owner);
   if (frame != nullptr) {
     ink_assert(frame->type() == QUICFrameType::STREAM);
     this->_records_stream_frame(level, *static_cast<QUICStreamFrame *>(frame));
@@ -201,7 +202,7 @@ QUICBidirectionalStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level,
 
   // RESET_STREAM
   if (this->_reset_reason && !this->_is_reset_sent) {
-    frame = QUICFrameFactory::create_rst_stream_frame(buf, *this->_reset_reason, this->_issue_frame_id(), this);
+    frame = QUICFrameFactory::create_rst_stream_frame(buf, *this->_reset_reason, this->_issue_frame_id(), owner);
     if (frame->size() > maximum_frame_size) {
       frame->~QUICFrame();
       return nullptr;
@@ -216,8 +217,8 @@ QUICBidirectionalStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level,
 
   // STOP_SENDING
   if (this->_stop_sending_reason && !this->_is_stop_sending_sent) {
-    frame =
-      QUICFrameFactory::create_stop_sending_frame(buf, this->id(), this->_stop_sending_reason->code, this->_issue_frame_id(), this);
+    frame = QUICFrameFactory::create_stop_sending_frame(buf, this->id(), this->_stop_sending_reason->code, this->_issue_frame_id(),
+                                                        owner);
     if (frame->size() > maximum_frame_size) {
       frame->~QUICFrame();
       return nullptr;
@@ -231,7 +232,8 @@ QUICBidirectionalStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level,
   }
 
   // MAX_STREAM_DATA
-  frame = this->_local_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num);
+  frame =
+    this->_local_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num, owner);
   if (frame) {
     // maximum_frame_size should be checked in QUICFlowController
     return frame;
@@ -266,8 +268,8 @@ QUICBidirectionalStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level,
     uint64_t stream_credit = this->_remote_flow_controller.credit();
     if (stream_credit == 0) {
       // STREAM_DATA_BLOCKED
-      frame =
-        this->_remote_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num);
+      frame = this->_remote_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num,
+                                                           owner);
       return frame;
     }
 
@@ -292,7 +294,7 @@ QUICBidirectionalStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level,
   // STREAM - Pure FIN or data length is lager than 0
   // FIXME has_length_flag and has_offset_flag should be configurable
   frame = QUICFrameFactory::create_stream_frame(buf, block, this->_id, this->_send_offset, fin, true, true, this->_issue_frame_id(),
-                                                this);
+                                                owner);
   if (!this->_state.is_allowed_to_send(*frame)) {
     QUICStreamDebug("Canceled sending %s frame due to the stream state", QUICDebugNames::frame_type(frame->type()));
     return frame;
@@ -338,6 +340,12 @@ QUICBidirectionalStream::_on_frame_acked(QUICFrameInformationUPtr &info)
       this->_is_transfer_complete = true;
     }
     break;
+  case QUICFrameType::STREAM_DATA_BLOCKED:
+    this->_remote_flow_controller.on_frame_acked(info);
+    break;
+  case QUICFrameType::MAX_STREAM_DATA:
+    this->_local_flow_controller.on_frame_acked(info);
+    break;
   case QUICFrameType::STOP_SENDING:
   default:
     break;
@@ -367,6 +375,12 @@ QUICBidirectionalStream::_on_frame_lost(QUICFrameInformationUPtr &info)
   case QUICFrameType::STOP_SENDING:
     this->_is_stop_sending_sent = false;
     break;
+  case QUICFrameType::STREAM_DATA_BLOCKED:
+    this->_remote_flow_controller.on_frame_lost(info);
+    break;
+  case QUICFrameType::MAX_STREAM_DATA:
+    this->_local_flow_controller.on_frame_lost(info);
+    break;
   default:
     break;
   }
diff --git a/iocore/net/quic/QUICBidirectionalStream.h b/iocore/net/quic/QUICBidirectionalStream.h
index 49b22c7ae..30295279e 100644
--- a/iocore/net/quic/QUICBidirectionalStream.h
+++ b/iocore/net/quic/QUICBidirectionalStream.h
@@ -44,7 +44,7 @@ public:
   // QUICFrameGenerator
   bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override;
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t seq_num) override;
+                            size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override;
 
   virtual QUICConnectionErrorUPtr recv(const QUICStreamFrame &frame) override;
   virtual QUICConnectionErrorUPtr recv(const QUICMaxStreamDataFrame &frame) override;
diff --git a/iocore/net/quic/QUICCryptoStream.cc b/iocore/net/quic/QUICCryptoStream.cc
index ba3afa277..556918efc 100644
--- a/iocore/net/quic/QUICCryptoStream.cc
+++ b/iocore/net/quic/QUICCryptoStream.cc
@@ -112,7 +112,8 @@ QUICCryptoStream::will_generate_frame(QUICEncryptionLevel level, size_t current_
  */
 QUICFrame *
 QUICCryptoStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t /* connection_credit */,
-                                 uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num)
+                                 uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num,
+                                 QUICFrameGenerator *owner)
 {
   QUICConnectionErrorUPtr error = nullptr;
 
@@ -120,7 +121,7 @@ QUICCryptoStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64
     return QUICFrameFactory::create_rst_stream_frame(buf, *this->_reset_reason);
   }
 
-  QUICFrame *frame = this->create_retransmitted_frame(buf, level, maximum_frame_size, this->_issue_frame_id(), this);
+  QUICFrame *frame = this->create_retransmitted_frame(buf, level, maximum_frame_size, this->_issue_frame_id(), owner);
   if (frame != nullptr) {
     ink_assert(frame->type() == QUICFrameType::CRYPTO);
     this->_records_crypto_frame(level, *static_cast<QUICCryptoFrame *>(frame));
@@ -143,7 +144,7 @@ QUICCryptoStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64
   block->_end = std::min(block->start() + frame_payload_size, block->_buf_end);
   ink_assert(static_cast<uint64_t>(block->read_avail()) == frame_payload_size);
 
-  frame = QUICFrameFactory::create_crypto_frame(buf, block, this->_send_offset, this->_issue_frame_id(), this);
+  frame = QUICFrameFactory::create_crypto_frame(buf, block, this->_send_offset, this->_issue_frame_id(), owner);
   this->_send_offset += frame_payload_size;
   this->_write_buffer_reader->consume(frame_payload_size);
   this->_records_crypto_frame(level, *static_cast<QUICCryptoFrame *>(frame));
diff --git a/iocore/net/quic/QUICCryptoStream.h b/iocore/net/quic/QUICCryptoStream.h
index 781d38f38..a1331f82d 100644
--- a/iocore/net/quic/QUICCryptoStream.h
+++ b/iocore/net/quic/QUICCryptoStream.h
@@ -55,7 +55,7 @@ public:
   // QUICFrameGenerator
   bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override;
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t seq_num) override;
+                            size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override;
 
 private:
   void _on_frame_acked(QUICFrameInformationUPtr &info) override;
diff --git a/iocore/net/quic/QUICFlowController.cc b/iocore/net/quic/QUICFlowController.cc
index 647e4c485..25864b742 100644
--- a/iocore/net/quic/QUICFlowController.cc
+++ b/iocore/net/quic/QUICFlowController.cc
@@ -87,6 +87,18 @@ QUICFlowController::forward_limit(QUICOffset limit)
   this->_limit = limit;
 }
 
+void
+QUICFlowController::on_frame_acked(QUICFrameInformationUPtr &info)
+{
+  this->_on_frame_acked(info);
+}
+
+void
+QUICFlowController::on_frame_lost(QUICFrameInformationUPtr &info)
+{
+  this->_on_frame_lost(info);
+}
+
 void
 QUICFlowController::set_limit(QUICOffset limit)
 {
@@ -110,16 +122,20 @@ QUICFlowController::will_generate_frame(QUICEncryptionLevel level, size_t curren
  */
 QUICFrame *
 QUICFlowController::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t /* connection_credit */,
-                                   uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num)
+                                   uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num,
+                                   QUICFrameGenerator *owner)
 {
   QUICFrame *frame = nullptr;
+  if (owner == nullptr) {
+    owner = this;
+  }
 
   if (!this->_is_level_matched(level)) {
     return frame;
   }
 
   if (this->_should_create_frame) {
-    frame = this->_create_frame(buf);
+    frame = this->_create_frame(buf, owner);
     if (frame) {
       if (frame->size() <= maximum_frame_size) {
         this->_should_create_frame                    = false;
@@ -231,25 +247,25 @@ QUICLocalFlowController::_need_to_forward_limit()
 // QUIC[Remote|Local][Connection|Stream]FlowController
 //
 QUICFrame *
-QUICRemoteConnectionFlowController::_create_frame(uint8_t *buf)
+QUICRemoteConnectionFlowController::_create_frame(uint8_t *buf, QUICFrameGenerator *owner)
 {
-  return QUICFrameFactory::create_data_blocked_frame(buf, this->_offset, this->_issue_frame_id(), this);
+  return QUICFrameFactory::create_data_blocked_frame(buf, this->_offset, this->_issue_frame_id(), owner);
 }
 
 QUICFrame *
-QUICLocalConnectionFlowController::_create_frame(uint8_t *buf)
+QUICLocalConnectionFlowController::_create_frame(uint8_t *buf, QUICFrameGenerator *owner)
 {
-  return QUICFrameFactory::create_max_data_frame(buf, this->_limit, this->_issue_frame_id(), this);
+  return QUICFrameFactory::create_max_data_frame(buf, this->_limit, this->_issue_frame_id(), owner);
 }
 
 QUICFrame *
-QUICRemoteStreamFlowController::_create_frame(uint8_t *buf)
+QUICRemoteStreamFlowController::_create_frame(uint8_t *buf, QUICFrameGenerator *owner)
 {
-  return QUICFrameFactory::create_stream_data_blocked_frame(buf, this->_stream_id, this->_offset, this->_issue_frame_id(), this);
+  return QUICFrameFactory::create_stream_data_blocked_frame(buf, this->_stream_id, this->_offset, this->_issue_frame_id(), owner);
 }
 
 QUICFrame *
-QUICLocalStreamFlowController::_create_frame(uint8_t *buf)
+QUICLocalStreamFlowController::_create_frame(uint8_t *buf, QUICFrameGenerator *owner)
 {
-  return QUICFrameFactory::create_max_stream_data_frame(buf, this->_stream_id, this->_limit, this->_issue_frame_id(), this);
+  return QUICFrameFactory::create_max_stream_data_frame(buf, this->_stream_id, this->_limit, this->_issue_frame_id(), owner);
 }
diff --git a/iocore/net/quic/QUICFlowController.h b/iocore/net/quic/QUICFlowController.h
index 3ff183827..151428808 100644
--- a/iocore/net/quic/QUICFlowController.h
+++ b/iocore/net/quic/QUICFlowController.h
@@ -53,6 +53,9 @@ public:
   virtual int update(QUICOffset offset);
   virtual void forward_limit(QUICOffset limit);
 
+  void on_frame_acked(QUICFrameInformationUPtr &info);
+  void on_frame_lost(QUICFrameInformationUPtr &info);
+
   /**
    * This is only for flow controllers initialized without a limit (== UINT64_MAX).
    * Once a limit is set, it should be updated with forward_limit().
@@ -62,11 +65,11 @@ public:
   // QUICFrameGenerator
   bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override;
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t seq_num) override;
+                            size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override;
 
 protected:
   QUICFlowController(uint64_t initial_limit) : _limit(initial_limit) {}
-  virtual QUICFrame *_create_frame(uint8_t *buf) = 0;
+  virtual QUICFrame *_create_frame(uint8_t *buf, QUICFrameGenerator *owner) = 0;
 
   QUICOffset _offset        = 0; //< Largest sent/received offset
   QUICOffset _limit         = 0; //< Maximum amount of data to send/receive
@@ -113,7 +116,7 @@ class QUICRemoteConnectionFlowController : public QUICRemoteFlowController
 {
 public:
   QUICRemoteConnectionFlowController(uint64_t initial_limit) : QUICRemoteFlowController(initial_limit) {}
-  QUICFrame *_create_frame(uint8_t *buf) override;
+  QUICFrame *_create_frame(uint8_t *buf, QUICFrameGenerator *owner) override;
 };
 
 class QUICLocalConnectionFlowController : public QUICLocalFlowController
@@ -123,7 +126,7 @@ public:
     : QUICLocalFlowController(rtt_provider, initial_limit)
   {
   }
-  QUICFrame *_create_frame(uint8_t *buf) override;
+  QUICFrame *_create_frame(uint8_t *buf, QUICFrameGenerator *owner) override;
 };
 
 class QUICRemoteStreamFlowController : public QUICRemoteFlowController
@@ -133,7 +136,7 @@ public:
     : QUICRemoteFlowController(initial_limit), _stream_id(stream_id)
   {
   }
-  QUICFrame *_create_frame(uint8_t *buf) override;
+  QUICFrame *_create_frame(uint8_t *buf, QUICFrameGenerator *owner) override;
 
 private:
   QUICStreamId _stream_id = 0;
@@ -146,7 +149,7 @@ public:
     : QUICLocalFlowController(rtt_provider, initial_limit), _stream_id(stream_id)
   {
   }
-  QUICFrame *_create_frame(uint8_t *buf) override;
+  QUICFrame *_create_frame(uint8_t *buf, QUICFrameGenerator *owner) override;
 
 private:
   QUICStreamId _stream_id = 0;
diff --git a/iocore/net/quic/QUICFrameGenerator.h b/iocore/net/quic/QUICFrameGenerator.h
index f415ff7aa..c54001e61 100644
--- a/iocore/net/quic/QUICFrameGenerator.h
+++ b/iocore/net/quic/QUICFrameGenerator.h
@@ -37,7 +37,8 @@ public:
    * It returns a pointer for the frame if it succeeded, and returns nullptr if it failed.
    */
   virtual QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit,
-                                    uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num) = 0;
+                                    uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num,
+                                    QUICFrameGenerator *owner = nullptr) = 0;
 
   void on_frame_acked(QUICFrameId id);
   void on_frame_lost(QUICFrameId id);
@@ -85,7 +86,7 @@ public:
 
   QUICFrame *
   generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                 size_t current_packet_size, uint32_t seq_num) override
+                 size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner = nullptr) override
   {
     this->_seq_num = seq_num;
     return this->_generate_frame(buf, level, connection_credit, maximum_frame_size, current_packet_size);
diff --git a/iocore/net/quic/QUICFrameRetransmitter.h b/iocore/net/quic/QUICFrameRetransmitter.h
index 64f1f9536..fd0756537 100644
--- a/iocore/net/quic/QUICFrameRetransmitter.h
+++ b/iocore/net/quic/QUICFrameRetransmitter.h
@@ -31,6 +31,7 @@ class QUICFrameGenerator;
 struct QUICFrameInformation {
   QUICFrameType type;
   QUICEncryptionLevel level;
+  QUICStreamId stream_id;
 
   uint8_t data[128] = {};
 };
diff --git a/iocore/net/quic/QUICHandshake.cc b/iocore/net/quic/QUICHandshake.cc
index cff8086ee..74b5e8d6a 100644
--- a/iocore/net/quic/QUICHandshake.cc
+++ b/iocore/net/quic/QUICHandshake.cc
@@ -395,14 +395,14 @@ QUICHandshake::will_generate_frame(QUICEncryptionLevel level, size_t current_pac
 
 QUICFrame *
 QUICHandshake::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                              size_t current_packet_size, uint32_t seq_num)
+                              size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner)
 {
   QUICFrame *frame = nullptr;
 
   if (this->_is_level_matched(level)) {
     // CRYPTO
     frame = this->_crypto_streams[static_cast<int>(level)].generate_frame(buf, level, connection_credit, maximum_frame_size,
-                                                                          current_packet_size, seq_num);
+                                                                          current_packet_size, seq_num, nullptr);
     if (frame) {
       return frame;
     }
diff --git a/iocore/net/quic/QUICHandshake.h b/iocore/net/quic/QUICHandshake.h
index 4f55985ba..923f0aaeb 100644
--- a/iocore/net/quic/QUICHandshake.h
+++ b/iocore/net/quic/QUICHandshake.h
@@ -55,7 +55,7 @@ public:
   // QUICFrameGenerator
   bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override;
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t seq_num) override;
+                            size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override;
 
   // for client side
   QUICConnectionErrorUPtr start(const QUICTPConfig &tp_config, QUICPacketFactory *packet_factory, bool vn_exercise_enabled);
diff --git a/iocore/net/quic/QUICPathValidator.cc b/iocore/net/quic/QUICPathValidator.cc
index e12022c75..20abbb9dd 100644
--- a/iocore/net/quic/QUICPathValidator.cc
+++ b/iocore/net/quic/QUICPathValidator.cc
@@ -200,7 +200,8 @@ QUICPathValidator::will_generate_frame(QUICEncryptionLevel level, size_t current
  */
 QUICFrame *
 QUICPathValidator::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t /* connection_credit */,
-                                  uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num)
+                                  uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num,
+                                  QUICFrameGenerator *owner)
 {
   QUICFrame *frame = nullptr;
 
diff --git a/iocore/net/quic/QUICPathValidator.h b/iocore/net/quic/QUICPathValidator.h
index 96f5f4f64..7f3119695 100644
--- a/iocore/net/quic/QUICPathValidator.h
+++ b/iocore/net/quic/QUICPathValidator.h
@@ -48,7 +48,7 @@ public:
   // QUICFrameGenerator
   bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override;
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t seq_num) override;
+                            size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override;
 
 private:
   enum class ValidationState : int {
diff --git a/iocore/net/quic/QUICStreamBase.cc b/iocore/net/quic/QUICStreamBase.cc
index 1fc3dc093..a2d98a249 100644
--- a/iocore/net/quic/QUICStreamBase.cc
+++ b/iocore/net/quic/QUICStreamBase.cc
@@ -79,6 +79,7 @@ QUICStreamBase::_records_stream_frame(QUICEncryptionLevel level, const QUICStrea
   QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc());
   info->type                    = frame.type();
   info->level                   = level;
+  info->stream_id               = this->id();
   StreamFrameInfo *frame_info   = reinterpret_cast<StreamFrameInfo *>(info->data);
   frame_info->stream_id         = frame.stream_id();
   frame_info->offset            = frame.offset();
@@ -93,6 +94,7 @@ QUICStreamBase::_records_rst_stream_frame(QUICEncryptionLevel level, const QUICR
   QUICFrameInformationUPtr info  = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc());
   info->type                     = frame.type();
   info->level                    = level;
+  info->stream_id                = this->id();
   RstStreamFrameInfo *frame_info = reinterpret_cast<RstStreamFrameInfo *>(info->data);
   frame_info->error_code         = frame.error_code();
   frame_info->final_offset       = frame.final_offset();
@@ -105,6 +107,7 @@ QUICStreamBase::_records_stop_sending_frame(QUICEncryptionLevel level, const QUI
   QUICFrameInformationUPtr info    = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc());
   info->type                       = frame.type();
   info->level                      = level;
+  info->stream_id                  = this->id();
   StopSendingFrameInfo *frame_info = reinterpret_cast<StopSendingFrameInfo *>(info->data);
   frame_info->error_code           = frame.error_code();
   this->_records_frame(frame.id(), std::move(info));
@@ -116,6 +119,7 @@ QUICStreamBase::_records_crypto_frame(QUICEncryptionLevel level, const QUICCrypt
   QUICFrameInformationUPtr info      = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc());
   info->type                         = QUICFrameType::CRYPTO;
   info->level                        = level;
+  info->stream_id                    = this->id();
   CryptoFrameInfo *crypto_frame_info = reinterpret_cast<CryptoFrameInfo *>(info->data);
   crypto_frame_info->offset          = frame.offset();
   crypto_frame_info->block           = frame.data();
@@ -167,3 +171,15 @@ void
 QUICStreamBase::on_read()
 {
 }
+
+void
+QUICStreamBase::on_frame_acked(QUICFrameInformationUPtr &info)
+{
+  this->_on_frame_acked(info);
+}
+
+void
+QUICStreamBase::on_frame_lost(QUICFrameInformationUPtr &info)
+{
+  this->_on_frame_lost(info);
+}
diff --git a/iocore/net/quic/QUICStreamManager_native.cc b/iocore/net/quic/QUICStreamManager_native.cc
index 77cbba860..7ecf64b7d 100644
--- a/iocore/net/quic/QUICStreamManager_native.cc
+++ b/iocore/net/quic/QUICStreamManager_native.cc
@@ -443,9 +443,13 @@ QUICStreamManagerImpl::will_generate_frame(QUICEncryptionLevel level, size_t cur
 
 QUICFrame *
 QUICStreamManagerImpl::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit,
-                                      uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num)
+                                      uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num,
+                                      QUICFrameGenerator *owner)
 {
   QUICFrame *frame = nullptr;
+  if (owner == nullptr) {
+    owner = this;
+  }
 
   if (!this->_is_level_matched(level)) {
     return frame;
@@ -458,7 +462,7 @@ QUICStreamManagerImpl::generate_frame(uint8_t *buf, QUICEncryptionLevel level, u
 
   // FIXME We should pick a stream based on priority
   for (QUICStreamBase *s = this->stream_list.head; s; s = s->link.next) {
-    frame = s->generate_frame(buf, level, connection_credit, maximum_frame_size, current_packet_size, seq_num);
+    frame = s->generate_frame(buf, level, connection_credit, maximum_frame_size, current_packet_size, seq_num, owner);
     if (frame) {
       break;
     }
@@ -471,6 +475,24 @@ QUICStreamManagerImpl::generate_frame(uint8_t *buf, QUICEncryptionLevel level, u
   return frame;
 }
 
+void
+QUICStreamManagerImpl::_on_frame_acked(QUICFrameInformationUPtr &info)
+{
+  auto stream = this->_find_stream(info->stream_id);
+  if (stream) {
+    stream->on_frame_acked(info);
+  }
+}
+
+void
+QUICStreamManagerImpl::_on_frame_lost(QUICFrameInformationUPtr &info)
+{
+  auto stream = this->_find_stream(info->stream_id);
+  if (stream) {
+    stream->on_frame_lost(info);
+  }
+}
+
 bool
 QUICStreamManagerImpl::_is_level_matched(QUICEncryptionLevel level)
 {
diff --git a/iocore/net/quic/QUICStreamManager_native.h b/iocore/net/quic/QUICStreamManager_native.h
index 34b991d50..a63d26209 100644
--- a/iocore/net/quic/QUICStreamManager_native.h
+++ b/iocore/net/quic/QUICStreamManager_native.h
@@ -62,7 +62,9 @@ public:
   // QUICFrameGenerator
   bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t timestamp) override;
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t timestamp) override;
+                            size_t current_packet_size, uint32_t timestamp, QUICFrameGenerator *owner) override;
+  void _on_frame_acked(QUICFrameInformationUPtr &info) override;
+  void _on_frame_lost(QUICFrameInformationUPtr &info) override;
 
   // QUICStreamStateListener
   void on_stream_state_close(const QUICStream *stream) override;
diff --git a/iocore/net/quic/QUICStreamVCAdapter.cc b/iocore/net/quic/QUICStreamVCAdapter.cc
index f268f228d..7ed06c4a4 100644
--- a/iocore/net/quic/QUICStreamVCAdapter.cc
+++ b/iocore/net/quic/QUICStreamVCAdapter.cc
@@ -234,14 +234,16 @@ QUICStreamVCAdapter::do_io_close(int lerrno)
   SET_HANDLER(&QUICStreamVCAdapter::state_stream_closed);
 
   this->_read_vio.buffer.clear();
-  this->_read_vio.nbytes = 0;
-  this->_read_vio.op     = VIO::NONE;
-  this->_read_vio.cont   = nullptr;
+  this->_read_vio.nbytes    = 0;
+  this->_read_vio.op        = VIO::NONE;
+  this->_read_vio.cont      = nullptr;
+  this->_read_vio.vc_server = nullptr;
 
   this->_write_vio.buffer.clear();
-  this->_write_vio.nbytes = 0;
-  this->_write_vio.op     = VIO::NONE;
-  this->_write_vio.cont   = nullptr;
+  this->_write_vio.nbytes    = 0;
+  this->_write_vio.op        = VIO::NONE;
+  this->_write_vio.cont      = nullptr;
+  this->_write_vio.vc_server = nullptr;
 }
 
 void
diff --git a/iocore/net/quic/QUICStreamVCAdapter.h b/iocore/net/quic/QUICStreamVCAdapter.h
index 4b185437c..aad2c361a 100644
--- a/iocore/net/quic/QUICStreamVCAdapter.h
+++ b/iocore/net/quic/QUICStreamVCAdapter.h
@@ -72,6 +72,7 @@ public:
   }
   ~IOInfo()
   {
+    adapter.do_io_close();
     free_MIOBuffer(this->read_buffer);
     free_MIOBuffer(this->write_buffer);
   }
diff --git a/iocore/net/quic/QUICStream_native.h b/iocore/net/quic/QUICStream_native.h
index 39aef372a..354d727c5 100644
--- a/iocore/net/quic/QUICStream_native.h
+++ b/iocore/net/quic/QUICStream_native.h
@@ -40,6 +40,9 @@ public:
   virtual void on_read() override;
   virtual void on_eos() override;
 
+  void on_frame_acked(QUICFrameInformationUPtr &info);
+  void on_frame_lost(QUICFrameInformationUPtr &info);
+
   virtual QUICConnectionErrorUPtr recv(const QUICStreamFrame &frame);
   virtual QUICConnectionErrorUPtr recv(const QUICMaxStreamDataFrame &frame);
   virtual QUICConnectionErrorUPtr recv(const QUICStreamDataBlockedFrame &frame);
diff --git a/iocore/net/quic/QUICTokenCreator.cc b/iocore/net/quic/QUICTokenCreator.cc
index 9d1ef622b..5faf03f54 100644
--- a/iocore/net/quic/QUICTokenCreator.cc
+++ b/iocore/net/quic/QUICTokenCreator.cc
@@ -34,7 +34,7 @@ QUICTokenCreator::will_generate_frame(QUICEncryptionLevel level, size_t current_
 
 QUICFrame *
 QUICTokenCreator::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                                 size_t current_packet_size, uint32_t seq_num)
+                                 size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner)
 {
   QUICFrame *frame = nullptr;
 
diff --git a/iocore/net/quic/QUICTokenCreator.h b/iocore/net/quic/QUICTokenCreator.h
index e107fb9d0..5675937f3 100644
--- a/iocore/net/quic/QUICTokenCreator.h
+++ b/iocore/net/quic/QUICTokenCreator.h
@@ -32,7 +32,7 @@ public:
 
   bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override;
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t seq_num) override;
+                            size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override;
 
 private:
   void _on_frame_lost(QUICFrameInformationUPtr &info) override;
diff --git a/iocore/net/quic/QUICUnidirectionalStream.cc b/iocore/net/quic/QUICUnidirectionalStream.cc
index fd53b4973..2352227b9 100644
--- a/iocore/net/quic/QUICUnidirectionalStream.cc
+++ b/iocore/net/quic/QUICUnidirectionalStream.cc
@@ -48,9 +48,13 @@ QUICSendStream::will_generate_frame(QUICEncryptionLevel level, size_t current_pa
 
 QUICFrame *
 QUICSendStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                               size_t current_packet_size, uint32_t seq_num)
+                               size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner)
 {
-  QUICFrame *frame = this->create_retransmitted_frame(buf, level, maximum_frame_size, this->_issue_frame_id(), this);
+  if (owner == nullptr) {
+    owner = this;
+  }
+
+  QUICFrame *frame = this->create_retransmitted_frame(buf, level, maximum_frame_size, this->_issue_frame_id(), owner);
   if (frame != nullptr) {
     ink_assert(frame->type() == QUICFrameType::STREAM);
     this->_records_stream_frame(level, *static_cast<QUICStreamFrame *>(frame));
@@ -59,7 +63,7 @@ QUICSendStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t
 
   // RESET_STREAM
   if (this->_reset_reason && !this->_is_reset_sent) {
-    frame = QUICFrameFactory::create_rst_stream_frame(buf, *this->_reset_reason, this->_issue_frame_id(), this);
+    frame = QUICFrameFactory::create_rst_stream_frame(buf, *this->_reset_reason, this->_issue_frame_id(), owner);
     if (frame->size() > maximum_frame_size) {
       frame->~QUICFrame();
       return nullptr;
@@ -101,8 +105,8 @@ QUICSendStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t
     uint64_t stream_credit = this->_remote_flow_controller.credit();
     if (stream_credit == 0) {
       // STREAM_DATA_BLOCKED
-      frame =
-        this->_remote_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num);
+      frame = this->_remote_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num,
+                                                           owner);
       return frame;
     }
 
@@ -127,7 +131,7 @@ QUICSendStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t
   // STREAM - Pure FIN or data length is lager than 0
   // FIXME has_length_flag and has_offset_flag should be configurable
   frame = QUICFrameFactory::create_stream_frame(buf, block, this->_id, this->_send_offset, fin, true, true, this->_issue_frame_id(),
-                                                this);
+                                                owner);
   if (!this->_state.is_allowed_to_send(*frame)) {
     QUICStreamDebug("Canceled sending %s frame due to the stream state", QUICDebugNames::frame_type(frame->type()));
     return frame;
@@ -288,13 +292,17 @@ QUICReceiveStream::will_generate_frame(QUICEncryptionLevel level, size_t current
 
 QUICFrame *
 QUICReceiveStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                                  size_t current_packet_size, uint32_t seq_num)
+                                  size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner)
 {
   QUICFrame *frame = nullptr;
+  if (owner == nullptr) {
+    owner = this;
+  }
+
   // STOP_SENDING
   if (this->_stop_sending_reason && !this->_is_stop_sending_sent) {
-    frame =
-      QUICFrameFactory::create_stop_sending_frame(buf, this->id(), this->_stop_sending_reason->code, this->_issue_frame_id(), this);
+    frame = QUICFrameFactory::create_stop_sending_frame(buf, this->id(), this->_stop_sending_reason->code, this->_issue_frame_id(),
+                                                        owner);
     if (frame->size() > maximum_frame_size) {
       frame->~QUICFrame();
       return nullptr;
@@ -308,7 +316,8 @@ QUICReceiveStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint6
   }
 
   // MAX_STREAM_DATA
-  frame = this->_local_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num);
+  frame =
+    this->_local_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num, owner);
   // maximum_frame_size should be checked in QUICFlowController
   return frame;
 }
diff --git a/iocore/net/quic/QUICUnidirectionalStream.h b/iocore/net/quic/QUICUnidirectionalStream.h
index 5cf6e06a3..e94cc144c 100644
--- a/iocore/net/quic/QUICUnidirectionalStream.h
+++ b/iocore/net/quic/QUICUnidirectionalStream.h
@@ -40,7 +40,7 @@ public:
   // QUICFrameGenerator
   bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override;
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t seq_num) override;
+                            size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override;
 
   virtual QUICConnectionErrorUPtr recv(const QUICMaxStreamDataFrame &frame) override;
   virtual QUICConnectionErrorUPtr recv(const QUICStopSendingFrame &frame) override;
@@ -82,7 +82,7 @@ public:
   // QUICFrameGenerator
   bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override;
   QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size,
-                            size_t current_packet_size, uint32_t seq_num) override;
+                            size_t current_packet_size, uint32_t seq_num, QUICFrameGenerator *owner) override;
 
   virtual QUICConnectionErrorUPtr recv(const QUICStreamFrame &frame) override;
   virtual QUICConnectionErrorUPtr recv(const QUICStreamDataBlockedFrame &frame) override;
diff --git a/iocore/net/quic/test/test_QUICAckFrameCreator.cc b/iocore/net/quic/test/test_QUICAckFrameCreator.cc
index cd65277a8..2090dad5a 100644
--- a/iocore/net/quic/test/test_QUICAckFrameCreator.cc
+++ b/iocore/net/quic/test/test_QUICAckFrameCreator.cc
@@ -32,13 +32,13 @@ TEST_CASE("QUICAckFrameManager", "[quic]")
   uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
 
   // Initial state
-  QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   QUICAckFrame *frame  = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame == nullptr);
 
   // One packet
   ack_manager.update(level, 1, 1, false);
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   frame     = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame != nullptr);
   CHECK(frame->ack_block_count() == 0);
@@ -54,7 +54,7 @@ TEST_CASE("QUICAckFrameManager", "[quic]")
   ack_manager.update(level, 5, 1, false);
   ack_manager.update(level, 3, 1, false);
   ack_manager.update(level, 4, 1, false);
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   frame     = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame != nullptr);
   CHECK(frame->ack_block_count() == 0);
@@ -66,7 +66,7 @@ TEST_CASE("QUICAckFrameManager", "[quic]")
   ack_manager.update(level, 6, 1, false);
   ack_manager.update(level, 7, 1, false);
   ack_manager.update(level, 10, 1, false);
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   frame     = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame != nullptr);
   CHECK(frame->ack_block_count() == 1);
@@ -79,13 +79,13 @@ TEST_CASE("QUICAckFrameManager", "[quic]")
   ack_manager.on_frame_acked(frame->id());
 
   CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == false);
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   CHECK(ack_frame == nullptr);
 
   ack_manager.update(level, 11, 1, false);
   ack_manager.update(level, 12, 1, false);
   ack_manager.update(level, 13, 1, false);
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   frame     = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame != nullptr);
   CHECK(frame->ack_block_count() == 0);
@@ -101,10 +101,10 @@ TEST_CASE("QUICAckFrameManager", "[quic]")
   ack_manager.update(level, 15, 1, true);
   ack_manager.update(level, 16, 1, true);
   CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == false);
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
 
   ack_manager.update(level, 17, 1, false);
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   frame     = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame != nullptr);
   CHECK(frame->ack_block_count() == 0);
@@ -152,7 +152,7 @@ TEST_CASE("QUICAckFrameManager should send", "[quic]")
     ack_manager.update(level, 1, 1, false);
     CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true);
 
-    QUICFrame *frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+    QUICFrame *frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
     CHECK(frame != nullptr);
     frame->~QUICFrame();
 
@@ -162,7 +162,7 @@ TEST_CASE("QUICAckFrameManager should send", "[quic]")
     ack_manager.update(level, 3, 1, false);
     CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true);
 
-    frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+    frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
     CHECK(frame != nullptr);
     frame->~QUICFrame();
   }
@@ -219,7 +219,7 @@ TEST_CASE("QUICAckFrameManager should send", "[quic]")
     QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT;
 
     uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
-    QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+    QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
     QUICAckFrame *frame  = static_cast<QUICAckFrame *>(ack_frame);
     CHECK(frame == nullptr);
 
@@ -233,7 +233,7 @@ TEST_CASE("QUICAckFrameManager should send", "[quic]")
     sleep(1);
     Thread::get_hrtime_updated();
     CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true);
-    ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+    ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
     frame     = static_cast<QUICAckFrame *>(ack_frame);
 
     CHECK(frame->ack_block_count() == 0);
@@ -251,7 +251,7 @@ TEST_CASE("QUICAckFrameManager_loss_recover", "[quic]")
 
   // Initial state
   uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
-  QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   QUICAckFrame *frame  = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame == nullptr);
 
@@ -261,7 +261,7 @@ TEST_CASE("QUICAckFrameManager_loss_recover", "[quic]")
   ack_manager.update(level, 8, 1, false);
   ack_manager.update(level, 9, 1, false);
 
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   frame     = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame != nullptr);
   CHECK(frame->ack_block_count() == 2);
@@ -274,7 +274,7 @@ TEST_CASE("QUICAckFrameManager_loss_recover", "[quic]")
 
   ack_manager.update(level, 7, 1, false);
   ack_manager.update(level, 4, 1, false);
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   frame     = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame != nullptr);
   CHECK(frame->ack_block_count() == 1);
@@ -331,7 +331,7 @@ TEST_CASE("QUICAckFrameManager lost_frame", "[quic]")
   uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
 
   // Initial state
-  QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   QUICAckFrame *frame  = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame == nullptr);
 
@@ -341,7 +341,7 @@ TEST_CASE("QUICAckFrameManager lost_frame", "[quic]")
   ack_manager.update(level, 8, 1, false);
   ack_manager.update(level, 9, 1, false);
 
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   frame     = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame != nullptr);
   CHECK(frame->ack_block_count() == 2);
@@ -352,7 +352,7 @@ TEST_CASE("QUICAckFrameManager lost_frame", "[quic]")
 
   ack_manager.on_frame_lost(frame->id());
   CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true);
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   frame     = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame != nullptr);
   CHECK(frame->ack_block_count() == 2);
@@ -367,7 +367,7 @@ TEST_CASE("QUICAckFrameManager lost_frame", "[quic]")
   ack_manager.update(level, 7, 1, false);
   ack_manager.update(level, 4, 1, false);
 
-  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+  ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
   frame     = static_cast<QUICAckFrame *>(ack_frame);
   CHECK(frame != nullptr);
   CHECK(frame->ack_block_count() == 1);
@@ -388,7 +388,7 @@ TEST_CASE("QUICAckFrameManager ack only packet", "[quic]")
     uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
 
     // Initial state
-    QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+    QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
     QUICAckFrame *frame  = static_cast<QUICAckFrame *>(ack_frame);
     CHECK(frame == nullptr);
 
@@ -400,7 +400,7 @@ TEST_CASE("QUICAckFrameManager ack only packet", "[quic]")
 
     CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true);
 
-    ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+    ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
     frame     = static_cast<QUICAckFrame *>(ack_frame);
     CHECK(frame != nullptr);
     CHECK(frame->ack_block_count() == 0);
@@ -421,7 +421,7 @@ TEST_CASE("QUICAckFrameManager ack only packet", "[quic]")
     uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
 
     // Initial state
-    QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+    QUICFrame *ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
     QUICAckFrame *frame  = static_cast<QUICAckFrame *>(ack_frame);
     CHECK(frame == nullptr);
 
@@ -433,7 +433,7 @@ TEST_CASE("QUICAckFrameManager ack only packet", "[quic]")
 
     CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true);
 
-    ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0);
+    ack_frame = ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0, nullptr);
     frame     = static_cast<QUICAckFrame *>(ack_frame);
     CHECK(frame != nullptr);
     CHECK(frame->ack_block_count() == 0);
diff --git a/iocore/net/quic/test/test_QUICFlowController.cc b/iocore/net/quic/test/test_QUICFlowController.cc
index 598b74efe..a4958f22d 100644
--- a/iocore/net/quic/test/test_QUICFlowController.cc
+++ b/iocore/net/quic/test/test_QUICFlowController.cc
@@ -116,7 +116,7 @@ TEST_CASE("QUICFlowController_Local_Connection", "[quic]")
   fc.forward_limit(2048);
   CHECK(fc.current_offset() == 1024);
   CHECK(fc.current_limit() == 2048);
-  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0);
+  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0, nullptr);
   CHECK(frame);
   CHECK(frame->type() == QUICFrameType::MAX_DATA);
 
@@ -168,7 +168,7 @@ TEST_CASE("QUICFlowController_Remote_Connection", "[quic]")
   CHECK(fc.current_offset() == 1000);
   CHECK(fc.current_limit() == 1024);
   CHECK(ret != 0);
-  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0);
+  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0, nullptr);
   CHECK(frame);
   CHECK(frame->type() == QUICFrameType::DATA_BLOCKED);
 
@@ -201,7 +201,7 @@ TEST_CASE("QUICFlowController_Remote_Connection_ZERO_Credit", "[quic]")
 
   CHECK(fc.will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, true, 0));
   // if there're anything to send
-  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0);
+  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0, nullptr);
   CHECK(frame);
   CHECK(frame->type() == QUICFrameType::DATA_BLOCKED);
 
@@ -234,9 +234,9 @@ TEST_CASE("QUICFlowController_Remote_Connection_Insufficient_maximum_frame_size"
 
   CHECK(fc.will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, true, 0));
   // if there're anything to send
-  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 0, 0, 0);
+  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 0, 0, 0, nullptr);
   CHECK(frame == nullptr);
-  frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0);
+  frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0, nullptr);
   CHECK(frame);
   CHECK(frame->type() == QUICFrameType::DATA_BLOCKED);
 
@@ -300,7 +300,7 @@ TEST_CASE("QUICFlowController_Local_Stream", "[quic]")
   fc.forward_limit(2048);
   CHECK(fc.current_offset() == 1024);
   CHECK(fc.current_limit() == 2048);
-  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0);
+  QUICFrame *frame = fc.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 0, 1024, 0, 0, nullptr);
   CHECK(frame);
   CHECK(frame->type() == QUICFrameType::MAX_STREAM_DATA);
 
@@ -377,36 +377,36 @@ TEST_CASE("Frame retransmission", "[quic]")
     QUICRemoteConnectionFlowController fc(1024);
 
     // Check initial state
-    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     CHECK(!frame);
 
     ret = fc.update(1024);
     CHECK(ret == 0);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICDataBlockedFrame *>(frame)->offset() == 1024);
     QUICFrameId id = frame->id();
 
     // Don't retransmit unless the frame is lost
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(!frame);
 
     // Retransmit
-    fc.on_frame_lost(id);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    static_cast<QUICFrameGenerator *>(&fc)->on_frame_lost(id);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICDataBlockedFrame *>(frame)->offset() == 1024);
 
     // Don't send if it was not blocked
-    fc.on_frame_lost(frame->id());
+    static_cast<QUICFrameGenerator *>(&fc)->on_frame_lost(frame->id());
     fc.forward_limit(2048);
     ret   = fc.update(1536);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     CHECK(!frame);
 
     // This should not be retransmission
     ret   = fc.update(2048);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICDataBlockedFrame *>(frame)->offset() == 2048);
   }
@@ -418,36 +418,36 @@ TEST_CASE("Frame retransmission", "[quic]")
     QUICRemoteStreamFlowController fc(1024, 0);
 
     // Check initial state
-    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     CHECK(!frame);
 
     ret = fc.update(1024);
     CHECK(ret == 0);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICStreamDataBlockedFrame *>(frame)->offset() == 1024);
     QUICFrameId id = frame->id();
 
     // Don't retransmit unless the frame is lost
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(!frame);
 
     // Retransmit
-    fc.on_frame_lost(id);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    static_cast<QUICFrameGenerator *>(&fc)->on_frame_lost(id);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICStreamDataBlockedFrame *>(frame)->offset() == 1024);
 
     // Don't send if it was not blocked
-    fc.on_frame_lost(frame->id());
+    static_cast<QUICFrameGenerator *>(&fc)->on_frame_lost(frame->id());
     fc.forward_limit(2048);
     ret   = fc.update(1536);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     CHECK(!frame);
 
     // This should not be retransmission
     ret   = fc.update(2048);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICStreamDataBlockedFrame *>(frame)->offset() == 2048);
   }
@@ -459,31 +459,31 @@ TEST_CASE("Frame retransmission", "[quic]")
     QUICLocalConnectionFlowController fc(&rp, 1024);
 
     // Check initial state
-    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     CHECK(!frame);
 
     fc.update(1024);
     fc.forward_limit(1024);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICMaxDataFrame *>(frame)->maximum_data() == 1024);
     QUICFrameId id = frame->id();
 
     // Don't retransmit unless the frame is lost
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(!frame);
 
     // Retransmit
-    fc.on_frame_lost(id);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    static_cast<QUICFrameGenerator *>(&fc)->on_frame_lost(id);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICMaxDataFrame *>(frame)->maximum_data() == 1024);
 
     // Send new one if it was updated
-    fc.on_frame_lost(id);
+    static_cast<QUICFrameGenerator *>(&fc)->on_frame_lost(id);
     fc.forward_limit(2048);
     fc.update(2048);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICMaxDataFrame *>(frame)->maximum_data() == 2048);
   }
@@ -495,31 +495,31 @@ TEST_CASE("Frame retransmission", "[quic]")
     QUICLocalStreamFlowController fc(&rp, 1024, 0);
 
     // Check initial state
-    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    auto frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     CHECK(!frame);
 
     fc.update(1024);
     fc.forward_limit(1024);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICMaxStreamDataFrame *>(frame)->maximum_stream_data() == 1024);
     QUICFrameId id = frame->id();
 
     // Don't retransmit unless the frame is lost
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(!frame);
 
     // Retransmit
-    fc.on_frame_lost(id);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    static_cast<QUICFrameGenerator *>(&fc)->on_frame_lost(id);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICMaxStreamDataFrame *>(frame)->maximum_stream_data() == 1024);
 
     // Send new one if it was updated
-    fc.on_frame_lost(id);
+    static_cast<QUICFrameGenerator *>(&fc)->on_frame_lost(id);
     fc.forward_limit(2048);
     fc.update(2048);
-    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0);
+    frame = fc.generate_frame(frame_buf, level, 1024, 1024, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(static_cast<QUICMaxStreamDataFrame *>(frame)->maximum_stream_data() == 2048);
   }
diff --git a/iocore/net/quic/test/test_QUICLossDetector.cc b/iocore/net/quic/test/test_QUICLossDetector.cc
index 5dd827143..96e663a85 100644
--- a/iocore/net/quic/test/test_QUICLossDetector.cc
+++ b/iocore/net/quic/test/test_QUICLossDetector.cc
@@ -59,7 +59,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]")
     // Check initial state
     uint8_t frame_buffer[1024] = {0};
     CHECK(g.lost_frame_count == 0);
-    QUICFrame *ping_frame = g.generate_frame(frame_buffer, QUICEncryptionLevel::HANDSHAKE, 4, UINT16_MAX, 0, 0);
+    QUICFrame *ping_frame = g.generate_frame(frame_buffer, QUICEncryptionLevel::HANDSHAKE, 4, UINT16_MAX, 0, 0, nullptr);
 
     uint8_t raw[4];
     size_t len             = 0;
@@ -274,7 +274,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]")
     afm.update(level, pn9, payload_len, false);
     afm.update(level, pn10, payload_len, false);
     uint8_t buf[QUICFrame::MAX_INSTANCE_SIZE];
-    QUICFrame *x = afm.generate_frame(buf, level, 2048, 2048, 0, 0);
+    QUICFrame *x = afm.generate_frame(buf, level, 2048, 2048, 0, 0, nullptr);
     frame        = static_cast<QUICAckFrame *>(x);
     ink_hrtime_sleep(HRTIME_MSECONDS(1000));
     detector.handle_frame(level, *frame);
diff --git a/iocore/net/quic/test/test_QUICPathValidator.cc b/iocore/net/quic/test/test_QUICPathValidator.cc
index cd96ef74a..57fe773f4 100644
--- a/iocore/net/quic/test/test_QUICPathValidator.cc
+++ b/iocore/net/quic/test/test_QUICPathValidator.cc
@@ -63,7 +63,7 @@ TEST_CASE("QUICPathValidator", "[quic]")
     CHECK(pv_c.is_validating(path));
     CHECK(!pv_c.is_validated(path));
     REQUIRE(pv_c.will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, false, seq_num));
-    auto frame = pv_c.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 1024, 1024, 0, seq_num);
+    auto frame = pv_c.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 1024, 1024, 0, seq_num, nullptr);
     REQUIRE(frame);
     CHECK(frame->type() == QUICFrameType::PATH_CHALLENGE);
     CHECK(pv_c.is_validating(path));
@@ -80,7 +80,7 @@ TEST_CASE("QUICPathValidator", "[quic]")
     CHECK(!pv_s.is_validated(path));
     REQUIRE(pv_s.will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, false, seq_num));
     frame->~QUICFrame();
-    frame = pv_s.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 1024, 1024, 0, seq_num);
+    frame = pv_s.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 1024, 1024, 0, seq_num, nullptr);
     REQUIRE(frame);
     CHECK(frame->type() == QUICFrameType::PATH_RESPONSE);
     CHECK(!pv_s.is_validating(path));
diff --git a/iocore/net/quic/test/test_QUICStream.cc b/iocore/net/quic/test/test_QUICStream.cc
index 92617812a..8f9019d20 100644
--- a/iocore/net/quic/test/test_QUICStream.cc
+++ b/iocore/net/quic/test/test_QUICStream.cc
@@ -241,28 +241,28 @@ TEST_CASE("QUICBidiStream", "[quic]")
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
 
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
 
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
 
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
 
@@ -270,7 +270,7 @@ TEST_CASE("QUICBidiStream", "[quic]")
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame);
     CHECK(frame->type() == QUICFrameType::STREAM_DATA_BLOCKED);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
@@ -281,7 +281,7 @@ TEST_CASE("QUICBidiStream", "[quic]")
     // This should send a frame
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
 
@@ -292,13 +292,13 @@ TEST_CASE("QUICBidiStream", "[quic]")
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
 
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM_DATA_BLOCKED);
 
     // Update window
@@ -306,7 +306,7 @@ TEST_CASE("QUICBidiStream", "[quic]")
 
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
   }
@@ -342,21 +342,21 @@ TEST_CASE("QUICBidiStream", "[quic]")
     write_buffer->write(data1, sizeof(data1));
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     // Generate STREAM frame
-    frame  = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame  = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     frame1 = static_cast<QUICStreamFrame *>(frame);
     CHECK(frame->type() == QUICFrameType::STREAM);
-    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0) == nullptr);
+    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr) == nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
-    stream->on_frame_lost(frame->id());
+    static_cast<QUICFrameGenerator *>(stream.get())->on_frame_lost(frame->id());
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
 
     // Write data2
     write_buffer->write(data2, sizeof(data2));
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     // Lost the frame
-    stream->on_frame_lost(frame->id());
+    static_cast<QUICFrameGenerator *>(stream.get())->on_frame_lost(frame->id());
     // Regenerate a frame
-    frame = stream->generate_frame(frame_buf2, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf2, level, 4096, 4096, 0, 0, nullptr);
     // Lost data should be resent first
     frame2 = static_cast<QUICStreamFrame *>(frame);
     CHECK(frame->type() == QUICFrameType::STREAM);
@@ -385,15 +385,15 @@ TEST_CASE("QUICBidiStream", "[quic]")
     QUICFrame *frame          = nullptr;
 
     stream->reset(QUICStreamErrorUPtr(new QUICStreamError(stream.get(), QUIC_APP_ERROR_CODE_STOPPING)));
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(frame->type() == QUICFrameType::RESET_STREAM);
     // Don't send it again until it is considers as lost
-    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0) == nullptr);
+    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr) == nullptr);
     // Loss the frame
-    stream->on_frame_lost(frame->id());
+    static_cast<QUICFrameGenerator *>(stream.get())->on_frame_lost(frame->id());
     // After the loss the frame should be regenerated
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(frame->type() == QUICFrameType::RESET_STREAM);
   }
@@ -418,15 +418,15 @@ TEST_CASE("QUICBidiStream", "[quic]")
     QUICFrame *frame          = nullptr;
 
     stream->stop_sending(QUICStreamErrorUPtr(new QUICStreamError(stream.get(), QUIC_APP_ERROR_CODE_STOPPING)));
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(frame->type() == QUICFrameType::STOP_SENDING);
     // Don't send it again until it is considers as lost
-    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0) == nullptr);
+    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr) == nullptr);
     // Loss the frame
-    stream->on_frame_lost(frame->id());
+    static_cast<QUICFrameGenerator *>(stream.get())->on_frame_lost(frame->id());
     // After the loss the frame should be regenerated
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(frame->type() == QUICFrameType::STOP_SENDING);
   }
@@ -449,7 +449,7 @@ TEST_CASE("QUICBidiStream", "[quic]")
     adapter1.do_io_write(&mock_cont1, INT64_MAX, write_buffer_reader);
     SCOPED_MUTEX_LOCK(lock1, adapter1.mutex, this_ethread());
     stream1->stop_sending(QUICStreamErrorUPtr(new QUICStreamError(stream1.get(), QUIC_APP_ERROR_CODE_STOPPING)));
-    frame = stream1->generate_frame(frame_buf, level, 4096, 0, 0, 0);
+    frame = stream1->generate_frame(frame_buf, level, 4096, 0, 0, 0, nullptr);
     CHECK(frame == nullptr);
 
     // RESET_STREAM
@@ -461,7 +461,7 @@ TEST_CASE("QUICBidiStream", "[quic]")
     adapter2.do_io_write(&mock_cont2, INT64_MAX, write_buffer_reader);
     SCOPED_MUTEX_LOCK(lock2, adapter2.mutex, this_ethread());
     stream2->reset(QUICStreamErrorUPtr(new QUICStreamError(stream2.get(), QUIC_APP_ERROR_CODE_STOPPING)));
-    frame = stream2->generate_frame(frame_buf, level, 4096, 0, 0, 0);
+    frame = stream2->generate_frame(frame_buf, level, 4096, 0, 0, 0, nullptr);
     CHECK(frame == nullptr);
 
     // STREAM
@@ -475,7 +475,7 @@ TEST_CASE("QUICBidiStream", "[quic]")
     const char data[] = "this is a test data";
     write_buffer->write(data, sizeof(data));
     adapter3.handleEvent(VC_EVENT_WRITE_READY, nullptr);
-    frame = stream3->generate_frame(frame_buf, level, 4096, 0, 0, 0);
+    frame = stream3->generate_frame(frame_buf, level, 4096, 0, 0, 0, nullptr);
     CHECK(frame == nullptr);
   }
 }
@@ -674,15 +674,15 @@ TEST_CASE("QUIC receive only stream", "[quic]")
     QUICFrame *frame          = nullptr;
 
     stream->stop_sending(QUICStreamErrorUPtr(new QUICStreamError(stream.get(), QUIC_APP_ERROR_CODE_STOPPING)));
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(frame->type() == QUICFrameType::STOP_SENDING);
     // Don't send it again until it is considers as lost
-    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0) == nullptr);
+    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr) == nullptr);
     // Loss the frame
-    stream->on_frame_lost(frame->id());
+    static_cast<QUICFrameGenerator *>(stream.get())->on_frame_lost(frame->id());
     // After the loss the frame should be regenerated
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(frame->type() == QUICFrameType::STOP_SENDING);
   }
@@ -701,7 +701,7 @@ TEST_CASE("QUIC receive only stream", "[quic]")
     MockContinuation mock_cont1(adapter1.mutex);
     SCOPED_MUTEX_LOCK(lock1, adapter1.mutex, this_ethread());
     stream1->stop_sending(QUICStreamErrorUPtr(new QUICStreamError(stream1.get(), QUIC_APP_ERROR_CODE_STOPPING)));
-    frame = stream1->generate_frame(frame_buf, level, 4096, 0, 0, 0);
+    frame = stream1->generate_frame(frame_buf, level, 4096, 0, 0, 0, nullptr);
     CHECK(frame == nullptr);
   }
 }
@@ -782,28 +782,28 @@ TEST_CASE("QUIC send only stream", "[quic]")
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
 
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
 
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
 
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
 
@@ -811,7 +811,7 @@ TEST_CASE("QUIC send only stream", "[quic]")
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame);
     CHECK(frame->type() == QUICFrameType::STREAM_DATA_BLOCKED);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
@@ -822,7 +822,7 @@ TEST_CASE("QUIC send only stream", "[quic]")
     // This should send a frame
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
 
@@ -833,13 +833,13 @@ TEST_CASE("QUIC send only stream", "[quic]")
     write_buffer->write(data, 1024);
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
 
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM_DATA_BLOCKED);
 
     // Update window
@@ -847,7 +847,7 @@ TEST_CASE("QUIC send only stream", "[quic]")
 
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     CHECK(frame->type() == QUICFrameType::STREAM);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
   }
@@ -881,21 +881,21 @@ TEST_CASE("QUIC send only stream", "[quic]")
     write_buffer->write(data1, sizeof(data1));
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     // Generate STREAM frame
-    frame  = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame  = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     frame1 = static_cast<QUICStreamFrame *>(frame);
     CHECK(frame->type() == QUICFrameType::STREAM);
-    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0) == nullptr);
+    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr) == nullptr);
     CHECK(stream->will_generate_frame(level, 0, false, 0) == false);
-    stream->on_frame_lost(frame->id());
+    static_cast<QUICFrameGenerator *>(stream.get())->on_frame_lost(frame->id());
     CHECK(stream->will_generate_frame(level, 0, false, 0) == true);
 
     // Write data2
     write_buffer->write(data2, sizeof(data2));
     adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr);
     // Lost the frame
-    stream->on_frame_lost(frame->id());
+    static_cast<QUICFrameGenerator *>(stream.get())->on_frame_lost(frame->id());
     // Regenerate a frame
-    frame = stream->generate_frame(frame_buf2, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf2, level, 4096, 4096, 0, 0, nullptr);
     // Lost data should be resent first
     frame2 = static_cast<QUICStreamFrame *>(frame);
     CHECK(frame->type() == QUICFrameType::STREAM);
@@ -922,15 +922,15 @@ TEST_CASE("QUIC send only stream", "[quic]")
     QUICFrame *frame          = nullptr;
 
     stream->reset(QUICStreamErrorUPtr(new QUICStreamError(stream.get(), QUIC_APP_ERROR_CODE_STOPPING)));
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(frame->type() == QUICFrameType::RESET_STREAM);
     // Don't send it again until it is considers as lost
-    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0) == nullptr);
+    CHECK(stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr) == nullptr);
     // Loss the frame
-    stream->on_frame_lost(frame->id());
+    static_cast<QUICFrameGenerator *>(stream.get())->on_frame_lost(frame->id());
     // After the loss the frame should be regenerated
-    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0);
+    frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0, nullptr);
     REQUIRE(frame);
     CHECK(frame->type() == QUICFrameType::RESET_STREAM);
   }
@@ -951,7 +951,7 @@ TEST_CASE("QUIC send only stream", "[quic]")
     adapter2.do_io_write(&mock_cont2, INT64_MAX, write_buffer_reader);
     SCOPED_MUTEX_LOCK(lock2, adapter2.mutex, this_ethread());
     stream2->reset(QUICStreamErrorUPtr(new QUICStreamError(stream2.get(), QUIC_APP_ERROR_CODE_STOPPING)));
-    frame = stream2->generate_frame(frame_buf, level, 4096, 0, 0, 0);
+    frame = stream2->generate_frame(frame_buf, level, 4096, 0, 0, 0, nullptr);
     CHECK(frame == nullptr);
 
     // STREAM
@@ -964,7 +964,7 @@ TEST_CASE("QUIC send only stream", "[quic]")
     const char data[] = "this is a test data";
     write_buffer->write(data, sizeof(data));
     adapter3.handleEvent(VC_EVENT_WRITE_READY, nullptr);
-    frame = stream3->generate_frame(frame_buf, level, 4096, 0, 0, 0);
+    frame = stream3->generate_frame(frame_buf, level, 4096, 0, 0, 0, nullptr);
     CHECK(frame == nullptr);
   }
 }
@@ -980,14 +980,14 @@ TEST_CASE("will_generate_frame", "[quic]")
     std::unique_ptr<QUICBidirectionalStream> stream_bidi(
       new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, 0, 1024, 1024));
     CHECK(stream_bidi->will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, false, 0) == false);
-    CHECK(stream_bidi->generate_frame(buf, QUICEncryptionLevel::ONE_RTT, 1024, 1024, 0, 0) == nullptr);
+    CHECK(stream_bidi->generate_frame(buf, QUICEncryptionLevel::ONE_RTT, 1024, 1024, 0, 0, nullptr) == nullptr);
 
     std::unique_ptr<QUICSendStream> stream_uni1(new QUICSendStream(&cinfo_provider, 2, 1024));
     CHECK(stream_uni1->will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, false, 0) == false);
-    CHECK(stream_uni1->generate_frame(buf, QUICEncryptionLevel::ONE_RTT, 1024, 1024, 0, 0) == nullptr);
+    CHECK(stream_uni1->generate_frame(buf, QUICEncryptionLevel::ONE_RTT, 1024, 1024, 0, 0, nullptr) == nullptr);
 
     std::unique_ptr<QUICReceiveStream> stream_uni2(new QUICReceiveStream(&rtt_provider, &cinfo_provider, 3, 1024));
     CHECK(stream_uni2->will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, false, 0) == false);
-    CHECK(stream_uni2->generate_frame(buf, QUICEncryptionLevel::ONE_RTT, 1024, 1024, 0, 0) == nullptr);
+    CHECK(stream_uni2->generate_frame(buf, QUICEncryptionLevel::ONE_RTT, 1024, 1024, 0, 0, nullptr) == nullptr);
   }
 }
diff --git a/iocore/net/quic/test/test_QUICStreamManager.cc b/iocore/net/quic/test/test_QUICStreamManager.cc
index 4f214aa75..130454f9d 100644
--- a/iocore/net/quic/test/test_QUICStreamManager.cc
+++ b/iocore/net/quic/test/test_QUICStreamManager.cc
@@ -233,12 +233,12 @@ TEST_CASE("QUICStreamManager_total_offset_sent", "[quic]")
   // total_offset should be a integer in unit of octets
   uint8_t frame_buf[4096];
   mock_app.send(reinterpret_cast<uint8_t *>(block_1024->buf()), 1024, 0);
-  sm.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 16384, 16384, 0, 0);
+  sm.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 16384, 16384, 0, 0, nullptr);
   CHECK(sm.total_offset_sent() == 1024);
 
   // total_offset should be a integer in unit of octets
   mock_app.send(reinterpret_cast<uint8_t *>(block_1024->buf()), 1024, 4);
-  sm.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 16384, 16384, 0, 0);
+  sm.generate_frame(frame_buf, QUICEncryptionLevel::ONE_RTT, 16384, 16384, 0, 0, nullptr);
   CHECK(sm.total_offset_sent() == 2048);
 
   // Wait for event processing
diff --git a/proxy/http3/Http3App.cc b/proxy/http3/Http3App.cc
index b6361d4bd..773bad22e 100644
--- a/proxy/http3/Http3App.cc
+++ b/proxy/http3/Http3App.cc
@@ -353,6 +353,7 @@ Http3App::_handle_bidi_stream_on_write_complete(int event, VIO *vio)
   // FIXME There may be data to read
   this->_ssn->remove_transaction(txn);
   this->_qc->stream_manager()->delete_stream(stream_id);
+  this->_streams.erase(stream_id);
 }
 
 //
diff --git a/tests/gold_tests/timeout/active_timeout.test.py b/tests/gold_tests/timeout/active_timeout.test.py
index aff42fe01..e43b68eaa 100644
--- a/tests/gold_tests/timeout/active_timeout.test.py
+++ b/tests/gold_tests/timeout/active_timeout.test.py
@@ -22,9 +22,7 @@ Test.SkipUnless(
     Condition.HasCurlFeature('http2')
 )
 
-# TODO: Add this back in (i.e., remove the False boolean) when ATS 10.x builds
-# correctly with openssl-quic.
-if False and Condition.HasATSFeature('TS_USE_QUIC') and Condition.HasCurlFeature('http3'):
+if Condition.HasATSFeature('TS_USE_QUIC') and Condition.HasCurlFeature('http3'):
     ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True, enable_quic=True)
 else:
     ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
@@ -66,11 +64,7 @@ tr3 = Test.AddTestRun("tr")
 tr3.Processes.Default.Command = 'curl -k -i --http2 https://127.0.0.1:{0}/file'.format(ts.Variables.ssl_port)
 tr3.Processes.Default.Streams.stdout = Testers.ContainsExpression("Activity Timeout", "Request should fail with active timeout")
 
-# Commenting out the HTTP/3 test for now until we fix our QUIC implementation
-# with openssl-quic. If this following test runs in CI it will currently fail.
-# TODO: add this test back in for 10-Dev once ATS can perform HTTP/3 when
-# built against openssl.
-# if Condition.HasATSFeature('TS_USE_QUIC') and Condition.HasCurlFeature('http3'):
-#     tr4 = Test.AddTestRun("tr")
-#     tr4.Processes.Default.Command = 'curl -k -i --http3 https://127.0.0.1:{0}/file'.format(ts.Variables.ssl_port)
-#     tr4.Processes.Default.Streams.stdout = Testers.ContainsExpression("Activity Timeout", "Request should fail with active timeout")
+if Condition.HasATSFeature('TS_USE_QUIC') and Condition.HasCurlFeature('http3'):
+    tr4 = Test.AddTestRun("tr")
+    tr4.Processes.Default.Command = 'curl -k -i --http3 https://127.0.0.1:{0}/file'.format(ts.Variables.ssl_port)
+    tr4.Processes.Default.Streams.stdout = Testers.ContainsExpression("Activity Timeout", "Request should fail with active timeout")