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 2018/03/20 06:09:32 UTC

[trafficserver] branch quic-latest updated: Add HQ frame support

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

maskit 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 4c6a3e8  Add HQ frame support
4c6a3e8 is described below

commit 4c6a3e851bbaf11c29660730fab0e3c051dd9db3
Author: Masakazu Kitajo <ma...@apache.org>
AuthorDate: Mon Mar 19 16:56:27 2018 +0900

    Add HQ frame support
---
 configure.ac                       |   1 +
 iocore/net/quic/QUICApplication.cc |   6 +
 iocore/net/quic/QUICApplication.h  |   1 +
 iocore/net/quic/QUICIntUtil.cc     |   3 +
 proxy/hq/HQClientTransaction.cc    | 167 ++++++++++++++------
 proxy/hq/HQClientTransaction.h     |  18 +++
 proxy/hq/HQDataFramer.cc           |  55 +++++++
 proxy/hq/HQDataFramer.h            |  44 ++++++
 proxy/hq/HQDebugNames.h            |  29 ++++
 proxy/hq/HQFrame.cc                | 313 +++++++++++++++++++++++++++++++++++++
 proxy/hq/HQFrame.h                 | 190 ++++++++++++++++++++++
 proxy/hq/HQFrameCollector.cc       |  62 ++++++++
 proxy/hq/HQFrameCollector.h        |  40 +++++
 proxy/hq/HQFrameDispatcher.cc      |  73 +++++++++
 proxy/hq/HQFrameDispatcher.h       |  40 +++++
 proxy/hq/HQFrameGenerator.h        |  32 ++++
 proxy/hq/HQFrameHandler.h          |  36 +++++
 proxy/hq/HQHeaderFramer.cc         |  98 ++++++++++++
 proxy/hq/HQHeaderFramer.h          |  54 +++++++
 proxy/hq/HQHeaderVIOAdaptor.cc     |  50 ++++++
 proxy/hq/HQHeaderVIOAdaptor.h      |  41 +++++
 proxy/hq/HQStreamDataVIOAdaptor.cc |  49 ++++++
 proxy/hq/HQStreamDataVIOAdaptor.h  |  41 +++++
 proxy/hq/HQTypes.h                 |  95 +++++++++++
 proxy/hq/Makefile.am               |   9 ++
 25 files changed, 1497 insertions(+), 50 deletions(-)

diff --git a/configure.ac b/configure.ac
index fddeed0..6d448dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2087,6 +2087,7 @@ AC_CONFIG_FILES([
   proxy/http/remap/Makefile
   proxy/http2/Makefile
   proxy/hq/Makefile
+  proxy/hq/test/Makefile
   proxy/logging/Makefile
   proxy/shared/Makefile
   rc/Makefile
diff --git a/iocore/net/quic/QUICApplication.cc b/iocore/net/quic/QUICApplication.cc
index e2d73bf..c15dd93 100644
--- a/iocore/net/quic/QUICApplication.cc
+++ b/iocore/net/quic/QUICApplication.cc
@@ -70,6 +70,12 @@ QUICStreamIO::write(const uint8_t *buf, int64_t len)
 }
 
 int64_t
+QUICStreamIO::write_avail()
+{
+  return this->_write_buffer->write_avail();
+}
+
+int64_t
 QUICStreamIO::write(IOBufferReader *r, int64_t alen, int64_t offset)
 {
   SCOPED_MUTEX_LOCK(lock, this->_write_vio->mutex, this_ethread());
diff --git a/iocore/net/quic/QUICApplication.h b/iocore/net/quic/QUICApplication.h
index cbffb95..d1f94de 100644
--- a/iocore/net/quic/QUICApplication.h
+++ b/iocore/net/quic/QUICApplication.h
@@ -43,6 +43,7 @@ public:
   int64_t read_avail();
   bool is_read_avail_more_than(int64_t size);
   int64_t read(uint8_t *buf, int64_t len);
+  int64_t write_avail();
   int64_t write(const uint8_t *buf, int64_t len);
   int64_t write(IOBufferReader *r, int64_t len = INT64_MAX, int64_t offset = 0);
   void set_write_vio_nbytes(int64_t);
diff --git a/iocore/net/quic/QUICIntUtil.cc b/iocore/net/quic/QUICIntUtil.cc
index d9e745d..683c449 100644
--- a/iocore/net/quic/QUICIntUtil.cc
+++ b/iocore/net/quic/QUICIntUtil.cc
@@ -84,6 +84,9 @@ QUICVariableInt::encode(uint8_t *dst, size_t dst_len, size_t &len, uint64_t src)
 int
 QUICVariableInt::decode(uint64_t &dst, size_t &len, const uint8_t *src, size_t src_len)
 {
+  if (src_len < 1) {
+    return -1;
+  }
   len = 1 << (src[0] >> 6);
   if (src_len < len) {
     return 1;
diff --git a/proxy/hq/HQClientTransaction.cc b/proxy/hq/HQClientTransaction.cc
index 779210d..c60cef6 100644
--- a/proxy/hq/HQClientTransaction.cc
+++ b/proxy/hq/HQClientTransaction.cc
@@ -26,6 +26,10 @@
 #include "QUICDebugNames.h"
 
 #include "HQClientSession.h"
+#include "HQStreamDataVIOAdaptor.h"
+#include "HQHeaderVIOAdaptor.h"
+#include "HQHeaderFramer.h"
+#include "HQDataFramer.h"
 #include "HttpSM.h"
 
 #define HQTransDebug(fmt, ...)                                                                                                \
@@ -45,16 +49,34 @@
 // }
 
 HQClientTransaction::HQClientTransaction(HQClientSession *session, QUICStreamIO *stream_io) : super(), _stream_io(stream_io)
-
 {
   this->mutex = new_ProxyMutex();
   this->set_parent(session);
   this->sm_reader = this->_read_vio_buf.alloc_reader();
   static_cast<HQClientSession *>(this->parent)->add_transaction(this);
 
+  this->_header_framer = new HQHeaderFramer(this, &this->_write_vio);
+  this->_data_framer   = new HQDataFramer(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 HQHeaderVIOAdaptor(&this->_read_vio);
+  this->_data_handler   = new HQStreamDataVIOAdaptor(&this->_read_vio);
+  this->_frame_dispatcher.add_handler(this->_header_handler);
+  this->_frame_dispatcher.add_handler(this->_data_handler);
+
   SET_HANDLER(&HQClientTransaction::state_stream_open);
 }
 
+HQClientTransaction::~HQClientTransaction()
+{
+  delete this->_header_framer;
+  delete this->_data_framer;
+  delete this->_header_handler;
+  delete this->_data_handler;
+}
+
 void
 HQClientTransaction::set_active_timeout(ink_hrtime timeout_in)
 {
@@ -356,33 +378,59 @@ HQClientTransaction::_process_read_vio()
 
   IOBufferReader *client_vio_reader = this->_stream_io->get_read_buffer_reader();
   int64_t bytes_avail               = client_vio_reader->read_avail();
-  MIOBuffer *writer                 = this->_read_vio.get_writer();
 
-  if (!this->_client_req_header_complete) {
-    int n = 2;
-    // Check client request is complete or not
-    if (bytes_avail < 2 || client_vio_reader->start()[bytes_avail - 1] != '\n') {
+  // Nuke this block when we drop 0.9 support
+  if (!this->_protocol_detected) {
+    if (bytes_avail < 3) {
       return 0;
     }
-    this->_client_req_header_complete = true;
-
-    // Check "CRLF" or "LF"
-    if (client_vio_reader->start()[bytes_avail - 2] != '\r') {
-      n = 1;
+    // 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.
+    const uint8_t *start = reinterpret_cast<uint8_t *>(client_vio_reader->start());
+    if (0x40 <= *start && *start < 0x80 && *(start + 2) > 0x20) {
+      this->_legacy_request = true;
     }
+    this->_protocol_detected = true;
+  }
 
-    writer->write(client_vio_reader, bytes_avail - n);
-    client_vio_reader->consume(bytes_avail);
+  if (this->_legacy_request) {
+    MIOBuffer *writer = this->_read_vio.get_writer();
+
+    // Nuke this branch when we drop 0.9 support
+    if (!this->_client_req_header_complete) {
+      int n = 2;
+      // Check client request is complete or not
+      if (bytes_avail < 2 || client_vio_reader->start()[bytes_avail - 1] != '\n') {
+        return 0;
+      }
+      this->_client_req_header_complete = true;
+
+      // Check "CRLF" or "LF"
+      if (client_vio_reader->start()[bytes_avail - 2] != '\r') {
+        n = 1;
+      }
+
+      writer->write(client_vio_reader, bytes_avail - n);
+      client_vio_reader->consume(bytes_avail);
+
+      // FIXME: Get hostname from SNI?
+      const char version[] = " HTTP/1.1\r\nHost: localhost\r\n\r\n";
+      writer->write(version, sizeof(version));
+    } else {
+      writer->write(client_vio_reader, bytes_avail);
+      client_vio_reader->consume(bytes_avail);
+    }
 
-    // FIXME: Get hostname from SNI?
-    const char version[] = " HTTP/1.1\r\nHost: localhost\r\n\r\n";
-    writer->write(version, sizeof(version));
+    return bytes_avail;
+    // End of code for HTTP/0.9
   } else {
-    writer->write(client_vio_reader, bytes_avail);
-    client_vio_reader->consume(bytes_avail);
+    // This branch is for HQ
+    uint16_t nread = 0;
+    this->_frame_dispatcher.on_read_ready(reinterpret_cast<uint8_t *>(client_vio_reader->start()), bytes_avail, nread);
+    client_vio_reader->consume(nread);
+    return nread;
   }
-
-  return bytes_avail;
 }
 
 // FIXME: already defined somewhere?
@@ -400,45 +448,52 @@ HQClientTransaction::_process_write_vio()
 
   IOBufferReader *reader = this->_write_vio.get_reader();
 
-  int64_t http_1_1_version_len = sizeof(http_1_1_version) - 1;
+  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;
+    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;
 
-    // The size of respons to client
-    this->_stream_io->set_write_vio_nbytes(this->_write_vio.nbytes - headers_size);
-  }
+      // The size of respons to client
+      this->_stream_io->set_write_vio_nbytes(this->_write_vio.nbytes - headers_size);
+    }
+
+    // Write HTTP/1.1 response body
+    int64_t bytes_avail   = reader->read_avail();
+    int64_t total_written = 0;
 
-  // Write HTTP/1.1 response body
-  int64_t bytes_avail   = reader->read_avail();
-  int64_t total_written = 0;
+    HQTransDebug("%" PRId64, bytes_avail);
 
-  HQTransDebug("%" PRId64, bytes_avail);
+    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;
+      }
 
-  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;
     }
 
-    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) {
+      this->_stream_io->shutdown();
+    }
 
-  // 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) {
-    this->_stream_io->shutdown();
+    return total_written;
+  } else {
+    size_t nwritten = 0;
+    this->_frame_collector.on_write_ready(this->_stream_io, nwritten);
+    return nwritten;
   }
-
-  return total_written;
 }
 
 void
@@ -453,3 +508,15 @@ HQClientTransaction::get_transaction_id() const
 {
   return this->_stream_io->get_transaction_id();
 }
+
+bool
+HQClientTransaction::is_response_header_sent() const
+{
+  return this->_header_framer->is_done();
+}
+
+bool
+HQClientTransaction::is_response_body_sent() const
+{
+  return this->_data_framer->is_done();
+}
diff --git a/proxy/hq/HQClientTransaction.h b/proxy/hq/HQClientTransaction.h
index 7a10e9d..155cbc5 100644
--- a/proxy/hq/HQClientTransaction.h
+++ b/proxy/hq/HQClientTransaction.h
@@ -25,9 +25,13 @@
 
 #include "I_VConnection.h"
 #include "ProxyClientTransaction.h"
+#include "HQFrameDispatcher.h"
+#include "HQFrameCollector.h"
 
 class QUICStreamIO;
 class HQClientSession;
+class HQHeaderFramer;
+class HQDataFramer;
 
 class HQClientTransaction : public ProxyClientTransaction
 {
@@ -35,6 +39,7 @@ public:
   using super = ProxyClientTransaction;
 
   HQClientTransaction(HQClientSession *session, QUICStreamIO *stream_io);
+  ~HQClientTransaction();
 
   // Implement ProxyClienTransaction interface
   void set_active_timeout(ink_hrtime timeout_in) override;
@@ -59,6 +64,8 @@ public:
   // HQClientTransaction 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:
   Event *_send_tracked_event(Event *, int, VIO *);
@@ -75,5 +82,16 @@ private:
   Event *_read_event  = nullptr;
   Event *_write_event = nullptr;
 
+  // These are for HQ
+  HQFrameDispatcher _frame_dispatcher;
+  HQFrameCollector _frame_collector;
+  HQFrameGenerator *_header_framer = nullptr;
+  HQFrameGenerator *_data_framer   = nullptr;
+  HQFrameHandler *_header_handler  = nullptr;
+  HQFrameHandler *_data_handler    = nullptr;
+
+  // These are for 0.9 support
+  bool _protocol_detected          = false;
+  bool _legacy_request             = false;
   bool _client_req_header_complete = false;
 };
diff --git a/proxy/hq/HQDataFramer.cc b/proxy/hq/HQDataFramer.cc
new file mode 100644
index 0000000..09f55ae
--- /dev/null
+++ b/proxy/hq/HQDataFramer.cc
@@ -0,0 +1,55 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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 "HQFrame.h"
+#include "HQDataFramer.h"
+#include "HQClientTransaction.h"
+
+HQDataFramer::HQDataFramer(HQClientTransaction *transaction, VIO *source) : _transaction(transaction), _source_vio(source)
+{
+}
+
+HQFrameUPtr
+HQDataFramer::generate_frame(uint16_t max_size)
+{
+  if (!this->_transaction->is_response_header_sent()) {
+    return HQFrameFactory::create_null_frame();
+  }
+
+  HQFrameUPtr frame      = HQFrameFactory::create_null_frame();
+  IOBufferReader *reader = this->_source_vio->get_reader();
+  size_t len             = std::min(reader->read_avail(), static_cast<int64_t>(max_size));
+  if (len) {
+    frame = HQFrameFactory::create_data_frame(reinterpret_cast<uint8_t *>(reader->start()), len);
+    reader->consume(len);
+    this->_source_vio->ndone += len;
+  }
+
+  return frame;
+}
+
+bool
+HQDataFramer::is_done() const
+{
+  return this->_source_vio->ntodo() == 0;
+}
diff --git a/proxy/hq/HQDataFramer.h b/proxy/hq/HQDataFramer.h
new file mode 100644
index 0000000..af6fe8d
--- /dev/null
+++ b/proxy/hq/HQDataFramer.h
@@ -0,0 +1,44 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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.
+ */
+
+#pragma once
+
+#include "HQFrameGenerator.h"
+#include "HQFrame.h"
+
+class HQClientTransaction;
+class VIO;
+
+class HQDataFramer : public HQFrameGenerator
+{
+public:
+  HQDataFramer(HQClientTransaction *transaction, VIO *source);
+
+  // HQFrameGenerator
+  HQFrameUPtr generate_frame(uint16_t max_size) override;
+  bool is_done() const override;
+
+private:
+  HQClientTransaction *_transaction = nullptr;
+  VIO *_source_vio                  = nullptr;
+};
diff --git a/proxy/hq/HQDebugNames.h b/proxy/hq/HQDebugNames.h
new file mode 100644
index 0000000..f1aa81d
--- /dev/null
+++ b/proxy/hq/HQDebugNames.h
@@ -0,0 +1,29 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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.
+ */
+
+#pragma once
+
+class HQDebugNames
+{
+  static const char *frame_type(HQFrameType type);
+};
diff --git a/proxy/hq/HQFrame.cc b/proxy/hq/HQFrame.cc
new file mode 100644
index 0000000..e92a7c1
--- /dev/null
+++ b/proxy/hq/HQFrame.cc
@@ -0,0 +1,313 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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 "ts/Diags.h"
+#include "quic/QUICIntUtil.h"
+#include "HQFrame.h"
+
+ClassAllocator<HQFrame> hqFrameAllocator("hqFrameAllocator");
+ClassAllocator<HQDataFrame> hqDataFrameAllocator("hqDataFrameAllocator");
+ClassAllocator<HQHeadersFrame> hqHeadersFrameAllocator("hqHeadersFrameAllocator");
+
+//
+// Static functions
+//
+
+int
+HQFrame::length(const uint8_t *buf, size_t buf_len, uint64_t &length)
+{
+  size_t length_field_length = 0;
+  return QUICVariableInt::decode(length, length_field_length, buf, buf_len);
+}
+
+HQFrameType
+HQFrame::type(const uint8_t *buf, size_t buf_len)
+{
+  uint64_t length            = 0;
+  size_t length_field_length = 0;
+  int ret                    = QUICVariableInt::decode(length, length_field_length, buf, buf_len);
+  ink_assert(ret != 1);
+  if (buf[length_field_length] <= static_cast<uint8_t>(HQFrameType::X_MAX_DEFINED)) {
+    return static_cast<HQFrameType>(buf[length_field_length]);
+  } else {
+    return HQFrameType::UNKNOWN;
+  }
+}
+
+//
+// Generic Frame
+//
+
+HQFrame::HQFrame(const uint8_t *buf, size_t buf_len)
+{
+  // Length
+  size_t length_field_length = 0;
+  int ret                    = QUICVariableInt::decode(this->_length, length_field_length, buf, buf_len);
+  ink_assert(ret != 1);
+
+  // Type
+  this->_type = HQFrameType(buf[length_field_length]);
+
+  // Flags
+  this->_flags = buf[length_field_length + 1];
+
+  // Payload offset
+  this->_payload_offset = length_field_length + 2;
+}
+
+HQFrame::HQFrame(HQFrameType type) : _type(type)
+{
+}
+
+uint64_t
+HQFrame::total_length() const
+{
+  return this->_payload_offset + this->length();
+}
+
+uint64_t
+HQFrame::length() const
+{
+  return this->_length;
+}
+
+HQFrameType
+HQFrame::type() const
+{
+  return this->_type;
+}
+
+uint8_t
+HQFrame::flags() const
+{
+  return this->_flags;
+}
+
+void
+HQFrame::store(uint8_t *buf, size_t *len) const
+{
+  // If you really need this, you should keep the data passed to its constructor
+  ink_assert(!"Not supported");
+}
+
+void
+HQFrame::reset(const uint8_t *buf, size_t len)
+{
+  this->~HQFrame();
+  new (this) HQFrame(buf, len);
+}
+
+//
+// UNKNOWN Frame
+//
+HQUnknownFrame::HQUnknownFrame(const uint8_t *buf, size_t buf_len) : HQFrame(buf, buf_len), _buf(buf), _buf_len(buf_len)
+{
+}
+
+void
+HQUnknownFrame::store(uint8_t *buf, size_t *len) const
+{
+  memcpy(buf, this->_buf, this->_buf_len);
+  *len = this->_buf_len;
+}
+
+//
+// DATA Frame
+//
+HQDataFrame::HQDataFrame(const uint8_t *buf, size_t buf_len) : HQFrame(buf, buf_len)
+{
+  this->_payload     = buf + this->_payload_offset;
+  this->_payload_len = buf_len - this->_payload_offset;
+}
+
+HQDataFrame::HQDataFrame(ats_unique_buf payload, size_t payload_len)
+  : HQFrame(HQFrameType::DATA), _payload_uptr(std::move(payload)), _payload_len(payload_len)
+{
+  this->_length  = this->_payload_len;
+  this->_payload = this->_payload_uptr.get();
+}
+
+void
+HQDataFrame::store(uint8_t *buf, size_t *len) const
+{
+  size_t written = 0;
+  QUICVariableInt::encode(buf, UINT64_MAX, written, this->_length);
+  buf[written++] = static_cast<uint8_t>(this->_type);
+  buf[written++] = this->_flags;
+  memcpy(buf + written, this->_payload, this->_payload_len);
+  written += this->_payload_len;
+  *len = written;
+}
+
+void
+HQDataFrame::reset(const uint8_t *buf, size_t len)
+{
+  this->~HQDataFrame();
+  new (this) HQDataFrame(buf, len);
+}
+
+const uint8_t *
+HQDataFrame::payload() const
+{
+  return this->_payload;
+}
+
+uint64_t
+HQDataFrame::payload_length() const
+{
+  return this->_payload_len;
+}
+
+//
+// HEADERS Frame
+//
+HQHeadersFrame::HQHeadersFrame(const uint8_t *buf, size_t buf_len) : HQFrame(buf, buf_len)
+{
+  this->_header_block     = buf + this->_payload_offset;
+  this->_header_block_len = buf_len - this->_payload_offset;
+}
+
+HQHeadersFrame::HQHeadersFrame(ats_unique_buf header_block, size_t header_block_len)
+  : HQFrame(HQFrameType::HEADERS), _header_block_uptr(std::move(header_block)), _header_block_len(header_block_len)
+{
+  this->_length       = header_block_len;
+  this->_header_block = this->_header_block_uptr.get();
+}
+
+void
+HQHeadersFrame::store(uint8_t *buf, size_t *len) const
+{
+  size_t written = 0;
+  QUICVariableInt::encode(buf, UINT64_MAX, written, this->_length);
+  buf[written++] = static_cast<uint8_t>(this->_type);
+  buf[written++] = this->_flags;
+  memcpy(buf + written, this->_header_block, this->_header_block_len);
+  written += this->_header_block_len;
+  *len = written;
+}
+
+void
+HQHeadersFrame::reset(const uint8_t *buf, size_t len)
+{
+  this->~HQHeadersFrame();
+  new (this) HQHeadersFrame(buf, len);
+}
+
+const uint8_t *
+HQHeadersFrame::header_block() const
+{
+  return this->_header_block;
+}
+
+uint64_t
+HQHeadersFrame::header_block_length() const
+{
+  return this->_header_block_len;
+}
+
+//
+// HQFrameFactory
+//
+HQFrameUPtr
+HQFrameFactory::create_null_frame()
+{
+  return {nullptr, &HQFrameDeleter::delete_null_frame};
+}
+
+HQFrameUPtr
+HQFrameFactory::create(const uint8_t *buf, size_t len)
+{
+  HQFrame *frame   = nullptr;
+  HQFrameType type = HQFrame::type(buf, len);
+
+  switch (type) {
+  case HQFrameType::HEADERS:
+    frame = hqHeadersFrameAllocator.alloc();
+    new (frame) HQHeadersFrame(buf, len);
+    return HQFrameUPtr(frame, &HQFrameDeleter::delete_headers_frame);
+  case HQFrameType::DATA:
+    frame = hqDataFrameAllocator.alloc();
+    new (frame) HQDataFrame(buf, len);
+    return HQFrameUPtr(frame, &HQFrameDeleter::delete_data_frame);
+  default:
+    // Unknown frame
+    Debug("hq_frame_factory", "Unknown frame type %hhx", type);
+    frame = hqFrameAllocator.alloc();
+    new (frame) HQFrame(buf, len);
+    return HQFrameUPtr(frame, &HQFrameDeleter::delete_frame);
+  }
+}
+
+std::shared_ptr<const HQFrame>
+HQFrameFactory::fast_create(const uint8_t *buf, size_t len)
+{
+  uint64_t frame_length = 0;
+  if (HQFrame::length(buf, len, frame_length) == -1 || frame_length > len) {
+    return nullptr;
+  }
+  HQFrameType type = HQFrame::type(buf, len);
+  if (type == HQFrameType::UNKNOWN) {
+    if (!this->_unknown_frame) {
+      this->_unknown_frame = HQFrameFactory::create(buf, len);
+    } else {
+      this->_unknown_frame->reset(buf, len);
+    }
+    return _unknown_frame;
+  }
+
+  std::shared_ptr<HQFrame> frame = this->_reusable_frames[static_cast<uint8_t>(type)];
+
+  if (frame == nullptr) {
+    frame = HQFrameFactory::create(buf, len);
+    if (frame != nullptr) {
+      this->_reusable_frames[static_cast<uint8_t>(type)] = frame;
+    }
+  } else {
+    frame->reset(buf, len);
+  }
+  fprintf(stderr, "%p\n", frame.get());
+
+  return frame;
+}
+
+HQHeadersFrameUPtr
+HQFrameFactory::create_headers_frame(const uint8_t *header_block, size_t header_block_len)
+{
+  ats_unique_buf buf = ats_unique_malloc(header_block_len);
+  memcpy(buf.get(), header_block, header_block_len);
+
+  HQHeadersFrame *frame = hqHeadersFrameAllocator.alloc();
+  new (frame) HQHeadersFrame(std::move(buf), header_block_len);
+  return HQHeadersFrameUPtr(frame, &HQFrameDeleter::delete_headers_frame);
+}
+
+HQDataFrameUPtr
+HQFrameFactory::create_data_frame(const uint8_t *payload, size_t payload_len)
+{
+  ats_unique_buf buf = ats_unique_malloc(payload_len);
+  memcpy(buf.get(), payload, payload_len);
+
+  HQDataFrame *frame = hqDataFrameAllocator.alloc();
+  new (frame) HQDataFrame(std::move(buf), payload_len);
+  return HQDataFrameUPtr(frame, &HQFrameDeleter::delete_data_frame);
+}
diff --git a/proxy/hq/HQFrame.h b/proxy/hq/HQFrame.h
new file mode 100644
index 0000000..d061e52
--- /dev/null
+++ b/proxy/hq/HQFrame.h
@@ -0,0 +1,190 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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.
+ */
+
+#pragma once
+
+#include "ts/Allocator.h"
+#include "ts/ink_memory.h"
+#include "ts/ink_assert.h"
+#include "HQTypes.h"
+
+class HQFrame
+{
+public:
+  HQFrame() {}
+  HQFrame(const uint8_t *buf, size_t len);
+  HQFrame(HQFrameType type);
+  virtual ~HQFrame() {}
+
+  uint64_t total_length() const;
+  uint64_t length() const;
+  HQFrameType type() const;
+  uint8_t flags() const;
+  virtual void store(uint8_t *buf, size_t *len) const;
+  virtual void reset(const uint8_t *buf, size_t len);
+  static int length(const uint8_t *buf, size_t buf_len, uint64_t &length);
+  static HQFrameType type(const uint8_t *buf, size_t buf_len);
+
+protected:
+  uint64_t _length       = 0;
+  HQFrameType _type      = HQFrameType::UNKNOWN;
+  uint8_t _flags         = 0;
+  size_t _payload_offset = 0;
+};
+
+class HQUnknownFrame : public HQFrame
+{
+public:
+  HQUnknownFrame() : HQFrame() {}
+  HQUnknownFrame(const uint8_t *buf, size_t len);
+
+  void store(uint8_t *buf, size_t *len) const override;
+
+protected:
+  const uint8_t *_buf = nullptr;
+  size_t _buf_len     = 0;
+};
+
+//
+// DATA Frame
+//
+
+class HQDataFrame : public HQFrame
+{
+public:
+  HQDataFrame() : HQFrame() {}
+  HQDataFrame(const uint8_t *buf, size_t len);
+  HQDataFrame(ats_unique_buf payload, size_t payload_len);
+
+  void store(uint8_t *buf, size_t *len) const override;
+  void reset(const uint8_t *buf, size_t len) override;
+
+  const uint8_t *payload() const;
+  uint64_t payload_length() const;
+
+private:
+  const uint8_t *_payload      = nullptr;
+  ats_unique_buf _payload_uptr = {nullptr, [](void *p) { ats_free(p); }};
+  size_t _payload_len          = 0;
+};
+
+//
+// HEADERS Frame
+//
+
+class HQHeadersFrame : public HQFrame
+{
+public:
+  HQHeadersFrame() : HQFrame() {}
+  HQHeadersFrame(const uint8_t *buf, size_t len);
+  HQHeadersFrame(ats_unique_buf header_block, size_t header_block_len);
+
+  void store(uint8_t *buf, size_t *len) const override;
+  void reset(const uint8_t *buf, size_t len) override;
+
+  const uint8_t *header_block() const;
+  uint64_t header_block_length() const;
+
+private:
+  const uint8_t *_header_block      = nullptr;
+  ats_unique_buf _header_block_uptr = {nullptr, [](void *p) { ats_free(p); }};
+  size_t _header_block_len          = 0;
+};
+
+using HQFrameDeleterFunc = void (*)(HQFrame *p);
+using HQFrameUPtr        = std::unique_ptr<HQFrame, HQFrameDeleterFunc>;
+using HQDataFrameUPtr    = std::unique_ptr<HQDataFrame, HQFrameDeleterFunc>;
+using HQHeadersFrameUPtr = std::unique_ptr<HQHeadersFrame, HQFrameDeleterFunc>;
+
+extern ClassAllocator<HQFrame> hqFrameAllocator;
+extern ClassAllocator<HQDataFrame> hqDataFrameAllocator;
+extern ClassAllocator<HQHeadersFrame> hqHeadersFrameAllocator;
+
+class HQFrameDeleter
+{
+public:
+  static void
+  delete_null_frame(HQFrame *frame)
+  {
+    ink_assert(frame == nullptr);
+  }
+
+  static void
+  delete_frame(HQFrame *frame)
+  {
+    frame->~HQFrame();
+    hqFrameAllocator.free(static_cast<HQFrame *>(frame));
+  }
+
+  static void
+  delete_data_frame(HQFrame *frame)
+  {
+    frame->~HQFrame();
+    hqDataFrameAllocator.free(static_cast<HQDataFrame *>(frame));
+  }
+
+  static void
+  delete_headers_frame(HQFrame *frame)
+  {
+    frame->~HQFrame();
+    hqHeadersFrameAllocator.free(static_cast<HQHeadersFrame *>(frame));
+  }
+};
+
+//
+// HQFrameFactory
+//
+class HQFrameFactory
+{
+public:
+  /*
+   * This is for an empty HQFrameUPtr.
+   * Empty frames are used for variable initialization and return value of frame creation failure
+   */
+  static HQFrameUPtr create_null_frame();
+
+  /*
+   * This is used for creating a HQFrame object based on received data.
+   */
+  static HQFrameUPtr create(const uint8_t *buf, size_t len);
+
+  /*
+   * This works almost the same as create() but it reuses created objects for performance.
+   * If you create a frame object which has the same frame type that you created before, the object will be reset by new data.
+   */
+  std::shared_ptr<const HQFrame> fast_create(const uint8_t *buf, size_t len);
+
+  /*
+   * Creates a HEADERS frame.
+   */
+  static HQHeadersFrameUPtr create_headers_frame(const uint8_t *header_block, size_t header_block_len);
+
+  /*
+   * Creates a DATA frame.
+   */
+  static HQDataFrameUPtr create_data_frame(const uint8_t *data, size_t data_len);
+
+private:
+  std::shared_ptr<HQFrame> _unknown_frame        = nullptr;
+  std::shared_ptr<HQFrame> _reusable_frames[256] = {nullptr};
+};
diff --git a/proxy/hq/HQFrameCollector.cc b/proxy/hq/HQFrameCollector.cc
new file mode 100644
index 0000000..f316524
--- /dev/null
+++ b/proxy/hq/HQFrameCollector.cc
@@ -0,0 +1,62 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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 "HQFrameCollector.h"
+
+HQErrorUPtr
+HQFrameCollector::on_write_ready(QUICStreamIO *stream_io, size_t &nwritten)
+{
+  bool all_done = true;
+  uint8_t tmp[32768];
+  nwritten = 0;
+
+  for (auto g : this->_generators) {
+    if (g->is_done()) {
+      continue;
+    }
+    size_t len        = 0;
+    HQFrameUPtr frame = g->generate_frame(sizeof(tmp) - nwritten);
+    if (frame) {
+      frame->store(tmp + nwritten, &len);
+      nwritten += len;
+    }
+    all_done &= g->is_done();
+  }
+
+  if (nwritten) {
+    int64_t len = stream_io->write(tmp, nwritten);
+    ink_assert(len > 0 && (uint64_t)len == nwritten);
+  }
+
+  if (all_done) {
+    stream_io->shutdown();
+  }
+
+  return HQErrorUPtr(new HQNoError());
+}
+
+void
+HQFrameCollector::add_generator(HQFrameGenerator *generator)
+{
+  this->_generators.push_back(generator);
+}
diff --git a/proxy/hq/HQFrameCollector.h b/proxy/hq/HQFrameCollector.h
new file mode 100644
index 0000000..8542a0a
--- /dev/null
+++ b/proxy/hq/HQFrameCollector.h
@@ -0,0 +1,40 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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.
+ */
+
+#pragma once
+
+#include "QUICApplication.h"
+#include "HQFrame.h"
+#include "HQFrameGenerator.h"
+#include <vector>
+
+class HQFrameCollector
+{
+public:
+  HQErrorUPtr on_write_ready(QUICStreamIO *stream_io, size_t &nread);
+
+  void add_generator(HQFrameGenerator *generator);
+
+private:
+  std::vector<HQFrameGenerator *> _generators;
+};
diff --git a/proxy/hq/HQFrameDispatcher.cc b/proxy/hq/HQFrameDispatcher.cc
new file mode 100644
index 0000000..e8ffc9e
--- /dev/null
+++ b/proxy/hq/HQFrameDispatcher.cc
@@ -0,0 +1,73 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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 "HQFrameDispatcher.h"
+#include "HQDebugNames.h"
+#include "ts/Diags.h"
+
+static constexpr char tag[] = "hq_frame";
+
+//
+// Frame Dispatcher
+//
+
+void
+HQFrameDispatcher::add_handler(HQFrameHandler *handler)
+{
+  for (HQFrameType t : handler->interests()) {
+    this->_handlers[static_cast<uint8_t>(t)].push_back(handler);
+  }
+}
+
+HQErrorUPtr
+HQFrameDispatcher::on_read_ready(const uint8_t *src, uint16_t read_avail, uint16_t &nread)
+{
+  std::shared_ptr<const HQFrame> frame(nullptr);
+  const uint8_t *cursor = src;
+  HQErrorUPtr error     = HQErrorUPtr(new HQNoError());
+  uint64_t frame_length = 0;
+
+  while (HQFrame::length(cursor, read_avail, frame_length) != -1 && read_avail >= frame_length) {
+    frame = this->_frame_factory.fast_create(cursor, read_avail);
+    if (frame == nullptr) {
+      Debug(tag, "Failed to create a frame");
+      // error = HQErrorUPtr(new HQStreamError());
+      break;
+    }
+    cursor += frame->total_length();
+    read_avail -= frame->total_length();
+
+    HQFrameType type                       = frame->type();
+    std::vector<HQFrameHandler *> handlers = this->_handlers[static_cast<uint8_t>(type)];
+    for (auto h : handlers) {
+      error = h->handle_frame(frame);
+      if (error->cls != HQErrorClass::NONE) {
+        return error;
+      }
+    }
+  }
+
+  nread = cursor - src;
+
+  return error;
+}
diff --git a/proxy/hq/HQFrameDispatcher.h b/proxy/hq/HQFrameDispatcher.h
new file mode 100644
index 0000000..6fa893d
--- /dev/null
+++ b/proxy/hq/HQFrameDispatcher.h
@@ -0,0 +1,40 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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.
+ */
+
+#pragma once
+
+#include "HQFrame.h"
+#include "HQFrameHandler.h"
+#include <vector>
+
+class HQFrameDispatcher
+{
+public:
+  HQErrorUPtr on_read_ready(const uint8_t *source, uint16_t read_avail, uint16_t &nread);
+
+  void add_handler(HQFrameHandler *handler);
+
+private:
+  HQFrameFactory _frame_factory;
+  std::vector<HQFrameHandler *> _handlers[256];
+};
diff --git a/proxy/hq/HQFrameGenerator.h b/proxy/hq/HQFrameGenerator.h
new file mode 100644
index 0000000..680a9ed
--- /dev/null
+++ b/proxy/hq/HQFrameGenerator.h
@@ -0,0 +1,32 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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.
+ */
+
+#pragma once
+
+class HQFrameGenerator
+{
+public:
+  virtual ~HQFrameGenerator(){};
+  virtual HQFrameUPtr generate_frame(uint16_t max_size) = 0;
+  virtual bool is_done() const                          = 0;
+};
diff --git a/proxy/hq/HQFrameHandler.h b/proxy/hq/HQFrameHandler.h
new file mode 100644
index 0000000..486c3de
--- /dev/null
+++ b/proxy/hq/HQFrameHandler.h
@@ -0,0 +1,36 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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.
+ */
+
+#pragma once
+
+#include <vector>
+#include <HQTypes.h>
+#include <HQFrame.h>
+
+class HQFrameHandler
+{
+public:
+  virtual ~HQFrameHandler(){};
+  virtual std::vector<HQFrameType> interests()                           = 0;
+  virtual HQErrorUPtr handle_frame(std::shared_ptr<const HQFrame> frame) = 0;
+};
diff --git a/proxy/hq/HQHeaderFramer.cc b/proxy/hq/HQHeaderFramer.cc
new file mode 100644
index 0000000..f8bf717
--- /dev/null
+++ b/proxy/hq/HQHeaderFramer.cc
@@ -0,0 +1,98 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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 "HQFrame.h"
+#include "HQHeaderFramer.h"
+#include "HQClientTransaction.h"
+#include "HTTP.h"
+#include "I_VIO.h"
+
+HQHeaderFramer::HQHeaderFramer(HQClientTransaction *transaction, VIO *source) : _transaction(transaction), _source_vio(source)
+{
+  http_parser_init(&this->_http_parser);
+}
+
+HQFrameUPtr
+HQHeaderFramer::generate_frame(uint16_t max_size)
+{
+  ink_assert(!this->_transaction->is_response_header_sent());
+
+  if (!this->_header_block) {
+    // this->_header_block will be filled if it is ready
+    this->_generate_header_block();
+  }
+
+  if (this->_header_block) {
+    // Create frames on demand base on max_size since we don't know how much we can write now
+    const uint8_t *start = this->_header_block + this->_header_block_wrote;
+    size_t len           = std::min(this->_header_block_len - this->_header_block_wrote, static_cast<size_t>(max_size));
+    HQFrameUPtr frame    = HQFrameFactory::create_headers_frame(start, len);
+    this->_header_block_wrote += len;
+    if (this->_header_block_len == this->_header_block_wrote) {
+      this->_sent_all_data = true;
+    }
+    return frame;
+  } else {
+    return HQFrameFactory::create_null_frame();
+  }
+}
+
+bool
+HQHeaderFramer::is_done() const
+{
+  return this->_sent_all_data;
+}
+
+void
+HQHeaderFramer::_generate_header_block()
+{
+  // Prase response header and generate header block
+  int bytes_used = 0;
+  // TODO Use HTTP_TYPE_REQUEST if this is for requests
+  this->_header.create(HTTP_TYPE_RESPONSE);
+  int parse_result = this->_header.parse_resp(&this->_http_parser, this->_source_vio->get_reader(), &bytes_used, false);
+  this->_source_vio->ndone += this->_header.length_get();
+
+  switch (parse_result) {
+  case PARSE_RESULT_DONE:
+    this->_compress_header();
+    break;
+  case PARSE_RESULT_CONT:
+    break;
+  default:
+    break;
+  }
+}
+
+void
+HQHeaderFramer::_compress_header()
+{
+  // TODO Compress the header data
+  // Just copy the header data for now.
+  int written         = 0;
+  int tmp             = 0;
+  int len             = this->_header.length_get();
+  this->_header_block = static_cast<uint8_t *>(ats_malloc(len));
+  this->_header.print(reinterpret_cast<char *>(this->_header_block), len, &written, &tmp);
+  this->_header_block_len = written;
+}
diff --git a/proxy/hq/HQHeaderFramer.h b/proxy/hq/HQHeaderFramer.h
new file mode 100644
index 0000000..b719efa
--- /dev/null
+++ b/proxy/hq/HQHeaderFramer.h
@@ -0,0 +1,54 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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.
+ */
+
+#pragma once
+
+#include "HQFrameGenerator.h"
+#include "HQFrame.h"
+#include "hdrs/HTTP.h"
+
+class HQClientTransaction;
+class VIO;
+
+class HQHeaderFramer : public HQFrameGenerator
+{
+public:
+  HQHeaderFramer(HQClientTransaction *transaction, VIO *source);
+
+  // HQFrameGenerator
+  HQFrameUPtr generate_frame(uint16_t max_size) override;
+  bool is_done() const override;
+
+private:
+  HQClientTransaction *_transaction = nullptr;
+  VIO *_source_vio                  = nullptr;
+  HTTPParser _http_parser;
+  HTTPHdr _header;
+  uint8_t *_header_block     = nullptr;
+  size_t _header_block_len   = 0;
+  size_t _header_block_wrote = 0;
+  bool _sent_all_data        = false;
+
+  void _generate_header_block();
+  void _compress_header();
+};
diff --git a/proxy/hq/HQHeaderVIOAdaptor.cc b/proxy/hq/HQHeaderVIOAdaptor.cc
new file mode 100644
index 0000000..366e865
--- /dev/null
+++ b/proxy/hq/HQHeaderVIOAdaptor.cc
@@ -0,0 +1,50 @@
+/** @file
+
+  A brief file description
+
+  @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 "HQHeaderVIOAdaptor.h"
+#include "I_VIO.h"
+
+HQHeaderVIOAdaptor::HQHeaderVIOAdaptor(VIO *sink) : _sink_vio(sink)
+{
+}
+
+std::vector<HQFrameType>
+HQHeaderVIOAdaptor::interests()
+{
+  return {HQFrameType::HEADERS};
+}
+
+HQErrorUPtr
+HQHeaderVIOAdaptor::handle_frame(std::shared_ptr<const HQFrame> frame)
+{
+  ink_assert(frame->type() == HQFrameType::HEADERS);
+  const HQHeadersFrame *hframe = dynamic_cast<const HQHeadersFrame *>(frame.get());
+
+  SCOPED_MUTEX_LOCK(lock, this->_sink_vio->mutex, this_ethread());
+
+  MIOBuffer *writer = this->_sink_vio->get_writer();
+  // TODO Uncompress header block
+  writer->write(hframe->header_block(), hframe->header_block_length());
+
+  return HQErrorUPtr(new HQNoError());
+}
diff --git a/proxy/hq/HQHeaderVIOAdaptor.h b/proxy/hq/HQHeaderVIOAdaptor.h
new file mode 100644
index 0000000..0ec8c79
--- /dev/null
+++ b/proxy/hq/HQHeaderVIOAdaptor.h
@@ -0,0 +1,41 @@
+/** @file
+
+  A brief file description
+
+  @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.
+ */
+
+#pragma once
+
+#include "HQFrameHandler.h"
+
+class VIO;
+
+class HQHeaderVIOAdaptor : public HQFrameHandler
+{
+public:
+  HQHeaderVIOAdaptor(VIO *sink);
+
+  // HQFrameHandler
+  std::vector<HQFrameType> interests() override;
+  HQErrorUPtr handle_frame(std::shared_ptr<const HQFrame> frame) override;
+
+private:
+  VIO *_sink_vio = nullptr;
+};
diff --git a/proxy/hq/HQStreamDataVIOAdaptor.cc b/proxy/hq/HQStreamDataVIOAdaptor.cc
new file mode 100644
index 0000000..5df1190
--- /dev/null
+++ b/proxy/hq/HQStreamDataVIOAdaptor.cc
@@ -0,0 +1,49 @@
+/** @file
+
+  A brief file description
+
+  @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 "HQStreamDataVIOAdaptor.h"
+#include "I_VIO.h"
+
+HQStreamDataVIOAdaptor::HQStreamDataVIOAdaptor(VIO *sink) : _sink_vio(sink)
+{
+}
+
+std::vector<HQFrameType>
+HQStreamDataVIOAdaptor::interests()
+{
+  return {HQFrameType::DATA};
+}
+
+HQErrorUPtr
+HQStreamDataVIOAdaptor::handle_frame(std::shared_ptr<const HQFrame> frame)
+{
+  ink_assert(frame->type() == HQFrameType::DATA);
+  const HQDataFrame *dframe = dynamic_cast<const HQDataFrame *>(frame.get());
+
+  SCOPED_MUTEX_LOCK(lock, this->_sink_vio->mutex, this_ethread());
+
+  MIOBuffer *writer = this->_sink_vio->get_writer();
+  writer->write(dframe->payload(), dframe->payload_length());
+
+  return HQErrorUPtr(new HQNoError());
+}
diff --git a/proxy/hq/HQStreamDataVIOAdaptor.h b/proxy/hq/HQStreamDataVIOAdaptor.h
new file mode 100644
index 0000000..d3f7968
--- /dev/null
+++ b/proxy/hq/HQStreamDataVIOAdaptor.h
@@ -0,0 +1,41 @@
+/** @file
+
+  A brief file description
+
+  @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.
+ */
+
+#pragma once
+
+#include "HQFrameHandler.h"
+
+class VIO;
+
+class HQStreamDataVIOAdaptor : public HQFrameHandler
+{
+public:
+  HQStreamDataVIOAdaptor(VIO *sink);
+
+  // HQFrameHandler
+  std::vector<HQFrameType> interests() override;
+  HQErrorUPtr handle_frame(std::shared_ptr<const HQFrame> frame) override;
+
+private:
+  VIO *_sink_vio = nullptr;
+};
diff --git a/proxy/hq/HQTypes.h b/proxy/hq/HQTypes.h
new file mode 100644
index 0000000..3676d22
--- /dev/null
+++ b/proxy/hq/HQTypes.h
@@ -0,0 +1,95 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @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.
+ */
+
+#pragma once
+
+// Update HQFrame::type(const uint8_t *) too when you modify this list
+enum class HQFrameType : uint8_t {
+  DATA          = 0x00,
+  HEADERS       = 0x01,
+  PRIORITY      = 0x02,
+  CANCEL_PUSH   = 0x03,
+  SETTINGS      = 0x04,
+  PUSH_PROMISE  = 0x05,
+  X_RESERVED_1  = 0x06,
+  GOAWAY        = 0x07,
+  HEADER_ACK    = 0x08,
+  X_RESERVED_2  = 0x09,
+  MAX_PUSH_ID   = 0x0D,
+  X_MAX_DEFINED = 0x0D,
+  UNKNOWN       = 0xFF,
+};
+
+enum class HQErrorClass {
+  NONE,
+  APPLICATION,
+};
+
+using HQAppErrorCode = uint16_t;
+
+class HQError
+{
+public:
+  virtual ~HQError() {}
+  uint16_t code();
+
+  HQErrorClass cls = HQErrorClass::NONE;
+  union {
+    HQAppErrorCode app_error_code;
+  };
+  const char *msg = nullptr;
+
+protected:
+  HQError(){};
+  HQError(const HQAppErrorCode error_code, const char *error_msg = nullptr)
+    : cls(HQErrorClass::APPLICATION), app_error_code(error_code), msg(error_msg){};
+};
+
+class HQNoError : public HQError
+{
+public:
+  HQNoError() : HQError() {}
+};
+
+class HQConnectionError : public HQError
+{
+public:
+  HQConnectionError() : HQError() {}
+  HQConnectionError(const HQAppErrorCode error_code, const char *error_msg = nullptr) : HQError(error_code, error_msg){};
+};
+
+class HQStream;
+
+class HQStreamError : public HQError
+{
+public:
+  HQStreamError() : HQError() {}
+  HQStreamError(HQStream *s, const HQAppErrorCode error_code, const char *error_msg = nullptr)
+    : HQError(error_code, error_msg), stream(s){};
+
+  HQStream *stream;
+};
+
+using HQErrorUPtr           = std::unique_ptr<HQError>;
+using HQConnectionErrorUPtr = std::unique_ptr<HQConnectionError>;
+using HQStreamErrorUPtr     = std::unique_ptr<HQStreamError>;
diff --git a/proxy/hq/Makefile.am b/proxy/hq/Makefile.am
index e0d900b..17b99b6 100644
--- a/proxy/hq/Makefile.am
+++ b/proxy/hq/Makefile.am
@@ -19,6 +19,8 @@
 
 include $(top_srcdir)/build/tidy.mk
 
+SUBDIRS = test
+
 AM_CPPFLAGS += \
   $(iocore_include_dirs) \
   -I$(abs_top_srcdir)/proxy/api/ts \
@@ -40,6 +42,13 @@ libhq_a_SOURCES = \
   HQSessionAccept.cc \
   HQClientSession.cc \
   HQClientTransaction.cc \
+  HQFrame.cc \
+  HQFrameCollector.cc \
+  HQFrameDispatcher.cc \
+  HQHeaderFramer.cc \
+  HQDataFramer.cc \
+  HQHeaderVIOAdaptor.cc \
+  HQStreamDataVIOAdaptor.cc \
   QUICSimpleApp.cc
 
 tidy-local: $(libhq_a_SOURCES) \

-- 
To stop receiving notification emails like this one, please contact
maskit@apache.org.