bneradt commented on code in PR #13186:
URL: https://github.com/apache/trafficserver/pull/13186#discussion_r3366115477


##########
src/iocore/net/OpenSSLQUICNetVConnection.cc:
##########
@@ -0,0 +1,1046 @@
+/** @file
+
+  OpenSSL native QUIC NetVConnection support.
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#include "P_SSLUtils.h"
+#include "P_QUICNetVConnection.h"
+#include "P_QUICPacketHandler.h"
+#include "P_UnixNet.h"
+#include "api/APIHook.h"
+#include "iocore/eventsystem/EThread.h"
+#include "iocore/net/QUICMultiCertConfigLoader.h"
+#include "iocore/net/SSLAPIHooks.h"
+#include "iocore/net/quic/QUICApplicationMap.h"
+#include "iocore/net/quic/QUICEvents.h"
+#include "iocore/net/quic/QUICGlobals.h"
+#include "iocore/net/quic/QUICStream.h"
+#include "iocore/net/quic/QUICStreamManager.h"
+#include "tscore/ink_config.h"
+
+#include <openssl/err.h>
+#include <openssl/quic.h>
+#include <openssl/ssl.h>
+
+#include <algorithm>
+#include <cerrno>
+#include <cstring>
+
+namespace
+{
+constexpr ink_hrtime OPENSSL_QUIC_EVENT_INTERVAL = HRTIME_MSECONDS(2);
+
+DbgCtl dbg_ctl_quic_net{"quic_net"};
+DbgCtl dbg_ctl_v_quic_net{"v_quic_net"};
+
+class OpenSSLQUICStreamManager : public QUICStreamManager
+{
+public:
+  OpenSSLQUICStreamManager(QUICContext *context, QUICApplicationMap *app_map, 
QUICNetVConnection *vc)
+    : QUICStreamManager(context, app_map), _vc(vc)
+  {
+  }
+
+  QUICConnectionErrorUPtr
+  create_uni_stream(QUICStreamId &new_stream_id) override
+  {
+    return this->_vc->create_openssl_stream(SSL_STREAM_FLAG_UNI | 
SSL_STREAM_FLAG_NO_BLOCK, new_stream_id);
+  }
+
+  QUICConnectionErrorUPtr
+  create_bidi_stream(QUICStreamId &new_stream_id) override
+  {
+    return this->_vc->create_openssl_stream(SSL_STREAM_FLAG_NO_BLOCK, 
new_stream_id);
+  }
+
+private:
+  QUICNetVConnection *_vc = nullptr;
+};
+
+} // end anonymous namespace
+
+#define QUICConDebug(fmt, ...)  Dbg(dbg_ctl_quic_net, "[%s] " fmt, 
this->cids().data(), ##__VA_ARGS__)
+#define QUICConVDebug(fmt, ...) Dbg(dbg_ctl_v_quic_net, "[%s] " fmt, 
this->cids().data(), ##__VA_ARGS__)
+
+ClassAllocator<QUICNetVConnection, false> 
quicNetVCAllocator("quicNetVCAllocator");
+
+QUICNetVConnection::QUICNetVConnection()
+{
+  this->_set_service(static_cast<ALPNSupport *>(this));
+  this->_set_service(static_cast<TLSBasicSupport *>(this));
+  this->_set_service(static_cast<TLSEventSupport *>(this));
+  this->_set_service(static_cast<TLSCertSwitchSupport *>(this));
+  this->_set_service(static_cast<TLSSNISupport *>(this));
+  this->_set_service(static_cast<TLSSessionResumptionSupport *>(this));
+  this->_set_service(static_cast<QUICSupport *>(this));
+}
+
+QUICNetVConnection::~QUICNetVConnection() {}
+
+void
+QUICNetVConnection::init(SSL *ssl, QUICPacketHandler *packet_handler)
+{
+  SET_HANDLER((NetVConnHandler)&QUICNetVConnection::acceptEvent);
+
+  this->_ssl            = ssl;
+  this->_packet_handler = packet_handler;
+  this->_quic_connection_id.randomize();
+  this->_initial_source_connection_id = this->_quic_connection_id;
+  this->_cid_text                     = this->_quic_connection_id.hex();
+
+  SSL_set_ex_data(ssl, QUIC::ssl_quic_qc_index, static_cast<QUICConnection 
*>(this));
+  SSL_set_blocking_mode(ssl, 0);
+  SSL_set_event_handling_mode(ssl, SSL_VALUE_EVENT_HANDLING_MODE_IMPLICIT);
+  SSL_set_incoming_stream_policy(ssl, SSL_INCOMING_STREAM_POLICY_ACCEPT, 0);
+  SSL_set_default_stream_mode(ssl, SSL_DEFAULT_STREAM_MODE_NONE);
+
+  QUICConfig::scoped_config params;
+  SSL_set_feature_request_uint(ssl, SSL_VALUE_QUIC_IDLE_TIMEOUT, 
params->no_activity_timeout_in());
+  SSL_set_feature_request_uint(ssl, SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, 
params->initial_max_streams_bidi_in());
+  SSL_set_feature_request_uint(ssl, SSL_VALUE_QUIC_STREAM_UNI_LOCAL_AVAIL, 
params->initial_max_streams_uni_in());
+
+  this->_bindSSLObject();
+}
+
+void
+QUICNetVConnection::set_quic_endpoints(IpEndpoint const &local, IpEndpoint 
const &remote)
+{
+  this->local_addr      = local;
+  this->remote_addr     = remote;
+  this->got_local_addr  = true;
+  this->got_remote_addr = true;
+}
+
+QUICConnectionErrorUPtr
+QUICNetVConnection::create_openssl_stream(uint64_t flags, QUICStreamId 
&new_stream_id)
+{
+  if (this->_ssl == nullptr) {
+    return 
std::make_unique<QUICConnectionError>(QUICTransErrorCode::INTERNAL_ERROR, "QUIC 
connection is not initialized");
+  }
+
+  SSL *stream_ssl = SSL_new_stream(this->_ssl, flags | 
SSL_STREAM_FLAG_NO_BLOCK);
+  if (stream_ssl == nullptr) {
+    return 
std::make_unique<QUICConnectionError>(QUICTransErrorCode::STREAM_LIMIT_ERROR, 
"Failed to create QUIC stream");
+  }
+
+  SSL_set_blocking_mode(stream_ssl, 0);
+  SSL_set_event_handling_mode(stream_ssl, 
SSL_VALUE_EVENT_HANDLING_MODE_IMPLICIT);
+  SSL_set_mode(stream_ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | 
SSL_MODE_ENABLE_PARTIAL_WRITE);
+
+  new_stream_id = SSL_get_stream_id(stream_ssl);
+  this->_openssl_streams.emplace(new_stream_id, stream_ssl);
+
+  [[maybe_unused]] QUICConnectionError err;
+  this->_stream_manager->create_stream(new_stream_id, err);
+
+  return nullptr;
+}
+
+void
+QUICNetVConnection::free()
+{
+  this->free_thread(this_ethread());
+}
+
+void
+QUICNetVConnection::remove_connection_ids()
+{
+}
+
+void
+QUICNetVConnection::destroy(EThread *t)
+{
+  QUICConDebug("Destroy connection");
+  if (from_accept_thread) {
+    quicNetVCAllocator.free(this);
+  } else {
+    THREAD_FREE(this, quicNetVCAllocator, t);
+  }
+}
+
+void
+QUICNetVConnection::set_local_addr()
+{
+}
+
+void
+QUICNetVConnection::free_thread(EThread * /* t ATS_UNUSED */)
+{
+  QUICConDebug("Free connection");
+
+  this->_unschedule_openssl_event();
+
+  for (auto &[stream_id, stream_ssl] : this->_openssl_streams) {
+    SSL_free(stream_ssl);
+  }
+  this->_openssl_streams.clear();
+
+  if (this->_ssl != nullptr) {
+    this->_unbindSSLObject();
+    SSL_free(this->_ssl);
+    this->_ssl = nullptr;
+  }
+
+  this->_application_map.reset();
+  this->_stream_manager.reset();
+
+  super::clear();
+  ALPNSupport::clear();
+  TLSBasicSupport::clear();
+  TLSEventSupport::clear();
+  TLSCertSwitchSupport::_clear();
+
+  if (this->_packet_handler != nullptr) {
+    this->_packet_handler->close_connection(this);
+    this->_packet_handler = nullptr;
+  }
+}
+
+void
+QUICNetVConnection::reenable(VIO * /* vio ATS_UNUSED */)
+{
+}
+
+int
+QUICNetVConnection::state_handshake(int event, Event *data)
+{
+  if (data == this->_packet_write_ready) {
+    this->_packet_write_ready = nullptr;
+  }
+
+  switch (event) {
+  case EVENT_IMMEDIATE:
+  case EVENT_INTERVAL:
+  case QUIC_EVENT_PACKET_READ_READY:
+  case QUIC_EVENT_PACKET_WRITE_READY:
+    this->_handle_openssl_events();
+    break;
+  case VC_EVENT_EOS:
+  case VC_EVENT_ERROR:
+  case VC_EVENT_ACTIVE_TIMEOUT:
+  case VC_EVENT_INACTIVITY_TIMEOUT:
+    this->_unschedule_openssl_event();
+    this->_propagate_event(event);
+    this->closed = 1;
+    break;
+  default:
+    QUICConDebug("Unhandled event: %d", event);
+    break;
+  }
+
+  if (this->closed != 1 && SSL_is_init_finished(this->_ssl)) {
+    this->_switch_to_established_state();
+    this->_handle_openssl_events();
+  }
+
+  if (this->closed != 1 && this->_openssl_connection_closed()) {
+    this->_schedule_closing_event();
+  } else if (this->closed != 1) {
+    this->_schedule_openssl_event();
+  }
+
+  return EVENT_DONE;
+}
+
+int
+QUICNetVConnection::state_established(int event, Event *data)
+{
+  if (this->_ssl == nullptr) {
+    return EVENT_DONE;
+  }
+
+  if (data == this->_packet_write_ready) {
+    this->_packet_write_ready = nullptr;
+  }
+
+  switch (event) {
+  case EVENT_IMMEDIATE:
+  case EVENT_INTERVAL:
+  case QUIC_EVENT_PACKET_READ_READY:
+  case QUIC_EVENT_PACKET_WRITE_READY:
+    this->_handle_openssl_events();
+    break;
+  case VC_EVENT_EOS:
+  case VC_EVENT_ERROR:
+  case VC_EVENT_ACTIVE_TIMEOUT:
+  case VC_EVENT_INACTIVITY_TIMEOUT:
+    this->_unschedule_openssl_event();
+    this->_propagate_event(event);
+    this->closed = 1;
+    break;
+  default:
+    QUICConDebug("Unhandled event: %d", event);
+    break;
+  }
+
+  if (this->closed != 1 && this->_openssl_connection_closed()) {
+    this->_schedule_closing_event();
+  } else if (this->closed != 1) {
+    this->_schedule_openssl_event();
+  }
+
+  return EVENT_DONE;
+}
+
+void
+QUICNetVConnection::_switch_to_established_state()
+{
+  QUICConDebug("Enter state_connection_established");
+  this->_record_tls_handshake_end_time();
+  this->_update_end_of_handshake_stats();
+  SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_established);
+  this->_handshake_completed = true;
+  this->_start_application();
+}
+
+void
+QUICNetVConnection::_start_application()
+{
+  if (this->_application_started) {
+    return;
+  }
+
+  this->_application_started = true;
+
+  unsigned char const *app_name     = nullptr;
+  unsigned int         app_name_len = 0;
+  SSL_get0_alpn_selected(this->_ssl, &app_name, &app_name_len);
+
+  if (app_name == nullptr || app_name_len == 0) {
+    app_name     = reinterpret_cast<unsigned char const 
*>(IP_PROTO_TAG_HTTP_QUIC.data());
+    app_name_len = IP_PROTO_TAG_HTTP_QUIC.size();
+  }
+
+  this->_negotiated_alpn.assign(reinterpret_cast<char const *>(app_name), 
app_name_len);
+  this->set_negotiated_protocol_id(this->_negotiated_alpn);
+
+  if (netvc_context == NET_VCONNECTION_IN) {
+    if (this->setSelectedProtocol(app_name, app_name_len)) {
+      this->endpoint()->handleEvent(NET_EVENT_ACCEPT, this);
+    }
+  } else {
+    this->action_.continuation->handleEvent(NET_EVENT_OPEN, this);
+  }
+}
+
+void
+QUICNetVConnection::_propagate_event(int event)
+{
+  QUICConVDebug("Propagating: %d", event);
+  if (this->read.vio.cont && this->read.vio.mutex == 
this->read.vio.cont->mutex) {
+    this->read.vio.cont->handleEvent(event, &this->read.vio);
+  } else if (this->write.vio.cont && this->write.vio.mutex == 
this->write.vio.cont->mutex) {
+    this->write.vio.cont->handleEvent(event, &this->write.vio);
+  } else {
+    QUICConVDebug("Session does not exist");
+  }
+}
+
+bool
+QUICNetVConnection::shouldDestroy()
+{
+  return this->refcount() == 0;
+}
+
+VIO *
+QUICNetVConnection::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf)
+{
+  auto vio           = super::do_io_read(c, nbytes, buf);
+  this->read.enabled = 1;
+  return vio;
+}
+
+VIO *
+QUICNetVConnection::do_io_write(Continuation *c, int64_t nbytes, 
IOBufferReader *buf, bool /* owner ATS_UNUSED */)
+{
+  auto vio            = super::do_io_write(c, nbytes, buf);
+  this->write.enabled = 1;
+  this->_schedule_openssl_event(false);
+  return vio;
+}
+
+int
+QUICNetVConnection::acceptEvent(int event, Event *e)
+{
+  EThread    *t = (e == nullptr) ? this_ethread() : e->ethread;
+  NetHandler *h = get_NetHandler(t);
+
+  MUTEX_TRY_LOCK(lock, h->mutex, t);
+  if (!lock.is_locked()) {
+    if (event == EVENT_NONE) {
+      t->schedule_in(this, HRTIME_MSECONDS(net_retry_delay));
+      return EVENT_DONE;
+    } else {
+      e->schedule_in(HRTIME_MSECONDS(net_retry_delay));
+      return EVENT_CONT;
+    }
+  }
+
+  this->_context         = std::make_unique<QUICContext>(this);
+  this->_application_map = std::make_unique<QUICApplicationMap>();
+  this->_stream_manager  = 
std::make_unique<OpenSSLQUICStreamManager>(this->_context.get(), 
this->_application_map.get(), this);
+
+  ink_assert(this->thread == this_ethread());
+
+  if (h->startIO(this) < 0) {
+    this->free_thread(t);
+    return EVENT_DONE;
+  }
+
+  this->read.enabled  = 1;
+  this->write.enabled = 1;
+
+  SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_handshake);
+
+  nh->startCop(this);
+  this->set_default_inactivity_timeout(0);
+
+  if (inactivity_timeout_in) {
+    this->set_inactivity_timeout(inactivity_timeout_in);
+  }
+
+  if (active_timeout_in) {
+    set_active_timeout(active_timeout_in);
+  }
+
+  action_.continuation->handleEvent(NET_EVENT_ACCEPT, this);
+  this->_schedule_openssl_event(false);
+
+  return EVENT_DONE;
+}
+
+int
+QUICNetVConnection::connectUp(EThread * /* t ATS_UNUSED */, int /* fd 
ATS_UNUSED */)
+{
+  return 0;
+}
+
+QUICStreamManager *
+QUICNetVConnection::stream_manager()
+{
+  return this->_stream_manager.get();
+}
+
+void
+QUICNetVConnection::close_quic_connection(QUICConnectionErrorUPtr error)
+{
+  if (this->_ssl != nullptr) {
+    SSL_SHUTDOWN_EX_ARGS args = {};
+    if (error != nullptr) {
+      args.quic_error_code = error->code;
+      args.quic_reason     = error->msg;
+    }
+    SSL_shutdown_ex(this->_ssl, SSL_SHUTDOWN_FLAG_NO_BLOCK, &args, 
sizeof(args));
+  }
+}
+
+void
+QUICNetVConnection::reset_quic_connection()
+{
+  if (this->_ssl != nullptr) {
+    SSL_shutdown_ex(this->_ssl, SSL_SHUTDOWN_FLAG_RAPID | 
SSL_SHUTDOWN_FLAG_NO_BLOCK, nullptr, 0);
+  }
+}
+
+void
+QUICNetVConnection::handle_received_packet(UDPPacket * /* packet ATS_UNUSED */)
+{
+}
+
+void
+QUICNetVConnection::ping()
+{
+}
+
+QUICConnectionId
+QUICNetVConnection::peer_connection_id() const
+{
+  return {};
+}
+
+QUICConnectionId
+QUICNetVConnection::original_connection_id() const
+{
+  return {};
+}
+
+QUICConnectionId
+QUICNetVConnection::first_connection_id() const
+{
+  return {};
+}
+
+QUICConnectionId
+QUICNetVConnection::retry_source_connection_id() const
+{
+  return {};
+}
+
+QUICConnectionId
+QUICNetVConnection::initial_source_connection_id() const
+{
+  return this->_initial_source_connection_id;
+}
+
+QUICConnectionId
+QUICNetVConnection::connection_id() const
+{
+  return this->_quic_connection_id;
+}
+
+std::string_view
+QUICNetVConnection::cids() const
+{
+  return this->_cid_text;
+}
+
+QUICFiveTuple const
+QUICNetVConnection::five_tuple() const
+{
+  return QUICFiveTuple(this->remote_addr, this->local_addr, IPPROTO_UDP);
+}
+
+uint32_t
+QUICNetVConnection::pmtu() const
+{
+  return 0;
+}
+
+NetVConnectionContext_t
+QUICNetVConnection::direction() const
+{
+  return NET_VCONNECTION_IN;
+}
+
+QUICVersion
+QUICNetVConnection::negotiated_version() const
+{
+  return QUIC_SUPPORTED_VERSIONS[0];
+}
+
+std::string_view
+QUICNetVConnection::negotiated_application_name() const
+{
+  return this->_negotiated_alpn;
+}
+
+void
+QUICNetVConnection::on_stream_updated()
+{
+  this->_schedule_openssl_event(false);
+}
+
+bool
+QUICNetVConnection::is_closed() const
+{
+  return this->_openssl_connection_closed();
+}
+
+bool
+QUICNetVConnection::is_at_anti_amplification_limit() const
+{
+  return false;
+}
+
+bool
+QUICNetVConnection::is_address_validation_completed() const
+{
+  return true;
+}
+
+bool
+QUICNetVConnection::is_handshake_completed() const
+{
+  return this->_handshake_completed;
+}
+
+void
+QUICNetVConnection::net_read_io(NetHandler * /* nh ATS_UNUSED */)
+{
+  SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
+  this->handleEvent(QUIC_EVENT_PACKET_READ_READY, nullptr);
+}
+
+int64_t
+QUICNetVConnection::load_buffer_and_write(int64_t /* towrite ATS_UNUSED */, 
MIOBufferAccessor & /* buf ATS_UNUSED */,
+                                          int64_t & /* total_written 
ATS_UNUSED */, int & /* needs ATS_UNUSED */)
+{
+  return 0;
+}
+
+void
+QUICNetVConnection::_bindSSLObject()
+{
+  TLSBasicSupport::bind(this->_ssl, this);
+  TLSEventSupport::bind(this->_ssl, this);
+  ALPNSupport::bind(this->_ssl, this);
+  TLSSessionResumptionSupport::bind(this->_ssl, this);
+  TLSSNISupport::bind(this->_ssl, this);
+  TLSCertSwitchSupport::bind(this->_ssl, this);
+  QUICSupport::bind(this->_ssl, this);
+}
+
+void
+QUICNetVConnection::_unbindSSLObject()
+{
+  TLSBasicSupport::unbind(this->_ssl);
+  TLSEventSupport::unbind(this->_ssl);
+  ALPNSupport::unbind(this->_ssl);
+  TLSSessionResumptionSupport::unbind(this->_ssl);
+  TLSSNISupport::unbind(this->_ssl);
+  TLSCertSwitchSupport::unbind(this->_ssl);
+  QUICSupport::unbind(this->_ssl);
+}
+
+void
+QUICNetVConnection::_schedule_packet_write_ready(bool delay)
+{
+  this->_schedule_openssl_event(delay);
+}
+
+void
+QUICNetVConnection::_unschedule_packet_write_ready()
+{
+  this->_unschedule_openssl_event();
+}
+
+void
+QUICNetVConnection::_close_packet_write_ready(Event *data)
+{
+  if (this->_packet_write_ready == data) {
+    this->_packet_write_ready = nullptr;
+  }
+}
+
+void
+QUICNetVConnection::_schedule_quiche_timeout()
+{
+}
+
+void
+QUICNetVConnection::_unschedule_quiche_timeout()
+{
+}
+
+void
+QUICNetVConnection::_close_quiche_timeout(Event * /* data ATS_UNUSED */)
+{
+}
+
+void
+QUICNetVConnection::_schedule_closing_event()
+{
+  QUICConDebug("Scheduling closing event");
+  SSL_CONN_CLOSE_INFO info = {};
+  if (this->_ssl != nullptr && SSL_get_conn_close_info(this->_ssl, &info, 
sizeof(info)) == 1) {
+    QUICConDebug("QUIC close info: error_code=%" PRIu64 " frame_type=%" PRIu64 
" flags=0x%x reason=\"%.*s\"", info.error_code,
+                 info.frame_type, info.flags, 
static_cast<int>(info.reason_len), info.reason == nullptr ? "" : info.reason);
+    if (info.error_code == OSSL_QUIC_LOCAL_ERR_IDLE_TIMEOUT) {
+      QUICConDebug("QUIC Idle timeout detected");
+      this->thread->schedule_imm(this, VC_EVENT_INACTIVITY_TIMEOUT);
+      return;
+    }
+  }
+
+  this->thread->schedule_imm(this, VC_EVENT_EOS);
+}
+
+void
+QUICNetVConnection::_handle_read_ready()
+{
+  this->_handle_openssl_events();
+}
+
+void
+QUICNetVConnection::_handle_write_ready()
+{
+  this->_handle_openssl_events();
+}
+
+void
+QUICNetVConnection::_handle_interval()
+{
+  this->_handle_openssl_events();
+}
+
+void
+QUICNetVConnection::_handle_openssl_events()
+{
+  if (this->_ssl == nullptr) {
+    return;
+  }
+
+  if (SSL_handle_events(this->_ssl) != 1) {
+    QUICConDebug("SSL_handle_events failed: %s", 
ERR_error_string(ERR_peek_error(), nullptr));
+  }
+
+  if (this->_stream_manager != nullptr && this->_application_started) {
+    this->_accept_openssl_streams();
+    this->_process_openssl_streams();
+  }
+
+  this->netActivity();
+}
+
+void
+QUICNetVConnection::_accept_openssl_streams()
+{
+  while (SSL *stream_ssl = SSL_accept_stream(this->_ssl, 
SSL_ACCEPT_STREAM_NO_BLOCK)) {
+    SSL_set_blocking_mode(stream_ssl, 0);
+    SSL_set_event_handling_mode(stream_ssl, 
SSL_VALUE_EVENT_HANDLING_MODE_IMPLICIT);
+    SSL_set_mode(stream_ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | 
SSL_MODE_ENABLE_PARTIAL_WRITE);
+
+    QUICStreamId stream_id = SSL_get_stream_id(stream_ssl);
+    this->_openssl_streams.emplace(stream_id, stream_ssl);
+
+    if (this->_stream_manager->find_stream(stream_id) == nullptr) {
+      [[maybe_unused]] QUICConnectionError err;
+      this->_stream_manager->create_stream(stream_id, err);
+    }
+  }
+}
+
+void
+QUICNetVConnection::_process_openssl_streams()
+{
+  for (auto &[stream_id, stream_ssl] : this->_openssl_streams) {

Review Comment:
   Fixed. _process_openssl_streams() now snapshots stream IDs and re-finds each 
stream before processing, so stream map mutation cannot invalidate the active 
iteration.
   
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to