This is an automated email from the ASF dual-hosted git repository.
bneradt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 83886d69b2 Extracted pseudo header validation logic to use in both h2
and h3 (#10139)
83886d69b2 is described below
commit 83886d69b2625124ac9dffa541f0d774a0ba2bee
Author: Zhengxi Li <[email protected]>
AuthorDate: Thu Aug 3 13:01:49 2023 -0400
Extracted pseudo header validation logic to use in both h2 and h3 (#10139)
This resolves #9994. Note that in h3, the request with the header failing
the validation won't be forwarded to the server as expected, but ATS doesn't
send out a error response as it should due to #10138 .
---
proxy/hdrs/CMakeLists.txt | 1 +
proxy/hdrs/HeaderValidator.cc | 135 ++++++++++++++
proxy/hdrs/HeaderValidator.h | 32 ++++
proxy/hdrs/Makefile.am | 7 +-
proxy/hdrs/unit_tests/test_HeaderValidator.cc | 250 ++++++++++++++++++++++++++
proxy/http2/HTTP2.cc | 107 +----------
proxy/http3/Http3HeaderVIOAdaptor.cc | 8 +
7 files changed, 434 insertions(+), 106 deletions(-)
diff --git a/proxy/hdrs/CMakeLists.txt b/proxy/hdrs/CMakeLists.txt
index e4ae8ca60d..b27ac2dd20 100644
--- a/proxy/hdrs/CMakeLists.txt
+++ b/proxy/hdrs/CMakeLists.txt
@@ -28,6 +28,7 @@ add_library(hdrs STATIC
VersionConverter.cc
HuffmanCodec.cc
XPACK.cc
+ HeaderValidator.cc
)
add_library(ts::hdrs ALIAS hdrs)
diff --git a/proxy/hdrs/HeaderValidator.cc b/proxy/hdrs/HeaderValidator.cc
new file mode 100644
index 0000000000..77abec7d2a
--- /dev/null
+++ b/proxy/hdrs/HeaderValidator.cc
@@ -0,0 +1,135 @@
+/** @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 "HeaderValidator.h"
+#include "HTTP.h"
+
+bool
+HeaderValidator::is_h2_h3_header_valid(const HTTPHdr &hdr, bool is_response,
bool is_trailing_header)
+{
+ const MIMEField *field = nullptr;
+ const char *name = nullptr;
+ int name_len = 0;
+ const char *value = nullptr;
+ int value_len = 0;
+ MIMEFieldIter iter;
+ auto method_field = hdr.field_find(PSEUDO_HEADER_METHOD.data(),
PSEUDO_HEADER_METHOD.size());
+ bool has_connect_method = false;
+ if (method_field) {
+ int method_len;
+ const char *method_value = method_field->value_get(&method_len);
+ has_connect_method = method_len == HTTP_LEN_CONNECT &&
strncmp(HTTP_METHOD_CONNECT, method_value, HTTP_LEN_CONNECT) == 0;
+ }
+ unsigned int expected_pseudo_header_count = is_response ? 1 :
has_connect_method ? 2 : 4;
+ unsigned int pseudo_header_count = 0;
+
+ if (is_trailing_header) {
+ expected_pseudo_header_count = 0;
+ }
+ for (auto &field : hdr) {
+ name = field.name_get(&name_len);
+ // Pseudo headers must appear before regular headers
+ if (name_len && name[0] == ':') {
+ ++pseudo_header_count;
+ if (pseudo_header_count > expected_pseudo_header_count) {
+ return false;
+ }
+ } else if (name_len <= 0) {
+ return false;
+ } else {
+ if (pseudo_header_count != expected_pseudo_header_count) {
+ return false;
+ }
+ }
+ }
+
+ // rfc7540,sec8.1.2.2 and rfc9114,sec4.2: Any message containing
+ // connection-specific header fields MUST be treated as malformed.
+ if (hdr.field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION) != nullptr ||
+ hdr.field_find(MIME_FIELD_KEEP_ALIVE, MIME_LEN_KEEP_ALIVE) != nullptr ||
+ hdr.field_find(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION)
!= nullptr ||
+ hdr.field_find(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE) != nullptr) {
+ return false;
+ }
+
+ // :path pseudo header MUST NOT empty for http or https URIs
+ field = hdr.field_find(PSEUDO_HEADER_PATH.data(), PSEUDO_HEADER_PATH.size());
+ if (field) {
+ field->value_get(&value_len);
+ if (value_len == 0) {
+ return false;
+ }
+ }
+
+ // when The TE header field is received, it MUST NOT contain any
+ // value other than "trailers".
+ field = hdr.field_find(MIME_FIELD_TE, MIME_LEN_TE);
+ if (field) {
+ value = field->value_get(&value_len);
+ if (!(value_len == 8 && memcmp(value, "trailers", 8) == 0)) {
+ return false;
+ }
+ }
+
+ if (is_trailing_header) {
+ // Done with validation for trailing headers, which doesn't have any
pseudo headers.
+ return true;
+ }
+ // Check pseudo headers
+ if (is_response) {
+ if (hdr.fields_count() >= 1) {
+ if (hdr.field_find(PSEUDO_HEADER_STATUS.data(),
PSEUDO_HEADER_STATUS.size()) == nullptr) {
+ return false;
+ }
+ } else {
+ // There should at least be :status pseudo header.
+ return false;
+ }
+ } else {
+ // This is a request.
+ if (!has_connect_method && hdr.fields_count() >= 4) {
+ if (hdr.field_find(PSEUDO_HEADER_SCHEME.data(),
PSEUDO_HEADER_SCHEME.size()) == nullptr ||
+ hdr.field_find(PSEUDO_HEADER_METHOD.data(),
PSEUDO_HEADER_METHOD.size()) == nullptr ||
+ hdr.field_find(PSEUDO_HEADER_PATH.data(), PSEUDO_HEADER_PATH.size())
== nullptr ||
+ hdr.field_find(PSEUDO_HEADER_AUTHORITY.data(),
PSEUDO_HEADER_AUTHORITY.size()) == nullptr ||
+ hdr.field_find(PSEUDO_HEADER_STATUS.data(),
PSEUDO_HEADER_STATUS.size()) != nullptr) {
+ // Decoded header field is invalid
+ return false;
+ }
+ } else if (has_connect_method && hdr.fields_count() >= 2) {
+ if (hdr.field_find(PSEUDO_HEADER_SCHEME.data(),
PSEUDO_HEADER_SCHEME.size()) != nullptr ||
+ hdr.field_find(PSEUDO_HEADER_METHOD.data(),
PSEUDO_HEADER_METHOD.size()) == nullptr ||
+ hdr.field_find(PSEUDO_HEADER_PATH.data(), PSEUDO_HEADER_PATH.size())
!= nullptr ||
+ hdr.field_find(PSEUDO_HEADER_AUTHORITY.data(),
PSEUDO_HEADER_AUTHORITY.size()) == nullptr ||
+ hdr.field_find(PSEUDO_HEADER_STATUS.data(),
PSEUDO_HEADER_STATUS.size()) != nullptr) {
+ // Decoded header field is invalid
+ return false;
+ }
+
+ } else {
+ // Pseudo headers is insufficient
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/proxy/hdrs/HeaderValidator.h b/proxy/hdrs/HeaderValidator.h
new file mode 100644
index 0000000000..859a88b92e
--- /dev/null
+++ b/proxy/hdrs/HeaderValidator.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 HTTPHdr;
+
+class HeaderValidator
+{
+public:
+ static bool is_h2_h3_header_valid(const HTTPHdr &hdr, bool is_response, bool
is_trailing_header);
+};
diff --git a/proxy/hdrs/Makefile.am b/proxy/hdrs/Makefile.am
index 70b9c2e118..ad7bc804ad 100644
--- a/proxy/hdrs/Makefile.am
+++ b/proxy/hdrs/Makefile.am
@@ -50,7 +50,9 @@ libhdrs_a_SOURCES = \
HuffmanCodec.cc \
HuffmanCodec.h \
XPACK.cc \
- XPACK.h
+ XPACK.h \
+ HeaderValidator.cc \
+ HeaderValidator.h
load_http_hdr_SOURCES = \
HTTP.h \
@@ -80,7 +82,8 @@ test_proxy_hdrs_SOURCES = \
unit_tests/test_Hdrs.cc \
unit_tests/test_HdrUtils.cc \
unit_tests/test_URL.cc \
- unit_tests/test_mime.cc
+ unit_tests/test_mime.cc \
+ unit_tests/test_HeaderValidator.cc
test_proxy_hdrs_LDFLAGS = @AM_LDFLAGS@ @SWOC_LDFLAGS@ @YAMLCPP_LDFLAGS@
@OPENSSL_LDFLAGS@
test_proxy_hdrs_LDADD = \
diff --git a/proxy/hdrs/unit_tests/test_HeaderValidator.cc
b/proxy/hdrs/unit_tests/test_HeaderValidator.cc
new file mode 100644
index 0000000000..0675ffa4cf
--- /dev/null
+++ b/proxy/hdrs/unit_tests/test_HeaderValidator.cc
@@ -0,0 +1,250 @@
+/** @file
+ *
+ * Catch-based unit tests for the HeaderValidator class
+ *
+ * @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 "catch.hpp"
+
+#include "HTTP.h"
+#include "HeaderValidator.h"
+#include <string_view>
+#include <vector>
+
+namespace
+{
+using Fields_type = std::vector<std::pair<std::string,
std::string>>;
+constexpr bool IS_VALID_HEADER = true;
+
+void
+add_field_value_to_hdr(HTTPHdr &hdr, std::string_view field_name,
std::string_view field_value)
+{
+ MIMEField *new_field = hdr.field_create(field_name.data(),
field_name.size());
+ new_field->value_set(hdr.m_heap, hdr.m_mime, field_value.data(),
field_value.size());
+ hdr.field_attach(new_field);
+}
+
+void
+check_header(const Fields_type &fields, HTTPHdr &hdr, bool expectation, bool
is_trailer = false)
+{
+ for (auto &field : fields) {
+ add_field_value_to_hdr(hdr, field.first, field.second);
+ }
+ auto ret = HeaderValidator::is_h2_h3_header_valid(hdr, hdr.type_get() ==
HTTP_TYPE_RESPONSE, is_trailer);
+ REQUIRE(ret == expectation);
+}
+} // end anonymous namespace
+
+TEST_CASE("testIsHeaderValid", "[proxy][hdrtest]")
+{
+ HTTPHdr hdr;
+ // extra to prevent proxy allocation.
+ HdrHeap *heap = new_HdrHeap(HdrHeap::DEFAULT_SIZE + 64);
+
+ SECTION("Test (valid) request with 4 required pseudo headers")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "GET" },
+ {":scheme", "https" },
+ {":authority", "www.this.com"},
+ {":path", "/some/path" },
+ };
+ check_header(fields, hdr, IS_VALID_HEADER);
+ }
+ SECTION("Test request with missing method field")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":scheme", "https" },
+ {":authority", "www.this.com"},
+ {":path", "/some/path" },
+ };
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test request with missing authority field")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "GET" },
+ {":scheme", "https" },
+ {":path", "/some/path"},
+ };
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test request with missing scheme field")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "GET" },
+ {":authority", "www.this.com"},
+ {":path", "/some/path" },
+ };
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test request with missing path field")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "GET" },
+ {":scheme", "https" },
+ {":authority", "www.this.com"},
+ };
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test request with extra pseudo headers")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "GET" },
+ {":scheme", "https" },
+ {":authority", "www.this.com"},
+ {":path", "/some/path" },
+ {":extra", "abc" },
+ };
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test CONNECT request with all required fields")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "CONNECT" },
+ {":authority", "www.this.com"},
+ {"extra", "abc" }
+ };
+ check_header(fields, hdr, IS_VALID_HEADER);
+ }
+ SECTION("Test CONNECT request with disallowed :scheme field")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "CONNECT" },
+ {":authority", "www.this.com"},
+ {":scheme", "https" },
+ {"extra", "abc" }
+ };
+ // :scheme and :path should be omitted in CONNECT requests.
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test CONNECT request with disallowed :path field")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "CONNECT" },
+ {":authority", "www.this.com"},
+ {":path", "/some/path" },
+ {"extra", "abc" }
+ };
+ // :scheme and :path should be omitted in CONNECT requests.
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test (valid) response with only the status field")
+ {
+ hdr.create(HTTP_TYPE_RESPONSE, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":status", "200"},
+ };
+ check_header(fields, hdr, IS_VALID_HEADER);
+ }
+
+ SECTION("Test response with more than the status field")
+ {
+ hdr.create(HTTP_TYPE_RESPONSE, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":status", "200"},
+ {":method", "GET"},
+ };
+ // Response headers cannot have pseudo headers other than :status.
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test response with no status field")
+ {
+ hdr.create(HTTP_TYPE_RESPONSE, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "GET"},
+ };
+ // Response headers must contain :status.
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test (invalid) trailer header with pseudo-header field")
+ {
+ hdr.create(HTTP_TYPE_RESPONSE, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":status", "500"},
+ };
+ static constexpr bool IS_TRAILER = true;
+ // Trailer headers may not contain any pseudo-header field.
+ check_header(fields, hdr, !IS_VALID_HEADER, IS_TRAILER);
+ }
+ SECTION("Test request with Connection headers")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "GET" },
+ {":scheme", "https" },
+ {":authority", "www.this.com"},
+ {":path", "/some/path" },
+ {"Connection", "Keep-Alive" },
+ };
+ // Connection-specific headers are not allowed.
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test request with Keep-Alive headers")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "GET" },
+ {":scheme", "https" },
+ {":authority", "www.this.com" },
+ {":path", "/some/path" },
+ {"Keep-Alive", "timeout=5, max=1000"},
+ };
+ // Connection-specific headers are not allowed.
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test request with Proxy-Connection headers")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "GET" },
+ {":scheme", "https" },
+ {":authority", "www.this.com"},
+ {":path", "/some/path" },
+ {"Proxy-Connection", "Keep-Alive" },
+ };
+ // Connection-specific headers are not allowed.
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ SECTION("Test request with Upgrade headers")
+ {
+ hdr.create(HTTP_TYPE_REQUEST, HTTP_1_1, heap);
+ Fields_type fields = {
+ {":method", "GET" },
+ {":scheme", "https" },
+ {":authority", "www.this.com"},
+ {":path", "/some/path" },
+ {"Upgrade", "HTTP/2.0" },
+ };
+ // Connection-specific headers are not allowed.
+ check_header(fields, hdr, !IS_VALID_HEADER);
+ }
+ // teardown
+ hdr.destroy();
+}
diff --git a/proxy/http2/HTTP2.cc b/proxy/http2/HTTP2.cc
index fb822769e0..454d040b61 100644
--- a/proxy/http2/HTTP2.cc
+++ b/proxy/http2/HTTP2.cc
@@ -22,6 +22,7 @@
*/
#include "hdrs/VersionConverter.h"
+#include "hdrs/HeaderValidator.h"
#include "HTTP2.h"
#include "HPACK.h"
@@ -470,11 +471,6 @@ Http2ErrorCode
http2_decode_header_blocks(HTTPHdr *hdr, const uint8_t *buf_start, const
uint32_t buf_len, uint32_t *len_read, HpackHandle &handle,
bool is_trailing_header, uint32_t
maximum_table_size, bool is_outbound)
{
- const MIMEField *field = nullptr;
- const char *name = nullptr;
- int name_len = 0;
- const char *value = nullptr;
- int value_len = 0;
int64_t result = hpack_decode_header_block(handle, hdr, buf_start, buf_len,
Http2::max_header_list_size, maximum_table_size);
if (result < 0) {
@@ -489,105 +485,8 @@ http2_decode_header_blocks(HTTPHdr *hdr, const uint8_t
*buf_start, const uint32_
if (len_read) {
*len_read = result;
}
-
- MIMEFieldIter iter;
- auto method_field = hdr->field_find(PSEUDO_HEADER_METHOD.data(),
PSEUDO_HEADER_METHOD.size());
- bool has_connect_method = false;
- if (method_field) {
- int method_len;
- const char *method_value = method_field->value_get(&method_len);
- has_connect_method = method_len == HTTP_LEN_CONNECT &&
strncmp(HTTP_METHOD_CONNECT, method_value, HTTP_LEN_CONNECT) == 0;
- }
- unsigned int expected_pseudo_header_count = is_outbound ? 1 :
has_connect_method ? 2 : 4;
- unsigned int pseudo_header_count = 0;
-
- if (is_trailing_header) {
- expected_pseudo_header_count = 0;
- }
- for (auto &field : *hdr) {
- name = field.name_get(&name_len);
- // Pseudo headers must appear before regular headers
- if (name_len && name[0] == ':') {
- ++pseudo_header_count;
- if (pseudo_header_count > expected_pseudo_header_count) {
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- }
- } else if (name_len <= 0) {
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- } else {
- if (pseudo_header_count != expected_pseudo_header_count) {
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- }
- }
- }
-
- // rfc7540,sec8.1.2.2: Any message containing connection-specific header
- // fields MUST be treated as malformed
- if (hdr->field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION) != nullptr ||
- hdr->field_find(MIME_FIELD_KEEP_ALIVE, MIME_LEN_KEEP_ALIVE) != nullptr ||
- hdr->field_find(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION)
!= nullptr ||
- hdr->field_find(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE) != nullptr) {
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- }
-
- // :path pseudo header MUST NOT empty for http or https URIs
- field = hdr->field_find(PSEUDO_HEADER_PATH.data(),
PSEUDO_HEADER_PATH.size());
- if (field) {
- field->value_get(&value_len);
- if (value_len == 0) {
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- }
- }
-
- // when The TE header field is received, it MUST NOT contain any
- // value other than "trailers".
- field = hdr->field_find(MIME_FIELD_TE, MIME_LEN_TE);
- if (field) {
- value = field->value_get(&value_len);
- if (!(value_len == 8 && memcmp(value, "trailers", 8) == 0)) {
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- }
- }
-
- if (!is_trailing_header) {
- // Check pseudo headers
- if (is_outbound) {
- if (hdr->fields_count() >= 1) {
- if (hdr->field_find(PSEUDO_HEADER_STATUS.data(),
PSEUDO_HEADER_STATUS.size()) == nullptr) {
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- }
- } else {
- // There should at least be :status pseudo header.
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- }
- } else {
- if (!has_connect_method && hdr->fields_count() >= 4) {
- if (hdr->field_find(PSEUDO_HEADER_SCHEME.data(),
PSEUDO_HEADER_SCHEME.size()) == nullptr ||
- hdr->field_find(PSEUDO_HEADER_METHOD.data(),
PSEUDO_HEADER_METHOD.size()) == nullptr ||
- hdr->field_find(PSEUDO_HEADER_PATH.data(),
PSEUDO_HEADER_PATH.size()) == nullptr ||
- hdr->field_find(PSEUDO_HEADER_AUTHORITY.data(),
PSEUDO_HEADER_AUTHORITY.size()) == nullptr ||
- hdr->field_find(PSEUDO_HEADER_STATUS.data(),
PSEUDO_HEADER_STATUS.size()) != nullptr) {
- // Decoded header field is invalid
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- }
- } else if (has_connect_method && hdr->fields_count() >= 2) {
- if (hdr->field_find(PSEUDO_HEADER_SCHEME.data(),
PSEUDO_HEADER_SCHEME.size()) != nullptr ||
- hdr->field_find(PSEUDO_HEADER_METHOD.data(),
PSEUDO_HEADER_METHOD.size()) == nullptr ||
- hdr->field_find(PSEUDO_HEADER_PATH.data(),
PSEUDO_HEADER_PATH.size()) != nullptr ||
- hdr->field_find(PSEUDO_HEADER_AUTHORITY.data(),
PSEUDO_HEADER_AUTHORITY.size()) == nullptr ||
- hdr->field_find(PSEUDO_HEADER_STATUS.data(),
PSEUDO_HEADER_STATUS.size()) != nullptr) {
- // Decoded header field is invalid
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- }
-
- } else {
- // Pseudo headers is insufficient
- return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
- }
- }
- }
-
- return Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
+ return HeaderValidator::is_h2_h3_header_valid(*hdr, is_outbound,
is_trailing_header) ? Http2ErrorCode::HTTP2_ERROR_NO_ERROR :
+
Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
}
// Initialize this subsystem with librecords configs (for now)
diff --git a/proxy/http3/Http3HeaderVIOAdaptor.cc
b/proxy/http3/Http3HeaderVIOAdaptor.cc
index 4d449af0b3..ffe59c85f3 100644
--- a/proxy/http3/Http3HeaderVIOAdaptor.cc
+++ b/proxy/http3/Http3HeaderVIOAdaptor.cc
@@ -22,6 +22,7 @@
*/
#include "Http3HeaderVIOAdaptor.h"
+#include "hdrs/HeaderValidator.h"
#include "I_VIO.h"
#include "HTTP.h"
@@ -95,6 +96,13 @@ Http3HeaderVIOAdaptor::event_handler(int event, Event *data)
int
Http3HeaderVIOAdaptor::_on_qpack_decode_complete()
{
+ // Currently trailer support for h3 is not implemented.
+ constexpr static bool NON_TRAILER = false;
+ if (!HeaderValidator::is_h2_h3_header_valid(this->_header,
http_hdr_type_get(this->_header.m_http) == HTTP_TYPE_RESPONSE,
+ NON_TRAILER)) {
+ Debug("http3", "Header is invalid");
+ return -1;
+ }
int res = this->_hvc.convert(this->_header, 3, 1);
if (res != 0) {
Debug("http3", "PARSE_RESULT_ERROR");