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.