diff --git a/src/http2.cc b/src/http2.cc index fb790677..2bc853f5 100644 --- a/src/http2.cc +++ b/src/http2.cc @@ -1665,6 +1665,31 @@ StringRef copy_lower(BlockAllocator &balloc, const StringRef &src) { return StringRef{iov.base, p}; } +bool contains_trailers(const StringRef &s) { + constexpr auto trailers = StringRef::from_lit("trailers"); + + for (auto p = std::begin(s), end = std::end(s);; ++p) { + p = std::find_if(p, end, [](char c) { return c != ' ' && c != '\t'; }); + if (p == end || end - p < trailers.size()) { + return false; + } + if (util::strieq(trailers, StringRef{p, p + trailers.size()})) { + // Make sure that there is no character other than white spaces + // before next "," or end of string. + p = std::find_if(p + trailers.size(), end, + [](char c) { return c != ' ' && c != '\t'; }); + if (p == end || *p == ',') { + return true; + } + } + // Skip to next ",". + p = std::find_if(p, end, [](char c) { return c == ','; }); + if (p == end) { + return false; + } + } +} + } // namespace http2 } // namespace nghttp2 diff --git a/src/http2.h b/src/http2.h index 6f12d2cd..5ebe11f1 100644 --- a/src/http2.h +++ b/src/http2.h @@ -384,6 +384,9 @@ int construct_push_component(BlockAllocator &balloc, StringRef &scheme, // Copies |src| and return its lower-cased version. StringRef copy_lower(BlockAllocator &balloc, const StringRef &src); +// Returns true if te header field value |s| contains "trailers". +bool contains_trailers(const StringRef &s); + } // namespace http2 } // namespace nghttp2 diff --git a/src/http2_test.cc b/src/http2_test.cc index e0da39d8..d2be6230 100644 --- a/src/http2_test.cc +++ b/src/http2_test.cc @@ -962,4 +962,20 @@ void test_http2_construct_push_component(void) { CU_ASSERT("/b/?q=a" == path); } +void test_http2_contains_trailers(void) { + CU_ASSERT(!http2::contains_trailers(StringRef::from_lit(""))); + CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers"))); + // Match must be case-insensitive. + CU_ASSERT(http2::contains_trailers(StringRef::from_lit("TRAILERS"))); + CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("trailer"))); + CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("trailers 3"))); + CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers,"))); + CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers,foo"))); + CU_ASSERT(http2::contains_trailers(StringRef::from_lit("foo,trailers"))); + CU_ASSERT(http2::contains_trailers(StringRef::from_lit("foo,trailers,bar"))); + CU_ASSERT( + http2::contains_trailers(StringRef::from_lit("foo, trailers ,bar"))); + CU_ASSERT(http2::contains_trailers(StringRef::from_lit(",trailers"))); +} + } // namespace shrpx diff --git a/src/http2_test.h b/src/http2_test.h index 029e8e6f..bd7ddc05 100644 --- a/src/http2_test.h +++ b/src/http2_test.h @@ -46,6 +46,7 @@ void test_http2_normalize_path(void); void test_http2_rewrite_clean_path(void); void test_http2_get_pure_path_component(void); void test_http2_construct_push_component(void); +void test_http2_contains_trailers(void); } // namespace shrpx diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc index 70230e82..e725bd9e 100644 --- a/src/shrpx-unittest.cc +++ b/src/shrpx-unittest.cc @@ -102,6 +102,8 @@ int main(int argc, char *argv[]) { shrpx::test_http2_get_pure_path_component) || !CU_add_test(pSuite, "http2_construct_push_component", shrpx::test_http2_construct_push_component) || + !CU_add_test(pSuite, "http2_contains_trailers", + shrpx::test_http2_contains_trailers) || !CU_add_test(pSuite, "downstream_field_store_append_last_header", shrpx::test_downstream_field_store_append_last_header) || !CU_add_test(pSuite, "downstream_field_store_header", diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index b6c891c8..f0876bc4 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -402,7 +402,7 @@ int Http2DownstreamConnection::push_request_headers() { // HTTP/1 upstream request can contain keyword other than // "trailers". We just forward "trailers". // TODO more strict handling required here. - if (te && util::strifind(te->value, StringRef::from_lit("trailers"))) { + if (te && http2::contains_trailers(te->value)) { nva.push_back(http2::make_nv_ll("te", "trailers")); }