diff options
-rw-r--r-- | .SRCINFO | 13 | ||||
-rw-r--r-- | 0001-arch-dynamically-link-libpsl.patch | 10 | ||||
-rw-r--r-- | 0002-patch-cel-cpp-to-not-break-build.patch | 78 | ||||
-rw-r--r-- | 0002-pkgconfig-builtin-glib-int-conversion.patch | 160 | ||||
-rw-r--r-- | 0003-close-http-connections-that-prematurely-reset-stream.patch | 312 | ||||
-rw-r--r-- | 0004-limit-http-requests-per-io-cycle.patch | 821 | ||||
-rw-r--r-- | PKGBUILD | 30 |
7 files changed, 179 insertions, 1245 deletions
@@ -1,6 +1,6 @@ pkgbase = envoyproxy pkgdesc = A high performance, open source, general RPC framework that puts mobile and HTTP/2 first. - pkgver = 1.29.2 + pkgver = 1.30.1 pkgrel = 1 url = https://envoyproxy.io arch = i686 @@ -9,6 +9,7 @@ pkgbase = envoyproxy makedepends = cmake makedepends = clang makedepends = git + makedepends = libc++ makedepends = lld makedepends = go makedepends = java-environment-openjdk @@ -17,11 +18,11 @@ pkgbase = envoyproxy makedepends = perl makedepends = python makedepends = libxcrypt-compat - source = https://github.com/envoyproxy/envoy/archive/v1.29.2.tar.gz + source = https://github.com/envoyproxy/envoy/archive/v1.30.1.tar.gz source = 0001-arch-dynamically-link-libpsl.patch - source = 0002-patch-cel-cpp-to-not-break-build.patch - sha512sums = 40db5d45e8620680a0aece41de7bf8d09ac81d0bf339a77a3d70baca61823c4aa4e8e951a2c57f417ebe4fecda8acf17edda593292170b7c5f39e5b753f4579a - sha512sums = 82a695c4c5ebd3f154acd424e00c5b9be27d809ed895870650b1ea0d1a757104753c242b67718bb68eef128d507668af2a5c7b101a64c346f27a9d0d5ad33cb9 - sha512sums = 6306c91abe482533dbc11aa4c39b0fc0ca145893c6692d56d5ecc77a8bf195e9495151c417ffa5d0702b85c0ad8a96133e1ec8fb01d04b1cdf5db9d71f51216f + source = 0002-pkgconfig-builtin-glib-int-conversion.patch + sha512sums = a1fec2eecf8da64f2cd11ad8fca825d436fe88b10b61d170d87d07dae56699da5aed7f8246aa43f6fd8f7c9644426f4a3b71578f4bb77bf6dd60229d03eb11c0 + sha512sums = 2b83157f8e8976e12c5e09c13aafe0da1ce478c448b07a070890a95d77fa9b396314d5886babf81e5c6fbf77338a49b45e2484a01f4ed0bb8868b45e425e7062 + sha512sums = d90b9cbebc2a5d0d6a47e3f7cb88561d7233e5d7fb1d6a0cbe52a75b403f12f5083cc7c6ef94e8846a75b53fbcc350e7309477a495701c6a3e4fd1c3b49f7a7f pkgname = envoyproxy diff --git a/0001-arch-dynamically-link-libpsl.patch b/0001-arch-dynamically-link-libpsl.patch index fd712ec067d0..863095a8b9f6 100644 --- a/0001-arch-dynamically-link-libpsl.patch +++ b/0001-arch-dynamically-link-libpsl.patch @@ -1,7 +1,7 @@ diff -Naur a/.bazelrc b/.bazelrc ---- a/.bazelrc 2024-02-09 22:41:26.000000000 -0500 -+++ b/.bazelrc 2024-02-14 13:22:15.574438344 -0500 -@@ -69,7 +69,7 @@ +--- a/.bazelrc 2024-04-18 19:43:45.000000000 -0400 ++++ b/.bazelrc 2024-05-03 14:38:18.334989983 -0400 +@@ -70,7 +70,7 @@ build:linux --fission=dbg,opt build:linux --features=per_object_debug_info build:linux --action_env=BAZEL_LINKLIBS=-l%:libstdc++.a @@ -10,12 +10,12 @@ diff -Naur a/.bazelrc b/.bazelrc # We already have absl in the build, define absl=1 to tell googletest to use absl for backtrace. build --define absl=1 -@@ -188,7 +188,7 @@ +@@ -199,7 +199,7 @@ build:libc++ --action_env=LDFLAGS=-stdlib=libc++ build:libc++ --action_env=BAZEL_CXXOPTS=-stdlib=libc++ build:libc++ --action_env=BAZEL_LINKLIBS=-l%:libc++.a:-l%:libc++abi.a -build:libc++ --action_env=BAZEL_LINKOPTS=-lm:-pthread +build:libc++ --action_env=BAZEL_LINKOPTS=-lm:-pthread:-lpsl build:libc++ --define force_libcpp=enabled + build:clang-libc++ --config=libc++ - build:libc++20 --config=libc++ diff --git a/0002-patch-cel-cpp-to-not-break-build.patch b/0002-patch-cel-cpp-to-not-break-build.patch deleted file mode 100644 index 439b0d88a58b..000000000000 --- a/0002-patch-cel-cpp-to-not-break-build.patch +++ /dev/null @@ -1,78 +0,0 @@ -From e9ce2fe81ccd79938d6ca83deb7afa5af0425604 Mon Sep 17 00:00:00 2001 -From: Raven Black <ravenblack@dropbox.com> -Date: Thu, 18 Jan 2024 16:34:15 +0000 -Subject: [PATCH] Patch cel-cpp to not break build - -Signed-off-by: Raven Black <ravenblack@dropbox.com> ---- - bazel/cel-cpp-memory.patch | 44 ++++++++++++++++++++++++++++++++++++++ - bazel/repositories.bzl | 5 ++++- - 2 files changed, 48 insertions(+), 1 deletion(-) - create mode 100644 bazel/cel-cpp-memory.patch - -diff --git a/bazel/cel-cpp-memory.patch b/bazel/cel-cpp-memory.patch -new file mode 100644 -index 0000000000000..84df54db60b82 ---- /dev/null -+++ b/bazel/cel-cpp-memory.patch -@@ -0,0 +1,44 @@ -+From 09a072b4bb5a75e1df15beba29a9f13b1948ff8b Mon Sep 17 00:00:00 2001 -+From: Ivan Prisyazhnyy <john.koepi@gmail.com> -+Date: Thu, 18 Jan 2024 13:55:29 +0000 -+Subject: [PATCH] Fix: use of sized deallocation in base/memory.h wo check -+ -+Dependant projects that do not use `-fsized-deallocation` -+would not compile with the call to delete(void*, size_t, align). -+ -+There are other places that already check for -+`defined(__cpp_sized_deallocation)` and this patch just shares -+this practice. -+ -+Testing: -+ -+ // fix .bazelrc to have: -+ build --cxxopt=-fno-sized-deallocation -+ -+ $ bazel build --verbose_failures //base:\* -+ -+Signed-off-by: Ivan Prisyazhnyy <john.koepi@gmail.com> -+--- -+ base/memory.h | 8 +++++++- -+ 1 file changed, 7 insertions(+), 1 deletion(-) -+ -+diff --git a/base/memory.h b/base/memory.h -+index 3552e19a..c310128a 100644 -+--- a/base/memory.h -++++ b/base/memory.h -+@@ -89,8 +89,14 @@ class Allocator { -+ -+ void deallocate(pointer p, size_type n) { -+ if (!allocation_only_) { -+- ::operator delete(static_cast<void*>(p), n * sizeof(T), -++#if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L -++ ::operator delete(static_cast<void *>(p), n * sizeof(T), -+ static_cast<std::align_val_t>(alignof(T))); -++#else -++ ::operator delete(static_cast<void *>(p), -++ static_cast<std::align_val_t>(alignof(T))); -++ static_cast<void>(n); // unused -++#endif -+ } -+ } -+ -diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl -index aa93c9c838d8a..3220aeb2ecb18 100644 ---- a/bazel/repositories.bzl -+++ b/bazel/repositories.bzl -@@ -702,7 +702,10 @@ def _com_github_facebook_zstd(): - def _com_google_cel_cpp(): - external_http_archive( - "com_google_cel_cpp", -- patches = ["@envoy//bazel:cel-cpp.patch"], -+ patches = [ -+ "@envoy//bazel:cel-cpp.patch", -+ "@envoy//bazel:cel-cpp-memory.patch", -+ ], - patch_args = ["-p1"], - ) - diff --git a/0002-pkgconfig-builtin-glib-int-conversion.patch b/0002-pkgconfig-builtin-glib-int-conversion.patch new file mode 100644 index 000000000000..4a3fbf65bb09 --- /dev/null +++ b/0002-pkgconfig-builtin-glib-int-conversion.patch @@ -0,0 +1,160 @@ +diff -Naur a/bazel/foreign_cc/pkgconfig-builtin-glib-int-conversion.patch b/bazel/foreign_cc/pkgconfig-builtin-glib-int-conversion.patch +--- a/bazel/foreign_cc/pkgconfig-builtin-glib-int-conversion.patch 1970-01-01 00:00:00.000000000 +0000 ++++ b/bazel/foreign_cc/pkgconfig-builtin-glib-int-conversion.patch 2024-05-03 18:19:07.490119872 +0000 +@@ -0,0 +1,140 @@ ++diff -Naur a/toolchains/built_toolchains.bzl b/toolchains/built_toolchains.bzl ++--- a/toolchains/built_toolchains.bzl 2023-10-25 21:46:56.000000000 +0000 +++++ b/toolchains/built_toolchains.bzl 2024-05-03 18:09:51.799114777 +0000 ++@@ -275,6 +275,9 @@ ++ ++ # This patch is required as rules_foreign_cc runs in MSYS2 on Windows and MSYS2's "mkdir" is used ++ Label("//toolchains:pkgconfig-makefile-vc.patch"), +++ +++ # This patch fixes explicit integer conversion which causes errors in clang >= 15 and gcc >= 14 +++ Label("//toolchains:pkgconfig-builtin-glib-int-conversion.patch") ++ ], ++ urls = [ ++ "https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz", ++diff -Naur a/toolchains/pkgconfig-builtin-glib-int-conversion.patch b/toolchains/pkgconfig-builtin-glib-int-conversion.patch ++--- a/toolchains/pkgconfig-builtin-glib-int-conversion.patch 1970-01-01 00:00:00.000000000 +0000 +++++ b/toolchains/pkgconfig-builtin-glib-int-conversion.patch 2024-05-03 18:18:26.705978705 +0000 ++@@ -0,0 +1,123 @@ +++diff -Naur glib/glib/gatomic.c glib/glib/gatomic.c +++--- glib/glib/gatomic.c 2016-04-11 21:39:26.000000000 +0000 ++++++ glib/glib/gatomic.c 2024-05-03 18:15:37.295886468 +0000 +++@@ -385,7 +385,7 @@ +++ * +++ * Since: 2.30 +++ **/ +++-gssize ++++gintptr +++ (g_atomic_pointer_add) (volatile void *atomic, +++ gssize val) +++ { +++@@ -409,11 +409,11 @@ +++ * +++ * Since: 2.30 +++ **/ +++-gsize ++++guintptr +++ (g_atomic_pointer_and) (volatile void *atomic, +++ gsize val) +++ { +++- return g_atomic_pointer_and ((volatile gpointer *) atomic, val); ++++ return g_atomic_pointer_and ((gpointer *) atomic, val); +++ } +++ +++ /** +++@@ -433,11 +433,11 @@ +++ * +++ * Since: 2.30 +++ **/ +++-gsize ++++guintptr +++ (g_atomic_pointer_or) (volatile void *atomic, +++ gsize val) +++ { +++- return g_atomic_pointer_or ((volatile gpointer *) atomic, val); ++++ return g_atomic_pointer_or ((gpointer *) atomic, val); +++ } +++ +++ /** +++@@ -457,11 +457,11 @@ +++ * +++ * Since: 2.30 +++ **/ +++-gsize ++++guintptr +++ (g_atomic_pointer_xor) (volatile void *atomic, +++ gsize val) +++ { +++- return g_atomic_pointer_xor ((volatile gpointer *) atomic, val); ++++ return g_atomic_pointer_xor ((gpointer *) atomic, val); +++ } +++ +++ #elif defined (G_PLATFORM_WIN32) +++diff -Naur glib/glib/gatomic.h glib/glib/gatomic.h +++--- glib/glib/gatomic.h 2016-04-11 21:39:26.000000000 +0000 ++++++ glib/glib/gatomic.h 2024-05-03 18:15:37.295886468 +0000 +++@@ -66,16 +66,16 @@ +++ gpointer oldval, +++ gpointer newval); +++ GLIB_AVAILABLE_IN_ALL +++-gssize g_atomic_pointer_add (volatile void *atomic, ++++gintptr g_atomic_pointer_add (volatile void *atomic, +++ gssize val); +++ GLIB_AVAILABLE_IN_2_30 +++-gsize g_atomic_pointer_and (volatile void *atomic, ++++guintptr g_atomic_pointer_and (volatile void *atomic, +++ gsize val); +++ GLIB_AVAILABLE_IN_2_30 +++-gsize g_atomic_pointer_or (volatile void *atomic, ++++guintptr g_atomic_pointer_or (volatile void *atomic, +++ gsize val); +++ GLIB_AVAILABLE_IN_ALL +++-gsize g_atomic_pointer_xor (volatile void *atomic, ++++guintptr g_atomic_pointer_xor (volatile void *atomic, +++ gsize val); +++ +++ GLIB_DEPRECATED_IN_2_30_FOR(g_atomic_add) +++@@ -167,28 +167,34 @@ +++ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \ +++ (void) (0 ? (gpointer) *(atomic) : 0); \ +++ (void) (0 ? (val) ^ (val) : 0); \ +++- (gssize) __sync_fetch_and_add ((atomic), (val)); \ ++++ (guintptr) __atomic_fetch_add ((atomic), (val), __ATOMIC_SEQ_CST); \ +++ })) +++ #define g_atomic_pointer_and(atomic, val) \ +++ (G_GNUC_EXTENSION ({ \ ++++ guintptr *gapa_atomic = (guintptr *) atomic; \ +++ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \ +++- (void) (0 ? (gpointer) *(atomic) : 0); \ +++- (void) (0 ? (val) ^ (val) : 0); \ +++- (gsize) __sync_fetch_and_and ((atomic), (val)); \ ++++ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (guintptr)); \ ++++ (void) (0 ? (gpointer) *(atomic) : NULL); \ ++++ (void) (0 ? (val) ^ (val) : 1); \ ++++ (guintptr) __atomic_fetch_and (gapa_atomic, (val), __ATOMIC_SEQ_CST); \ +++ })) +++ #define g_atomic_pointer_or(atomic, val) \ +++ (G_GNUC_EXTENSION ({ \ ++++ guintptr *gapa_atomic = (guintptr *) atomic; \ +++ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \ +++- (void) (0 ? (gpointer) *(atomic) : 0); \ +++- (void) (0 ? (val) ^ (val) : 0); \ +++- (gsize) __sync_fetch_and_or ((atomic), (val)); \ ++++ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (guintptr)); \ ++++ (void) (0 ? (gpointer) *(atomic) : NULL); \ ++++ (void) (0 ? (val) ^ (val) : 1); \ ++++ (guintptr) __atomic_fetch_or (gapa_atomic, (val), __ATOMIC_SEQ_CST); \ +++ })) +++ #define g_atomic_pointer_xor(atomic, val) \ +++ (G_GNUC_EXTENSION ({ \ ++++ guintptr *gapa_atomic = (guintptr *) atomic; \ +++ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \ +++- (void) (0 ? (gpointer) *(atomic) : 0); \ +++- (void) (0 ? (val) ^ (val) : 0); \ +++- (gsize) __sync_fetch_and_xor ((atomic), (val)); \ ++++ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (guintptr)); \ ++++ (void) (0 ? (gpointer) *(atomic) : NULL); \ ++++ (void) (0 ? (val) ^ (val) : 1); \ ++++ (guintptr) __atomic_fetch_xor (gapa_atomic, (val), __ATOMIC_SEQ_CST); \ +++ })) +++ +++ #else /* defined(G_ATOMIC_LOCK_FREE) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) */ +diff -Naur a/bazel/repositories.bzl b/bazel/repositories.bzl +--- a/bazel/repositories.bzl 2024-04-18 23:43:45.000000000 +0000 ++++ b/bazel/repositories.bzl 2024-05-03 18:11:00.983659410 +0000 +@@ -1464,7 +1464,11 @@ + external_http_archive("rules_ruby") + + def _foreign_cc_dependencies(): +- external_http_archive("rules_foreign_cc") ++ external_http_archive( ++ name = "rules_foreign_cc", ++ patch_args = ["-p1"], ++ patches = ["@envoy//bazel/foreign_cc:pkgconfig-builtin-glib-int-conversion.patch"], ++ ) + + def _com_github_maxmind_libmaxminddb(): + external_http_archive( diff --git a/0003-close-http-connections-that-prematurely-reset-stream.patch b/0003-close-http-connections-that-prematurely-reset-stream.patch deleted file mode 100644 index 936730eea7ec..000000000000 --- a/0003-close-http-connections-that-prematurely-reset-stream.patch +++ /dev/null @@ -1,312 +0,0 @@ -From 481a9d54b8f5d73e16c6673eaea1a781c5e5705e Mon Sep 17 00:00:00 2001 -From: Yan Avlasov <yavlasov@google.com> -Date: Thu, 28 Sep 2023 16:11:58 +0000 -Subject: [PATCH] Close HTTP connections that prematurely reset streams - -Signed-off-by: Yan Avlasov <yavlasov@google.com> ---- - changelogs/current.yaml | 9 +++ - source/common/http/conn_manager_config.h | 1 + - source/common/http/conn_manager_impl.cc | 65 +++++++++++++++++ - source/common/http/conn_manager_impl.h | 23 ++++++ - source/common/runtime/runtime_features.cc | 1 + - .../multiplexed_integration_test.cc | 72 +++++++++++++++++++ - 6 files changed, 171 insertions(+) - -diff --git a/changelogs/current.yaml b/changelogs/current.yaml -index 32fc3bc047fd2..b5d45089f337c 100644 ---- a/changelogs/current.yaml -+++ b/changelogs/current.yaml -@@ -1,6 +1,15 @@ - date: July 26, 2023 - - behavior_changes: -+- area: http -+ change: | -+ Close HTTP/2 and HTTP/3 connections that prematurely reset streams. The runtime key -+ ``overload.premature_reset_min_stream_lifetime_seconds`` determines the interval where received stream -+ reset is considered premature (with 1 second default). The runtime key ``overload.premature_reset_total_stream_count``, -+ with the default value of 500, determines the number of requests received from a connection before the check for premature -+ resets is applied. The connection is disconnected if more than 50% of resets are premature. -+ Setting the runtime key ``envoy.restart_features.send_goaway_for_premature_rst_streams`` to ``false`` completely disables -+ this check. - - area: build - change: | - Moved the subset, ring_hash, and maglev LB code into extensions. If you use these load balancers and override -diff --git a/source/common/http/conn_manager_config.h b/source/common/http/conn_manager_config.h -index 52f8188c205c5..a07eb825f789b 100644 ---- a/source/common/http/conn_manager_config.h -+++ b/source/common/http/conn_manager_config.h -@@ -66,6 +66,7 @@ namespace Http { - COUNTER(downstream_rq_rejected_via_ip_detection) \ - COUNTER(downstream_rq_response_before_rq_complete) \ - COUNTER(downstream_rq_rx_reset) \ -+ COUNTER(downstream_rq_too_many_premature_resets) \ - COUNTER(downstream_rq_timeout) \ - COUNTER(downstream_rq_header_timeout) \ - COUNTER(downstream_rq_too_large) \ -diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc -index 5a603aaed80b6..021c55bc4ccae 100644 ---- a/source/common/http/conn_manager_impl.cc -+++ b/source/common/http/conn_manager_impl.cc -@@ -1,5 +1,6 @@ - #include "source/common/http/conn_manager_impl.h" - -+#include <chrono> - #include <cstdint> - #include <functional> - #include <list> -@@ -55,6 +56,11 @@ - namespace Envoy { - namespace Http { - -+const absl::string_view ConnectionManagerImpl::PrematureResetTotalStreamCountKey = -+ "overload.premature_reset_total_stream_count"; -+const absl::string_view ConnectionManagerImpl::PrematureResetMinStreamLifetimeSecondsKey = -+ "overload.premature_reset_min_stream_lifetime_seconds"; -+ - bool requestWasConnect(const RequestHeaderMapSharedPtr& headers, Protocol protocol) { - if (!headers) { - return false; -@@ -267,6 +273,12 @@ void ConnectionManagerImpl::doEndStream(ActiveStream& stream, bool check_for_def - } - - void ConnectionManagerImpl::doDeferredStreamDestroy(ActiveStream& stream) { -+ if (!stream.state_.is_internally_destroyed_) { -+ ++closed_non_internally_destroyed_requests_; -+ if (isPrematureRstStream(stream)) { -+ ++number_premature_stream_resets_; -+ } -+ } - if (stream.max_stream_duration_timer_ != nullptr) { - stream.max_stream_duration_timer_->disableTimer(); - stream.max_stream_duration_timer_ = nullptr; -@@ -343,6 +355,7 @@ void ConnectionManagerImpl::doDeferredStreamDestroy(ActiveStream& stream) { - if (connection_idle_timer_ && streams_.empty()) { - connection_idle_timer_->enableTimer(config_.idleTimeout().value()); - } -+ maybeDrainDueToPrematureResets(); - } - - RequestDecoder& ConnectionManagerImpl::newStream(ResponseEncoder& response_encoder, -@@ -607,6 +620,58 @@ void ConnectionManagerImpl::doConnectionClose( - } - } - -+bool ConnectionManagerImpl::isPrematureRstStream(const ActiveStream& stream) const { -+ // Check if the request was prematurely reset, by comparing its lifetime to the configured -+ // threshold. -+ ASSERT(!stream.state_.is_internally_destroyed_); -+ absl::optional<std::chrono::nanoseconds> duration = -+ stream.filter_manager_.streamInfo().currentDuration(); -+ -+ // Check if request lifetime is longer than the premature reset threshold. -+ if (duration) { -+ const uint64_t lifetime = std::chrono::duration_cast<std::chrono::seconds>(*duration).count(); -+ const uint64_t min_lifetime = runtime_.snapshot().getInteger( -+ ConnectionManagerImpl::PrematureResetMinStreamLifetimeSecondsKey, 1); -+ if (lifetime > min_lifetime) { -+ return false; -+ } -+ } -+ -+ // If request has completed before configured threshold, also check if the Envoy proxied the -+ // response from the upstream. Requests without the response status were reset. -+ // TODO(RyanTheOptimist): Possibly support half_closed_local instead. -+ return !stream.filter_manager_.streamInfo().responseCode(); -+} -+ -+// Sends a GOAWAY if too many streams have been reset prematurely on this -+// connection. -+void ConnectionManagerImpl::maybeDrainDueToPrematureResets() { -+ if (!Runtime::runtimeFeatureEnabled( -+ "envoy.restart_features.send_goaway_for_premature_rst_streams") || -+ closed_non_internally_destroyed_requests_ == 0) { -+ return; -+ } -+ -+ const uint64_t limit = -+ runtime_.snapshot().getInteger(ConnectionManagerImpl::PrematureResetTotalStreamCountKey, 500); -+ -+ if (closed_non_internally_destroyed_requests_ < limit) { -+ return; -+ } -+ -+ if (static_cast<double>(number_premature_stream_resets_) / -+ closed_non_internally_destroyed_requests_ < -+ .5) { -+ return; -+ } -+ -+ if (drain_state_ == DrainState::NotDraining) { -+ stats_.named_.downstream_rq_too_many_premature_resets_.inc(); -+ doConnectionClose(Network::ConnectionCloseType::Abort, absl::nullopt, -+ "too_many_premature_resets"); -+ } -+} -+ - void ConnectionManagerImpl::onGoAway(GoAwayErrorCode) { - // Currently we do nothing with remote go away frames. In the future we can decide to no longer - // push resources if applicable. -diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h -index b82b1967a5115..dad494c953bc0 100644 ---- a/source/common/http/conn_manager_impl.h -+++ b/source/common/http/conn_manager_impl.h -@@ -115,6 +115,14 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, - void setClearHopByHopResponseHeaders(bool value) { clear_hop_by_hop_response_headers_ = value; } - bool clearHopByHopResponseHeaders() const { return clear_hop_by_hop_response_headers_; } - -+ // This runtime key configures the number of streams which must be closed on a connection before -+ // envoy will potentially drain a connection due to excessive prematurely reset streams. -+ static const absl::string_view PrematureResetTotalStreamCountKey; -+ -+ // The minimum lifetime of a stream, in seconds, in order not to be considered -+ // prematurely closed. -+ static const absl::string_view PrematureResetMinStreamLifetimeSecondsKey; -+ - private: - struct ActiveStream; - class MobileConnectionManagerImpl; -@@ -544,6 +552,15 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, - void doConnectionClose(absl::optional<Network::ConnectionCloseType> close_type, - absl::optional<StreamInfo::ResponseFlag> response_flag, - absl::string_view details); -+ // Returns true if a RST_STREAM for the given stream is premature. Premature -+ // means the RST_STREAM arrived before response headers were sent and than -+ // the stream was alive for short period of time. This period is specified -+ // by the optional runtime value PrematureResetMinStreamLifetimeSecondsKey, -+ // or one second if that is not present. -+ bool isPrematureRstStream(const ActiveStream& stream) const; -+ // Sends a GOAWAY if both sufficient streams have been closed on a connection -+ // and at least half have been prematurely reset? -+ void maybeDrainDueToPrematureResets(); - - enum class DrainState { NotDraining, Draining, Closing }; - -@@ -584,6 +601,12 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, - bool clear_hop_by_hop_response_headers_{true}; - // The number of requests accumulated on the current connection. - uint64_t accumulated_requests_{}; -+ // The number of requests closed on the current connection which were -+ // not internally destroyed -+ uint64_t closed_non_internally_destroyed_requests_{}; -+ // The number of requests that received a premature RST_STREAM, according to -+ // the definition given in `isPrematureRstStream()`. -+ uint64_t number_premature_stream_resets_{0}; - const std::string proxy_name_; // for Proxy-Status. - - const bool refresh_rtt_after_request_{}; -diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc -index 8b75d6bbbfb1c..60d594c964319 100644 ---- a/source/common/runtime/runtime_features.cc -+++ b/source/common/runtime/runtime_features.cc -@@ -91,6 +91,7 @@ RUNTIME_GUARD(envoy_reloadable_features_validate_grpc_header_before_log_grpc_sta - RUNTIME_GUARD(envoy_reloadable_features_validate_upstream_headers); - RUNTIME_GUARD(envoy_restart_features_explicit_wildcard_resource); - RUNTIME_GUARD(envoy_restart_features_remove_runtime_singleton); -+RUNTIME_GUARD(envoy_restart_features_send_goaway_for_premature_rst_streams); - RUNTIME_GUARD(envoy_restart_features_udp_read_normalize_addresses); - RUNTIME_GUARD(envoy_restart_features_use_apple_api_for_dns_lookups); - -diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc -index 190e57065d1c6..b4260ff3fa9ec 100644 ---- a/test/integration/multiplexed_integration_test.cc -+++ b/test/integration/multiplexed_integration_test.cc -@@ -1,4 +1,5 @@ - #include <algorithm> -+#include <chrono> - #include <memory> - #include <string> - -@@ -25,6 +26,7 @@ - #include "test/mocks/http/mocks.h" - #include "test/test_common/network_utility.h" - #include "test/test_common/printers.h" -+#include "test/test_common/simulated_time_system.h" - #include "test/test_common/utility.h" - - #include "gtest/gtest.h" -@@ -92,6 +94,15 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, MultiplexedIntegrationTest, - {Http::CodecType::HTTP1})), - HttpProtocolIntegrationTest::protocolTestParamsToString); - -+class MultiplexedIntegrationTestWithSimulatedTime : public Event::TestUsingSimulatedTime, -+ public MultiplexedIntegrationTest {}; -+ -+INSTANTIATE_TEST_SUITE_P(IpVersions, MultiplexedIntegrationTestWithSimulatedTime, -+ testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( -+ {Http::CodecType::HTTP2, Http::CodecType::HTTP3}, -+ {Http::CodecType::HTTP1})), -+ HttpProtocolIntegrationTest::protocolTestParamsToString); -+ - TEST_P(MultiplexedIntegrationTest, RouterRequestAndResponseWithBodyNoBuffer) { - testRouterRequestAndResponseWithBody(1024, 512, false, false); - } -@@ -1076,6 +1087,67 @@ TEST_P(MultiplexedIntegrationTest, GoAway) { - EXPECT_EQ("200", response->headers().getStatusValue()); - } - -+// TODO(rch): Add a unit test which covers internal redirect handling. -+TEST_P(MultiplexedIntegrationTestWithSimulatedTime, GoAwayAfterTooManyResets) { -+ EXCLUDE_DOWNSTREAM_HTTP3; // Need to wait for the server to reset the stream -+ // before opening new one. -+ config_helper_.addRuntimeOverride("envoy.restart_features.send_goaway_for_premature_rst_streams", -+ "true"); -+ const int total_streams = 100; -+ config_helper_.addRuntimeOverride("overload.premature_reset_total_stream_count", -+ absl::StrCat(total_streams)); -+ initialize(); -+ -+ Http::TestRequestHeaderMapImpl headers{ -+ {":method", "GET"}, {":path", "/healthcheck"}, {":scheme", "http"}, {":authority", "host"}}; -+ codec_client_ = makeHttpConnection(lookupPort("http")); -+ for (int i = 0; i < total_streams; ++i) { -+ auto encoder_decoder = codec_client_->startRequest(headers); -+ request_encoder_ = &encoder_decoder.first; -+ auto response = std::move(encoder_decoder.second); -+ codec_client_->sendReset(*request_encoder_); -+ ASSERT_TRUE(response->waitForReset()); -+ } -+ -+ // Envoy should disconnect client due to premature reset check -+ ASSERT_TRUE(codec_client_->waitForDisconnect()); -+ test_server_->waitForCounterEq("http.config_test.downstream_rq_rx_reset", total_streams); -+ test_server_->waitForCounterEq("http.config_test.downstream_rq_too_many_premature_resets", 1); -+} -+ -+TEST_P(MultiplexedIntegrationTestWithSimulatedTime, DontGoAwayAfterTooManyResetsForLongStreams) { -+ EXCLUDE_DOWNSTREAM_HTTP3; // Need to wait for the server to reset the stream -+ // before opening new one. -+ config_helper_.addRuntimeOverride("envoy.restart_features.send_goaway_for_premature_rst_streams", -+ "true"); -+ const int total_streams = 100; -+ const int stream_lifetime_seconds = 2; -+ config_helper_.addRuntimeOverride("overload.premature_reset_total_stream_count", -+ absl::StrCat(total_streams)); -+ -+ config_helper_.addRuntimeOverride("overload.premature_reset_min_stream_lifetime_seconds", -+ absl::StrCat(stream_lifetime_seconds)); -+ -+ initialize(); -+ -+ Http::TestRequestHeaderMapImpl headers{ -+ {":method", "GET"}, {":path", "/healthcheck"}, {":scheme", "http"}, {":authority", "host"}}; -+ codec_client_ = makeHttpConnection(lookupPort("http")); -+ -+ std::string request_counter = "http.config_test.downstream_rq_total"; -+ std::string reset_counter = "http.config_test.downstream_rq_rx_reset"; -+ for (int i = 0; i < total_streams * 2; ++i) { -+ auto encoder_decoder = codec_client_->startRequest(headers); -+ request_encoder_ = &encoder_decoder.first; -+ auto response = std::move(encoder_decoder.second); -+ test_server_->waitForCounterEq(request_counter, i + 1); -+ timeSystem().advanceTimeWait(std::chrono::seconds(2 * stream_lifetime_seconds)); -+ codec_client_->sendReset(*request_encoder_); -+ ASSERT_TRUE(response->waitForReset()); -+ test_server_->waitForCounterEq(reset_counter, i + 1); -+ } -+} -+ - TEST_P(MultiplexedIntegrationTest, Trailers) { testTrailers(1024, 2048, false, false); } - - TEST_P(MultiplexedIntegrationTest, TrailersGiantBody) { diff --git a/0004-limit-http-requests-per-io-cycle.patch b/0004-limit-http-requests-per-io-cycle.patch deleted file mode 100644 index 702e799513ec..000000000000 --- a/0004-limit-http-requests-per-io-cycle.patch +++ /dev/null @@ -1,821 +0,0 @@ -From 2e4228b0ee73ae640c92e0974c91e251997a3d2f Mon Sep 17 00:00:00 2001 -From: Yan Avlasov <yavlasov@google.com> -Date: Sat, 30 Sep 2023 13:58:46 +0000 -Subject: [PATCH] Limit on the number of HTTP requests processed from a - connection in an I/O cycle - -Signed-off-by: Yan Avlasov <yavlasov@google.com> ---- - changelogs/current.yaml | 7 + - source/common/http/conn_manager_impl.cc | 89 ++++++- - source/common/http/conn_manager_impl.h | 25 +- - test/common/http/conn_manager_impl_test_2.cc | 245 ++++++++++++++++++ - .../http/conn_manager_impl_test_base.cc | 19 ++ - .../common/http/conn_manager_impl_test_base.h | 2 + - test/common/http/http2/http2_frame.cc | 12 +- - test/common/http/http2/http2_frame.h | 14 +- - .../multiplexed_integration_test.cc | 170 ++++++++++++ - 9 files changed, 569 insertions(+), 14 deletions(-) - -diff --git a/changelogs/current.yaml b/changelogs/current.yaml -index b5d45089f337..86d0eac2339f 100644 ---- a/changelogs/current.yaml -+++ b/changelogs/current.yaml -@@ -10,6 +10,13 @@ - resets is applied. The connection is disconnected if more than 50% of resets are premature. - Setting the runtime key ``envoy.restart_features.send_goaway_for_premature_rst_streams`` to ``false`` completely disables - this check. -+- area: http -+ change: | -+ Add runtime flag ``http.max_requests_per_io_cycle`` for setting the limit on the number of HTTP requests processed -+ from a single connection in a single I/O cycle. Requests over this limit are processed in subsequent I/O cycles. This -+ mitigates CPU starvation by connections that simultaneously send high number of requests by allowing requests from other -+ connections to make progress. This runtime value can be set to 1 in the presence of abusive HTTP/2 or HTTP/3 connections. -+ By default this limit is disabled. - - area: build - change: | - Moved the subset, ring_hash, and maglev LB code into extensions. If you use these load balancers and override -diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc -index 021c55bc4cca..bf0439708413 100644 ---- a/source/common/http/conn_manager_impl.cc -+++ b/source/common/http/conn_manager_impl.cc -@@ -60,6 +60,10 @@ const absl::string_view ConnectionManagerImpl::PrematureResetTotalStreamCountKey - "overload.premature_reset_total_stream_count"; - const absl::string_view ConnectionManagerImpl::PrematureResetMinStreamLifetimeSecondsKey = - "overload.premature_reset_min_stream_lifetime_seconds"; -+// Runtime key for maximum number of requests that can be processed from a single connection per -+// I/O cycle. Requests over this limit are deferred until the next I/O cycle. -+const absl::string_view ConnectionManagerImpl::MaxRequestsPerIoCycle = -+ "http.max_requests_per_io_cycle"; - - bool requestWasConnect(const RequestHeaderMapSharedPtr& headers, Protocol protocol) { - if (!headers) { -@@ -116,6 +120,8 @@ ConnectionManagerImpl::ConnectionManagerImpl(ConnectionManagerConfig& config, - /*node_id=*/local_info_.node().id(), - /*server_name=*/config_.serverName(), - /*proxy_status_config=*/config_.proxyStatusConfig())), -+ max_requests_during_dispatch_( -+ runtime_.snapshot().getInteger(ConnectionManagerImpl::MaxRequestsPerIoCycle, UINT32_MAX)), - refresh_rtt_after_request_( - Runtime::runtimeFeatureEnabled("envoy.reloadable_features.refresh_rtt_after_request")) {} - -@@ -128,6 +134,10 @@ const ResponseHeaderMap& ConnectionManagerImpl::continueHeader() { - void ConnectionManagerImpl::initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) { - read_callbacks_ = &callbacks; - dispatcher_ = &callbacks.connection().dispatcher(); -+ if (max_requests_during_dispatch_ != UINT32_MAX) { -+ deferred_request_processing_callback_ = -+ dispatcher_->createSchedulableCallback([this]() -> void { onDeferredRequestProcessing(); }); -+ } - - stats_.named_.downstream_cx_total_.inc(); - stats_.named_.downstream_cx_active_.inc(); -@@ -454,6 +464,7 @@ void ConnectionManagerImpl::createCodec(Buffer::Instance& data) { - } - - Network::FilterStatus ConnectionManagerImpl::onData(Buffer::Instance& data, bool) { -+ requests_during_dispatch_count_ = 0; - if (!codec_) { - // Http3 codec should have been instantiated by now. - createCodec(data); -@@ -1366,7 +1377,12 @@ void ConnectionManagerImpl::ActiveStream::decodeHeaders(RequestHeaderMapSharedPt - traceRequest(); - } - -- filter_manager_.decodeHeaders(*request_headers_, end_stream); -+ if (!connection_manager_.shouldDeferRequestProxyingToNextIoCycle()) { -+ filter_manager_.decodeHeaders(*request_headers_, end_stream); -+ } else { -+ state_.deferred_to_next_io_iteration_ = true; -+ state_.deferred_end_stream_ = end_stream; -+ } - - // Reset it here for both global and overridden cases. - resetIdleTimer(); -@@ -1433,8 +1449,15 @@ void ConnectionManagerImpl::ActiveStream::decodeData(Buffer::Instance& data, boo - connection_manager_.read_callbacks_->connection().dispatcher()); - maybeEndDecode(end_stream); - filter_manager_.streamInfo().addBytesReceived(data.length()); -- -- filter_manager_.decodeData(data, end_stream); -+ if (!state_.deferred_to_next_io_iteration_) { -+ filter_manager_.decodeData(data, end_stream); -+ } else { -+ if (!deferred_data_) { -+ deferred_data_ = std::make_unique<Buffer::OwnedImpl>(); -+ } -+ deferred_data_->move(data); -+ state_.deferred_end_stream_ = end_stream; -+ } - } - - void ConnectionManagerImpl::ActiveStream::decodeTrailers(RequestTrailerMapPtr&& trailers) { -@@ -1450,7 +1473,9 @@ void ConnectionManagerImpl::ActiveStream::decodeTrailers(RequestTrailerMapPtr&& - return; - } - maybeEndDecode(true); -- filter_manager_.decodeTrailers(*request_trailers_); -+ if (!state_.deferred_to_next_io_iteration_) { -+ filter_manager_.decodeTrailers(*request_trailers_); -+ } - } - - void ConnectionManagerImpl::ActiveStream::decodeMetadata(MetadataMapPtr&& metadata_map) { -@@ -2158,5 +2183,61 @@ void ConnectionManagerImpl::ActiveStream::resetStream(Http::StreamResetReason, a - connection_manager_.doEndStream(*this); - } - -+bool ConnectionManagerImpl::ActiveStream::onDeferredRequestProcessing() { -+ // TODO(yanavlasov): Merge this with the filter manager continueIteration() method -+ if (!state_.deferred_to_next_io_iteration_) { -+ return false; -+ } -+ state_.deferred_to_next_io_iteration_ = false; -+ bool end_stream = -+ state_.deferred_end_stream_ && deferred_data_ == nullptr && request_trailers_ == nullptr; -+ filter_manager_.decodeHeaders(*request_headers_, end_stream); -+ if (end_stream) { -+ return true; -+ } -+ if (deferred_data_ != nullptr) { -+ end_stream = state_.deferred_end_stream_ && request_trailers_ == nullptr; -+ filter_manager_.decodeData(*deferred_data_, end_stream); -+ } -+ if (request_trailers_ != nullptr) { -+ filter_manager_.decodeTrailers(*request_trailers_); -+ } -+ return true; -+} -+ -+bool ConnectionManagerImpl::shouldDeferRequestProxyingToNextIoCycle() { -+ // Do not defer this stream if stream deferral is disabled -+ if (deferred_request_processing_callback_ == nullptr) { -+ return false; -+ } -+ // Defer this stream if there are already deferred streams, so they are not -+ // processed out of order -+ if (deferred_request_processing_callback_->enabled()) { -+ return true; -+ } -+ ++requests_during_dispatch_count_; -+ bool defer = requests_during_dispatch_count_ > max_requests_during_dispatch_; -+ if (defer) { -+ deferred_request_processing_callback_->scheduleCallbackNextIteration(); -+ } -+ return defer; -+} -+ -+void ConnectionManagerImpl::onDeferredRequestProcessing() { -+ requests_during_dispatch_count_ = 1; // 1 stream is always let through -+ // Streams are inserted at the head of the list. As such process deferred -+ // streams at the back of the list first. -+ for (auto reverse_iter = streams_.rbegin(); reverse_iter != streams_.rend();) { -+ auto& stream_ptr = *reverse_iter; -+ // Move the iterator to the next item in case the `onDeferredRequestProcessing` call removes the -+ // stream from the list. -+ ++reverse_iter; -+ bool was_deferred = stream_ptr->onDeferredRequestProcessing(); -+ if (was_deferred && shouldDeferRequestProxyingToNextIoCycle()) { -+ break; -+ } -+ } -+} -+ - } // namespace Http - } // namespace Envoy -diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h -index dad494c953bc..e79a6a81c082 100644 ---- a/source/common/http/conn_manager_impl.h -+++ b/source/common/http/conn_manager_impl.h -@@ -122,6 +122,7 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, - // The minimum lifetime of a stream, in seconds, in order not to be considered - // prematurely closed. - static const absl::string_view PrematureResetMinStreamLifetimeSecondsKey; -+ static const absl::string_view MaxRequestsPerIoCycle; - - private: - struct ActiveStream; -@@ -345,7 +346,8 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, - : codec_saw_local_complete_(false), codec_encode_complete_(false), - on_reset_stream_called_(false), is_zombie_stream_(false), saw_connection_close_(false), - successful_upgrade_(false), is_internally_destroyed_(false), -- is_internally_created_(false), is_tunneling_(false), decorated_propagate_(true) {} -+ is_internally_created_(false), is_tunneling_(false), decorated_propagate_(true), -+ deferred_to_next_io_iteration_(false) {} - - // It's possibly for the codec to see the completed response but not fully - // encode it. -@@ -371,6 +373,14 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, - bool is_tunneling_ : 1; - - bool decorated_propagate_ : 1; -+ -+ // Indicates that sending headers to the filter manager is deferred to the -+ // next I/O cycle. If data or trailers are received when this flag is set -+ // they are deferred too. -+ // TODO(yanavlasov): encapsulate the entire state of deferred streams into a separate -+ // structure, so it can be atomically created and cleared. -+ bool deferred_to_next_io_iteration_ : 1; -+ bool deferred_end_stream_ : 1; - }; - - bool canDestroyStream() const { -@@ -418,6 +428,11 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, - // HTTP connection manager configuration, then the entire connection is closed. - bool validateTrailers(); - -+ // Dispatch deferred headers, body and trailers to the filter manager. -+ // Return true if this stream was deferred and dispatched pending headers, body and trailers (if -+ // present). Return false if this stream was not deferred. -+ bool onDeferredRequestProcessing(); -+ - ConnectionManagerImpl& connection_manager_; - OptRef<const TracingConnectionManagerConfig> connection_manager_tracing_config_; - // TODO(snowp): It might make sense to move this to the FilterManager to avoid storing it in -@@ -504,6 +519,8 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, - const Tracing::CustomTagMap* customTags() const override; - bool verbose() const override; - uint32_t maxPathTagLength() const override; -+ -+ std::unique_ptr<Buffer::OwnedImpl> deferred_data_; - }; - - using ActiveStreamPtr = std::unique_ptr<ActiveStream>; -@@ -562,6 +579,9 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, - // and at least half have been prematurely reset? - void maybeDrainDueToPrematureResets(); - -+ bool shouldDeferRequestProxyingToNextIoCycle(); -+ void onDeferredRequestProcessing(); -+ - enum class DrainState { NotDraining, Draining, Closing }; - - ConnectionManagerConfig& config_; -@@ -608,6 +628,9 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>, - // the definition given in `isPrematureRstStream()`. - uint64_t number_premature_stream_resets_{0}; - const std::string proxy_name_; // for Proxy-Status. -+ uint32_t requests_during_dispatch_count_{0}; -+ const uint32_t max_requests_during_dispatch_{UINT32_MAX}; -+ Event::SchedulableCallbackPtr deferred_request_processing_callback_; - - const bool refresh_rtt_after_request_{}; - }; -diff --git a/test/common/http/conn_manager_impl_test_2.cc b/test/common/http/conn_manager_impl_test_2.cc -index 5daf2b1a45e6..078bd1d85ab7 100644 ---- a/test/common/http/conn_manager_impl_test_2.cc -+++ b/test/common/http/conn_manager_impl_test_2.cc -@@ -11,6 +11,7 @@ using testing::InvokeWithoutArgs; - using testing::Mock; - using testing::Ref; - using testing::Return; -+using testing::ReturnArg; - using testing::ReturnRef; - - namespace Envoy { -@@ -3767,5 +3768,249 @@ TEST_F(HttpConnectionManagerImplTest, NoProxyProtocolAdded) { - // Clean up. - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); - } -+ -+// Validate that deferred streams are processed with a variety of -+// headers, data and trailer arriving in the same I/O cycle -+TEST_F(HttpConnectionManagerImplTest, LimitWorkPerIOCycle) { -+ const int kRequestsSentPerIOCycle = 100; -+ EXPECT_CALL(runtime_.snapshot_, getInteger(_, _)).WillRepeatedly(ReturnArg<1>()); -+ // Process 1 request per I/O cycle -+ auto* deferred_request_callback = enableStreamsPerIoLimit(1); -+ setup(false, ""); -+ -+ // Store the basic request encoder during filter chain setup. -+ std::vector<std::shared_ptr<MockStreamDecoderFilter>> encoder_filters; -+ int decode_headers_call_count = 0; -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ std::shared_ptr<MockStreamDecoderFilter> filter(new NiceMock<MockStreamDecoderFilter>()); -+ -+ // Each 4th request is headers only -+ EXPECT_CALL(*filter, decodeHeaders(_, i % 4 == 0 ? true : false)) -+ .WillRepeatedly(Invoke([&](RequestHeaderMap&, bool) -> FilterHeadersStatus { -+ ++decode_headers_call_count; -+ return FilterHeadersStatus::StopIteration; -+ })); -+ -+ // Each 1st request is headers and data only -+ // Each 2nd request is headers, data and trailers -+ if (i % 4 == 1 || i % 4 == 2) { -+ EXPECT_CALL(*filter, decodeData(_, i % 4 == 1 ? true : false)) -+ .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); -+ } -+ -+ // Each 3rd request is headers and trailers (no data) -+ if (i % 4 == 2 || i % 4 == 3) { -+ EXPECT_CALL(*filter, decodeTrailers(_)).WillOnce(Return(FilterTrailersStatus::StopIteration)); -+ } -+ -+ EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); -+ encoder_filters.push_back(std::move(filter)); -+ } -+ -+ uint64_t random_value = 0; -+ EXPECT_CALL(random_, random()).WillRepeatedly(Invoke([&random_value]() { -+ return random_value++; -+ })); -+ -+ EXPECT_CALL(filter_factory_, createFilterChain(_)) -+ .Times(kRequestsSentPerIOCycle) -+ .WillRepeatedly(Invoke([&encoder_filters](FilterChainManager& manager) -> bool { -+ static int index = 0; -+ int i = index++; -+ FilterFactoryCb factory([&encoder_filters, i](FilterChainFactoryCallbacks& callbacks) { -+ callbacks.addStreamDecoderFilter(encoder_filters[i]); -+ }); -+ manager.applyFilterFactoryCb({}, factory); -+ return true; -+ })); -+ -+ EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)) -+ .Times(kRequestsSentPerIOCycle); -+ -+ std::vector<NiceMock<MockResponseEncoder>> response_encoders(kRequestsSentPerIOCycle); -+ for (auto& encoder : response_encoders) { -+ EXPECT_CALL(encoder, getStream()).WillRepeatedly(ReturnRef(encoder.stream_)); -+ } -+ -+ EXPECT_CALL(*codec_, dispatch(_)) -+ .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ decoder_ = &conn_manager_->newStream(response_encoders[i]); -+ -+ RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ -+ {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; -+ -+ RequestTrailerMapPtr trailers{ -+ new TestRequestTrailerMapImpl{{"key1", "value1"}, {"key2", "value2"}}}; -+ -+ Buffer::OwnedImpl data("data"); -+ -+ switch (i % 4) { -+ case 0: -+ decoder_->decodeHeaders(std::move(headers), true); -+ break; -+ case 1: -+ decoder_->decodeHeaders(std::move(headers), false); -+ decoder_->decodeData(data, true); -+ break; -+ case 2: -+ decoder_->decodeHeaders(std::move(headers), false); -+ decoder_->decodeData(data, false); -+ decoder_->decodeTrailers(std::move(trailers)); -+ break; -+ case 3: -+ decoder_->decodeHeaders(std::move(headers), false); -+ decoder_->decodeTrailers(std::move(trailers)); -+ break; -+ } -+ } -+ -+ data.drain(4); -+ return Http::okStatus(); -+ })); -+ -+ // Kick off the incoming data. -+ Buffer::OwnedImpl fake_input("1234"); -+ conn_manager_->onData(fake_input, false); -+ -+ EXPECT_TRUE(deferred_request_callback->enabled_); -+ // Only one request should go through the filter chain -+ ASSERT_EQ(decode_headers_call_count, 1); -+ -+ // Let other requests to go through the filter chain. Call expectations will fail -+ // if this is not the case. -+ int deferred_request_count = 0; -+ while (deferred_request_callback->enabled_) { -+ deferred_request_callback->invokeCallback(); -+ ++deferred_request_count; -+ } -+ -+ ASSERT_EQ(deferred_request_count, kRequestsSentPerIOCycle); -+ -+ for (auto& filter : encoder_filters) { -+ ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; -+ filter->callbacks_->streamInfo().setResponseCodeDetails(""); -+ filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); -+ } -+ -+ EXPECT_EQ(kRequestsSentPerIOCycle, stats_.named_.downstream_rq_2xx_.value()); -+ EXPECT_EQ(kRequestsSentPerIOCycle, listener_stats_.downstream_rq_2xx_.value()); -+ EXPECT_EQ(kRequestsSentPerIOCycle, stats_.named_.downstream_rq_completed_.value()); -+ EXPECT_EQ(kRequestsSentPerIOCycle, listener_stats_.downstream_rq_completed_.value()); -+} -+ -+TEST_F(HttpConnectionManagerImplTest, StreamDeferralPreservesOrder) { -+ EXPECT_CALL(runtime_.snapshot_, getInteger(_, _)).WillRepeatedly(ReturnArg<1>()); -+ // Process 1 request per I/O cycle -+ auto* deferred_request_callback = enableStreamsPerIoLimit(1); -+ setup(false, ""); -+ -+ std::vector<std::shared_ptr<MockStreamDecoderFilter>> encoder_filters; -+ int expected_request_id = 0; -+ const Http::LowerCaseString request_id_header(absl::string_view("request-id")); -+ // Two requests are processed in 2 I/O reads -+ const int TotalRequests = 2 * 2; -+ for (int i = 0; i < TotalRequests; ++i) { -+ std::shared_ptr<MockStreamDecoderFilter> filter(new NiceMock<MockStreamDecoderFilter>()); -+ -+ EXPECT_CALL(*filter, decodeHeaders(_, true)) -+ .WillRepeatedly(Invoke([&](RequestHeaderMap& headers, bool) -> FilterHeadersStatus { -+ // Check that requests are decoded in expected order -+ int request_id = 0; -+ ASSERT(absl::SimpleAtoi(headers.get(request_id_header)[0]->value().getStringView(), -+ &request_id)); -+ ASSERT(request_id == expected_request_id); -+ ++expected_request_id; -+ return FilterHeadersStatus::StopIteration; -+ })); -+ -+ EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); -+ encoder_filters.push_back(std::move(filter)); -+ } -+ -+ uint64_t random_value = 0; -+ EXPECT_CALL(random_, random()).WillRepeatedly(Invoke([&random_value]() { -+ return random_value++; -+ })); -+ -+ EXPECT_CALL(filter_factory_, createFilterChain(_)) -+ .Times(TotalRequests) -+ .WillRepeatedly(Invoke([&encoder_filters](FilterChainManager& manager) -> bool { -+ static int index = 0; -+ int i = index++; -+ FilterFactoryCb factory([&encoder_filters, i](FilterChainFactoryCallbacks& callbacks) { -+ callbacks.addStreamDecoderFilter(encoder_filters[i]); -+ }); -+ manager.applyFilterFactoryCb({}, factory); -+ return true; -+ })); -+ -+ EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(TotalRequests); -+ -+ std::vector<NiceMock<MockResponseEncoder>> response_encoders(TotalRequests); -+ for (auto& encoder : response_encoders) { -+ EXPECT_CALL(encoder, getStream()).WillRepeatedly(ReturnRef(encoder.stream_)); -+ } -+ auto response_encoders_iter = response_encoders.begin(); -+ -+ int request_id = 0; -+ EXPECT_CALL(*codec_, dispatch(_)) -+ .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { -+ // The second request should be deferred -+ for (int i = 0; i < 2; ++i) { -+ decoder_ = &conn_manager_->newStream(*response_encoders_iter); -+ ++response_encoders_iter; -+ -+ RequestHeaderMapPtr headers{ -+ new TestRequestHeaderMapImpl{{":authority", "host"}, -+ {":path", "/"}, -+ {":method", "GET"}, -+ {"request-id", absl::StrCat(request_id)}}}; -+ -+ ++request_id; -+ decoder_->decodeHeaders(std::move(headers), true); -+ } -+ -+ data.drain(4); -+ return Http::okStatus(); -+ })); -+ -+ // Kick off the incoming data. -+ Buffer::OwnedImpl fake_input("1234"); -+ conn_manager_->onData(fake_input, false); -+ -+ EXPECT_TRUE(deferred_request_callback->enabled_); -+ // Only one request should go through the filter chain -+ ASSERT_EQ(expected_request_id, 1); -+ -+ // Test arrival of another request. New request is read from the socket before deferred callbacks. -+ Buffer::OwnedImpl fake_input2("1234"); -+ conn_manager_->onData(fake_input2, false); -+ -+ // No requests from the second read should go through as there are deferred stream present -+ ASSERT_EQ(expected_request_id, 1); -+ -+ // Let other requests to go through the filter chain. Call expectations will fail -+ // if this is not the case. -+ int deferred_request_count = 0; -+ while (deferred_request_callback->enabled_) { -+ deferred_request_callback->invokeCallback(); -+ ++deferred_request_count; -+ } -+ -+ ASSERT_EQ(deferred_request_count, TotalRequests); -+ -+ for (auto& filter : encoder_filters) { -+ ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; -+ filter->callbacks_->streamInfo().setResponseCodeDetails(""); -+ filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); -+ } -+ -+ EXPECT_EQ(TotalRequests, stats_.named_.downstream_rq_2xx_.value()); -+ EXPECT_EQ(TotalRequests, listener_stats_.downstream_rq_2xx_.value()); -+ EXPECT_EQ(TotalRequests, stats_.named_.downstream_rq_completed_.value()); -+ EXPECT_EQ(TotalRequests, listener_stats_.downstream_rq_completed_.value()); -+} -+ - } // namespace Http - } // namespace Envoy -diff --git a/test/common/http/conn_manager_impl_test_base.cc b/test/common/http/conn_manager_impl_test_base.cc -index 1c5053246e44..641ca6fae082 100644 ---- a/test/common/http/conn_manager_impl_test_base.cc -+++ b/test/common/http/conn_manager_impl_test_base.cc -@@ -78,6 +78,7 @@ void HttpConnectionManagerImplMixin::setup(bool ssl, const std::string& server_n - conn_manager_ = std::make_unique<ConnectionManagerImpl>( - *this, drain_close_, random_, http_context_, runtime_, local_info_, cluster_manager_, - overload_manager_, test_time_.timeSystem()); -+ - conn_manager_->initializeReadFilterCallbacks(filter_callbacks_); - - if (tracing) { -@@ -370,5 +371,23 @@ void HttpConnectionManagerImplMixin::expectUhvTrailerCheck( - })); - } - -+Event::MockSchedulableCallback* -+HttpConnectionManagerImplMixin::enableStreamsPerIoLimit(uint32_t limit) { -+ EXPECT_CALL(runtime_.snapshot_, getInteger("http.max_requests_per_io_cycle", _)) -+ .WillOnce(Return(limit)); -+ -+ // Expect HCM to create and set schedulable callback -+ auto* deferred_request_callback = -+ new Event::MockSchedulableCallback(&filter_callbacks_.connection_.dispatcher_); -+ EXPECT_CALL(*deferred_request_callback, enabled()) -+ .WillRepeatedly( -+ Invoke([deferred_request_callback]() { return deferred_request_callback->enabled_; })); -+ EXPECT_CALL(*deferred_request_callback, scheduleCallbackNextIteration()) -+ .WillRepeatedly( -+ Invoke([deferred_request_callback]() { deferred_request_callback->enabled_ = true; })); -+ -+ return deferred_request_callback; -+} -+ - } // namespace Http - } // namespace Envoy -diff --git a/test/common/http/conn_manager_impl_test_base.h b/test/common/http/conn_manager_impl_test_base.h -index 99fdc9344118..10a4ff15b54a 100644 ---- a/test/common/http/conn_manager_impl_test_base.h -+++ b/test/common/http/conn_manager_impl_test_base.h -@@ -202,6 +202,8 @@ class HttpConnectionManagerImplMixin : public ConnectionManagerConfig { - HeaderValidator::TransformationResult transformation_result, - bool expect_response = true); - -+ Event::MockSchedulableCallback* enableStreamsPerIoLimit(uint32_t limit); -+ - Envoy::Event::SimulatedTimeSystem test_time_; - NiceMock<Router::MockRouteConfigProvider> route_config_provider_; - std::shared_ptr<Router::MockConfig> route_config_{new NiceMock<Router::MockConfig>()}; -diff --git a/test/common/http/http2/http2_frame.cc b/test/common/http/http2/http2_frame.cc -index c5172938a804..46ba3751ba24 100644 ---- a/test/common/http/http2/http2_frame.cc -+++ b/test/common/http/http2/http2_frame.cc -@@ -341,7 +341,11 @@ Http2Frame Http2Frame::makeRequest(uint32_t stream_index, absl::string_view host - makeNetworkOrderStreamId(stream_index)); - frame.appendStaticHeader(StaticHeaderIndex::MethodGet); - frame.appendStaticHeader(StaticHeaderIndex::SchemeHttps); -- frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Path, path); -+ if (path.empty() || path == "/") { -+ frame.appendStaticHeader(StaticHeaderIndex::Path); -+ } else { -+ frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Path, path); -+ } - frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Authority, host); - frame.adjustPayloadSize(); - return frame; -@@ -365,7 +369,11 @@ Http2Frame Http2Frame::makePostRequest(uint32_t stream_index, absl::string_view - makeNetworkOrderStreamId(stream_index)); - frame.appendStaticHeader(StaticHeaderIndex::MethodPost); - frame.appendStaticHeader(StaticHeaderIndex::SchemeHttps); -- frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Path, path); -+ if (path.empty() || path == "/") { -+ frame.appendStaticHeader(StaticHeaderIndex::Path); -+ } else { -+ frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Path, path); -+ } - frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Authority, host); - frame.adjustPayloadSize(); - return frame; -diff --git a/test/common/http/http2/http2_frame.h b/test/common/http/http2/http2_frame.h -index 7fdf510ea256..5df3375c1661 100644 ---- a/test/common/http/http2/http2_frame.h -+++ b/test/common/http/http2/http2_frame.h -@@ -253,6 +253,13 @@ class Http2Frame { - ConstIterator end() const { return data_.end(); } - bool empty() const { return data_.empty(); } - -+ void appendHeaderWithoutIndexing(const Header& header); -+ // This method updates payload length in the HTTP2 header based on the size of the data_ -+ void adjustPayloadSize() { -+ ASSERT(size() >= HeaderSize); -+ setPayloadSize(size() - HeaderSize); -+ } -+ - private: - void buildHeader(Type type, uint32_t payload_size = 0, uint8_t flags = 0, uint32_t stream_id = 0); - void setPayloadSize(uint32_t size); -@@ -272,15 +279,8 @@ class Http2Frame { - // Headers are directly encoded - void appendStaticHeader(StaticHeaderIndex index); - void appendHeaderWithoutIndexing(StaticHeaderIndex index, absl::string_view value); -- void appendHeaderWithoutIndexing(const Header& header); - void appendEmptyHeader(); - -- // This method updates payload length in the HTTP2 header based on the size of the data_ -- void adjustPayloadSize() { -- ASSERT(size() >= HeaderSize); -- setPayloadSize(size() - HeaderSize); -- } -- - DataContainer data_; - }; - -diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc -index b4260ff3fa9e..edce9a1838d9 100644 ---- a/test/integration/multiplexed_integration_test.cc -+++ b/test/integration/multiplexed_integration_test.cc -@@ -2130,6 +2130,176 @@ TEST_P(Http2FrameIntegrationTest, AccessLogOfWireBytesIfResponseSizeGreaterThanW - tcp_client_->close(); - } - -+TEST_P(Http2FrameIntegrationTest, MultipleHeaderOnlyRequests) { -+ const int kRequestsSentPerIOCycle = 20; -+ autonomous_upstream_ = true; -+ config_helper_.addRuntimeOverride("http.max_requests_per_io_cycle", "1"); -+ beginSession(); -+ -+ std::string buffer; -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto request = Http2Frame::makeRequest(Http2Frame::makeClientStreamId(i), "a", "/", -+ {{"response_data_blocks", "0"}, {"no_trailers", "1"}}); -+ absl::StrAppend(&buffer, std::string(request)); -+ } -+ -+ ASSERT_TRUE(tcp_client_->write(buffer, false, false)); -+ -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto frame = readFrame(); -+ EXPECT_EQ(Http2Frame::Type::Headers, frame.type()); -+ EXPECT_EQ(Http2Frame::ResponseStatus::Ok, frame.responseStatus()); -+ } -+ tcp_client_->close(); -+} -+ -+TEST_P(Http2FrameIntegrationTest, MultipleRequests) { -+ const int kRequestsSentPerIOCycle = 20; -+ autonomous_upstream_ = true; -+ config_helper_.addRuntimeOverride("http.max_requests_per_io_cycle", "1"); -+ beginSession(); -+ -+ std::string buffer; -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto request = -+ Http2Frame::makePostRequest(Http2Frame::makeClientStreamId(i), "a", "/", -+ {{"response_data_blocks", "0"}, {"no_trailers", "1"}}); -+ absl::StrAppend(&buffer, std::string(request)); -+ } -+ -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto data = Http2Frame::makeDataFrame(Http2Frame::makeClientStreamId(i), "a", -+ Http2Frame::DataFlags::EndStream); -+ absl::StrAppend(&buffer, std::string(data)); -+ } -+ -+ ASSERT_TRUE(tcp_client_->write(buffer, false, false)); -+ -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto frame = readFrame(); -+ EXPECT_EQ(Http2Frame::Type::Headers, frame.type()); -+ EXPECT_EQ(Http2Frame::ResponseStatus::Ok, frame.responseStatus()); -+ } -+ tcp_client_->close(); -+} -+ -+TEST_P(Http2FrameIntegrationTest, MultipleRequestsWithTrailers) { -+ const int kRequestsSentPerIOCycle = 20; -+ autonomous_upstream_ = true; -+ config_helper_.addRuntimeOverride("http.max_requests_per_io_cycle", "1"); -+ beginSession(); -+ -+ std::string buffer; -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto request = -+ Http2Frame::makePostRequest(Http2Frame::makeClientStreamId(i), "a", "/", -+ {{"response_data_blocks", "0"}, {"no_trailers", "1"}}); -+ absl::StrAppend(&buffer, std::string(request)); -+ } -+ -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto data = Http2Frame::makeDataFrame(Http2Frame::makeClientStreamId(i), "a"); -+ absl::StrAppend(&buffer, std::string(data)); -+ } -+ -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto trailers = Http2Frame::makeEmptyHeadersFrame( -+ Http2Frame::makeClientStreamId(i), -+ static_cast<Http2Frame::HeadersFlags>(Http::Http2::orFlags( -+ Http2Frame::HeadersFlags::EndStream, Http2Frame::HeadersFlags::EndHeaders))); -+ trailers.appendHeaderWithoutIndexing({"k", "v"}); -+ trailers.adjustPayloadSize(); -+ absl::StrAppend(&buffer, std::string(trailers)); -+ } -+ -+ ASSERT_TRUE(tcp_client_->write(buffer, false, false)); -+ -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto frame = readFrame(); -+ EXPECT_EQ(Http2Frame::Type::Headers, frame.type()); -+ EXPECT_EQ(Http2Frame::ResponseStatus::Ok, frame.responseStatus()); -+ } -+ tcp_client_->close(); -+} -+ -+TEST_P(Http2FrameIntegrationTest, MultipleHeaderOnlyRequestsFollowedByReset) { -+ // This number of requests stays below premature reset detection. -+ const int kRequestsSentPerIOCycle = 20; -+ config_helper_.addRuntimeOverride("http.max_requests_per_io_cycle", "1"); -+ beginSession(); -+ -+ std::string buffer; -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto request = Http2Frame::makeRequest(Http2Frame::makeClientStreamId(i), "a", "/", -+ {{"response_data_blocks", "0"}, {"no_trailers", "1"}}); -+ absl::StrAppend(&buffer, std::string(request)); -+ } -+ -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto reset = Http2Frame::makeResetStreamFrame(Http2Frame::makeClientStreamId(i), -+ Http2Frame::ErrorCode::Cancel); -+ absl::StrAppend(&buffer, std::string(reset)); -+ } -+ -+ ASSERT_TRUE(tcp_client_->write(buffer, false, false)); -+ test_server_->waitForCounterEq("http.config_test.downstream_rq_rx_reset", -+ kRequestsSentPerIOCycle); -+ // Client should remain connected -+ ASSERT_TRUE(tcp_client_->connected()); -+ tcp_client_->close(); -+} -+ -+TEST_P(Http2FrameIntegrationTest, ResettingDeferredRequestsTriggersPrematureResetCheck) { -+ const int kRequestsSentPerIOCycle = 20; -+ // Set premature stream count to the number of streams we are about to send -+ config_helper_.addRuntimeOverride("overload.premature_reset_total_stream_count", "20"); -+ config_helper_.addRuntimeOverride("http.max_requests_per_io_cycle", "1"); -+ beginSession(); -+ -+ std::string buffer; -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto request = Http2Frame::makeRequest(Http2Frame::makeClientStreamId(i), "a", "/", -+ {{"response_data_blocks", "0"}, {"no_trailers", "1"}}); -+ absl::StrAppend(&buffer, std::string(request)); -+ } -+ -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto reset = Http2Frame::makeResetStreamFrame(Http2Frame::makeClientStreamId(i), -+ Http2Frame::ErrorCode::Cancel); -+ absl::StrAppend(&buffer, std::string(reset)); -+ } -+ -+ ASSERT_TRUE(tcp_client_->write(buffer, false, false)); -+ // Envoy should close the client connection due to too many premature resets -+ tcp_client_->waitForDisconnect(); -+ test_server_->waitForCounterEq("http.config_test.downstream_rq_too_many_premature_resets", 1); -+} -+ -+TEST_P(Http2FrameIntegrationTest, CloseConnectionWithDeferredStreams) { -+ // Use large number of requests to ensure close is detected while there are -+ // still some deferred streams. -+ const int kRequestsSentPerIOCycle = 1000; -+ config_helper_.addRuntimeOverride("http.max_requests_per_io_cycle", "1"); -+ // Ensure premature reset detection does not get in the way -+ config_helper_.addRuntimeOverride("overload.premature_reset_total_stream_count", "1001"); -+ beginSession(); -+ -+ std::string buffer; -+ for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { -+ auto request = Http2Frame::makeRequest(Http2Frame::makeClientStreamId(i), "a", "/", -+ {{"response_data_blocks", "0"}, {"no_trailers", "1"}}); -+ absl::StrAppend(&buffer, std::string(request)); -+ } -+ -+ ASSERT_TRUE(tcp_client_->write(buffer, false, false)); -+ ASSERT_TRUE(tcp_client_->connected()); -+ // Drop the downstream connection -+ tcp_client_->close(); -+ // Test that Envoy can clean-up deferred streams -+ test_server_->waitForCounterEq("http.config_test.downstream_rq_rx_reset", -+ kRequestsSentPerIOCycle); -+} -+ - INSTANTIATE_TEST_SUITE_P(IpVersions, Http2FrameIntegrationTest, - testing::ValuesIn(Http2FrameIntegrationTest::testParams()), - frameIntegrationTestParamToString); ---- envoy-1.27.0.a/changelogs/current.yaml 2023-10-10 11:36:11.553031312 -0400 -+++ envoy-1.27.0.b/changelogs/current.yaml 2023-10-10 11:37:56.601045565 -0400 @@ -4,7 +4,7 @@ # Contributor: Dan Fuhry <dan@fuhry.com> pkgname=envoyproxy -pkgver=1.29.2 +pkgver=1.30.1 pkgrel=1 pkgdesc="A high performance, open source, general RPC framework that puts mobile and HTTP/2 first." arch=('i686' 'x86_64') @@ -14,6 +14,7 @@ makedepends=( 'cmake' 'clang' 'git' + 'libc++' 'lld' 'go' 'java-environment-openjdk' @@ -26,11 +27,11 @@ makedepends=( source=( "https://github.com/$pkgname/envoy/archive/v$pkgver.tar.gz" 0001-arch-dynamically-link-libpsl.patch - 0002-patch-cel-cpp-to-not-break-build.patch + 0002-pkgconfig-builtin-glib-int-conversion.patch ) -sha512sums=('40db5d45e8620680a0aece41de7bf8d09ac81d0bf339a77a3d70baca61823c4aa4e8e951a2c57f417ebe4fecda8acf17edda593292170b7c5f39e5b753f4579a' - '82a695c4c5ebd3f154acd424e00c5b9be27d809ed895870650b1ea0d1a757104753c242b67718bb68eef128d507668af2a5c7b101a64c346f27a9d0d5ad33cb9' - '6306c91abe482533dbc11aa4c39b0fc0ca145893c6692d56d5ecc77a8bf195e9495151c417ffa5d0702b85c0ad8a96133e1ec8fb01d04b1cdf5db9d71f51216f') +sha512sums=('a1fec2eecf8da64f2cd11ad8fca825d436fe88b10b61d170d87d07dae56699da5aed7f8246aa43f6fd8f7c9644426f4a3b71578f4bb77bf6dd60229d03eb11c0' + '2b83157f8e8976e12c5e09c13aafe0da1ce478c448b07a070890a95d77fa9b396314d5886babf81e5c6fbf77338a49b45e2484a01f4ed0bb8868b45e425e7062' + 'd90b9cbebc2a5d0d6a47e3f7cb88561d7233e5d7fb1d6a0cbe52a75b403f12f5083cc7c6ef94e8846a75b53fbcc350e7309477a495701c6a3e4fd1c3b49f7a7f') prepare() { cd "envoy-$pkgver" @@ -62,24 +63,7 @@ build() { $BAZELISK build \ --workspace_status_command bazel/get_workspace_status \ - --config=clang --verbose_failures -c opt \ - --features=-default_compile_flags \ - --copt=-g \ - --copt=-fstack-protector \ - --copt=-fcolor-diagnostics \ - --copt=-D_FORTIFY_SOURCE=1 \ - --copt=-O2 \ - --copt=-DNDEBUG \ - --copt=-ffunction-sections \ - --copt=-fdata-sections \ - --cxxopt=-std=c++17 \ - --host_cxxopt=-std=c++17 \ - --cxxopt='-includecstdint' \ - --host_cxxopt='-includecstdint' \ - --cxxopt='-includestring' \ - --host_cxxopt='-includestring' \ - --cxxopt='-includestring_view' \ - --host_cxxopt='-includestring_view' \ + --config=libc++ --verbose_failures -c opt \ //source/exe:envoy-static } |