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

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


The following commit(s) were added to refs/heads/quic-latest by this push:
     new 24df75e  Decouple HTTP/0.9 and HTTP/3
24df75e is described below

commit 24df75e9748f73a4cb8858f6c552909e0406da01
Author: Masaori Koshiba <[email protected]>
AuthorDate: Mon Mar 11 11:20:14 2019 +0900

    Decouple HTTP/0.9 and HTTP/3
    
    Add Http09ClientSession and Http09ClientTransaction to clarify HTTP/0.9 and 
HTTP/3 code.
    HTTP/0.9 and HTTP/3 is completely switched by ALPN (hq-* vs h3-*).
---
 proxy/http3/Http09App.cc              |   8 +-
 proxy/http3/Http09App.h               |   4 +-
 proxy/http3/Http3App.cc               |   4 +-
 proxy/http3/Http3ClientSession.cc     | 146 ++++---
 proxy/http3/Http3ClientSession.h      |  64 ++-
 proxy/http3/Http3ClientTransaction.cc | 724 +++++++++++++++++++++-------------
 proxy/http3/Http3ClientTransaction.h  |  92 +++--
 7 files changed, 666 insertions(+), 376 deletions(-)

diff --git a/proxy/http3/Http09App.cc b/proxy/http3/Http09App.cc
index f89d0bb..1168b5a 100644
--- a/proxy/http3/Http09App.cc
+++ b/proxy/http3/Http09App.cc
@@ -35,7 +35,7 @@ static constexpr char debug_tag_v[] = "v_quic_simple_app";
 
 Http09App::Http09App(QUICNetVConnection *client_vc, IpAllow::ACL session_acl) 
: QUICApplication(client_vc)
 {
-  this->_client_session      = new Http3ClientSession(client_vc);
+  this->_client_session      = new Http09ClientSession(client_vc);
   this->_client_session->acl = std::move(session_acl);
   this->_client_session->new_connection(client_vc, nullptr, nullptr);
 
@@ -62,8 +62,8 @@ Http09App::main_event_handler(int event, Event *data)
     return -1;
   }
 
-  QUICStreamId stream_id      = stream_io->stream_id();
-  Http3ClientTransaction *txn = 
this->_client_session->get_transaction(stream_id);
+  QUICStreamId stream_id       = stream_io->stream_id();
+  Http09ClientTransaction *txn = static_cast<Http09ClientTransaction 
*>(this->_client_session->get_transaction(stream_id));
 
   uint8_t dummy;
   switch (event) {
@@ -75,7 +75,7 @@ Http09App::main_event_handler(int event, Event *data)
     }
     if (stream_io->peek(&dummy, 1)) {
       if (txn == nullptr) {
-        txn = new Http3ClientTransaction(this->_client_session, stream_io);
+        txn = new Http09ClientTransaction(this->_client_session, stream_io);
         SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread());
 
         txn->new_transaction();
diff --git a/proxy/http3/Http09App.h b/proxy/http3/Http09App.h
index 1132ef2..b3356a0 100644
--- a/proxy/http3/Http09App.h
+++ b/proxy/http3/Http09App.h
@@ -28,7 +28,7 @@
 #include "QUICApplication.h"
 
 class QUICNetVConnection;
-class Http3ClientSession;
+class Http09ClientSession;
 
 /**
  * @brief A simple multi-streamed application.
@@ -45,5 +45,5 @@ public:
   int main_event_handler(int event, Event *data);
 
 private:
-  Http3ClientSession *_client_session = nullptr;
+  Http09ClientSession *_client_session = nullptr;
 };
diff --git a/proxy/http3/Http3App.cc b/proxy/http3/Http3App.cc
index 9b034b6..7b34fa2 100644
--- a/proxy/http3/Http3App.cc
+++ b/proxy/http3/Http3App.cc
@@ -196,7 +196,7 @@ Http3App::_handle_bidi_stream_on_read_ready(int event, 
QUICStreamIO *stream_io)
   uint8_t dummy;
   if (stream_io->peek(&dummy, 1)) {
     QUICStreamId stream_id      = stream_io->stream_id();
-    Http3ClientTransaction *txn = 
this->_client_session->get_transaction(stream_id);
+    Http3ClientTransaction *txn = static_cast<Http3ClientTransaction 
*>(this->_client_session->get_transaction(stream_id));
 
     if (txn == nullptr) {
       txn = new Http3ClientTransaction(this->_client_session, stream_io);
@@ -261,7 +261,7 @@ void
 Http3App::_handle_bidi_stream_on_write_ready(int event, QUICStreamIO 
*stream_io)
 {
   QUICStreamId stream_id      = stream_io->stream_id();
-  Http3ClientTransaction *txn = 
this->_client_session->get_transaction(stream_id);
+  Http3ClientTransaction *txn = static_cast<Http3ClientTransaction 
*>(this->_client_session->get_transaction(stream_id));
   if (txn != nullptr) {
     SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread());
     txn->handleEvent(event);
diff --git a/proxy/http3/Http3ClientSession.cc 
b/proxy/http3/Http3ClientSession.cc
index 2038e78..3fdd13d 100644
--- a/proxy/http3/Http3ClientSession.cc
+++ b/proxy/http3/Http3ClientSession.cc
@@ -25,103 +25,134 @@
 
 #include "Http3.h"
 
-Http3ClientSession::Http3ClientSession(NetVConnection *vc) : _client_vc(vc)
+//
+// HQClientSession
+//
+HQClientSession ::~HQClientSession()
 {
-  this->_local_qpack  = new QPACK(static_cast<QUICNetVConnection *>(vc), 
HTTP3_DEFAULT_MAX_HEADER_LIST_SIZE,
-                                 HTTP3_DEFAULT_HEADER_TABLE_SIZE, 
HTTP3_DEFAULT_QPACK_BLOCKED_STREAMS);
-  this->_remote_qpack = new QPACK(static_cast<QUICNetVConnection *>(vc), 
HTTP3_DEFAULT_MAX_HEADER_LIST_SIZE,
-                                  HTTP3_DEFAULT_HEADER_TABLE_SIZE, 
HTTP3_DEFAULT_QPACK_BLOCKED_STREAMS);
+  for (HQClientTransaction *t = this->_transaction_list.head; t; t = 
static_cast<HQClientTransaction *>(t->link.next)) {
+    delete t;
+  }
 }
 
-Http3ClientSession::~Http3ClientSession()
+void
+HQClientSession::add_transaction(HQClientTransaction *trans)
 {
-  this->_client_vc = nullptr;
-  for (Http3ClientTransaction *t = this->_transaction_list.head; t; t = 
static_cast<Http3ClientTransaction *>(t->link.next)) {
-    delete t;
+  this->_transaction_list.enqueue(trans);
+
+  return;
+}
+
+HQClientTransaction *
+HQClientSession::get_transaction(QUICStreamId id)
+{
+  for (HQClientTransaction *t = this->_transaction_list.head; t; t = 
static_cast<Http3ClientTransaction *>(t->link.next)) {
+    if (t->get_transaction_id() == static_cast<int>(id)) {
+      return t;
+    }
   }
-  delete this->_local_qpack;
-  delete this->_remote_qpack;
+
+  return nullptr;
 }
 
 VIO *
-Http3ClientSession::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf)
+HQClientSession::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf)
 {
   ink_assert(false);
   return nullptr;
 }
 
 VIO *
-Http3ClientSession::do_io_write(Continuation *c, int64_t nbytes, 
IOBufferReader *buf, bool owner)
+HQClientSession::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader 
*buf, bool owner)
 {
   ink_assert(false);
   return nullptr;
 }
 
 void
-Http3ClientSession::do_io_close(int lerrno)
+HQClientSession::do_io_close(int lerrno)
 {
   // TODO
   return;
 }
 
 void
-Http3ClientSession::do_io_shutdown(ShutdownHowTo_t howto)
+HQClientSession::do_io_shutdown(ShutdownHowTo_t howto)
 {
   ink_assert(false);
   return;
 }
 
 void
-Http3ClientSession::reenable(VIO *vio)
+HQClientSession::reenable(VIO *vio)
 {
   ink_assert(false);
   return;
 }
 
 void
-Http3ClientSession::destroy()
+HQClientSession::new_connection(NetVConnection *new_vc, MIOBuffer *iobuf, 
IOBufferReader *reade)
 {
-  ink_assert(false);
+  this->con_id = static_cast<QUICConnection 
*>(reinterpret_cast<QUICNetVConnection *>(new_vc))->connection_id();
+
   return;
 }
 
 void
-Http3ClientSession::start()
+HQClientSession::start()
 {
   ink_assert(false);
   return;
 }
 
 void
-Http3ClientSession::new_connection(NetVConnection *new_vc, MIOBuffer *iobuf, 
IOBufferReader *reade)
+HQClientSession::destroy()
 {
-  this->con_id = static_cast<QUICConnection 
*>(reinterpret_cast<QUICNetVConnection *>(new_vc))->connection_id();
+  ink_assert(false);
+  return;
+}
 
+void
+HQClientSession::release(ProxyClientTransaction *trans)
+{
   return;
 }
 
 NetVConnection *
-Http3ClientSession::get_netvc() const
+HQClientSession::get_netvc() const
 {
   return this->_client_vc;
 }
 
 int
-Http3ClientSession::get_transact_count() const
+HQClientSession::get_transact_count() const
 {
   return 0;
 }
 
-const char *
-Http3ClientSession::get_protocol_string() const
+//
+// Http3ClientSession
+//
+Http3ClientSession::Http3ClientSession(NetVConnection *vc) : 
HQClientSession(vc)
 {
-  return IP_PROTO_TAG_HTTP_QUIC.data();
+  this->_local_qpack  = new QPACK(static_cast<QUICNetVConnection *>(vc), 
HTTP3_DEFAULT_MAX_HEADER_LIST_SIZE,
+                                 HTTP3_DEFAULT_HEADER_TABLE_SIZE, 
HTTP3_DEFAULT_QPACK_BLOCKED_STREAMS);
+  this->_remote_qpack = new QPACK(static_cast<QUICNetVConnection *>(vc), 
HTTP3_DEFAULT_MAX_HEADER_LIST_SIZE,
+                                  HTTP3_DEFAULT_HEADER_TABLE_SIZE, 
HTTP3_DEFAULT_QPACK_BLOCKED_STREAMS);
 }
 
-void
-Http3ClientSession::release(ProxyClientTransaction *trans)
+Http3ClientSession::~Http3ClientSession()
 {
-  return;
+  this->_client_vc = nullptr;
+  delete this->_local_qpack;
+  delete this->_remote_qpack;
+  super::~HQClientSession();
+}
+
+const char *
+Http3ClientSession::get_protocol_string() const
+{
+  return IP_PROTO_TAG_HTTP_3.data();
 }
 
 int
@@ -129,7 +160,7 @@ Http3ClientSession::populate_protocol(std::string_view 
*result, int size) const
 {
   int retval = 0;
   if (size > retval) {
-    result[retval++] = IP_PROTO_TAG_HTTP_QUIC;
+    result[retval++] = IP_PROTO_TAG_HTTP_3;
     if (size > retval) {
       retval += super::populate_protocol(result + retval, size - retval);
     }
@@ -149,33 +180,54 @@ 
Http3ClientSession::decrement_current_active_client_connections_stat()
   // TODO Implement stats
 }
 
-void
-Http3ClientSession::add_transaction(Http3ClientTransaction *trans)
+QPACK *
+Http3ClientSession::local_qpack()
 {
-  this->_transaction_list.enqueue(trans);
-  return;
+  return this->_local_qpack;
 }
 
-// this->_transaction_list should be map?
-Http3ClientTransaction *
-Http3ClientSession::get_transaction(QUICStreamId id)
+QPACK *
+Http3ClientSession::remote_qpack()
 {
-  for (Http3ClientTransaction *t = this->_transaction_list.head; t; t = 
static_cast<Http3ClientTransaction *>(t->link.next)) {
-    if (t->get_transaction_id() == static_cast<int>(id)) {
-      return t;
+  return this->_remote_qpack;
+}
+
+//
+// Http09ClientSession
+//
+Http09ClientSession::~Http09ClientSession()
+{
+  this->_client_vc = nullptr;
+  super::~HQClientSession();
+}
+
+const char *
+Http09ClientSession::get_protocol_string() const
+{
+  return IP_PROTO_TAG_HTTP_QUIC.data();
+}
+
+int
+Http09ClientSession::populate_protocol(std::string_view *result, int size) 
const
+{
+  int retval = 0;
+  if (size > retval) {
+    result[retval++] = IP_PROTO_TAG_HTTP_QUIC;
+    if (size > retval) {
+      retval += super::populate_protocol(result + retval, size - retval);
     }
   }
-  return nullptr;
+  return retval;
 }
 
-QPACK *
-Http3ClientSession::local_qpack()
+void
+Http09ClientSession::increment_current_active_client_connections_stat()
 {
-  return this->_local_qpack;
+  // TODO Implement stats
 }
 
-QPACK *
-Http3ClientSession::remote_qpack()
+void
+Http09ClientSession::decrement_current_active_client_connections_stat()
 {
-  return this->_remote_qpack;
+  // TODO Implement stats
 }
diff --git a/proxy/http3/Http3ClientSession.h b/proxy/http3/Http3ClientSession.h
index 8af10b0..996de11 100644
--- a/proxy/http3/Http3ClientSession.h
+++ b/proxy/http3/Http3ClientSession.h
@@ -27,13 +27,13 @@
 #include "Http3ClientTransaction.h"
 #include "QPACK.h"
 
-class Http3ClientSession : public ProxyClientSession
+class HQClientSession : public ProxyClientSession
 {
 public:
-  typedef ProxyClientSession super; ///< Parent type.
+  using super = ProxyClientSession; ///< Parent type
 
-  Http3ClientSession(NetVConnection *vc);
-  ~Http3ClientSession();
+  HQClientSession(NetVConnection *vc) : _client_vc(vc){};
+  ~HQClientSession();
 
   // Implement VConnection interface
   VIO *do_io_read(Continuation *c, int64_t nbytes = INT64_MAX, MIOBuffer *buf 
= nullptr) override;
@@ -43,27 +43,63 @@ public:
   void reenable(VIO *vio) override;
 
   // Implement ProxyClienSession interface
+  void new_connection(NetVConnection *new_vc, MIOBuffer *iobuf, IOBufferReader 
*reader) override;
   void start() override;
   void destroy() override;
-  void new_connection(NetVConnection *new_vc, MIOBuffer *iobuf, IOBufferReader 
*reader) override;
+  void release(ProxyClientTransaction *trans) override;
   NetVConnection *get_netvc() const override;
   int get_transact_count() const override;
+
+  // HQClientSession
+  void add_transaction(HQClientTransaction *);
+  HQClientTransaction *get_transaction(QUICStreamId);
+
+protected:
+  NetVConnection *_client_vc = nullptr;
+
+private:
+  // this should be unordered map?
+  Queue<HQClientTransaction> _transaction_list;
+};
+
+class Http3ClientSession : public HQClientSession
+{
+public:
+  using super = HQClientSession; ///< Parent type
+
+  Http3ClientSession(NetVConnection *vc);
+  ~Http3ClientSession();
+
+  // ProxyClienSession interface
   const char *get_protocol_string() const override;
-  void release(ProxyClientTransaction *trans) override;
   int populate_protocol(std::string_view *result, int size) const override;
   void increment_current_active_client_connections_stat() override;
   void decrement_current_active_client_connections_stat() override;
 
-  // Http3ClientSession specific methods
-  void add_transaction(Http3ClientTransaction *);
-  Http3ClientTransaction *get_transaction(QUICStreamId);
-
   QPACK *local_qpack();
   QPACK *remote_qpack();
 
 private:
-  NetVConnection *_client_vc = nullptr;
-  QPACK *_remote_qpack       = nullptr; // QPACK for decoding
-  QPACK *_local_qpack        = nullptr; // QPACK for encoding
-  Queue<Http3ClientTransaction> _transaction_list;
+  QPACK *_remote_qpack = nullptr; // QPACK for decoding
+  QPACK *_local_qpack  = nullptr; // QPACK for encoding
+};
+
+/**
+   Only for interop. Will be removed.
+ */
+class Http09ClientSession : public HQClientSession
+{
+public:
+  using super = HQClientSession; ///< Parent type
+
+  Http09ClientSession(NetVConnection *vc) : HQClientSession(vc) {}
+  ~Http09ClientSession();
+
+  // ProxyClienSession interface
+  const char *get_protocol_string() const override;
+  int populate_protocol(std::string_view *result, int size) const override;
+  void increment_current_active_client_connections_stat() override;
+  void decrement_current_active_client_connections_stat() override;
+
+private:
 };
diff --git a/proxy/http3/Http3ClientTransaction.cc 
b/proxy/http3/Http3ClientTransaction.cc
index 849a132..228621b 100644
--- a/proxy/http3/Http3ClientTransaction.cc
+++ b/proxy/http3/Http3ClientTransaction.cc
@@ -53,45 +53,27 @@
 //   Debug("v_http3_trans", "len=%" PRId64 "\n%s\n", read_len, msg);
 // }
 
-Http3ClientTransaction::Http3ClientTransaction(Http3ClientSession *session, 
QUICStreamIO *stream_io)
-  : super(), _stream_io(stream_io)
+//
+// HQClientTransaction
+//
+HQClientTransaction::HQClientTransaction(HQClientSession *session, 
QUICStreamIO *stream_io) : super(), _stream_io(stream_io)
 {
   this->mutex   = new_ProxyMutex();
   this->_thread = this_ethread();
 
   this->set_parent(session);
-  this->sm_reader = this->_read_vio_buf.alloc_reader();
-  static_cast<Http3ClientSession *>(this->parent)->add_transaction(this);
-
-  this->_header_framer = new Http3HeaderFramer(this, &this->_write_vio, 
session->local_qpack(), stream_io->stream_id());
-  this->_data_framer   = new Http3DataFramer(this, &this->_write_vio);
-  this->_frame_collector.add_generator(this->_header_framer);
-  this->_frame_collector.add_generator(this->_data_framer);
-  // this->_frame_collector.add_generator(this->_push_controller);
-
-  this->_header_handler = new Http3HeaderVIOAdaptor(&this->_request_header, 
session->remote_qpack(), this, stream_io->stream_id());
-  this->_data_handler   = new Http3StreamDataVIOAdaptor(&this->_read_vio);
-
-  this->_frame_dispatcher.add_handler(this->_header_handler);
-  this->_frame_dispatcher.add_handler(this->_data_handler);
 
+  this->sm_reader = this->_read_vio_buf.alloc_reader();
   this->_request_header.create(HTTP_TYPE_REQUEST);
-
-  SET_HANDLER(&Http3ClientTransaction::state_stream_open);
 }
 
-Http3ClientTransaction::~Http3ClientTransaction()
+HQClientTransaction::~HQClientTransaction()
 {
   this->_request_header.destroy();
-
-  delete this->_header_framer;
-  delete this->_data_framer;
-  delete this->_header_handler;
-  delete this->_data_handler;
 }
 
 void
-Http3ClientTransaction::set_active_timeout(ink_hrtime timeout_in)
+HQClientTransaction::set_active_timeout(ink_hrtime timeout_in)
 {
   if (parent) {
     parent->set_active_timeout(timeout_in);
@@ -99,7 +81,7 @@ Http3ClientTransaction::set_active_timeout(ink_hrtime 
timeout_in)
 }
 
 void
-Http3ClientTransaction::set_inactivity_timeout(ink_hrtime timeout_in)
+HQClientTransaction::set_inactivity_timeout(ink_hrtime timeout_in)
 {
   if (parent) {
     parent->set_inactivity_timeout(timeout_in);
@@ -107,7 +89,7 @@ Http3ClientTransaction::set_inactivity_timeout(ink_hrtime 
timeout_in)
 }
 
 void
-Http3ClientTransaction::cancel_inactivity_timeout()
+HQClientTransaction::cancel_inactivity_timeout()
 {
   if (parent) {
     parent->cancel_inactivity_timeout();
@@ -115,121 +97,20 @@ Http3ClientTransaction::cancel_inactivity_timeout()
 }
 
 void
-Http3ClientTransaction::release(IOBufferReader *r)
+HQClientTransaction::release(IOBufferReader *r)
 {
   super::release(r);
   this->current_reader = nullptr;
 }
 
 bool
-Http3ClientTransaction::allow_half_open() const
+HQClientTransaction::allow_half_open() const
 {
   return false;
 }
 
-int
-Http3ClientTransaction::state_stream_open(int event, void *edata)
-{
-  // TODO: should check recursive call?
-  Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
-
-  if (this->_thread != this_ethread()) {
-    // Send on to the owning thread
-    if (this->_cross_thread_event == nullptr) {
-      this->_cross_thread_event = this->_thread->schedule_imm(this, event, 
edata);
-    }
-    return 0;
-  }
-
-  SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
-
-  Event *e = static_cast<Event *>(edata);
-  if (e == this->_cross_thread_event) {
-    this->_cross_thread_event = nullptr;
-  }
-
-  switch (event) {
-  case VC_EVENT_READ_READY:
-  case VC_EVENT_READ_COMPLETE: {
-    int64_t len = this->_process_read_vio();
-    // if no progress, don't need to signal
-    if (len > 0) {
-      this->_signal_read_event();
-    }
-    this->_stream_io->read_reenable();
-
-    break;
-  }
-  case VC_EVENT_WRITE_READY:
-  case VC_EVENT_WRITE_COMPLETE: {
-    int64_t len = this->_process_write_vio();
-    if (len > 0) {
-      this->_signal_write_event();
-    }
-    this->_stream_io->write_reenable();
-
-    break;
-  }
-  case VC_EVENT_EOS:
-  case VC_EVENT_ERROR:
-  case VC_EVENT_INACTIVITY_TIMEOUT:
-  case VC_EVENT_ACTIVE_TIMEOUT: {
-    ink_assert(false);
-    break;
-  }
-  case QPACK_EVENT_DECODE_COMPLETE: {
-    int res = this->_on_qpack_decode_complete();
-    if (res) {
-      // If READ_READY event is scheduled, should it be canceled?
-      this->_signal_read_event();
-    }
-    break;
-  }
-  case QPACK_EVENT_DECODE_FAILED: {
-    // FIXME: handle error
-    break;
-  }
-  default:
-    Http3TransDebug("Unknown event %d", event);
-    ink_assert(false);
-  }
-
-  return EVENT_DONE;
-}
-
-int
-Http3ClientTransaction::state_stream_closed(int event, void *data)
-{
-  Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
-
-  switch (event) {
-  case VC_EVENT_READ_READY:
-  case VC_EVENT_READ_COMPLETE: {
-    // ignore
-    break;
-  }
-  case VC_EVENT_WRITE_READY:
-  case VC_EVENT_WRITE_COMPLETE: {
-    // ignore
-    break;
-  }
-  case VC_EVENT_EOS:
-  case VC_EVENT_ERROR:
-  case VC_EVENT_INACTIVITY_TIMEOUT:
-  case VC_EVENT_ACTIVE_TIMEOUT: {
-    // TODO
-    ink_assert(false);
-    break;
-  }
-  default:
-    ink_assert(false);
-  }
-
-  return EVENT_DONE;
-}
-
 VIO *
-Http3ClientTransaction::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer 
*buf)
+HQClientTransaction::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer 
*buf)
 {
   if (buf) {
     this->_read_vio.buffer.writer_for(buf);
@@ -251,7 +132,7 @@ Http3ClientTransaction::do_io_read(Continuation *c, int64_t 
nbytes, MIOBuffer *b
 }
 
 VIO *
-Http3ClientTransaction::do_io_write(Continuation *c, int64_t nbytes, 
IOBufferReader *buf, bool owner)
+HQClientTransaction::do_io_write(Continuation *c, int64_t nbytes, 
IOBufferReader *buf, bool owner)
 {
   if (buf) {
     this->_write_vio.buffer.reader_for(buf);
@@ -273,10 +154,8 @@ Http3ClientTransaction::do_io_write(Continuation *c, 
int64_t nbytes, IOBufferRea
 }
 
 void
-Http3ClientTransaction::do_io_close(int lerrno)
+HQClientTransaction::do_io_close(int lerrno)
 {
-  SET_HANDLER(&Http3ClientTransaction::state_stream_closed);
-
   if (this->_read_event) {
     this->_read_event->cancel();
     this->_read_event = nullptr;
@@ -301,13 +180,13 @@ Http3ClientTransaction::do_io_close(int lerrno)
 }
 
 void
-Http3ClientTransaction::do_io_shutdown(ShutdownHowTo_t howto)
+HQClientTransaction::do_io_shutdown(ShutdownHowTo_t howto)
 {
   return;
 }
 
 void
-Http3ClientTransaction::reenable(VIO *vio)
+HQClientTransaction::reenable(VIO *vio)
 {
   if (vio->op == VIO::READ) {
     int64_t len = this->_process_read_vio();
@@ -326,11 +205,42 @@ Http3ClientTransaction::reenable(VIO *vio)
   }
 }
 
+void
+HQClientTransaction::destroy()
+{
+  current_reader = nullptr;
+}
+
+void
+HQClientTransaction::transaction_done()
+{
+  // TODO: start closing transaction
+  return;
+}
+
+int
+HQClientTransaction::get_transaction_id() const
+{
+  return this->_stream_io->stream_id();
+}
+
+void
+HQClientTransaction::increment_client_transactions_stat()
+{
+  // TODO
+}
+
+void
+HQClientTransaction::decrement_client_transactions_stat()
+{
+  // TODO
+}
+
 /**
  * @brief Replace existing event only if the new event is different than the 
inprogress event
  */
 Event *
-Http3ClientTransaction::_send_tracked_event(Event *event, int send_event, VIO 
*vio)
+HQClientTransaction::_send_tracked_event(Event *event, int send_event, VIO 
*vio)
 {
   if (event != nullptr) {
     if (event->callback_event != send_event) {
@@ -346,29 +256,11 @@ Http3ClientTransaction::_send_tracked_event(Event *event, 
int send_event, VIO *v
   return event;
 }
 
-void
-Http3ClientTransaction::set_read_vio_nbytes(int64_t nbytes)
-{
-  this->_read_vio.nbytes = nbytes;
-}
-
-void
-Http3ClientTransaction::set_write_vio_nbytes(int64_t nbytes)
-{
-  this->_write_vio.nbytes = nbytes;
-}
-
-void
-Http3ClientTransaction::destroy()
-{
-  current_reader = nullptr;
-}
-
 /**
  * @brief Signal event to this->_read_vio.cont
  */
 void
-Http3ClientTransaction::_signal_read_event()
+HQClientTransaction::_signal_read_event()
 {
   if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) {
     return;
@@ -389,7 +281,7 @@ Http3ClientTransaction::_signal_read_event()
  * @brief Signal event to this->_write_vio.cont
  */
 void
-Http3ClientTransaction::_signal_write_event()
+HQClientTransaction::_signal_write_event()
 {
   if (this->_write_vio.cont == nullptr || this->_write_vio.op == VIO::NONE) {
     return;
@@ -406,93 +298,159 @@ Http3ClientTransaction::_signal_write_event()
   Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
 }
 
-// Convert HTTP/0.9 to HTTP/1.1
-int64_t
-Http3ClientTransaction::_process_read_vio()
+//
+// Http3ClientTransaction
+//
+Http3ClientTransaction::Http3ClientTransaction(Http3ClientSession *session, 
QUICStreamIO *stream_io) : super(session, stream_io)
 {
-  if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) {
-    return 0;
-  }
+  static_cast<HQClientSession 
*>(this->parent)->add_transaction(static_cast<HQClientTransaction *>(this));
+
+  this->_header_framer = new Http3HeaderFramer(this, &this->_write_vio, 
session->local_qpack(), stream_io->stream_id());
+  this->_data_framer   = new Http3DataFramer(this, &this->_write_vio);
+  this->_frame_collector.add_generator(this->_header_framer);
+  this->_frame_collector.add_generator(this->_data_framer);
+  // this->_frame_collector.add_generator(this->_push_controller);
+
+  this->_header_handler = new Http3HeaderVIOAdaptor(&this->_request_header, 
session->remote_qpack(), this, stream_io->stream_id());
+  this->_data_handler   = new Http3StreamDataVIOAdaptor(&this->_read_vio);
+
+  this->_frame_dispatcher.add_handler(this->_header_handler);
+  this->_frame_dispatcher.add_handler(this->_data_handler);
+
+  SET_HANDLER(&Http3ClientTransaction::state_stream_open);
+}
+
+Http3ClientTransaction::~Http3ClientTransaction()
+{
+  delete this->_header_framer;
+  delete this->_data_framer;
+  delete this->_header_handler;
+  delete this->_data_handler;
+}
+
+int
+Http3ClientTransaction::state_stream_open(int event, void *edata)
+{
+  // TODO: should check recursive call?
+  Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
 
   if (this->_thread != this_ethread()) {
-    SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
+    // Send on to the owning thread
     if (this->_cross_thread_event == nullptr) {
-      // Send to the right thread
-      this->_cross_thread_event = this->_thread->schedule_imm(this, 
VC_EVENT_READ_READY, nullptr);
+      this->_cross_thread_event = this->_thread->schedule_imm(this, event, 
edata);
     }
     return 0;
   }
 
-  SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread());
+  SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
 
-  // Nuke this block when we drop 0.9 support
-  if (!this->_protocol_detected) {
-    uint8_t start[3];
-    if (this->_stream_io->peek(start, 3) < 3) {
-      return 0;
-    }
-    // If the first two bit are 0 and 1, the 3rd byte is type field.
-    // Because there is no type value larger than 0x20, we can assume that the
-    // request is HTTP/0.9 if the value is larger than 0x20.
-    if (0x40 <= start[0] && start[0] < 0x80 && start[2] > 0x20) {
-      this->_legacy_request = true;
-    }
-    this->_protocol_detected = true;
+  Event *e = static_cast<Event *>(edata);
+  if (e == this->_cross_thread_event) {
+    this->_cross_thread_event = nullptr;
   }
 
-  if (this->_legacy_request) {
-    uint64_t nread    = 0;
-    MIOBuffer *writer = this->_read_vio.get_writer();
+  switch (event) {
+  case VC_EVENT_READ_READY:
+  case VC_EVENT_READ_COMPLETE: {
+    int64_t len = this->_process_read_vio();
+    // if no progress, don't need to signal
+    if (len > 0) {
+      this->_signal_read_event();
+    }
+    this->_stream_io->read_reenable();
 
-    // Nuke this branch when we drop 0.9 support
-    if (!this->_client_req_header_complete) {
-      uint8_t buf[4096];
-      int len = this->_stream_io->peek(buf, 4096);
-      // Check client request is complete or not
-      if (len < 2 || buf[len - 1] != '\n') {
-        return 0;
-      }
-      this->_stream_io->consume(len);
-      nread += len;
-      this->_client_req_header_complete = true;
+    break;
+  }
+  case VC_EVENT_WRITE_READY:
+  case VC_EVENT_WRITE_COMPLETE: {
+    int64_t len = this->_process_write_vio();
+    if (len > 0) {
+      this->_signal_write_event();
+    }
+    this->_stream_io->write_reenable();
 
-      // Check "CRLF" or "LF"
-      int n = 2;
-      if (buf[len - 2] != '\r') {
-        n = 1;
-      }
-
-      writer->write(buf, len - n);
-      // FIXME: Get hostname from SNI?
-      const char version[] = " HTTP/1.1\r\nHost: localhost\r\n\r\n";
-      writer->write(version, sizeof(version));
-    } else {
-      uint8_t buf[4096];
-      int len;
-      while ((len = this->_stream_io->read(buf, 4096)) > 0) {
-        nread += len;
-        writer->write(buf, len);
-      }
+    break;
+  }
+  case VC_EVENT_EOS:
+  case VC_EVENT_ERROR:
+  case VC_EVENT_INACTIVITY_TIMEOUT:
+  case VC_EVENT_ACTIVE_TIMEOUT: {
+    Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
+    break;
+  }
+  case QPACK_EVENT_DECODE_COMPLETE: {
+    int res = this->_on_qpack_decode_complete();
+    if (res) {
+      // If READ_READY event is scheduled, should it be canceled?
+      this->_signal_read_event();
     }
+    break;
+  }
+  case QPACK_EVENT_DECODE_FAILED: {
+    // FIXME: handle error
+    break;
+  }
+  default:
+    Http3TransDebug("Unknown event %d", event);
+  }
 
-    return nread;
-    // End of code for HTTP/0.9
-  } else {
-    // This branch is for HTTP/3
-    uint64_t nread = 0;
-    this->_frame_dispatcher.on_read_ready(*this->_stream_io, nread);
-    return nread;
+  return EVENT_DONE;
+}
+
+int
+Http3ClientTransaction::state_stream_closed(int event, void *data)
+{
+  Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
+
+  switch (event) {
+  case VC_EVENT_READ_READY:
+  case VC_EVENT_READ_COMPLETE: {
+    // ignore
+    break;
+  }
+  case VC_EVENT_WRITE_READY:
+  case VC_EVENT_WRITE_COMPLETE: {
+    // ignore
+    break;
+  }
+  case VC_EVENT_EOS:
+  case VC_EVENT_ERROR:
+  case VC_EVENT_INACTIVITY_TIMEOUT:
+  case VC_EVENT_ACTIVE_TIMEOUT: {
+    // TODO
+    Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
+    break;
+  }
+  default:
+    Http3TransDebug("Unknown event %d", event);
   }
+
+  return EVENT_DONE;
 }
 
-// FIXME: already defined somewhere?
-static constexpr char http_1_1_version[] = "HTTP/1.1";
+void
+Http3ClientTransaction::do_io_close(int lerrno)
+{
+  SET_HANDLER(&Http3ClientTransaction::state_stream_closed);
+  super::do_io_close(lerrno);
+}
+
+bool
+Http3ClientTransaction::is_response_header_sent() const
+{
+  return this->_header_framer->is_done();
+}
+
+bool
+Http3ClientTransaction::is_response_body_sent() const
+{
+  return this->_data_framer->is_done();
+}
 
-// Convert HTTP/1.1 to HTTP/0.9
 int64_t
-Http3ClientTransaction::_process_write_vio()
+Http3ClientTransaction::_process_read_vio()
 {
-  if (this->_write_vio.cont == nullptr || this->_write_vio.op == VIO::NONE) {
+  if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) {
     return 0;
   }
 
@@ -500,57 +458,40 @@ Http3ClientTransaction::_process_write_vio()
     SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
     if (this->_cross_thread_event == nullptr) {
       // Send to the right thread
-      this->_cross_thread_event = this->_thread->schedule_imm(this, 
VC_EVENT_WRITE_READY, nullptr);
+      this->_cross_thread_event = this->_thread->schedule_imm(this, 
VC_EVENT_READ_READY, nullptr);
     }
     return 0;
   }
 
-  SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread());
+  SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread());
 
-  IOBufferReader *reader = this->_write_vio.get_reader();
+  uint64_t nread = 0;
+  this->_frame_dispatcher.on_read_ready(*this->_stream_io, nread);
+  return nread;
+}
 
-  if (this->_legacy_request) {
-    // This branch is for HTTP/0.9
-    int64_t http_1_1_version_len = sizeof(http_1_1_version) - 1;
+int64_t
+Http3ClientTransaction::_process_write_vio()
+{
+  if (this->_write_vio.cont == nullptr || this->_write_vio.op == VIO::NONE) {
+    return 0;
+  }
 
-    if (reader->is_read_avail_more_than(http_1_1_version_len) &&
-        memcmp(reader->start(), http_1_1_version, http_1_1_version_len) == 0) {
-      // Skip HTTP/1.1 response headers
-      IOBufferBlock *headers = reader->get_current_block();
-      int64_t headers_size   = headers->read_avail();
-      reader->consume(headers_size);
-      this->_write_vio.ndone += headers_size;
+  if (this->_thread != this_ethread()) {
+    SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
+    if (this->_cross_thread_event == nullptr) {
+      // Send to the right thread
+      this->_cross_thread_event = this->_thread->schedule_imm(this, 
VC_EVENT_WRITE_READY, nullptr);
     }
+    return 0;
+  }
 
-    // Write HTTP/1.1 response body
-    int64_t bytes_avail   = reader->read_avail();
-    int64_t total_written = 0;
-
-    while (total_written < bytes_avail) {
-      int64_t data_len      = reader->block_read_avail();
-      int64_t bytes_written = this->_stream_io->write(reader, data_len);
-      if (bytes_written <= 0) {
-        break;
-      }
-
-      reader->consume(bytes_written);
-      this->_write_vio.ndone += bytes_written;
-      total_written += bytes_written;
-    }
+  SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread());
 
-    // NOTE: When Chunked Transfer Coding is supported, check ChunkedState of 
ChunkedHandler
-    // is CHUNK_READ_DONE and set FIN flag
-    if (this->_write_vio.ntodo() == 0) {
-      // The size of respons to client
-      this->_stream_io->write_done();
-    }
+  size_t nwritten = 0;
+  this->_frame_collector.on_write_ready(this->_stream_io, nwritten);
 
-    return total_written;
-  } else {
-    size_t nwritten = 0;
-    this->_frame_collector.on_write_ready(this->_stream_io, nwritten);
-    return nwritten;
-  }
+  return nwritten;
 }
 
 // Constant strings for pseudo headers
@@ -624,39 +565,260 @@ Http3ClientTransaction::_on_qpack_decode_complete()
   return 1;
 }
 
-void
-Http3ClientTransaction::transaction_done()
+//
+// Http09ClientTransaction
+//
+Http09ClientTransaction::Http09ClientTransaction(Http09ClientSession *session, 
QUICStreamIO *stream_io) : super(session, stream_io)
 {
-  // TODO: start closing transaction
-  return;
+  static_cast<HQClientSession 
*>(this->parent)->add_transaction(static_cast<HQClientTransaction *>(this));
+
+  SET_HANDLER(&Http09ClientTransaction::state_stream_open);
 }
 
+Http09ClientTransaction::~Http09ClientTransaction() {}
+
 int
-Http3ClientTransaction::get_transaction_id() const
+Http09ClientTransaction::state_stream_open(int event, void *edata)
 {
-  return this->_stream_io->stream_id();
+  // TODO: should check recursive call?
+  Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
+
+  if (this->_thread != this_ethread()) {
+    // Send on to the owning thread
+    if (this->_cross_thread_event == nullptr) {
+      this->_cross_thread_event = this->_thread->schedule_imm(this, event, 
edata);
+    }
+    return 0;
+  }
+
+  SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
+
+  Event *e = static_cast<Event *>(edata);
+  if (e == this->_cross_thread_event) {
+    this->_cross_thread_event = nullptr;
+  }
+
+  switch (event) {
+  case VC_EVENT_READ_READY:
+  case VC_EVENT_READ_COMPLETE: {
+    int64_t len = this->_process_read_vio();
+    // if no progress, don't need to signal
+    if (len > 0) {
+      this->_signal_read_event();
+    }
+    this->_stream_io->read_reenable();
+
+    break;
+  }
+  case VC_EVENT_WRITE_READY:
+  case VC_EVENT_WRITE_COMPLETE: {
+    int64_t len = this->_process_write_vio();
+    if (len > 0) {
+      this->_signal_write_event();
+    }
+    this->_stream_io->write_reenable();
+
+    break;
+  }
+  case VC_EVENT_EOS:
+  case VC_EVENT_ERROR:
+  case VC_EVENT_INACTIVITY_TIMEOUT:
+  case VC_EVENT_ACTIVE_TIMEOUT: {
+    Http3TransDebug("%d", event);
+    break;
+  }
+  default:
+    Http3TransDebug("Unknown event %d", event);
+  }
+
+  return EVENT_DONE;
 }
 
 void
-Http3ClientTransaction::increment_client_transactions_stat()
+Http09ClientTransaction::do_io_close(int lerrno)
 {
-  // TODO
+  SET_HANDLER(&Http09ClientTransaction::state_stream_closed);
+  super::do_io_close(lerrno);
 }
 
-void
-Http3ClientTransaction::decrement_client_transactions_stat()
+int
+Http09ClientTransaction::state_stream_closed(int event, void *data)
 {
-  // TODO
+  Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
+
+  switch (event) {
+  case VC_EVENT_READ_READY:
+  case VC_EVENT_READ_COMPLETE: {
+    // ignore
+    break;
+  }
+  case VC_EVENT_WRITE_READY:
+  case VC_EVENT_WRITE_COMPLETE: {
+    // ignore
+    break;
+  }
+  case VC_EVENT_EOS:
+  case VC_EVENT_ERROR:
+  case VC_EVENT_INACTIVITY_TIMEOUT:
+  case VC_EVENT_ACTIVE_TIMEOUT: {
+    // TODO
+    break;
+  }
+  default:
+    Http3TransDebug("Unknown event %d", event);
+  }
+
+  return EVENT_DONE;
 }
 
-bool
-Http3ClientTransaction::is_response_header_sent() const
+// Convert HTTP/0.9 to HTTP/1.1
+int64_t
+Http09ClientTransaction::_process_read_vio()
 {
-  return this->_header_framer->is_done();
+  if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) {
+    return 0;
+  }
+
+  if (this->_thread != this_ethread()) {
+    SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
+    if (this->_cross_thread_event == nullptr) {
+      // Send to the right thread
+      this->_cross_thread_event = this->_thread->schedule_imm(this, 
VC_EVENT_READ_READY, nullptr);
+    }
+    return 0;
+  }
+
+  SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread());
+
+  // Nuke this block when we drop 0.9 support
+  if (!this->_protocol_detected) {
+    uint8_t start[3];
+    if (this->_stream_io->peek(start, 3) < 3) {
+      return 0;
+    }
+    // If the first two bit are 0 and 1, the 3rd byte is type field.
+    // Because there is no type value larger than 0x20, we can assume that the
+    // request is HTTP/0.9 if the value is larger than 0x20.
+    if (0x40 <= start[0] && start[0] < 0x80 && start[2] > 0x20) {
+      this->_legacy_request = true;
+    }
+    this->_protocol_detected = true;
+  }
+
+  if (this->_legacy_request) {
+    uint64_t nread    = 0;
+    MIOBuffer *writer = this->_read_vio.get_writer();
+
+    // Nuke this branch when we drop 0.9 support
+    if (!this->_client_req_header_complete) {
+      uint8_t buf[4096];
+      int len = this->_stream_io->peek(buf, 4096);
+      // Check client request is complete or not
+      if (len < 2 || buf[len - 1] != '\n') {
+        return 0;
+      }
+      this->_stream_io->consume(len);
+      nread += len;
+      this->_client_req_header_complete = true;
+
+      // Check "CRLF" or "LF"
+      int n = 2;
+      if (buf[len - 2] != '\r') {
+        n = 1;
+      }
+
+      writer->write(buf, len - n);
+      // FIXME: Get hostname from SNI?
+      const char version[] = " HTTP/1.1\r\nHost: localhost\r\n\r\n";
+      writer->write(version, sizeof(version));
+    } else {
+      uint8_t buf[4096];
+      int len;
+      while ((len = this->_stream_io->read(buf, 4096)) > 0) {
+        nread += len;
+        writer->write(buf, len);
+      }
+    }
+
+    return nread;
+    // End of code for HTTP/0.9
+  } else {
+    // Ignore malformed data
+    uint8_t buf[4096];
+    int len;
+    uint64_t nread = 0;
+
+    while ((len = this->_stream_io->read(buf, 4096)) > 0) {
+      nread += len;
+    }
+
+    return nread;
+  }
 }
 
-bool
-Http3ClientTransaction::is_response_body_sent() const
+// FIXME: already defined somewhere?
+static constexpr char http_1_1_version[] = "HTTP/1.1";
+
+// Convert HTTP/1.1 to HTTP/0.9
+int64_t
+Http09ClientTransaction::_process_write_vio()
 {
-  return this->_data_framer->is_done();
+  if (this->_write_vio.cont == nullptr || this->_write_vio.op == VIO::NONE) {
+    return 0;
+  }
+
+  if (this->_thread != this_ethread()) {
+    SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
+    if (this->_cross_thread_event == nullptr) {
+      // Send to the right thread
+      this->_cross_thread_event = this->_thread->schedule_imm(this, 
VC_EVENT_WRITE_READY, nullptr);
+    }
+    return 0;
+  }
+
+  SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread());
+
+  IOBufferReader *reader = this->_write_vio.get_reader();
+
+  if (this->_legacy_request) {
+    // This branch is for HTTP/0.9
+    int64_t http_1_1_version_len = sizeof(http_1_1_version) - 1;
+
+    if (reader->is_read_avail_more_than(http_1_1_version_len) &&
+        memcmp(reader->start(), http_1_1_version, http_1_1_version_len) == 0) {
+      // Skip HTTP/1.1 response headers
+      IOBufferBlock *headers = reader->get_current_block();
+      int64_t headers_size   = headers->read_avail();
+      reader->consume(headers_size);
+      this->_write_vio.ndone += headers_size;
+    }
+
+    // Write HTTP/1.1 response body
+    int64_t bytes_avail   = reader->read_avail();
+    int64_t total_written = 0;
+
+    while (total_written < bytes_avail) {
+      int64_t data_len      = reader->block_read_avail();
+      int64_t bytes_written = this->_stream_io->write(reader, data_len);
+      if (bytes_written <= 0) {
+        break;
+      }
+
+      reader->consume(bytes_written);
+      this->_write_vio.ndone += bytes_written;
+      total_written += bytes_written;
+    }
+
+    // NOTE: When Chunked Transfer Coding is supported, check ChunkedState of 
ChunkedHandler
+    // is CHUNK_READ_DONE and set FIN flag
+    if (this->_write_vio.ntodo() == 0) {
+      // The size of respons to client
+      this->_stream_io->write_done();
+    }
+
+    return total_written;
+  } else {
+    // nothing to do
+    return 0;
+  }
 }
diff --git a/proxy/http3/Http3ClientTransaction.h 
b/proxy/http3/Http3ClientTransaction.h
index 32297d5..9d415ad 100644
--- a/proxy/http3/Http3ClientTransaction.h
+++ b/proxy/http3/Http3ClientTransaction.h
@@ -29,17 +29,19 @@
 #include "Http3FrameCollector.h"
 
 class QUICStreamIO;
+class HQClientSession;
+class Http09ClientSession;
 class Http3ClientSession;
 class Http3HeaderFramer;
 class Http3DataFramer;
 
-class Http3ClientTransaction : public ProxyClientTransaction
+class HQClientTransaction : public ProxyClientTransaction
 {
 public:
   using super = ProxyClientTransaction;
 
-  Http3ClientTransaction(Http3ClientSession *session, QUICStreamIO *stream_io);
-  ~Http3ClientTransaction();
+  HQClientTransaction(HQClientSession *session, QUICStreamIO *stream_io);
+  ~HQClientTransaction();
 
   // Implement ProxyClienTransaction interface
   void set_active_timeout(ink_hrtime timeout_in) override;
@@ -54,30 +56,23 @@ public:
   void decrement_client_transactions_stat() override;
 
   // VConnection interface
-  VIO *do_io_read(Continuation *c, int64_t nbytes = INT64_MAX, MIOBuffer *buf 
= 0) override;
-  VIO *do_io_write(Continuation *c = nullptr, int64_t nbytes = INT64_MAX, 
IOBufferReader *buf = 0, bool owner = false) override;
-  void do_io_close(int lerrno = -1) override;
-  void do_io_shutdown(ShutdownHowTo_t) override;
-  void reenable(VIO *) override;
-
-  void set_read_vio_nbytes(int64_t nbytes);
-  void set_write_vio_nbytes(int64_t nbytes);
-
-  // Http3ClientTransaction specific methods
-  int state_stream_open(int, void *);
-  int state_stream_closed(int event, void *data);
-  bool is_response_header_sent() const;
-  bool is_response_body_sent() const;
-
-private:
+  virtual VIO *do_io_read(Continuation *c, int64_t nbytes = INT64_MAX, 
MIOBuffer *buf = 0) override;
+  virtual VIO *do_io_write(Continuation *c = nullptr, int64_t nbytes = 
INT64_MAX, IOBufferReader *buf = 0,
+                           bool owner = false) override;
+  virtual void do_io_close(int lerrno = -1) override;
+  virtual void do_io_shutdown(ShutdownHowTo_t) override;
+  virtual void reenable(VIO *) override;
+
+  // HQClientTransaction
+  virtual int state_stream_open(int, void *)             = 0;
+  virtual int state_stream_closed(int event, void *data) = 0;
+
+protected:
+  virtual int64_t _process_read_vio()  = 0;
+  virtual int64_t _process_write_vio() = 0;
   Event *_send_tracked_event(Event *, int, VIO *);
   void _signal_read_event();
   void _signal_write_event();
-  int64_t _process_read_vio();
-  int64_t _process_write_vio();
-
-  ParseResult _convert_header_from_3_to_1_1(HTTPHdr *hdr);
-  int _on_qpack_decode_complete();
 
   EThread *_thread           = nullptr;
   Event *_cross_thread_event = nullptr;
@@ -91,16 +86,61 @@ private:
   Event *_write_event = nullptr;
 
   HTTPHdr _request_header;
+};
+
+class Http3ClientTransaction : public HQClientTransaction
+{
+public:
+  using super = HQClientTransaction;
 
-  // These are for Http3
+  Http3ClientTransaction(Http3ClientSession *session, QUICStreamIO *stream_io);
+  ~Http3ClientTransaction();
+
+  int state_stream_open(int event, void *data) override;
+  int state_stream_closed(int event, void *data) override;
+
+  void do_io_close(int lerrno = -1) override;
+
+  bool is_response_header_sent() const;
+  bool is_response_body_sent() const;
+
+private:
+  int64_t _process_read_vio() override;
+  int64_t _process_write_vio() override;
+
+  ParseResult _convert_header_from_3_to_1_1(HTTPHdr *hdr);
+  int _on_qpack_decode_complete();
+
+  // These are for HTTP/3
   Http3FrameDispatcher _frame_dispatcher;
   Http3FrameCollector _frame_collector;
   Http3FrameGenerator *_header_framer = nullptr;
   Http3FrameGenerator *_data_framer   = nullptr;
   Http3FrameHandler *_header_handler  = nullptr;
   Http3FrameHandler *_data_handler    = nullptr;
+};
+
+/**
+   Only for interop. Will be removed.
+ */
+class Http09ClientTransaction : public HQClientTransaction
+{
+public:
+  using super = HQClientTransaction;
+
+  Http09ClientTransaction(Http09ClientSession *session, QUICStreamIO 
*stream_io);
+  ~Http09ClientTransaction();
+
+  int state_stream_open(int event, void *data) override;
+  int state_stream_closed(int event, void *data) override;
+
+  void do_io_close(int lerrno = -1) override;
+
+private:
+  int64_t _process_read_vio() override;
+  int64_t _process_write_vio() override;
 
-  // These are for 0.9 support
+  // These are for HTTP/0.9
   bool _protocol_detected          = false;
   bool _legacy_request             = false;
   bool _client_req_header_complete = false;

Reply via email to