From dc7066d789ce4497969ef66f32d82a95090b5790 Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 6 Oct 2020 15:58:28 +0100 Subject: [PATCH 01/27] docs: Update examples developer documentation (#13200) Signed-off-by: Ryan Northey --- examples/DEVELOPER.md | 79 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/examples/DEVELOPER.md b/examples/DEVELOPER.md index d9a2c8e739dd..1538d1f605f9 100644 --- a/examples/DEVELOPER.md +++ b/examples/DEVELOPER.md @@ -182,7 +182,6 @@ bring_up_example If your sandbox has multiple compositions, and uses the `$PATHS` env var described above, `bring_up_example` will bring all of your compositions up. - ### Additional arguments to `docker-compose up -d` If you need to pass additional arguments to compose you can set the `UPARGS` @@ -208,3 +207,81 @@ export UPARGS="--scale http_service=2" If your example asks the user to run commands inside containers, you can mimick this using `docker-compose exec -T`. The `-T` flag is necessary as the tests do not have access to a `tty` in the CI pipeline. + +### Note on permissions and configuration + +The sandbox tests are run with a `umask` setting of `027` to ensure they will run in environments +where this is the case. + +As the Envoy containers run as non-root, it is essential that any configurations required +by the daemon are included in the relevant example `Dockerfile` rather than mounted in +any `docker-compose.yaml` files. + +The Docker recipe should also ensure that added configurations are world-readable. + +For example, with an added configuration file named `front-envoy.yaml`, you should add +the following in the Docker recipe: + +``` +RUN chmod go+r /etc/front-envoy.yaml +``` + +## Sandbox configuration tests + +Example configuration files are tested to ensure they are valid and well-formed, and do +not contain deprecated features. + +### Exclude configs from example configuration tests + +The CI script searches for all files in the examples folders with a `yaml` or `lua` extension. + +These files are bundled into a test and the `yaml` files are used to try to start an Envoy server. + +If your example includes `yaml` files that are either not Envoy configuration, or for some reason +cannot be tested in this way, you should add the files to the `exclude` list in the `filegroup.srcs` +section of the `examples/BUILD` file. + +The `exclude` patterns are evaluated as `globs` in the context of the `examples` folder. + + +## Verifying your sandbox + +Once you have built your sandbox, and added the `verify.sh` script you can run it directly in the +sandbox folder. + +For example: + +``` +cd examples/example-sandbox +./verify.sh + +``` + +You should see the docker composition brought up, your tests run, and the composition brought down again. + +The script should exit with `0` for the tests to pass. + + +## Verifying multiple/all sandboxes + +In continuous integration, all of the sandboxes are checked using the `ci/verify-examples.sh`. + +This can also be called with a filter argument, which is a `glob` evaluated in the context of the `examples` folder. + +For example, to run all sandboxes with names beginning `jaeger`: + +``` +./ci/verify-examples.sh jaeger* +``` + +--- + +**NOTE** + +You can use this script locally to test the sandboxes on your platform, but you should be aware that it requires +a lot of resources as it downloads and builds many Docker images, and then runs them in turn. + +--- + +One way to run the tests in an isolated environment is to mount the `envoy` source into a `docker-in-docker` container +or similar, and then run the script from inside that container. From 9dd666d841133fbba40940af0a424f7d82a6b0f1 Mon Sep 17 00:00:00 2001 From: "Adi (Suissa) Peleg" Date: Tue, 6 Oct 2020 11:11:02 -0400 Subject: [PATCH 02/27] http: Header-map updating remove_if to an O(n) operation for lazy-headermap (#13390) Signed-off-by: Adi Suissa-Peleg --- source/common/http/header_map_impl.cc | 2 +- source/common/http/header_map_impl.h | 78 +++++++++++---------- test/common/http/header_map_impl_test.cc | 86 ++++++++++++++---------- 3 files changed, 95 insertions(+), 71 deletions(-) diff --git a/source/common/http/header_map_impl.cc b/source/common/http/header_map_impl.cc index 6c8afffbfa6c..af82752b04c8 100644 --- a/source/common/http/header_map_impl.cc +++ b/source/common/http/header_map_impl.cc @@ -547,7 +547,7 @@ void HeaderMapImpl::clear() { size_t HeaderMapImpl::removeIf(const HeaderMap::HeaderMatchPredicate& predicate) { const size_t old_size = headers_.size(); - headers_.remove_if([&predicate, this](const HeaderEntryImpl& entry) { + headers_.removeIf([&predicate, this](const HeaderEntryImpl& entry) { const bool to_remove = predicate(entry); if (to_remove) { // If this header should be removed, make sure any references in the diff --git a/source/common/http/header_map_impl.h b/source/common/http/header_map_impl.h index 8e8949330ddf..6fa14e6b5d1d 100644 --- a/source/common/http/header_map_impl.h +++ b/source/common/http/header_map_impl.h @@ -222,45 +222,51 @@ class HeaderMapImpl : NonCopyable { return headers_.erase(i); } - template void remove_if(UnaryPredicate p) { - headers_.remove_if([&](const HeaderEntryImpl& entry) { - const bool to_remove = p(entry); - if (to_remove) { - if (pseudo_headers_end_ == entry.entry_) { - pseudo_headers_end_++; + template void removeIf(UnaryPredicate p) { + if (!lazy_map_.empty()) { + // Lazy map is used, iterate over its elements and remove those that satisfy the predicate + // from the map and from the list. + for (auto map_it = lazy_map_.begin(); map_it != lazy_map_.end();) { + auto& values_vec = map_it->second; + ASSERT(!values_vec.empty()); + // The following call to std::remove_if removes the elements that satisfy the + // UnaryPredicate and shifts the vector elements, but does not resize the vector. + // The call to erase that follows erases the unneeded cells (from remove_pos to the + // end) and modifies the vector's size. + const auto remove_pos = + std::remove_if(values_vec.begin(), values_vec.end(), [&](HeaderNode it) { + if (p(*(it->entry_))) { + // Remove the element from the list. + if (pseudo_headers_end_ == it->entry_) { + pseudo_headers_end_++; + } + headers_.erase(it); + return true; + } + return false; + }); + values_vec.erase(remove_pos, values_vec.end()); + + // If all elements were removed from the map entry, erase it. + if (values_vec.empty()) { + lazy_map_.erase(map_it++); + } else { + map_it++; } - if (!lazy_map_.empty()) { - ASSERT(lazy_map_.find(entry.key().getStringView()) != lazy_map_.end()); - auto& values_vec = lazy_map_[entry.key().getStringView()]; - if (values_vec.size() == 1) { - // If the map contains a single value with the same key of the entry, it is certain - // that this is the only value that needs to be removed, and can be erased from the - // map. - lazy_map_.erase(entry.key().getStringView()); - } else { - // Otherwise, find the exact element that is removed from headers_ (entry) in the - // vector, and remove it. This keeps an invariant that only the element that is - // currently removed from the headers list will also be removed from the map. - // In case the predicate holds for all the vector elements, the size of the vector - // will eventually decrease to 1 and the entire vector will be erased by the if - // clause above. - // Note: it is possible to remove all elements in the vector that satisfy the - // predicate during this iteration, but it will complicate the code by requiring - // handling of missing values in the lazy map. - - // The call to std::remove_if removes the elements that satisfy the predicate and - // shifts the vector elements, but does not resize the vector. The call to erase that - // follows erases the unneeded cells (from remove_pos to the end) and modifies the - // vector's size. - const auto remove_pos = - std::remove_if(values_vec.begin(), values_vec.end(), - [&](HeaderNode it) { return it == entry.entry_; }); - values_vec.erase(remove_pos, values_vec.end()); + } + } else { + // The lazy map isn't used, iterate over the list elements and remove elements that satisfy + // the predicate. + headers_.remove_if([&](const HeaderEntryImpl& entry) { + const bool to_remove = p(entry); + if (to_remove) { + if (pseudo_headers_end_ == entry.entry_) { + pseudo_headers_end_++; } } - } - return to_remove; - }); + return to_remove; + }); + } } /* diff --git a/test/common/http/header_map_impl_test.cc b/test/common/http/header_map_impl_test.cc index 851a871b91ab..ba55a42220ec 100644 --- a/test/common/http/header_map_impl_test.cc +++ b/test/common/http/header_map_impl_test.cc @@ -360,8 +360,27 @@ Http::RegisterCustomInlineHeader custom_header_1_copy(Http::LowerCaseString{"foo_custom_header"}); +class HeaderMapImplTest : public testing::TestWithParam { +public: + HeaderMapImplTest() { + // Set the lazy map threshold using the test parameter. + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.http.headermap.lazy_map_min_size", absl::StrCat(GetParam())}}); + } + + static std::string testParamsToString(const ::testing::TestParamInfo& params) { + return absl::StrCat(params.param); + } + + TestScopedRuntime runtime; +}; + +INSTANTIATE_TEST_SUITE_P(HeaderMapThreshold, HeaderMapImplTest, + testing::Values(0, 1, std::numeric_limits::max()), + HeaderMapImplTest::testParamsToString); + // Make sure that the same header registered twice points to the same location. -TEST(HeaderMapImplTest, CustomRegisteredHeaders) { +TEST_P(HeaderMapImplTest, CustomRegisteredHeaders) { TestRequestHeaderMapImpl headers; EXPECT_EQ(custom_header_1.handle(), custom_header_1_copy.handle()); EXPECT_EQ(nullptr, headers.getInline(custom_header_1.handle())); @@ -381,7 +400,7 @@ TEST(HeaderMapImplTest, CustomRegisteredHeaders) { EXPECT_EQ(header_map->get(Headers::get().name)->value().getStringView(), #name); // Make sure that the O(1) headers are wired up properly. -TEST(HeaderMapImplTest, AllInlineHeaders) { +TEST_P(HeaderMapImplTest, AllInlineHeaders) { { auto header_map = RequestHeaderMapImpl::create(); INLINE_REQ_HEADERS(TEST_INLINE_HEADER_FUNCS) @@ -401,7 +420,7 @@ TEST(HeaderMapImplTest, AllInlineHeaders) { } } -TEST(HeaderMapImplTest, InlineInsert) { +TEST_P(HeaderMapImplTest, InlineInsert) { TestRequestHeaderMapImpl headers; EXPECT_TRUE(headers.empty()); EXPECT_EQ(0, headers.size()); @@ -414,7 +433,7 @@ TEST(HeaderMapImplTest, InlineInsert) { EXPECT_EQ("hello", headers.get(Headers::get().Host)->value().getStringView()); } -TEST(HeaderMapImplTest, InlineAppend) { +TEST_P(HeaderMapImplTest, InlineAppend) { { TestRequestHeaderMapImpl headers; // Create via header and append. @@ -460,7 +479,7 @@ TEST(HeaderMapImplTest, InlineAppend) { } } -TEST(HeaderMapImplTest, MoveIntoInline) { +TEST_P(HeaderMapImplTest, MoveIntoInline) { TestRequestHeaderMapImpl headers; HeaderString key; key.setCopy(Headers::get().EnvoyRetryOn.get()); @@ -479,7 +498,7 @@ TEST(HeaderMapImplTest, MoveIntoInline) { EXPECT_EQ("hello,there", headers.getEnvoyRetryOnValue()); } -TEST(HeaderMapImplTest, Remove) { +TEST_P(HeaderMapImplTest, Remove) { TestRequestHeaderMapImpl headers; // Add random header and then remove by name. @@ -521,7 +540,7 @@ TEST(HeaderMapImplTest, Remove) { EXPECT_EQ(0UL, headers.remove(Headers::get().ContentLength)); } -TEST(HeaderMapImplTest, RemoveHost) { +TEST_P(HeaderMapImplTest, RemoveHost) { TestRequestHeaderMapImpl headers; headers.setHost("foo"); EXPECT_EQ("foo", headers.get_("host")); @@ -535,7 +554,7 @@ TEST(HeaderMapImplTest, RemoveHost) { EXPECT_EQ(nullptr, headers.Host()); } -TEST(HeaderMapImplTest, RemoveIf) { +TEST_P(HeaderMapImplTest, RemoveIf) { LowerCaseString key1 = LowerCaseString("X-postfix-foo"); LowerCaseString key2 = LowerCaseString("X-postfix-"); LowerCaseString key3 = LowerCaseString("x-postfix-eep"); @@ -578,7 +597,7 @@ TEST(HeaderMapImplTest, RemoveIf) { } } -TEST(HeaderMapImplTest, RemovePrefix) { +TEST_P(HeaderMapImplTest, RemovePrefix) { // These will match. LowerCaseString key1 = LowerCaseString("X-prefix-foo"); LowerCaseString key3 = LowerCaseString("X-Prefix-"); @@ -630,7 +649,7 @@ class HeaderAndValueCb } }; -TEST(HeaderMapImplTest, SetRemovesAllValues) { +TEST_P(HeaderMapImplTest, SetRemovesAllValues) { TestRequestHeaderMapImpl headers; LowerCaseString key1("hello"); @@ -671,7 +690,7 @@ TEST(HeaderMapImplTest, SetRemovesAllValues) { } } -TEST(HeaderMapImplTest, DoubleInlineAdd) { +TEST_P(HeaderMapImplTest, DoubleInlineAdd) { { TestRequestHeaderMapImpl headers; const std::string foo("foo"); @@ -707,7 +726,7 @@ TEST(HeaderMapImplTest, DoubleInlineAdd) { // Per https://github.com/envoyproxy/envoy/issues/7488 make sure we don't // combine set-cookie headers -TEST(HeaderMapImplTest, DoubleCookieAdd) { +TEST_P(HeaderMapImplTest, DoubleCookieAdd) { TestRequestHeaderMapImpl headers; const std::string foo("foo"); const std::string bar("bar"); @@ -723,7 +742,7 @@ TEST(HeaderMapImplTest, DoubleCookieAdd) { ASSERT_EQ(out[1], "bar"); } -TEST(HeaderMapImplTest, DoubleInlineSet) { +TEST_P(HeaderMapImplTest, DoubleInlineSet) { TestRequestHeaderMapImpl headers; headers.setReferenceKey(Headers::get().ContentType, "blah"); headers.setReferenceKey(Headers::get().ContentType, "text/html"); @@ -731,7 +750,7 @@ TEST(HeaderMapImplTest, DoubleInlineSet) { EXPECT_EQ(1UL, headers.size()); } -TEST(HeaderMapImplTest, AddReferenceKey) { +TEST_P(HeaderMapImplTest, AddReferenceKey) { TestRequestHeaderMapImpl headers; LowerCaseString foo("hello"); headers.addReferenceKey(foo, "world"); @@ -739,7 +758,7 @@ TEST(HeaderMapImplTest, AddReferenceKey) { EXPECT_EQ("world", headers.get(foo)->value().getStringView()); } -TEST(HeaderMapImplTest, SetReferenceKey) { +TEST_P(HeaderMapImplTest, SetReferenceKey) { TestRequestHeaderMapImpl headers; LowerCaseString foo("hello"); headers.setReferenceKey(foo, "world"); @@ -751,8 +770,7 @@ TEST(HeaderMapImplTest, SetReferenceKey) { EXPECT_EQ("monde", headers.get(foo)->value().getStringView()); } -TEST(HeaderMapImplTest, SetCopyOldBehavior) { - TestScopedRuntime runtime; +TEST_P(HeaderMapImplTest, SetCopyOldBehavior) { Runtime::LoaderSingleton::getExisting()->mergeValues( {{"envoy.reloadable_features.http_set_copy_replace_all_headers", "false"}}); @@ -800,7 +818,7 @@ TEST(HeaderMapImplTest, SetCopyOldBehavior) { EXPECT_EQ(headers.getPathValue(), "/foo"); } -TEST(HeaderMapImplTest, SetCopyNewBehavior) { +TEST_P(HeaderMapImplTest, SetCopyNewBehavior) { TestRequestHeaderMapImpl headers; LowerCaseString foo("hello"); headers.setCopy(foo, "world"); @@ -844,7 +862,7 @@ TEST(HeaderMapImplTest, SetCopyNewBehavior) { EXPECT_EQ(headers.getPathValue(), "/foo"); } -TEST(HeaderMapImplTest, AddCopy) { +TEST_P(HeaderMapImplTest, AddCopy) { TestRequestHeaderMapImpl headers; // Start with a string value. @@ -915,7 +933,7 @@ TEST(HeaderMapImplTest, AddCopy) { headers.get(envoy_retry_on)->value().getStringView()); } -TEST(HeaderMapImplTest, Equality) { +TEST_P(HeaderMapImplTest, Equality) { TestRequestHeaderMapImpl headers1; TestRequestHeaderMapImpl headers2; EXPECT_EQ(headers1, headers2); @@ -927,7 +945,7 @@ TEST(HeaderMapImplTest, Equality) { EXPECT_FALSE(headers1 == headers2); } -TEST(HeaderMapImplTest, LargeCharInHeader) { +TEST_P(HeaderMapImplTest, LargeCharInHeader) { TestRequestHeaderMapImpl headers; LowerCaseString static_key("\x90hello"); std::string ref_value("value"); @@ -935,7 +953,7 @@ TEST(HeaderMapImplTest, LargeCharInHeader) { EXPECT_EQ("value", headers.get(static_key)->value().getStringView()); } -TEST(HeaderMapImplTest, Iterate) { +TEST_P(HeaderMapImplTest, Iterate) { TestRequestHeaderMapImpl headers; headers.addCopy(LowerCaseString("hello"), "world"); headers.addCopy(LowerCaseString("foo"), "xxx"); @@ -952,7 +970,7 @@ TEST(HeaderMapImplTest, Iterate) { headers.iterate(cb.asIterateCb()); } -TEST(HeaderMapImplTest, IterateReverse) { +TEST_P(HeaderMapImplTest, IterateReverse) { TestRequestHeaderMapImpl headers; headers.addCopy(LowerCaseString("hello"), "world"); headers.addCopy(LowerCaseString("foo"), "bar"); @@ -975,7 +993,7 @@ TEST(HeaderMapImplTest, IterateReverse) { }); } -TEST(HeaderMapImplTest, Get) { +TEST_P(HeaderMapImplTest, Get) { { auto headers = TestRequestHeaderMapImpl({{Headers::get().Path.get(), "/"}, {"hello", "world"}}); EXPECT_EQ("/", headers.get(LowerCaseString(":path"))->value().getStringView()); @@ -996,7 +1014,7 @@ TEST(HeaderMapImplTest, Get) { } } -TEST(HeaderMapImplTest, CreateHeaderMapFromIterator) { +TEST_P(HeaderMapImplTest, CreateHeaderMapFromIterator) { std::vector> iter_headers{ {LowerCaseString(Headers::get().Path), "/"}, {LowerCaseString("hello"), "world"}}; auto headers = createHeaderMap(iter_headers.cbegin(), iter_headers.cend()); @@ -1005,7 +1023,7 @@ TEST(HeaderMapImplTest, CreateHeaderMapFromIterator) { EXPECT_EQ(nullptr, headers->get(LowerCaseString("foo"))); } -TEST(HeaderMapImplTest, TestHeaderList) { +TEST_P(HeaderMapImplTest, TestHeaderList) { std::array keys{Headers::get().Path.get(), "hello"}; std::array values{"/", "world"}; @@ -1023,7 +1041,7 @@ TEST(HeaderMapImplTest, TestHeaderList) { EXPECT_THAT(to_string_views(header_list.values()), ElementsAre("/", "world")); } -TEST(HeaderMapImplTest, TestAppendHeader) { +TEST_P(HeaderMapImplTest, TestAppendHeader) { // Test appending to a string with a value. { TestRequestHeaderMapImpl headers; @@ -1086,7 +1104,7 @@ TEST(TestHeaderMapImplDeathTest, TestHeaderLengthChecks) { "Trying to allocate overly large headers."); } -TEST(HeaderMapImplTest, PseudoHeaderOrder) { +TEST_P(HeaderMapImplTest, PseudoHeaderOrder) { HeaderAndValueCb cb; { @@ -1235,7 +1253,7 @@ TEST(HeaderMapImplTest, PseudoHeaderOrder) { // Validate that TestRequestHeaderMapImpl copy construction and assignment works. This is a // regression for where we were missing a valid copy constructor and had the // default (dangerous) move semantics takeover. -TEST(HeaderMapImplTest, TestRequestHeaderMapImplCopy) { +TEST_P(HeaderMapImplTest, TestRequestHeaderMapImplCopy) { TestRequestHeaderMapImpl foo; foo.addCopy(LowerCaseString("foo"), "bar"); auto headers = std::make_unique(foo); @@ -1249,7 +1267,7 @@ TEST(HeaderMapImplTest, TestRequestHeaderMapImplCopy) { } // Make sure 'host' -> ':authority' auto translation only occurs for request headers. -TEST(HeaderMapImplTest, HostHeader) { +TEST_P(HeaderMapImplTest, HostHeader) { TestRequestHeaderMapImpl request_headers{{"host", "foo"}}; EXPECT_EQ(request_headers.size(), 1); EXPECT_EQ(request_headers.get_(":authority"), "foo"); @@ -1267,14 +1285,14 @@ TEST(HeaderMapImplTest, HostHeader) { EXPECT_EQ(response_trailers.get_("host"), "foo"); } -TEST(HeaderMapImplTest, TestInlineHeaderAdd) { +TEST_P(HeaderMapImplTest, TestInlineHeaderAdd) { TestRequestHeaderMapImpl foo; foo.addCopy(LowerCaseString(":path"), "GET"); EXPECT_EQ(foo.size(), 1); EXPECT_TRUE(foo.Path() != nullptr); } -TEST(HeaderMapImplTest, ClearHeaderMap) { +TEST_P(HeaderMapImplTest, ClearHeaderMap) { TestRequestHeaderMapImpl headers; LowerCaseString static_key("hello"); std::string ref_value("value"); @@ -1316,7 +1334,7 @@ TEST(HeaderMapImplTest, ClearHeaderMap) { } // Validates byte size is properly accounted for in different inline header setting scenarios. -TEST(HeaderMapImplTest, InlineHeaderByteSize) { +TEST_P(HeaderMapImplTest, InlineHeaderByteSize) { { TestRequestHeaderMapImpl headers; std::string foo = "foo"; @@ -1370,7 +1388,7 @@ TEST(HeaderMapImplTest, InlineHeaderByteSize) { } } -TEST(HeaderMapImplTest, ValidHeaderString) { +TEST_P(HeaderMapImplTest, ValidHeaderString) { EXPECT_TRUE(validHeaderString("abc")); EXPECT_FALSE(validHeaderString(absl::string_view("a\000bc", 4))); EXPECT_FALSE(validHeaderString("abc\n")); From 22683a0a24ffbb0cdeb4111eec5ec90246bec9cb Mon Sep 17 00:00:00 2001 From: Alex Konradi Date: Tue, 6 Oct 2020 11:14:25 -0400 Subject: [PATCH 03/27] server: remove RandomGenerator from everywhere except Api (#13360) Signed-off-by: Alex Konradi --- include/envoy/server/factory_context.h | 5 -- include/envoy/server/instance.h | 5 -- .../envoy/server/transport_socket_config.h | 5 -- include/envoy/upstream/cluster_factory.h | 5 -- source/common/access_log/access_log_impl.cc | 3 +- .../config/subscription_factory_impl.cc | 14 ++-- .../common/config/subscription_factory_impl.h | 3 +- source/common/local_reply/local_reply.cc | 2 +- source/common/router/router.h | 2 +- source/common/tcp_proxy/tcp_proxy.cc | 2 +- .../common/upstream/cluster_factory_impl.cc | 6 +- source/common/upstream/cluster_factory_impl.h | 3 +- .../common/upstream/cluster_manager_impl.cc | 27 ++++---- source/common/upstream/cluster_manager_impl.h | 23 ++++--- .../upstream/health_discovery_service.cc | 4 +- source/common/upstream/logical_dns_cluster.cc | 2 +- source/common/upstream/strict_dns_cluster.cc | 2 +- source/common/upstream/upstream_impl.cc | 7 +- .../extensions/clusters/aggregate/cluster.cc | 7 +- .../clusters/dynamic_forward_proxy/cluster.cc | 4 +- .../clusters/redis/redis_cluster.cc | 5 +- .../network/http_connection_manager/config.cc | 14 ++-- .../tracers/xray/xray_tracer_impl.cc | 10 +-- source/extensions/tracers/zipkin/config.cc | 3 +- source/server/admin/admin.cc | 12 ++-- .../config_validation/cluster_manager.cc | 13 ++-- .../config_validation/cluster_manager.h | 20 +++--- source/server/config_validation/server.cc | 4 +- source/server/config_validation/server.h | 1 - source/server/configuration_impl.cc | 2 +- source/server/drain_manager_impl.cc | 2 +- source/server/filter_chain_manager_impl.cc | 5 -- source/server/filter_chain_manager_impl.h | 2 - source/server/listener_impl.cc | 10 +-- source/server/listener_impl.h | 2 - source/server/listener_manager_impl.cc | 2 +- source/server/server.cc | 8 +-- source/server/server.h | 2 - source/server/transport_socket_config_impl.h | 22 +++--- .../common/access_log/access_log_impl_test.cc | 12 ++-- .../config/subscription_factory_impl_test.cc | 6 +- test/common/tcp_proxy/tcp_proxy_test.cc | 16 ++--- .../upstream/cluster_factory_impl_test.cc | 23 +++---- .../upstream/cluster_manager_impl_test.cc | 13 ++-- test/common/upstream/eds_speed_test.cc | 2 +- test/common/upstream/eds_test.cc | 2 +- test/common/upstream/hds_test.cc | 4 +- .../upstream/logical_dns_cluster_test.cc | 4 +- .../upstream/original_dst_cluster_test.cc | 2 +- test/common/upstream/test_cluster_manager.h | 33 +++++---- test/common/upstream/upstream_impl_test.cc | 68 +++++++++---------- test/config_test/config_test.cc | 10 +-- .../clusters/aggregate/cluster_test.cc | 9 +-- .../clusters/aggregate/cluster_update_test.cc | 9 ++- .../dynamic_forward_proxy/cluster_test.cc | 3 +- .../redis/redis_cluster_integration_test.cc | 3 +- .../clusters/redis/redis_cluster_test.cc | 6 +- test/extensions/tracers/xray/tracer_test.cc | 49 ++++++++----- test/mocks/server/factory_context.cc | 1 - test/mocks/server/factory_context.h | 2 - test/mocks/server/instance.cc | 2 - test/mocks/server/instance.h | 4 -- test/per_file_coverage.sh | 2 +- .../config_validation/cluster_manager_test.cc | 4 +- test/server/configuration_impl_test.cc | 8 +-- test/server/drain_manager_impl_test.cc | 12 ++-- .../listener_manager_impl_quic_only_test.cc | 2 +- test/server/listener_manager_impl_test.cc | 64 ++++++++--------- test/server/listener_manager_impl_test.h | 2 +- 69 files changed, 309 insertions(+), 338 deletions(-) diff --git a/include/envoy/server/factory_context.h b/include/envoy/server/factory_context.h index 08f67e31cc3b..7d496a6d2eb4 100644 --- a/include/envoy/server/factory_context.h +++ b/include/envoy/server/factory_context.h @@ -64,11 +64,6 @@ class CommonFactoryContext { */ virtual ProtobufMessage::ValidationContext& messageValidationContext() PURE; - /** - * @return RandomGenerator& the random generator for the server. - */ - virtual Envoy::Random::RandomGenerator& random() PURE; - /** * @return Runtime::Loader& the singleton runtime loader for the server. */ diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index c2d294ac3cb8..8bba884e8b47 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -135,11 +135,6 @@ class Instance { */ virtual const Options& options() PURE; - /** - * @return RandomGenerator& the random generator for the server. - */ - virtual Random::RandomGenerator& random() PURE; - /** * @return Runtime::Loader& the singleton runtime loader for the server. */ diff --git a/include/envoy/server/transport_socket_config.h b/include/envoy/server/transport_socket_config.h index e08405f9b4ab..faee6f2728d9 100644 --- a/include/envoy/server/transport_socket_config.h +++ b/include/envoy/server/transport_socket_config.h @@ -64,11 +64,6 @@ class TransportSocketFactoryContext { */ virtual Event::Dispatcher& dispatcher() PURE; - /** - * @return RandomGenerator& the random generator for the server. - */ - virtual Envoy::Random::RandomGenerator& random() PURE; - /** * @return the server-wide stats store. */ diff --git a/include/envoy/upstream/cluster_factory.h b/include/envoy/upstream/cluster_factory.h index 68bbff008baf..019e87a51d23 100644 --- a/include/envoy/upstream/cluster_factory.h +++ b/include/envoy/upstream/cluster_factory.h @@ -79,11 +79,6 @@ class ClusterFactoryContext { */ virtual AccessLog::AccessLogManager& logManager() PURE; - /** - * @return RandomGenerator& the random generator for the server. - */ - virtual Random::RandomGenerator& random() PURE; - /** * @return Runtime::Loader& the singleton runtime loader for the server. */ diff --git a/source/common/access_log/access_log_impl.cc b/source/common/access_log/access_log_impl.cc index 447f951bc2f8..3163921b4800 100644 --- a/source/common/access_log/access_log_impl.cc +++ b/source/common/access_log/access_log_impl.cc @@ -300,7 +300,8 @@ InstanceSharedPtr AccessLogFactory::fromProto(const envoy::config::accesslog::v3 Server::Configuration::FactoryContext& context) { FilterPtr filter; if (config.has_filter()) { - filter = FilterFactory::fromProto(config.filter(), context.runtime(), context.random(), + filter = FilterFactory::fromProto(config.filter(), context.runtime(), + context.api().randomGenerator(), context.messageValidationVisitor()); } diff --git a/source/common/config/subscription_factory_impl.cc b/source/common/config/subscription_factory_impl.cc index 22b1aeef7990..9283654cf419 100644 --- a/source/common/config/subscription_factory_impl.cc +++ b/source/common/config/subscription_factory_impl.cc @@ -18,9 +18,9 @@ namespace Config { SubscriptionFactoryImpl::SubscriptionFactoryImpl( const LocalInfo::LocalInfo& local_info, Event::Dispatcher& dispatcher, - Upstream::ClusterManager& cm, Random::RandomGenerator& random, - ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api, Runtime::Loader& runtime) - : local_info_(local_info), dispatcher_(dispatcher), cm_(cm), random_(random), + Upstream::ClusterManager& cm, ProtobufMessage::ValidationVisitor& validation_visitor, + Api::Api& api, Runtime::Loader& runtime) + : local_info_(local_info), dispatcher_(dispatcher), cm_(cm), validation_visitor_(validation_visitor), api_(api), runtime_(runtime) {} SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( @@ -60,8 +60,8 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( config.DebugString()); case envoy::config::core::v3::ApiConfigSource::REST: return std::make_unique( - local_info_, cm_, api_config_source.cluster_names()[0], dispatcher_, random_, - Utility::apiConfigSourceRefreshDelay(api_config_source), + local_info_, cm_, api_config_source.cluster_names()[0], dispatcher_, + api_.randomGenerator(), Utility::apiConfigSourceRefreshDelay(api_config_source), Utility::apiConfigSourceRequestTimeout(api_config_source), restMethod(type_url, api_config_source.transport_api_version()), type_url, api_config_source.transport_api_version(), callbacks, resource_decoder, stats, @@ -74,7 +74,7 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( api_config_source, scope, true) ->create(), dispatcher_, sotwGrpcMethod(type_url, api_config_source.transport_api_version()), - api_config_source.transport_api_version(), random_, scope, + api_config_source.transport_api_version(), api_.randomGenerator(), scope, Utility::parseRateLimitSettings(api_config_source), api_config_source.set_node_on_first_message_only()), callbacks, resource_decoder, stats, type_url, dispatcher_, @@ -87,7 +87,7 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( api_config_source, scope, true) ->create(), dispatcher_, deltaGrpcMethod(type_url, api_config_source.transport_api_version()), - api_config_source.transport_api_version(), random_, scope, + api_config_source.transport_api_version(), api_.randomGenerator(), scope, Utility::parseRateLimitSettings(api_config_source), local_info_), callbacks, resource_decoder, stats, type_url, dispatcher_, Utility::configSourceInitialFetchTimeout(config), false); diff --git a/source/common/config/subscription_factory_impl.h b/source/common/config/subscription_factory_impl.h index bad716ab208e..6e65a7211f13 100644 --- a/source/common/config/subscription_factory_impl.h +++ b/source/common/config/subscription_factory_impl.h @@ -16,7 +16,7 @@ namespace Config { class SubscriptionFactoryImpl : public SubscriptionFactory, Logger::Loggable { public: SubscriptionFactoryImpl(const LocalInfo::LocalInfo& local_info, Event::Dispatcher& dispatcher, - Upstream::ClusterManager& cm, Random::RandomGenerator& random, + Upstream::ClusterManager& cm, ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api, Runtime::Loader& runtime); @@ -36,7 +36,6 @@ class SubscriptionFactoryImpl : public SubscriptionFactory, Logger::Loggable(config.status_code().value()); diff --git a/source/common/router/router.h b/source/common/router/router.h index e1be0571e2b6..f5512919d7da 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -207,7 +207,7 @@ class FilterConfig { ShadowWriterPtr&& shadow_writer, const envoy::extensions::filters::http::router::v3::Router& config) : FilterConfig(stat_prefix, context.localInfo(), context.scope(), context.clusterManager(), - context.runtime(), context.random(), std::move(shadow_writer), + context.runtime(), context.api().randomGenerator(), std::move(shadow_writer), PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, dynamic_stats, true), config.start_child_span(), config.suppress_envoy_headers(), config.respect_expected_rq_timeout(), config.strict_check_headers(), diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index b8e6f4d1905f..48d6994a6028 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -122,7 +122,7 @@ Config::Config(const envoy::extensions::filters::network::tcp_proxy::v3::TcpProx : max_connect_attempts_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, max_connect_attempts, 1)), upstream_drain_manager_slot_(context.threadLocal().allocateSlot()), shared_config_(std::make_shared(config, context)), - random_generator_(context.random()) { + random_generator_(context.api().randomGenerator()) { upstream_drain_manager_slot_->set([](Event::Dispatcher&) { ThreadLocal::ThreadLocalObjectSharedPtr drain_manager = diff --git a/source/common/upstream/cluster_factory_impl.cc b/source/common/upstream/cluster_factory_impl.cc index 8c0d04151145..cda9c64b51ec 100644 --- a/source/common/upstream/cluster_factory_impl.cc +++ b/source/common/upstream/cluster_factory_impl.cc @@ -26,7 +26,7 @@ Stats::ScopePtr generateStatsScope(const envoy::config::cluster::v3::Cluster& co std::pair ClusterFactoryImplBase::create( const envoy::config::cluster::v3::Cluster& cluster, ClusterManager& cluster_manager, Stats::Store& stats, ThreadLocal::Instance& tls, Network::DnsResolverSharedPtr dns_resolver, - Ssl::ContextManager& ssl_context_manager, Runtime::Loader& runtime, Random::RandomGenerator&, + Ssl::ContextManager& ssl_context_manager, Runtime::Loader& runtime, Event::Dispatcher& dispatcher, AccessLog::AccessLogManager& log_manager, const LocalInfo::LocalInfo& local_info, Server::Admin& admin, Singleton::Manager& singleton_manager, Outlier::EventLoggerSharedPtr outlier_event_logger, @@ -107,8 +107,8 @@ ClusterFactoryImplBase::create(const envoy::config::cluster::v3::Cluster& cluste auto stats_scope = generateStatsScope(cluster, context.stats()); Server::Configuration::TransportSocketFactoryContextImpl factory_context( context.admin(), context.sslContextManager(), *stats_scope, context.clusterManager(), - context.localInfo(), context.dispatcher(), context.random(), context.stats(), - context.singletonManager(), context.tls(), context.messageValidationVisitor(), context.api()); + context.localInfo(), context.dispatcher(), context.stats(), context.singletonManager(), + context.tls(), context.messageValidationVisitor(), context.api()); std::pair new_cluster_pair = createClusterImpl(cluster, context, factory_context, std::move(stats_scope)); diff --git a/source/common/upstream/cluster_factory_impl.h b/source/common/upstream/cluster_factory_impl.h index 9dd1b6010988..0cc4b76e844e 100644 --- a/source/common/upstream/cluster_factory_impl.h +++ b/source/common/upstream/cluster_factory_impl.h @@ -73,7 +73,6 @@ class ClusterFactoryContextImpl : public ClusterFactoryContext { Network::DnsResolverSharedPtr dnsResolver() override { return dns_resolver_; } Ssl::ContextManager& sslContextManager() override { return ssl_context_manager_; } Runtime::Loader& runtime() override { return runtime_; } - Random::RandomGenerator& random() override { return api_.randomGenerator(); } Event::Dispatcher& dispatcher() override { return dispatcher_; } AccessLog::AccessLogManager& logManager() override { return log_manager_; } const LocalInfo::LocalInfo& localInfo() override { return local_info_; } @@ -118,7 +117,7 @@ class ClusterFactoryImplBase : public ClusterFactory { create(const envoy::config::cluster::v3::Cluster& cluster, ClusterManager& cluster_manager, Stats::Store& stats, ThreadLocal::Instance& tls, Network::DnsResolverSharedPtr dns_resolver, Ssl::ContextManager& ssl_context_manager, - Runtime::Loader& runtime, Random::RandomGenerator& random, Event::Dispatcher& dispatcher, + Runtime::Loader& runtime, Event::Dispatcher& dispatcher, AccessLog::AccessLogManager& log_manager, const LocalInfo::LocalInfo& local_info, Server::Admin& admin, Singleton::Manager& singleton_manager, Outlier::EventLoggerSharedPtr outlier_event_logger, bool added_via_api, diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 5f814b498028..15df928be990 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -234,19 +234,20 @@ void ClusterManagerInitHelper::setPrimaryClustersInitializedCb( ClusterManagerImpl::ClusterManagerImpl( const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, - Random::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, - Server::Admin& admin, ProtobufMessage::ValidationContext& validation_context, Api::Api& api, + const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, + Event::Dispatcher& main_thread_dispatcher, Server::Admin& admin, + ProtobufMessage::ValidationContext& validation_context, Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context) : factory_(factory), runtime_(runtime), stats_(stats), tls_(tls.allocateSlot()), - random_(random), bind_config_(bootstrap.cluster_manager().upstream_bind_config()), - local_info_(local_info), cm_stats_(generateStats(stats)), + random_(api.randomGenerator()), + bind_config_(bootstrap.cluster_manager().upstream_bind_config()), local_info_(local_info), + cm_stats_(generateStats(stats)), init_helper_(*this, [this](Cluster& cluster) { onClusterInit(cluster); }), config_tracker_entry_( admin.getConfigTracker().add("clusters", [this] { return dumpClusterConfigs(); })), time_source_(main_thread_dispatcher.timeSource()), dispatcher_(main_thread_dispatcher), http_context_(http_context), - subscription_factory_(local_info, main_thread_dispatcher, *this, random, + subscription_factory_(local_info, main_thread_dispatcher, *this, validation_context.dynamicValidationVisitor(), api, runtime_) { async_client_manager_ = std::make_unique( *this, tls, time_source_, api, grpc_context.statNames()); @@ -1456,8 +1457,8 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::tcpConnPool( ClusterManagerPtr ProdClusterManagerFactory::clusterManagerFromProto( const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { return ClusterManagerPtr{new ClusterManagerImpl( - bootstrap, *this, stats_, tls_, runtime_, random_, local_info_, log_manager_, - main_thread_dispatcher_, admin_, validation_context_, api_, http_context_, grpc_context_)}; + bootstrap, *this, stats_, tls_, runtime_, local_info_, log_manager_, main_thread_dispatcher_, + admin_, validation_context_, api_, http_context_, grpc_context_)}; } Http::ConnectionPool::InstancePtr ProdClusterManagerFactory::allocateConnPool( @@ -1466,14 +1467,14 @@ Http::ConnectionPool::InstancePtr ProdClusterManagerFactory::allocateConnPool( const Network::TransportSocketOptionsSharedPtr& transport_socket_options) { if (protocol == Http::Protocol::Http2 && runtime_.snapshot().featureEnabled("upstream.use_http2", 100)) { - return Http::Http2::allocateConnPool(dispatcher, random_, host, priority, options, - transport_socket_options); + return Http::Http2::allocateConnPool(dispatcher, api_.randomGenerator(), host, priority, + options, transport_socket_options); } else if (protocol == Http::Protocol::Http3) { // Quic connection pool is not implemented. NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } else { - return Http::Http1::allocateConnPool(dispatcher, random_, host, priority, options, - transport_socket_options); + return Http::Http1::allocateConnPool(dispatcher, api_.randomGenerator(), host, priority, + options, transport_socket_options); } } @@ -1494,7 +1495,7 @@ std::pair ProdClusterManagerFactor const envoy::config::cluster::v3::Cluster& cluster, ClusterManager& cm, Outlier::EventLoggerSharedPtr outlier_event_logger, bool added_via_api) { return ClusterFactoryImplBase::create( - cluster, cm, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, random_, + cluster, cm, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, main_thread_dispatcher_, log_manager_, local_info_, admin_, singleton_manager_, outlier_event_logger, added_via_api, added_via_api ? validation_context_.dynamicValidationVisitor() diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index b235be21d99c..920681bff0ef 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -40,17 +40,19 @@ namespace Upstream { */ class ProdClusterManagerFactory : public ClusterManagerFactory { public: - ProdClusterManagerFactory( - Server::Admin& admin, Runtime::Loader& runtime, Stats::Store& stats, - ThreadLocal::Instance& tls, Random::RandomGenerator& random, - Network::DnsResolverSharedPtr dns_resolver, Ssl::ContextManager& ssl_context_manager, - Event::Dispatcher& main_thread_dispatcher, const LocalInfo::LocalInfo& local_info, - Secret::SecretManager& secret_manager, ProtobufMessage::ValidationContext& validation_context, - Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, - AccessLog::AccessLogManager& log_manager, Singleton::Manager& singleton_manager) + ProdClusterManagerFactory(Server::Admin& admin, Runtime::Loader& runtime, Stats::Store& stats, + ThreadLocal::Instance& tls, Network::DnsResolverSharedPtr dns_resolver, + Ssl::ContextManager& ssl_context_manager, + Event::Dispatcher& main_thread_dispatcher, + const LocalInfo::LocalInfo& local_info, + Secret::SecretManager& secret_manager, + ProtobufMessage::ValidationContext& validation_context, Api::Api& api, + Http::Context& http_context, Grpc::Context& grpc_context, + AccessLog::AccessLogManager& log_manager, + Singleton::Manager& singleton_manager) : main_thread_dispatcher_(main_thread_dispatcher), validation_context_(validation_context), api_(api), http_context_(http_context), grpc_context_(grpc_context), admin_(admin), - runtime_(runtime), stats_(stats), tls_(tls), random_(random), dns_resolver_(dns_resolver), + runtime_(runtime), stats_(stats), tls_(tls), dns_resolver_(dns_resolver), ssl_context_manager_(ssl_context_manager), local_info_(local_info), secret_manager_(secret_manager), log_manager_(log_manager), singleton_manager_(singleton_manager) {} @@ -84,7 +86,6 @@ class ProdClusterManagerFactory : public ClusterManagerFactory { Runtime::Loader& runtime_; Stats::Store& stats_; ThreadLocal::Instance& tls_; - Random::RandomGenerator& random_; Network::DnsResolverSharedPtr dns_resolver_; Ssl::ContextManager& ssl_context_manager_; const LocalInfo::LocalInfo& local_info_; @@ -193,7 +194,7 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable( - cluster, dns_refresh_rate_ms_.count(), factory_context.random()); + cluster, dns_refresh_rate_ms_.count(), factory_context.api().randomGenerator()); const auto& locality_lb_endpoints = load_assignment_.endpoints(); if (locality_lb_endpoints.size() != 1 || locality_lb_endpoints[0].lb_endpoints().size() != 1) { diff --git a/source/common/upstream/strict_dns_cluster.cc b/source/common/upstream/strict_dns_cluster.cc index 279bf47bef27..328f2f7a79f8 100644 --- a/source/common/upstream/strict_dns_cluster.cc +++ b/source/common/upstream/strict_dns_cluster.cc @@ -21,7 +21,7 @@ StrictDnsClusterImpl::StrictDnsClusterImpl( respect_dns_ttl_(cluster.respect_dns_ttl()) { failure_backoff_strategy_ = Config::Utility::prepareDnsRefreshStrategy( - cluster, dns_refresh_rate_ms_.count(), factory_context.random()); + cluster, dns_refresh_rate_ms_.count(), factory_context.api().randomGenerator()); std::list resolve_targets; const envoy::config::endpoint::v3::ClusterLoadAssignment load_assignment( diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index e7a5c129a06b..8e6a6db3c507 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -641,14 +641,12 @@ class FactoryContextImpl : public Server::Configuration::CommonFactoryContext { FactoryContextImpl(Stats::Scope& stats_scope, Envoy::Runtime::Loader& runtime, Server::Configuration::TransportSocketFactoryContext& c) : admin_(c.admin()), stats_scope_(stats_scope), cluster_manager_(c.clusterManager()), - local_info_(c.localInfo()), dispatcher_(c.dispatcher()), random_(c.random()), - runtime_(runtime), singleton_manager_(c.singletonManager()), tls_(c.threadLocal()), - api_(c.api()) {} + local_info_(c.localInfo()), dispatcher_(c.dispatcher()), runtime_(runtime), + singleton_manager_(c.singletonManager()), tls_(c.threadLocal()), api_(c.api()) {} Upstream::ClusterManager& clusterManager() override { return cluster_manager_; } Event::Dispatcher& dispatcher() override { return dispatcher_; } const LocalInfo::LocalInfo& localInfo() const override { return local_info_; } - Envoy::Random::RandomGenerator& random() override { return random_; } Envoy::Runtime::Loader& runtime() override { return runtime_; } Stats::Scope& scope() override { return stats_scope_; } Singleton::Manager& singletonManager() override { return singleton_manager_; } @@ -667,7 +665,6 @@ class FactoryContextImpl : public Server::Configuration::CommonFactoryContext { Upstream::ClusterManager& cluster_manager_; const LocalInfo::LocalInfo& local_info_; Event::Dispatcher& dispatcher_; - Envoy::Random::RandomGenerator& random_; Envoy::Runtime::Loader& runtime_; Singleton::Manager& singleton_manager_; ThreadLocal::SlotAllocator& tls_; diff --git a/source/extensions/clusters/aggregate/cluster.cc b/source/extensions/clusters/aggregate/cluster.cc index c3aa00eadfda..958c678d0202 100644 --- a/source/extensions/clusters/aggregate/cluster.cc +++ b/source/extensions/clusters/aggregate/cluster.cc @@ -176,9 +176,10 @@ ClusterFactory::createClusterWithConfig( Upstream::ClusterFactoryContext& context, Server::Configuration::TransportSocketFactoryContextImpl& socket_factory_context, Stats::ScopePtr&& stats_scope) { - auto new_cluster = std::make_shared( - cluster, proto_config, context.clusterManager(), context.runtime(), context.random(), - socket_factory_context, std::move(stats_scope), context.tls(), context.addedViaApi()); + auto new_cluster = + std::make_shared(cluster, proto_config, context.clusterManager(), context.runtime(), + context.api().randomGenerator(), socket_factory_context, + std::move(stats_scope), context.tls(), context.addedViaApi()); auto lb = std::make_unique(*new_cluster); return std::make_pair(new_cluster, std::move(lb)); } diff --git a/source/extensions/clusters/dynamic_forward_proxy/cluster.cc b/source/extensions/clusters/dynamic_forward_proxy/cluster.cc index c5af40dfd401..c5215d894b57 100644 --- a/source/extensions/clusters/dynamic_forward_proxy/cluster.cc +++ b/source/extensions/clusters/dynamic_forward_proxy/cluster.cc @@ -197,8 +197,8 @@ ClusterFactory::createClusterWithConfig( Server::Configuration::TransportSocketFactoryContextImpl& socket_factory_context, Stats::ScopePtr&& stats_scope) { Extensions::Common::DynamicForwardProxy::DnsCacheManagerFactoryImpl cache_manager_factory( - context.singletonManager(), context.dispatcher(), context.tls(), context.random(), - context.runtime(), context.stats()); + context.singletonManager(), context.dispatcher(), context.tls(), + context.api().randomGenerator(), context.runtime(), context.stats()); envoy::config::cluster::v3::Cluster cluster_config = cluster; if (cluster_config.has_upstream_http_protocol_options()) { if (!proto_config.allow_insecure_cluster_options() && diff --git a/source/extensions/clusters/redis/redis_cluster.cc b/source/extensions/clusters/redis/redis_cluster.cc index db5f04d91807..b87c00a4982d 100644 --- a/source/extensions/clusters/redis/redis_cluster.cc +++ b/source/extensions/clusters/redis/redis_cluster.cc @@ -43,7 +43,7 @@ RedisCluster::RedisCluster( cluster.has_load_assignment() ? cluster.load_assignment() : Config::Utility::translateClusterHosts(cluster.hidden_envoy_deprecated_hosts())), - local_info_(factory_context.localInfo()), random_(factory_context.random()), + local_info_(factory_context.localInfo()), random_(api.randomGenerator()), redis_discovery_session_(*this, redis_client_factory), lb_factory_(std::move(lb_factory)), auth_username_( NetworkFilters::RedisProxy::ProtocolOptionsConfigImpl::authUsername(info(), api)), @@ -397,7 +397,8 @@ RedisClusterFactory::createClusterWithConfig( std::move(stats_scope), context.addedViaApi(), nullptr), nullptr); } - auto lb_factory = std::make_shared(context.random()); + auto lb_factory = + std::make_shared(context.api().randomGenerator()); return std::make_pair(std::make_shared( cluster, proto_config, NetworkFilters::Common::Redis::Client::ClientFactoryImpl::instance_, diff --git a/source/extensions/filters/network/http_connection_manager/config.cc b/source/extensions/filters/network/http_connection_manager/config.cc index 2b502d0af7c5..93fff20c3ca9 100644 --- a/source/extensions/filters/network/http_connection_manager/config.cc +++ b/source/extensions/filters/network/http_connection_manager/config.cc @@ -583,14 +583,14 @@ HttpConnectionManagerConfig::createCodec(Network::Connection& connection, return std::make_unique( connection, callbacks, Http::Http2::CodecStats::atomicGet(http2_codec_stats_, context_.scope()), - context_.random(), http2_options_, maxRequestHeadersKb(), maxRequestHeadersCount(), - headersWithUnderscoresAction()); + context_.api().randomGenerator(), http2_options_, maxRequestHeadersKb(), + maxRequestHeadersCount(), headersWithUnderscoresAction()); } else { return std::make_unique( connection, callbacks, Http::Http2::CodecStats::atomicGet(http2_codec_stats_, context_.scope()), - context_.random(), http2_options_, maxRequestHeadersKb(), maxRequestHeadersCount(), - headersWithUnderscoresAction()); + context_.api().randomGenerator(), http2_options_, maxRequestHeadersKb(), + maxRequestHeadersCount(), headersWithUnderscoresAction()); } } case CodecType::HTTP3: @@ -604,9 +604,9 @@ HttpConnectionManagerConfig::createCodec(Network::Connection& connection, .createQuicServerConnection(connection, callbacks)); case CodecType::AUTO: return Http::ConnectionManagerUtility::autoCreateCodec( - connection, data, callbacks, context_.scope(), context_.random(), http1_codec_stats_, - http2_codec_stats_, http1_settings_, http2_options_, maxRequestHeadersKb(), - maxRequestHeadersCount(), headersWithUnderscoresAction()); + connection, data, callbacks, context_.scope(), context_.api().randomGenerator(), + http1_codec_stats_, http2_codec_stats_, http1_settings_, http2_options_, + maxRequestHeadersKb(), maxRequestHeadersCount(), headersWithUnderscoresAction()); } NOT_REACHED_GCOVR_EXCL_LINE; } diff --git a/source/extensions/tracers/xray/xray_tracer_impl.cc b/source/extensions/tracers/xray/xray_tracer_impl.cc index 57f6c9e869f4..d2fa82cf88b5 100644 --- a/source/extensions/tracers/xray/xray_tracer_impl.cc +++ b/source/extensions/tracers/xray/xray_tracer_impl.cc @@ -47,16 +47,16 @@ Driver::Driver(const XRayConfiguration& config, ENVOY_LOG(debug, "send X-Ray generated segments to daemon address on {}", daemon_endpoint); sampling_strategy_ = std::make_unique( - xray_config_.sampling_rules_, context.serverFactoryContext().random(), + xray_config_.sampling_rules_, context.serverFactoryContext().api().randomGenerator(), context.serverFactoryContext().timeSource()); tls_slot_ptr_->set([this, daemon_endpoint, &context](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { DaemonBrokerPtr broker = std::make_unique(daemon_endpoint); - TracerPtr tracer = std::make_unique(xray_config_.segment_name_, xray_config_.origin_, - xray_config_.aws_metadata_, std::move(broker), - context.serverFactoryContext().timeSource(), - context.serverFactoryContext().random()); + TracerPtr tracer = std::make_unique( + xray_config_.segment_name_, xray_config_.origin_, xray_config_.aws_metadata_, + std::move(broker), context.serverFactoryContext().timeSource(), + context.serverFactoryContext().api().randomGenerator()); return std::make_shared(std::move(tracer), *this); }); } diff --git a/source/extensions/tracers/zipkin/config.cc b/source/extensions/tracers/zipkin/config.cc index 36d1f38fae8e..f2171dee9b72 100644 --- a/source/extensions/tracers/zipkin/config.cc +++ b/source/extensions/tracers/zipkin/config.cc @@ -23,7 +23,8 @@ Tracing::HttpTracerSharedPtr ZipkinTracerFactory::createHttpTracerTyped( proto_config, context.serverFactoryContext().clusterManager(), context.serverFactoryContext().scope(), context.serverFactoryContext().threadLocal(), context.serverFactoryContext().runtime(), context.serverFactoryContext().localInfo(), - context.serverFactoryContext().random(), context.serverFactoryContext().timeSource()); + context.serverFactoryContext().api().randomGenerator(), + context.serverFactoryContext().timeSource()); return std::make_shared(std::move(zipkin_driver), context.serverFactoryContext().localInfo()); diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 11ec9cdd9efc..5b0d7d7a9f1d 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -146,8 +146,8 @@ void AdminImpl::startHttpListener(const std::string& access_log_path, } AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server) - : server_(server), - request_id_extension_(Http::RequestIDExtensionFactory::defaultInstance(server_.random())), + : server_(server), request_id_extension_(Http::RequestIDExtensionFactory::defaultInstance( + server_.api().randomGenerator())), profile_path_(profile_path), stats_(Http::ConnectionManagerImpl::generateStats("http.admin.", server_.stats())), null_overload_manager_(server_.threadLocal()), @@ -227,8 +227,8 @@ Http::ServerConnectionPtr AdminImpl::createCodec(Network::Connection& connection const Buffer::Instance& data, Http::ServerConnectionCallbacks& callbacks) { return Http::ConnectionManagerUtility::autoCreateCodec( - connection, data, callbacks, server_.stats(), server_.random(), http1_codec_stats_, - http2_codec_stats_, Http::Http1Settings(), + connection, data, callbacks, server_.stats(), server_.api().randomGenerator(), + http1_codec_stats_, http2_codec_stats_, Http::Http1Settings(), ::Envoy::Http2::Utility::initializeAndValidateOptions( envoy::config::core::v3::Http2ProtocolOptions()), maxRequestHeadersKb(), maxRequestHeadersCount(), headersWithUnderscoresAction()); @@ -239,8 +239,8 @@ bool AdminImpl::createNetworkFilterChain(Network::Connection& connection, // Pass in the null overload manager so that the admin interface is accessible even when Envoy is // overloaded. connection.addReadFilter(Network::ReadFilterSharedPtr{new Http::ConnectionManagerImpl( - *this, server_.drainManager(), server_.random(), server_.httpContext(), server_.runtime(), - server_.localInfo(), server_.clusterManager(), null_overload_manager_, + *this, server_.drainManager(), server_.api().randomGenerator(), server_.httpContext(), + server_.runtime(), server_.localInfo(), server_.clusterManager(), null_overload_manager_, server_.timeSource())}); return true; } diff --git a/source/server/config_validation/cluster_manager.cc b/source/server/config_validation/cluster_manager.cc index d5f4489c918c..38e1413da9d9 100644 --- a/source/server/config_validation/cluster_manager.cc +++ b/source/server/config_validation/cluster_manager.cc @@ -11,9 +11,8 @@ namespace Upstream { ClusterManagerPtr ValidationClusterManagerFactory::clusterManagerFromProto( const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { return std::make_unique( - bootstrap, *this, stats_, tls_, runtime_, random_, local_info_, log_manager_, - main_thread_dispatcher_, admin_, validation_context_, api_, http_context_, grpc_context_, - time_system_); + bootstrap, *this, stats_, tls_, runtime_, local_info_, log_manager_, main_thread_dispatcher_, + admin_, validation_context_, api_, http_context_, grpc_context_, time_system_); } CdsApiPtr @@ -28,11 +27,11 @@ ValidationClusterManagerFactory::createCds(const envoy::config::core::v3::Config ValidationClusterManager::ValidationClusterManager( const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, - Random::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, - Server::Admin& admin, ProtobufMessage::ValidationContext& validation_context, Api::Api& api, + const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, + Event::Dispatcher& main_thread_dispatcher, Server::Admin& admin, + ProtobufMessage::ValidationContext& validation_context, Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, Event::TimeSystem& time_system) - : ClusterManagerImpl(bootstrap, factory, stats, tls, runtime, random, local_info, log_manager, + : ClusterManagerImpl(bootstrap, factory, stats, tls, runtime, local_info, log_manager, main_thread_dispatcher, admin, validation_context, api, http_context, grpc_context), async_client_(api, time_system) {} diff --git a/source/server/config_validation/cluster_manager.h b/source/server/config_validation/cluster_manager.h index 6ce2c46941fe..38bda5ad2f8d 100644 --- a/source/server/config_validation/cluster_manager.h +++ b/source/server/config_validation/cluster_manager.h @@ -23,17 +23,17 @@ class ValidationClusterManagerFactory : public ProdClusterManagerFactory { explicit ValidationClusterManagerFactory( Server::Admin& admin, Runtime::Loader& runtime, Stats::Store& stats, - ThreadLocal::Instance& tls, Random::RandomGenerator& random, - Network::DnsResolverSharedPtr dns_resolver, Ssl::ContextManager& ssl_context_manager, - Event::Dispatcher& main_thread_dispatcher, const LocalInfo::LocalInfo& local_info, - Secret::SecretManager& secret_manager, ProtobufMessage::ValidationContext& validation_context, - Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, + ThreadLocal::Instance& tls, Network::DnsResolverSharedPtr dns_resolver, + Ssl::ContextManager& ssl_context_manager, Event::Dispatcher& main_thread_dispatcher, + const LocalInfo::LocalInfo& local_info, Secret::SecretManager& secret_manager, + ProtobufMessage::ValidationContext& validation_context, Api::Api& api, + Http::Context& http_context, Grpc::Context& grpc_context, AccessLog::AccessLogManager& log_manager, Singleton::Manager& singleton_manager, Event::TimeSystem& time_system) - : ProdClusterManagerFactory(admin, runtime, stats, tls, random, dns_resolver, - ssl_context_manager, main_thread_dispatcher, local_info, - secret_manager, validation_context, api, http_context, - grpc_context, log_manager, singleton_manager), + : ProdClusterManagerFactory(admin, runtime, stats, tls, dns_resolver, ssl_context_manager, + main_thread_dispatcher, local_info, secret_manager, + validation_context, api, http_context, grpc_context, log_manager, + singleton_manager), grpc_context_(grpc_context), time_system_(time_system) {} ClusterManagerPtr @@ -57,7 +57,7 @@ class ValidationClusterManager : public ClusterManagerImpl { ValidationClusterManager(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, - Random::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, + const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, Event::Dispatcher& dispatcher, Server::Admin& admin, ProtobufMessage::ValidationContext& validation_context, Api::Api& api, diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index 2f3e888f8e40..877e7293f293 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -99,8 +99,8 @@ void ValidationInstance::initialize(const Options& options, secret_manager_ = std::make_unique(admin().getConfigTracker()); ssl_context_manager_ = createContextManager("ssl_context_manager", api_->timeSource()); cluster_manager_factory_ = std::make_unique( - admin(), runtime(), stats(), threadLocal(), random(), dnsResolver(), sslContextManager(), - dispatcher(), localInfo(), *secret_manager_, messageValidationContext(), *api_, http_context_, + admin(), runtime(), stats(), threadLocal(), dnsResolver(), sslContextManager(), dispatcher(), + localInfo(), *secret_manager_, messageValidationContext(), *api_, http_context_, grpc_context_, accessLogManager(), singletonManager(), time_system_); config_.initialize(bootstrap, *this, *cluster_manager_factory_); runtime().initialize(clusterManager()); diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index a82364038ad9..4909e5270f20 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -85,7 +85,6 @@ class ValidationInstance final : Logger::Loggable, ServerLifecycleNotifier& lifecycleNotifier() override { return *this; } ListenerManager& listenerManager() override { return *listener_manager_; } Secret::SecretManager& secretManager() override { return *secret_manager_; } - Random::RandomGenerator& random() override { return random_generator_; } Runtime::Loader& runtime() override { return Runtime::LoaderSingleton::get(); } void shutdown() override; bool isShutdown() override { return false; } diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index 237f576c37af..c0b3524adcff 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -164,7 +164,7 @@ WatchdogImpl::WatchdogImpl(const envoy::config::bootstrap::v3::Watchdog& watchdo // We shouldn't have overflow issues due to the range of Duration. // This won't be entirely uniform, depending on how large max_skew // is relation to uint64. - kill_timeout += (server.random().random() % max_kill_timeout_jitter) + 1; + kill_timeout += (server.api().randomGenerator().random() % max_kill_timeout_jitter) + 1; } kill_timeout_ = std::chrono::milliseconds(kill_timeout); diff --git a/source/server/drain_manager_impl.cc b/source/server/drain_manager_impl.cc index d9b1e1dcfa8f..132108b95eaf 100644 --- a/source/server/drain_manager_impl.cc +++ b/source/server/drain_manager_impl.cc @@ -49,7 +49,7 @@ bool DrainManagerImpl::drainClose() const { ASSERT(server_.options().drainTime() >= remaining_time); const auto elapsed_time = server_.options().drainTime() - remaining_time; return static_cast(elapsed_time.count()) > - (server_.random().random() % server_.options().drainTime().count()); + (server_.api().randomGenerator().random() % server_.options().drainTime().count()); } void DrainManagerImpl::startDrainSequence(std::function drain_complete_cb) { diff --git a/source/server/filter_chain_manager_impl.cc b/source/server/filter_chain_manager_impl.cc index a342e686f935..0de4282f3314 100644 --- a/source/server/filter_chain_manager_impl.cc +++ b/source/server/filter_chain_manager_impl.cc @@ -88,10 +88,6 @@ const LocalInfo::LocalInfo& PerFilterChainFactoryContextImpl::localInfo() const return parent_context_.localInfo(); } -Envoy::Random::RandomGenerator& PerFilterChainFactoryContextImpl::random() { - return parent_context_.random(); -} - Envoy::Runtime::Loader& PerFilterChainFactoryContextImpl::runtime() { return parent_context_.runtime(); } @@ -635,7 +631,6 @@ bool FactoryContextImpl::healthCheckFailed() { return server_.healthCheckFailed( Http::Context& FactoryContextImpl::httpContext() { return server_.httpContext(); } Init::Manager& FactoryContextImpl::initManager() { return server_.initManager(); } const LocalInfo::LocalInfo& FactoryContextImpl::localInfo() const { return server_.localInfo(); } -Envoy::Random::RandomGenerator& FactoryContextImpl::random() { return server_.random(); } Envoy::Runtime::Loader& FactoryContextImpl::runtime() { return server_.runtime(); } Stats::Scope& FactoryContextImpl::scope() { return global_scope_; } Singleton::Manager& FactoryContextImpl::singletonManager() { return server_.singletonManager(); } diff --git a/source/server/filter_chain_manager_impl.h b/source/server/filter_chain_manager_impl.h index 59af0bb78ac5..7c5f830666b3 100644 --- a/source/server/filter_chain_manager_impl.h +++ b/source/server/filter_chain_manager_impl.h @@ -58,7 +58,6 @@ class PerFilterChainFactoryContextImpl : public Configuration::FilterChainFactor Http::Context& httpContext() override; Init::Manager& initManager() override; const LocalInfo::LocalInfo& localInfo() const override; - Envoy::Random::RandomGenerator& random() override; Envoy::Runtime::Loader& runtime() override; Stats::Scope& scope() override; Singleton::Manager& singletonManager() override; @@ -131,7 +130,6 @@ class FactoryContextImpl : public Configuration::FactoryContext { Http::Context& httpContext() override; Init::Manager& initManager() override; const LocalInfo::LocalInfo& localInfo() const override; - Envoy::Random::RandomGenerator& random() override; Envoy::Runtime::Loader& runtime() override; Stats::Scope& scope() override; Singleton::Manager& singletonManager() override; diff --git a/source/server/listener_impl.cc b/source/server/listener_impl.cc index 3238108fa712..4f3d0b3bc523 100644 --- a/source/server/listener_impl.cc +++ b/source/server/listener_impl.cc @@ -172,9 +172,6 @@ Http::Context& ListenerFactoryContextBaseImpl::httpContext() { return server_.ht const LocalInfo::LocalInfo& ListenerFactoryContextBaseImpl::localInfo() const { return server_.localInfo(); } -Envoy::Random::RandomGenerator& ListenerFactoryContextBaseImpl::random() { - return server_.random(); -} Envoy::Runtime::Loader& ListenerFactoryContextBaseImpl::runtime() { return server_.runtime(); } Stats::Scope& ListenerFactoryContextBaseImpl::scope() { return *global_scope_; } Singleton::Manager& ListenerFactoryContextBaseImpl::singletonManager() { @@ -481,8 +478,8 @@ void ListenerImpl::buildFilterChains() { Server::Configuration::TransportSocketFactoryContextImpl transport_factory_context( parent_.server_.admin(), parent_.server_.sslContextManager(), listenerScope(), parent_.server_.clusterManager(), parent_.server_.localInfo(), parent_.server_.dispatcher(), - parent_.server_.random(), parent_.server_.stats(), parent_.server_.singletonManager(), - parent_.server_.threadLocal(), validation_visitor_, parent_.server_.api()); + parent_.server_.stats(), parent_.server_.singletonManager(), parent_.server_.threadLocal(), + validation_visitor_, parent_.server_.api()); transport_factory_context.setInitManager(*dynamic_init_manager_); // The init manager is a little messy. Will refactor when filter chain manager could accept // network filter chain update. @@ -583,9 +580,6 @@ Http::Context& PerListenerFactoryContextImpl::httpContext() { const LocalInfo::LocalInfo& PerListenerFactoryContextImpl::localInfo() const { return listener_factory_context_base_->localInfo(); } -Envoy::Random::RandomGenerator& PerListenerFactoryContextImpl::random() { - return listener_factory_context_base_->random(); -} Envoy::Runtime::Loader& PerListenerFactoryContextImpl::runtime() { return listener_factory_context_base_->runtime(); } diff --git a/source/server/listener_impl.h b/source/server/listener_impl.h index ec0119b32b2f..b0fb4f5ec772 100644 --- a/source/server/listener_impl.h +++ b/source/server/listener_impl.h @@ -106,7 +106,6 @@ class ListenerFactoryContextBaseImpl final : public Configuration::FactoryContex Http::Context& httpContext() override; Init::Manager& initManager() override; const LocalInfo::LocalInfo& localInfo() const override; - Envoy::Random::RandomGenerator& random() override; Envoy::Runtime::Loader& runtime() override; Stats::Scope& scope() override; Singleton::Manager& singletonManager() override; @@ -172,7 +171,6 @@ class PerListenerFactoryContextImpl : public Configuration::ListenerFactoryConte Http::Context& httpContext() override; Init::Manager& initManager() override; const LocalInfo::LocalInfo& localInfo() const override; - Envoy::Random::RandomGenerator& random() override; Envoy::Runtime::Loader& runtime() override; Stats::Scope& scope() override; Singleton::Manager& singletonManager() override; diff --git a/source/server/listener_manager_impl.cc b/source/server/listener_manager_impl.cc index 8506d5cbc286..d2d81a57a914 100644 --- a/source/server/listener_manager_impl.cc +++ b/source/server/listener_manager_impl.cc @@ -359,7 +359,7 @@ bool ListenerManagerImpl::addOrUpdateListener(const envoy::config::listener::v3: if (!config.name().empty()) { name = config.name(); } else { - name = server_.random().uuid(); + name = server_.api().randomGenerator().uuid(); } auto it = error_state_tracker_.find(name); diff --git a/source/server/server.cc b/source/server/server.cc index 48279e046bc5..b0a16884d982 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -487,8 +487,8 @@ void InstanceImpl::initialize(const Options& options, dns_resolver_ = dispatcher_->createDnsResolver({}, use_tcp_for_dns_lookups); cluster_manager_factory_ = std::make_unique( - *admin_, Runtime::LoaderSingleton::get(), stats_store_, thread_local_, *random_generator_, - dns_resolver_, *ssl_context_manager_, *dispatcher_, *local_info_, *secret_manager_, + *admin_, Runtime::LoaderSingleton::get(), stats_store_, thread_local_, dns_resolver_, + *ssl_context_manager_, *dispatcher_, *local_info_, *secret_manager_, messageValidationContext(), *api_, http_context_, grpc_context_, access_log_manager_, *singleton_manager_); @@ -583,8 +583,8 @@ Runtime::LoaderPtr InstanceUtil::createRuntime(Instance& server, ENVOY_LOG(info, "runtime: {}", MessageUtil::getYamlStringFromMessage(config.runtime())); return std::make_unique( server.dispatcher(), server.threadLocal(), config.runtime(), server.localInfo(), - server.stats(), server.random(), server.messageValidationContext().dynamicValidationVisitor(), - server.api()); + server.stats(), server.api().randomGenerator(), + server.messageValidationContext().dynamicValidationVisitor(), server.api()); } void InstanceImpl::loadServerFlags(const absl::optional& flags_path) { diff --git a/source/server/server.h b/source/server/server.h index 5cd839001480..f9ce2954d581 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -163,7 +163,6 @@ class ServerFactoryContextImpl : public Configuration::ServerFactoryContext, ProtobufMessage::ValidationContext& messageValidationContext() override { return server_.messageValidationContext(); } - Envoy::Random::RandomGenerator& random() override { return server_.api().randomGenerator(); } Envoy::Runtime::Loader& runtime() override { return server_.runtime(); } Stats::Scope& scope() override { return *server_scope_; } Singleton::Manager& singletonManager() override { return server_.singletonManager(); } @@ -239,7 +238,6 @@ class InstanceImpl final : Logger::Loggable, Secret::SecretManager& secretManager() override { return *secret_manager_; } Envoy::MutexTracer* mutexTracer() override { return mutex_tracer_; } OverloadManager& overloadManager() override { return *overload_manager_; } - Random::RandomGenerator& random() override { return api_->randomGenerator(); } Runtime::Loader& runtime() override; void shutdown() override; bool isShutdown() final { return shutdown_; } diff --git a/source/server/transport_socket_config_impl.h b/source/server/transport_socket_config_impl.h index 560b9cf61aed..4ad45487e73d 100644 --- a/source/server/transport_socket_config_impl.h +++ b/source/server/transport_socket_config_impl.h @@ -12,16 +12,18 @@ namespace Configuration { */ class TransportSocketFactoryContextImpl : public TransportSocketFactoryContext { public: - TransportSocketFactoryContextImpl( - Server::Admin& admin, Ssl::ContextManager& context_manager, Stats::Scope& stats_scope, - Upstream::ClusterManager& cm, const LocalInfo::LocalInfo& local_info, - Event::Dispatcher& dispatcher, Envoy::Random::RandomGenerator& random, Stats::Store& stats, - Singleton::Manager& singleton_manager, ThreadLocal::SlotAllocator& tls, - ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api) + TransportSocketFactoryContextImpl(Server::Admin& admin, Ssl::ContextManager& context_manager, + Stats::Scope& stats_scope, Upstream::ClusterManager& cm, + const LocalInfo::LocalInfo& local_info, + Event::Dispatcher& dispatcher, Stats::Store& stats, + Singleton::Manager& singleton_manager, + ThreadLocal::SlotAllocator& tls, + ProtobufMessage::ValidationVisitor& validation_visitor, + Api::Api& api) : admin_(admin), context_manager_(context_manager), stats_scope_(stats_scope), - cluster_manager_(cm), local_info_(local_info), dispatcher_(dispatcher), random_(random), - stats_(stats), singleton_manager_(singleton_manager), tls_(tls), - validation_visitor_(validation_visitor), api_(api) {} + cluster_manager_(cm), local_info_(local_info), dispatcher_(dispatcher), stats_(stats), + singleton_manager_(singleton_manager), tls_(tls), validation_visitor_(validation_visitor), + api_(api) {} /** * Pass an init manager to register dynamic secret provider. @@ -39,7 +41,6 @@ class TransportSocketFactoryContextImpl : public TransportSocketFactoryContext { Upstream::ClusterManager& clusterManager() override { return cluster_manager_; } const LocalInfo::LocalInfo& localInfo() const override { return local_info_; } Event::Dispatcher& dispatcher() override { return dispatcher_; } - Envoy::Random::RandomGenerator& random() override { return random_; } Stats::Store& stats() override { return stats_; } Init::Manager& initManager() override { ASSERT(init_manager_ != nullptr); @@ -59,7 +60,6 @@ class TransportSocketFactoryContextImpl : public TransportSocketFactoryContext { Upstream::ClusterManager& cluster_manager_; const LocalInfo::LocalInfo& local_info_; Event::Dispatcher& dispatcher_; - Envoy::Random::RandomGenerator& random_; Stats::Store& stats_; Singleton::Manager& singleton_manager_; ThreadLocal::SlotAllocator& tls_; diff --git a/test/common/access_log/access_log_impl_test.cc b/test/common/access_log/access_log_impl_test.cc index a53bba8de6c8..0b6ce4734f9d 100644 --- a/test/common/access_log/access_log_impl_test.cc +++ b/test/common/access_log/access_log_impl_test.cc @@ -283,13 +283,13 @@ name: accesslog InstanceSharedPtr log = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); // Value is taken from random generator. - EXPECT_CALL(context_.random_, random()).WillOnce(Return(42)); + EXPECT_CALL(context_.api_.random_, random()).WillOnce(Return(42)); EXPECT_CALL(runtime_.snapshot_, featureEnabled("access_log.test_key", 0, 42, 100)) .WillOnce(Return(true)); EXPECT_CALL(*file_, write(_)); log->log(&request_headers_, &response_headers_, &response_trailers_, stream_info_); - EXPECT_CALL(context_.random_, random()).WillOnce(Return(43)); + EXPECT_CALL(context_.api_.random_, random()).WillOnce(Return(43)); EXPECT_CALL(runtime_.snapshot_, featureEnabled("access_log.test_key", 0, 43, 100)) .WillOnce(Return(false)); EXPECT_CALL(*file_, write(_)).Times(0); @@ -326,13 +326,13 @@ name: accesslog InstanceSharedPtr log = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); // Value is taken from random generator. - EXPECT_CALL(context_.random_, random()).WillOnce(Return(42)); + EXPECT_CALL(context_.api_.random_, random()).WillOnce(Return(42)); EXPECT_CALL(runtime_.snapshot_, featureEnabled("access_log.test_key", 5, 42, 10000)) .WillOnce(Return(true)); EXPECT_CALL(*file_, write(_)); log->log(&request_headers_, &response_headers_, &response_trailers_, stream_info_); - EXPECT_CALL(context_.random_, random()).WillOnce(Return(43)); + EXPECT_CALL(context_.api_.random_, random()).WillOnce(Return(43)); EXPECT_CALL(runtime_.snapshot_, featureEnabled("access_log.test_key", 5, 43, 10000)) .WillOnce(Return(false)); EXPECT_CALL(*file_, write(_)).Times(0); @@ -370,13 +370,13 @@ name: accesslog // Value should not be taken from x-request-id. request_headers_.addCopy("x-request-id", "000000ff-0000-0000-0000-000000000000"); - EXPECT_CALL(context_.random_, random()).WillOnce(Return(42)); + EXPECT_CALL(context_.api_.random_, random()).WillOnce(Return(42)); EXPECT_CALL(runtime_.snapshot_, featureEnabled("access_log.test_key", 5, 42, 1000000)) .WillOnce(Return(true)); EXPECT_CALL(*file_, write(_)); log->log(&request_headers_, &response_headers_, &response_trailers_, stream_info_); - EXPECT_CALL(context_.random_, random()).WillOnce(Return(43)); + EXPECT_CALL(context_.api_.random_, random()).WillOnce(Return(43)); EXPECT_CALL(runtime_.snapshot_, featureEnabled("access_log.test_key", 5, 43, 1000000)) .WillOnce(Return(false)); EXPECT_CALL(*file_, write(_)).Times(0); diff --git a/test/common/config/subscription_factory_impl_test.cc b/test/common/config/subscription_factory_impl_test.cc index bc8c58750d95..65a7e1b7bd12 100644 --- a/test/common/config/subscription_factory_impl_test.cc +++ b/test/common/config/subscription_factory_impl_test.cc @@ -38,9 +38,9 @@ namespace { class SubscriptionFactoryTest : public testing::Test { public: SubscriptionFactoryTest() - : http_request_(&cm_.async_client_), api_(Api::createApiForTest(stats_store_)), - subscription_factory_(local_info_, dispatcher_, cm_, random_, validation_visitor_, *api_, - runtime_) {} + : http_request_(&cm_.async_client_), api_(Api::createApiForTest(stats_store_, random_)), + subscription_factory_(local_info_, dispatcher_, cm_, validation_visitor_, *api_, runtime_) { + } SubscriptionPtr subscriptionFromConfigSource(const envoy::config::core::v3::ConfigSource& config) { diff --git a/test/common/tcp_proxy/tcp_proxy_test.cc b/test/common/tcp_proxy/tcp_proxy_test.cc index 773e8457c1cc..803381c4192c 100644 --- a/test/common/tcp_proxy/tcp_proxy_test.cc +++ b/test/common/tcp_proxy/tcp_proxy_test.cc @@ -435,10 +435,10 @@ TEST(ConfigTest, WeightedClustersConfig) { Config config_obj(constructConfigFromV3Yaml(yaml, factory_context)); NiceMock connection; - EXPECT_CALL(factory_context.random_, random()).WillOnce(Return(0)); + EXPECT_CALL(factory_context.api_.random_, random()).WillOnce(Return(0)); EXPECT_EQ(std::string("cluster1"), config_obj.getRouteFromEntries(connection)->clusterName()); - EXPECT_CALL(factory_context.random_, random()).WillOnce(Return(2)); + EXPECT_CALL(factory_context.api_.random_, random()).WillOnce(Return(2)); EXPECT_EQ(std::string("cluster2"), config_obj.getRouteFromEntries(connection)->clusterName()); } @@ -475,7 +475,7 @@ TEST(ConfigTest, WeightedClustersWithMetadataMatchConfig) { HashedValue hv1(v1), hv2(v2); NiceMock connection; - EXPECT_CALL(factory_context.random_, random()).WillOnce(Return(0)); + EXPECT_CALL(factory_context.api_.random_, random()).WillOnce(Return(0)); const auto route = config_obj.getRouteFromEntries(connection); EXPECT_NE(nullptr, route); @@ -502,7 +502,7 @@ TEST(ConfigTest, WeightedClustersWithMetadataMatchConfig) { HashedValue hv3(v3), hv4(v4); NiceMock connection; - EXPECT_CALL(factory_context.random_, random()).WillOnce(Return(2)); + EXPECT_CALL(factory_context.api_.random_, random()).WillOnce(Return(2)); const auto route = config_obj.getRouteFromEntries(connection); EXPECT_NE(nullptr, route); @@ -568,7 +568,7 @@ TEST(ConfigTest, WeightedClustersWithMetadataMatchAndTopLevelMetadataMatchConfig HashedValue hv1(v1), hv2(v2); NiceMock connection; - EXPECT_CALL(factory_context.random_, random()).WillOnce(Return(0)); + EXPECT_CALL(factory_context.api_.random_, random()).WillOnce(Return(0)); const auto route = config_obj.getRouteFromEntries(connection); EXPECT_NE(nullptr, route); @@ -601,7 +601,7 @@ TEST(ConfigTest, WeightedClustersWithMetadataMatchAndTopLevelMetadataMatchConfig HashedValue hv3(v3), hv4(v4); NiceMock connection; - EXPECT_CALL(factory_context.random_, random()).WillOnce(Return(2)); + EXPECT_CALL(factory_context.api_.random_, random()).WillOnce(Return(2)); const auto route = config_obj.getRouteFromEntries(connection); EXPECT_NE(nullptr, route); @@ -1336,7 +1336,7 @@ TEST_F(TcpProxyTest, WeightedClusterWithMetadataMatch) { { Upstream::LoadBalancerContext* context; - EXPECT_CALL(factory_context_.random_, random()).WillOnce(Return(0)); + EXPECT_CALL(factory_context_.api_.random_, random()).WillOnce(Return(0)); EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster("cluster1", _, _)) .WillOnce(DoAll(SaveArg<2>(&context), Return(nullptr))); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); @@ -1360,7 +1360,7 @@ TEST_F(TcpProxyTest, WeightedClusterWithMetadataMatch) { { Upstream::LoadBalancerContext* context; - EXPECT_CALL(factory_context_.random_, random()).WillOnce(Return(2)); + EXPECT_CALL(factory_context_.api_.random_, random()).WillOnce(Return(2)); EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster("cluster2", _, _)) .WillOnce(DoAll(SaveArg<2>(&context), Return(nullptr))); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); diff --git a/test/common/upstream/cluster_factory_impl_test.cc b/test/common/upstream/cluster_factory_impl_test.cc index bf6032ca4f6f..7196fe2c99be 100644 --- a/test/common/upstream/cluster_factory_impl_test.cc +++ b/test/common/upstream/cluster_factory_impl_test.cc @@ -62,7 +62,6 @@ class ClusterFactoryTestBase { const NiceMock local_info_; NiceMock dispatcher_; NiceMock runtime_; - NiceMock random_; Stats::IsolatedStoreImpl stats_; Singleton::ManagerImpl singleton_manager_{Thread::threadFactoryForTest()}; NiceMock tls_; @@ -97,9 +96,9 @@ TEST_F(TestStaticClusterImplTest, CreateWithoutConfig) { const envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml); auto create_result = ClusterFactoryImplBase::create( - cluster_config, cm_, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, random_, - dispatcher_, log_manager_, local_info_, admin_, singleton_manager_, - std::move(outlier_event_logger_), false, validation_visitor_, *api_); + cluster_config, cm_, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, dispatcher_, + log_manager_, local_info_, admin_, singleton_manager_, std::move(outlier_event_logger_), + false, validation_visitor_, *api_); auto cluster = create_result.first; cluster->initialize([] {}); @@ -142,9 +141,9 @@ TEST_F(TestStaticClusterImplTest, CreateWithStructConfig) { const envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml); auto create_result = ClusterFactoryImplBase::create( - cluster_config, cm_, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, random_, - dispatcher_, log_manager_, local_info_, admin_, singleton_manager_, - std::move(outlier_event_logger_), false, validation_visitor_, *api_); + cluster_config, cm_, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, dispatcher_, + log_manager_, local_info_, admin_, singleton_manager_, std::move(outlier_event_logger_), + false, validation_visitor_, *api_); auto cluster = create_result.first; cluster->initialize([] {}); @@ -185,9 +184,9 @@ TEST_F(TestStaticClusterImplTest, CreateWithTypedConfig) { const envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml); auto create_result = ClusterFactoryImplBase::create( - cluster_config, cm_, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, random_, - dispatcher_, log_manager_, local_info_, admin_, singleton_manager_, - std::move(outlier_event_logger_), false, validation_visitor_, *api_); + cluster_config, cm_, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, dispatcher_, + log_manager_, local_info_, admin_, singleton_manager_, std::move(outlier_event_logger_), + false, validation_visitor_, *api_); auto cluster = create_result.first; cluster->initialize([] {}); @@ -229,7 +228,7 @@ TEST_F(TestStaticClusterImplTest, UnsupportedClusterType) { const envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml); ClusterFactoryImplBase::create( cluster_config, cm_, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, - random_, dispatcher_, log_manager_, local_info_, admin_, singleton_manager_, + dispatcher_, log_manager_, local_info_, admin_, singleton_manager_, std::move(outlier_event_logger_), false, validation_visitor_, *api_); }, EnvoyException, @@ -262,7 +261,7 @@ TEST_F(TestStaticClusterImplTest, HostnameWithoutDNS) { const envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml); ClusterFactoryImplBase::create( cluster_config, cm_, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, - random_, dispatcher_, log_manager_, local_info_, admin_, singleton_manager_, + dispatcher_, log_manager_, local_info_, admin_, singleton_manager_, std::move(outlier_event_logger_), false, validation_visitor_, *api_); }, EnvoyException, diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index b0a51582b784..b24c45330de5 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -43,14 +43,14 @@ std::string clustersJson(const std::vector& clusters) { class ClusterManagerImplTest : public testing::Test { public: ClusterManagerImplTest() - : api_(Api::createApiForTest()), http_context_(factory_.stats_.symbolTable()), - grpc_context_(factory_.stats_.symbolTable()) {} + : http_context_(factory_.stats_.symbolTable()), grpc_context_(factory_.stats_.symbolTable()) { + } void create(const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { cluster_manager_ = std::make_unique( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.random_, + bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *api_, http_context_, grpc_context_); + *factory_.api_, http_context_, grpc_context_); cluster_manager_->setPrimaryClustersInitializedCb( [this, bootstrap]() { cluster_manager_->initializeSecondaryClusters(bootstrap); }); } @@ -92,9 +92,9 @@ class ClusterManagerImplTest : public testing::Test { const auto& bootstrap = parseBootstrapFromV3Yaml(yaml); cluster_manager_ = std::make_unique( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.random_, + bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *api_, local_cluster_update_, local_hosts_removed_, http_context_, grpc_context_); + *factory_.api_, local_cluster_update_, local_hosts_removed_, http_context_, grpc_context_); } void checkStats(uint64_t added, uint64_t modified, uint64_t removed, uint64_t active, @@ -135,7 +135,6 @@ class ClusterManagerImplTest : public testing::Test { } Event::SimulatedTimeSystem time_system_; - Api::ApiPtr api_; NiceMock factory_; NiceMock validation_context_; std::unique_ptr cluster_manager_; diff --git a/test/common/upstream/eds_speed_test.cc b/test/common/upstream/eds_speed_test.cc index 20b9a5a9fae7..41b56317653a 100644 --- a/test/common/upstream/eds_speed_test.cc +++ b/test/common/upstream/eds_speed_test.cc @@ -76,7 +76,7 @@ class EdsSpeedTest { "cluster.{}.", eds_cluster_.alt_stat_name().empty() ? eds_cluster_.name() : eds_cluster_.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); cluster_ = std::make_shared(eds_cluster_, runtime_, factory_context, std::move(scope), false); diff --git a/test/common/upstream/eds_test.cc b/test/common/upstream/eds_test.cc index e52b807c1d71..d2c330c14153 100644 --- a/test/common/upstream/eds_test.cc +++ b/test/common/upstream/eds_test.cc @@ -95,7 +95,7 @@ class EdsTest : public testing::Test { "cluster.{}.", eds_cluster_.alt_stat_name().empty() ? eds_cluster_.name() : eds_cluster_.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); cluster_ = std::make_shared(eds_cluster_, runtime_, factory_context, std::move(scope), false); diff --git a/test/common/upstream/hds_test.cc b/test/common/upstream/hds_test.cc index a3a531f9255c..f2012e3d31d7 100644 --- a/test/common/upstream/hds_test.cc +++ b/test/common/upstream/hds_test.cc @@ -533,8 +533,8 @@ TEST_F(HdsTest, TestSocketContext) { params.stats_.createScope(fmt::format("cluster.{}.", params.cluster_.name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( params.admin_, params.ssl_context_manager_, *scope, params.cm_, params.local_info_, - params.dispatcher_, params.api_.randomGenerator(), params.stats_, - params.singleton_manager_, params.tls_, params.validation_visitor_, params.api_); + params.dispatcher_, params.stats_, params.singleton_manager_, params.tls_, + params.validation_visitor_, params.api_); // Create a mock socket_factory for the scope of this unit test. std::unique_ptr socket_factory = diff --git a/test/common/upstream/logical_dns_cluster_test.cc b/test/common/upstream/logical_dns_cluster_test.cc index 349697415c98..c348e5132148 100644 --- a/test/common/upstream/logical_dns_cluster_test.cc +++ b/test/common/upstream/logical_dns_cluster_test.cc @@ -41,7 +41,7 @@ namespace { class LogicalDnsClusterTest : public testing::Test { protected: - LogicalDnsClusterTest() : api_(Api::createApiForTest(stats_store_)) {} + LogicalDnsClusterTest() : api_(Api::createApiForTest(stats_store_, random_)) {} void setupFromV3Yaml(const std::string& yaml, bool avoid_boosting = true) { resolve_timer_ = new Event::MockTimer(&dispatcher_); @@ -52,7 +52,7 @@ class LogicalDnsClusterTest : public testing::Test { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm, local_info_, dispatcher_, random_, stats_store_, + admin_, ssl_context_manager_, *scope, cm, local_info_, dispatcher_, stats_store_, singleton_manager_, tls_, validation_visitor_, *api_); cluster_ = std::make_shared(cluster_config, runtime_, dns_resolver_, factory_context, std::move(scope), false); diff --git a/test/common/upstream/original_dst_cluster_test.cc b/test/common/upstream/original_dst_cluster_test.cc index 87e408d521da..4e95e2303882 100644 --- a/test/common/upstream/original_dst_cluster_test.cc +++ b/test/common/upstream/original_dst_cluster_test.cc @@ -80,7 +80,7 @@ class OriginalDstClusterTest : public testing::Test { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm, local_info_, dispatcher_, random_, stats_store_, + admin_, ssl_context_manager_, *scope, cm, local_info_, dispatcher_, stats_store_, singleton_manager_, tls_, validation_visitor_, *api_); cluster_ = std::make_shared(cluster_config, runtime_, factory_context, std::move(scope), false); diff --git a/test/common/upstream/test_cluster_manager.h b/test/common/upstream/test_cluster_manager.h index 596a3acd870d..d76c5b06b0f1 100644 --- a/test/common/upstream/test_cluster_manager.h +++ b/test/common/upstream/test_cluster_manager.h @@ -3,6 +3,7 @@ #include #include +#include "envoy/common/random_generator.h" #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/config/cluster/v3/cluster.pb.h" #include "envoy/config/core/v3/config_source.pb.h" @@ -68,7 +69,7 @@ class TestClusterManagerFactory : public ClusterManagerFactory { Outlier::EventLoggerSharedPtr outlier_event_logger, bool added_via_api) -> std::pair { auto result = ClusterFactoryImplBase::create( - cluster, cm, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, random_, + cluster, cm, stats_, tls_, dns_resolver_, ssl_context_manager_, runtime_, dispatcher_, log_manager_, local_info_, admin_, singleton_manager_, outlier_event_logger, added_via_api, validation_visitor_, *api_); // Convert from load balancer unique_ptr -> raw pointer -> unique_ptr. @@ -126,7 +127,6 @@ class TestClusterManagerFactory : public ClusterManagerFactory { std::shared_ptr> dns_resolver_{ new NiceMock}; NiceMock runtime_; - NiceMock random_; NiceMock dispatcher_; Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{ dispatcher_.timeSource()}; @@ -136,6 +136,7 @@ class TestClusterManagerFactory : public ClusterManagerFactory { NiceMock log_manager_; Singleton::ManagerImpl singleton_manager_{Thread::threadFactoryForTest()}; NiceMock validation_visitor_; + NiceMock random_; Api::ApiPtr api_; }; @@ -160,12 +161,12 @@ class TestClusterManagerImpl : public ClusterManagerImpl { TestClusterManagerImpl(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, - Random::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, + const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, Server::Admin& admin, ProtobufMessage::ValidationContext& validation_context, Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context) - : ClusterManagerImpl(bootstrap, factory, stats, tls, runtime, random, local_info, log_manager, + : ClusterManagerImpl(bootstrap, factory, stats, tls, runtime, local_info, log_manager, main_thread_dispatcher, admin, validation_context, api, http_context, grpc_context) {} @@ -182,17 +183,19 @@ class TestClusterManagerImpl : public ClusterManagerImpl { // it with the right values at the right times. class MockedUpdatedClusterManagerImpl : public TestClusterManagerImpl { public: - MockedUpdatedClusterManagerImpl( - const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, - Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, - Random::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, - Server::Admin& admin, ProtobufMessage::ValidationContext& validation_context, Api::Api& api, - MockLocalClusterUpdate& local_cluster_update, MockLocalHostsRemoved& local_hosts_removed, - Http::Context& http_context, Grpc::Context& grpc_context) - : TestClusterManagerImpl(bootstrap, factory, stats, tls, runtime, random, local_info, - log_manager, main_thread_dispatcher, admin, validation_context, api, - http_context, grpc_context), + MockedUpdatedClusterManagerImpl(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, + ClusterManagerFactory& factory, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::Loader& runtime, + const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager, + Event::Dispatcher& main_thread_dispatcher, Server::Admin& admin, + ProtobufMessage::ValidationContext& validation_context, + Api::Api& api, MockLocalClusterUpdate& local_cluster_update, + MockLocalHostsRemoved& local_hosts_removed, + Http::Context& http_context, Grpc::Context& grpc_context) + : TestClusterManagerImpl(bootstrap, factory, stats, tls, runtime, local_info, log_manager, + main_thread_dispatcher, admin, validation_context, api, http_context, + grpc_context), local_cluster_update_(local_cluster_update), local_hosts_removed_(local_hosts_removed) {} protected: diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index e61ae42ebc4e..336f40d86d91 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -55,7 +55,7 @@ namespace { class UpstreamImplTestBase { protected: - UpstreamImplTestBase() : api_(Api::createApiForTest(stats_)) {} + UpstreamImplTestBase() : api_(Api::createApiForTest(stats_, random_)) {} NiceMock admin_; Ssl::MockContextManager ssl_context_manager_; @@ -187,7 +187,7 @@ TEST_P(StrictDnsParamTest, ImmediateResolve) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver, factory_context, @@ -221,7 +221,7 @@ TEST_F(StrictDnsClusterImplTest, ZeroHostsIsInializedImmediately) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver_, factory_context, std::move(scope), false); @@ -256,7 +256,7 @@ TEST_F(StrictDnsClusterImplTest, ZeroHostsHealthChecker) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver_, factory_context, std::move(scope), false); @@ -327,7 +327,7 @@ TEST_F(StrictDnsClusterImplTest, Basic) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver_, factory_context, std::move(scope), false); @@ -479,7 +479,7 @@ TEST_F(StrictDnsClusterImplTest, HostRemovalActiveHealthSkipped) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver_, factory_context, std::move(scope), false); @@ -540,7 +540,7 @@ TEST_F(StrictDnsClusterImplTest, HostRemovalAfterHcFail) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver_, factory_context, std::move(scope), false); @@ -667,7 +667,7 @@ TEST_F(StrictDnsClusterImplTest, LoadAssignmentBasic) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver_, factory_context, std::move(scope), false); @@ -902,7 +902,7 @@ TEST_F(StrictDnsClusterImplTest, LoadAssignmentBasicMultiplePriorities) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver_, factory_context, std::move(scope), false); @@ -1014,7 +1014,7 @@ TEST_F(StrictDnsClusterImplTest, CustomResolverFails) { Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format("cluster.{}.", cluster_config.name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); EXPECT_THROW_WITH_MESSAGE( @@ -1050,7 +1050,7 @@ TEST_F(StrictDnsClusterImplTest, FailureRefreshRateBackoffResetsWhenSuccessHappe "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver_, factory_context, std::move(scope), false); @@ -1099,7 +1099,7 @@ TEST_F(StrictDnsClusterImplTest, TtlAsDnsRefreshRate) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver_, factory_context, std::move(scope), false); @@ -1179,7 +1179,7 @@ TEST_F(StrictDnsClusterImplTest, Http2UserDefinedSettingsParametersValidation) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); EXPECT_THROW_WITH_REGEX( StrictDnsClusterImpl(cluster_config, runtime_, dns_resolver_, factory_context, @@ -1322,7 +1322,7 @@ TEST_F(StaticClusterImplTest, InitialHosts) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); cluster.initialize([] {}); @@ -1357,7 +1357,7 @@ TEST_F(StaticClusterImplTest, LoadAssignmentEmptyHostname) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); cluster.initialize([] {}); @@ -1392,7 +1392,7 @@ TEST_F(StaticClusterImplTest, LoadAssignmentNonEmptyHostname) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); cluster.initialize([] {}); @@ -1427,7 +1427,7 @@ TEST_F(StaticClusterImplTest, LoadAssignmentNonEmptyHostnameWithHealthChecks) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); cluster.initialize([] {}); @@ -1480,7 +1480,7 @@ TEST_F(StaticClusterImplTest, LoadAssignmentMultiplePriorities) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); cluster.initialize([] {}); @@ -1525,7 +1525,7 @@ TEST_F(StaticClusterImplTest, LoadAssignmentLocality) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); cluster.initialize([] {}); @@ -1571,7 +1571,7 @@ TEST_F(StaticClusterImplTest, LoadAssignmentEdsHealth) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); cluster.initialize([] {}); @@ -1603,7 +1603,7 @@ TEST_F(StaticClusterImplTest, AltStatName) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); cluster.initialize([] {}); @@ -1633,7 +1633,7 @@ TEST_F(StaticClusterImplTest, RingHash) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), true); cluster.initialize([] {}); @@ -1669,7 +1669,7 @@ TEST_F(StaticClusterImplTest, OutlierDetector) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); @@ -1727,7 +1727,7 @@ TEST_F(StaticClusterImplTest, HealthyStat) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); @@ -1868,7 +1868,7 @@ TEST_F(StaticClusterImplTest, UrlConfig) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); cluster.initialize([] {}); @@ -1920,7 +1920,7 @@ TEST_F(StaticClusterImplTest, UnsupportedLBType) { ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); @@ -1951,7 +1951,7 @@ TEST_F(StaticClusterImplTest, MalformedHostIP) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); EXPECT_THROW_WITH_MESSAGE( StaticClusterImpl(cluster_config, runtime_, factory_context, std::move(scope), false), @@ -1978,7 +1978,7 @@ TEST_F(StaticClusterImplTest, NoHostsTest) { Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format("cluster.{}.", cluster_config.name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); cluster.initialize([] {}); @@ -1997,7 +1997,7 @@ TEST_F(StaticClusterImplTest, SourceAddressPriority) { Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format( "cluster.{}.", config.alt_stat_name().empty() ? config.name() : config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(config, runtime_, factory_context, std::move(scope), false); EXPECT_EQ("1.2.3.5:0", cluster.info()->sourceAddress()->asString()); @@ -2010,7 +2010,7 @@ TEST_F(StaticClusterImplTest, SourceAddressPriority) { Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format( "cluster.{}.", config.alt_stat_name().empty() ? config.name() : config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(config, runtime_, factory_context, std::move(scope), false); EXPECT_EQ(cluster_address, cluster.info()->sourceAddress()->ip()->addressAsString()); @@ -2022,7 +2022,7 @@ TEST_F(StaticClusterImplTest, SourceAddressPriority) { Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format( "cluster.{}.", config.alt_stat_name().empty() ? config.name() : config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StaticClusterImpl cluster(config, runtime_, factory_context, std::move(scope), false); EXPECT_EQ(cluster_address, cluster.info()->sourceAddress()->ip()->addressAsString()); @@ -2057,7 +2057,7 @@ TEST_F(ClusterImplTest, CloseConnectionsOnHostHealthFailure) { "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); StrictDnsClusterImpl cluster(cluster_config, runtime_, dns_resolver, factory_context, @@ -2180,7 +2180,7 @@ TEST(PrioritySet, Extend) { class ClusterInfoImplTest : public testing::Test { public: - ClusterInfoImplTest() : api_(Api::createApiForTest(stats_)) {} + ClusterInfoImplTest() : api_(Api::createApiForTest(stats_, random_)) {} std::unique_ptr makeCluster(const std::string& yaml, bool avoid_boosting = true) { @@ -2189,7 +2189,7 @@ class ClusterInfoImplTest : public testing::Test { ? cluster_config_.name() : cluster_config_.alt_stat_name())); factory_context_ = std::make_unique( - admin_, ssl_context_manager_, *scope_, cm_, local_info_, dispatcher_, random_, stats_, + admin_, ssl_context_manager_, *scope_, cm_, local_info_, dispatcher_, stats_, singleton_manager_, tls_, validation_visitor_, *api_); return std::make_unique(cluster_config_, runtime_, dns_resolver_, diff --git a/test/config_test/config_test.cc b/test/config_test/config_test.cc index 47f7e88037ba..5d153d124ddc 100644 --- a/test/config_test/config_test.cc +++ b/test/config_test/config_test.cc @@ -61,9 +61,9 @@ class ConfigTest { ConfigTest(const OptionsImpl& options) : api_(Api::createApiForTest(time_system_)), options_(options) { ON_CALL(server_, options()).WillByDefault(ReturnRef(options_)); - ON_CALL(server_, random()).WillByDefault(ReturnRef(random_)); ON_CALL(server_, sslContextManager()).WillByDefault(ReturnRef(ssl_context_manager_)); ON_CALL(server_.api_, fileSystem()).WillByDefault(ReturnRef(file_system_)); + ON_CALL(server_.api_, randomGenerator()).WillByDefault(ReturnRef(random_)); ON_CALL(file_system_, fileReadToEnd(StrEq("/etc/envoy/lightstep_access_token"))) .WillByDefault(Return("access_token")); ON_CALL(file_system_, fileReadToEnd(StrNe("/etc/envoy/lightstep_access_token"))) @@ -91,10 +91,10 @@ class ConfigTest { cluster_manager_factory_ = std::make_unique( server_.admin(), server_.runtime(), server_.stats(), server_.threadLocal(), - server_.random(), server_.dnsResolver(), ssl_context_manager_, server_.dispatcher(), - server_.localInfo(), server_.secretManager(), server_.messageValidationContext(), *api_, - server_.httpContext(), server_.grpcContext(), server_.accessLogManager(), - server_.singletonManager(), time_system_); + server_.dnsResolver(), ssl_context_manager_, server_.dispatcher(), server_.localInfo(), + server_.secretManager(), server_.messageValidationContext(), *api_, server_.httpContext(), + server_.grpcContext(), server_.accessLogManager(), server_.singletonManager(), + time_system_); ON_CALL(server_, clusterManager()).WillByDefault(Invoke([&]() -> Upstream::ClusterManager& { return *main_config.clusterManager(); diff --git a/test/extensions/clusters/aggregate/cluster_test.cc b/test/extensions/clusters/aggregate/cluster_test.cc index bc1b6f61a948..e98d79524a9f 100644 --- a/test/extensions/clusters/aggregate/cluster_test.cc +++ b/test/extensions/clusters/aggregate/cluster_test.cc @@ -98,11 +98,12 @@ class AggregateClusterTest : public testing::Test { ProtobufMessage::getStrictValidationVisitor(), config); Stats::ScopePtr scope = stats_store_.createScope("cluster.name."); Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_store_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_store_, singleton_manager_, tls_, validation_visitor_, *api_); - cluster_ = std::make_shared(cluster_config, config, cm_, runtime_, random_, - factory_context, std::move(scope), tls_, false); + cluster_ = + std::make_shared(cluster_config, config, cm_, runtime_, api_->randomGenerator(), + factory_context, std::move(scope), tls_, false); thread_aware_lb_ = std::make_unique(*cluster_); lb_factory_ = thread_aware_lb_->factory(); @@ -133,7 +134,7 @@ class AggregateClusterTest : public testing::Test { NiceMock admin_; Singleton::ManagerImpl singleton_manager_{Thread::threadFactoryForTest()}; NiceMock validation_visitor_; - Api::ApiPtr api_{Api::createApiForTest(stats_store_)}; + Api::ApiPtr api_{Api::createApiForTest(stats_store_, random_)}; std::shared_ptr cluster_; Upstream::ThreadAwareLoadBalancerPtr thread_aware_lb_; Upstream::LoadBalancerFactorySharedPtr lb_factory_; diff --git a/test/extensions/clusters/aggregate/cluster_update_test.cc b/test/extensions/clusters/aggregate/cluster_update_test.cc index b13d1e76db99..8755ba8a29ef 100644 --- a/test/extensions/clusters/aggregate/cluster_update_test.cc +++ b/test/extensions/clusters/aggregate/cluster_update_test.cc @@ -38,9 +38,9 @@ class AggregateClusterUpdateTest : public testing::Test { void initialize(const std::string& yaml_config) { auto bootstrap = parseBootstrapFromV2Yaml(yaml_config); cluster_manager_ = std::make_unique( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.random_, + bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *api_, http_context_, grpc_context_); + *factory_.api_, http_context_, grpc_context_); cluster_manager_->initializeSecondaryClusters(bootstrap); EXPECT_EQ(cluster_manager_->activeClusters().size(), 1); cluster_ = cluster_manager_->get("aggregate_cluster"); @@ -49,7 +49,6 @@ class AggregateClusterUpdateTest : public testing::Test { Stats::IsolatedStoreImpl stats_store_; NiceMock admin_; NiceMock factory_; - Api::ApiPtr api_{Api::createApiForTest(stats_store_, factory_.random_)}; Upstream::ThreadLocalCluster* cluster_; Event::SimulatedTimeSystem time_system_; @@ -262,8 +261,8 @@ TEST_F(AggregateClusterUpdateTest, InitializeAggregateClusterAfterOtherClusters) auto bootstrap = parseBootstrapFromV2Yaml(config); cluster_manager_ = std::make_unique( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.random_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, *api_, + bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.local_info_, + log_manager_, factory_.dispatcher_, admin_, validation_context_, *factory_.api_, http_context_, grpc_context_); cluster_manager_->initializeSecondaryClusters(bootstrap); EXPECT_EQ(cluster_manager_->activeClusters().size(), 2); diff --git a/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc b/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc index c01a02a60f75..2ed6655eea17 100644 --- a/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc +++ b/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc @@ -40,7 +40,7 @@ class ClusterTest : public testing::Test, ProtobufMessage::getStrictValidationVisitor(), config); Stats::ScopePtr scope = stats_store_.createScope("cluster.name."); Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, random_, stats_store_, + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_store_, singleton_manager_, tls_, validation_visitor_, *api_); if (uses_tls) { EXPECT_CALL(ssl_context_manager_, createSslClientContext(_, _)); @@ -110,7 +110,6 @@ class ClusterTest : public testing::Test, Stats::IsolatedStoreImpl stats_store_; Ssl::MockContextManager ssl_context_manager_; NiceMock cm_; - NiceMock random_; NiceMock tls_; NiceMock runtime_; NiceMock dispatcher_; diff --git a/test/extensions/clusters/redis/redis_cluster_integration_test.cc b/test/extensions/clusters/redis/redis_cluster_integration_test.cc index cceaed87a9db..53f4d6454ec9 100644 --- a/test/extensions/clusters/redis/redis_cluster_integration_test.cc +++ b/test/extensions/clusters/redis/redis_cluster_integration_test.cc @@ -171,7 +171,8 @@ class RedisClusterIntegrationTest : public testing::TestWithParam(&(test_server.server().random())); + mock_rng_ = dynamic_cast( + &(test_server.server().api().randomGenerator())); // Abort now if we cannot downcast the server's random number generator pointer. ASSERT_TRUE(mock_rng_ != nullptr); // Ensure that fake_upstreams_[0] is the load balancer's host of choice by default. diff --git a/test/extensions/clusters/redis/redis_cluster_test.cc b/test/extensions/clusters/redis/redis_cluster_test.cc index fbb805c409bc..6b5845359069 100644 --- a/test/extensions/clusters/redis/redis_cluster_test.cc +++ b/test/extensions/clusters/redis/redis_cluster_test.cc @@ -84,7 +84,7 @@ class RedisClusterTest : public testing::Test, MOCK_METHOD(Extensions::NetworkFilters::Common::Redis::Client::Client*, create_, (std::string)); protected: - RedisClusterTest() : api_(Api::createApiForTest(stats_store_)) {} + RedisClusterTest() : api_(Api::createApiForTest(stats_store_, random_)) {} std::list hostListToAddresses(const Upstream::HostVector& hosts) { std::list addresses; @@ -104,7 +104,7 @@ class RedisClusterTest : public testing::Test, "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm, local_info_, dispatcher_, random_, stats_store_, + admin_, ssl_context_manager_, *scope, cm, local_info_, dispatcher_, stats_store_, singleton_manager_, tls_, validation_visitor_, *api_); envoy::config::cluster::redis::RedisClusterConfig config; @@ -134,7 +134,7 @@ class RedisClusterTest : public testing::Test, "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() : cluster_config.alt_stat_name())); Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( - admin_, ssl_context_manager_, *scope, cm, local_info_, dispatcher_, random_, stats_store_, + admin_, ssl_context_manager_, *scope, cm, local_info_, dispatcher_, stats_store_, singleton_manager_, tls_, validation_visitor_, *api_); envoy::config::cluster::redis::RedisClusterConfig config; diff --git a/test/extensions/tracers/xray/tracer_test.cc b/test/extensions/tracers/xray/tracer_test.cc index 58b6a2bfc32b..091dd8983d67 100644 --- a/test/extensions/tracers/xray/tracer_test.cc +++ b/test/extensions/tracers/xray/tracer_test.cc @@ -89,7 +89,7 @@ TEST_F(XRayTracerTest, SerializeSpanTest) { EXPECT_CALL(*broker_, send(_)).WillOnce(Invoke(on_send)); aws_metadata_.insert({"key", ValueUtil::stringValue(expected_aws_key_value)}); Tracer tracer{expected_span_name, expected_origin_name, aws_metadata_, - std::move(broker_), server_.timeSource(), server_.random()}; + std::move(broker_), server_.timeSource(), server_.api().randomGenerator()}; auto span = tracer.startSpan(expected_operation_name, server_.timeSource().systemTime(), absl::nullopt /*headers*/); span->setTag("http.method", expected_http_method); @@ -104,14 +104,14 @@ TEST_F(XRayTracerTest, SerializeSpanTest) { TEST_F(XRayTracerTest, NonSampledSpansNotSerialized) { Tracer tracer{"" /*span name*/, "" /*origin*/, aws_metadata_, - std::move(broker_), server_.timeSource(), server_.random()}; + std::move(broker_), server_.timeSource(), server_.api().randomGenerator()}; auto span = tracer.createNonSampledSpan(); span->finishSpan(); } TEST_F(XRayTracerTest, BaggageNotImplemented) { Tracer tracer{"" /*span name*/, "" /*origin*/, aws_metadata_, - std::move(broker_), server_.timeSource(), server_.random()}; + std::move(broker_), server_.timeSource(), server_.api().randomGenerator()}; auto span = tracer.createNonSampledSpan(); span->setBaggage("baggage_key", "baggage_value"); span->finishSpan(); @@ -125,10 +125,11 @@ TEST_F(XRayTracerTest, ChildSpanHasParentInfo) { constexpr auto expected_span_name = "Service 1"; constexpr auto expected_operation_name = "Create"; const auto& broker = *broker_; - Tracer tracer{expected_span_name, "", aws_metadata_, std::move(broker_), server_.timeSource(), - server_.random()}; + Tracer tracer{expected_span_name, "", + aws_metadata_, std::move(broker_), + server_.timeSource(), server_.api().randomGenerator()}; // Span id taken from random generator - EXPECT_CALL(server_.random_, random()).WillOnce(Return(999)); + EXPECT_CALL(server_.api_.random_, random()).WillOnce(Return(999)); auto parent_span = tracer.startSpan(expected_operation_name, server_.timeSource().systemTime(), absl::nullopt /*headers*/); @@ -147,7 +148,7 @@ TEST_F(XRayTracerTest, ChildSpanHasParentInfo) { EXPECT_CALL(broker, send(_)).WillOnce(Invoke(on_send)); // Span id taken from random generator - EXPECT_CALL(server_.random_, random()).WillOnce(Return(262626262626)); + EXPECT_CALL(server_.api_.random_, random()).WillOnce(Return(262626262626)); auto child = parent_span->spawnChild(config, expected_operation_name, server_.timeSource().systemTime()); child->finishSpan(); @@ -160,8 +161,12 @@ TEST_F(XRayTracerTest, UseExistingHeaderInformation) { constexpr auto span_name = "my span"; constexpr auto operation_name = "my operation"; - Tracer tracer{span_name, "", aws_metadata_, std::move(broker_), server_.timeSource(), - server_.random()}; + Tracer tracer{span_name, + "", + aws_metadata_, + std::move(broker_), + server_.timeSource(), + server_.api().randomGenerator()}; auto span = tracer.startSpan(operation_name, server_.timeSource().systemTime(), xray_header); const XRay::Span* xray_span = static_cast(span.get()); @@ -173,8 +178,12 @@ TEST_F(XRayTracerTest, SpanInjectContextHasXRayHeader) { constexpr auto span_name = "my span"; constexpr auto operation_name = "my operation"; - Tracer tracer{span_name, "", aws_metadata_, std::move(broker_), server_.timeSource(), - server_.random()}; + Tracer tracer{span_name, + "", + aws_metadata_, + std::move(broker_), + server_.timeSource(), + server_.api().randomGenerator()}; auto span = tracer.startSpan(operation_name, server_.timeSource().systemTime(), absl::nullopt /*headers*/); Http::TestRequestHeaderMapImpl request_headers; @@ -188,8 +197,12 @@ TEST_F(XRayTracerTest, SpanInjectContextHasXRayHeader) { TEST_F(XRayTracerTest, SpanInjectContextHasXRayHeaderNonSampled) { constexpr auto span_name = "my span"; - Tracer tracer{span_name, "", aws_metadata_, std::move(broker_), server_.timeSource(), - server_.random()}; + Tracer tracer{span_name, + "", + aws_metadata_, + std::move(broker_), + server_.timeSource(), + server_.api().randomGenerator()}; auto span = tracer.createNonSampledSpan(); Http::TestRequestHeaderMapImpl request_headers; span->injectContext(request_headers); @@ -202,8 +215,12 @@ TEST_F(XRayTracerTest, SpanInjectContextHasXRayHeaderNonSampled) { TEST_F(XRayTracerTest, TraceIDFormatTest) { constexpr auto span_name = "my span"; - Tracer tracer{span_name, "", aws_metadata_, std::move(broker_), server_.timeSource(), - server_.random()}; + Tracer tracer{span_name, + "", + aws_metadata_, + std::move(broker_), + server_.timeSource(), + server_.api().randomGenerator()}; auto span = tracer.createNonSampledSpan(); // startSpan and createNonSampledSpan use the same // logic to create a trace ID XRay::Span* xray_span = span.get(); @@ -227,7 +244,7 @@ TEST_P(XRayDaemonTest, VerifyUdpPacketContents) { const std::string daemon_endpoint = xray_fake_daemon.localAddress()->asString(); Tracer tracer{"my_segment", "origin", aws_metadata, std::make_unique(daemon_endpoint), - server.timeSource(), server.random()}; + server.timeSource(), server.api().randomGenerator()}; auto span = tracer.startSpan("ingress" /*operation name*/, server.timeSource().systemTime(), absl::nullopt /*headers*/); diff --git a/test/mocks/server/factory_context.cc b/test/mocks/server/factory_context.cc index 3dcca0e28cf7..b02076911f4f 100644 --- a/test/mocks/server/factory_context.cc +++ b/test/mocks/server/factory_context.cc @@ -26,7 +26,6 @@ MockFactoryContext::MockFactoryContext() ON_CALL(*this, initManager()).WillByDefault(ReturnRef(init_manager_)); ON_CALL(*this, lifecycleNotifier()).WillByDefault(ReturnRef(lifecycle_notifier_)); ON_CALL(*this, localInfo()).WillByDefault(ReturnRef(local_info_)); - ON_CALL(*this, random()).WillByDefault(ReturnRef(random_)); ON_CALL(*this, runtime()).WillByDefault(ReturnRef(runtime_loader_)); ON_CALL(*this, scope()).WillByDefault(ReturnRef(scope_)); ON_CALL(*this, singletonManager()).WillByDefault(ReturnRef(*singleton_manager_)); diff --git a/test/mocks/server/factory_context.h b/test/mocks/server/factory_context.h index ecc3423f072e..d8be7913563a 100644 --- a/test/mocks/server/factory_context.h +++ b/test/mocks/server/factory_context.h @@ -29,7 +29,6 @@ class MockFactoryContext : public virtual FactoryContext { MOCK_METHOD(bool, healthCheckFailed, ()); MOCK_METHOD(Init::Manager&, initManager, ()); MOCK_METHOD(ServerLifecycleNotifier&, lifecycleNotifier, ()); - MOCK_METHOD(Envoy::Random::RandomGenerator&, random, ()); MOCK_METHOD(Envoy::Runtime::Loader&, runtime, ()); MOCK_METHOD(Stats::Scope&, scope, ()); MOCK_METHOD(Singleton::Manager&, singletonManager, ()); @@ -58,7 +57,6 @@ class MockFactoryContext : public virtual FactoryContext { testing::NiceMock init_manager_; testing::NiceMock lifecycle_notifier_; testing::NiceMock local_info_; - testing::NiceMock random_; testing::NiceMock runtime_loader_; testing::NiceMock scope_; testing::NiceMock thread_local_; diff --git a/test/mocks/server/instance.cc b/test/mocks/server/instance.cc index 91c102c45267..f89bb4e31d19 100644 --- a/test/mocks/server/instance.cc +++ b/test/mocks/server/instance.cc @@ -33,7 +33,6 @@ MockInstance::MockInstance() ON_CALL(*this, runtime()).WillByDefault(ReturnRef(runtime_loader_)); ON_CALL(*this, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); ON_CALL(*this, hotRestart()).WillByDefault(ReturnRef(hot_restart_)); - ON_CALL(*this, random()).WillByDefault(ReturnRef(random_)); ON_CALL(*this, lifecycleNotifier()).WillByDefault(ReturnRef(lifecycle_notifier_)); ON_CALL(*this, localInfo()).WillByDefault(ReturnRef(local_info_)); ON_CALL(*this, options()).WillByDefault(ReturnRef(options_)); @@ -60,7 +59,6 @@ MockServerFactoryContext::MockServerFactoryContext() ON_CALL(*this, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); ON_CALL(*this, drainDecision()).WillByDefault(ReturnRef(drain_manager_)); ON_CALL(*this, localInfo()).WillByDefault(ReturnRef(local_info_)); - ON_CALL(*this, random()).WillByDefault(ReturnRef(random_)); ON_CALL(*this, runtime()).WillByDefault(ReturnRef(runtime_loader_)); ON_CALL(*this, scope()).WillByDefault(ReturnRef(scope_)); ON_CALL(*this, singletonManager()).WillByDefault(ReturnRef(*singleton_manager_)); diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index 9b83b0bbf42c..8de5491105fc 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -65,7 +65,6 @@ class MockInstance : public Instance { MOCK_METHOD(Envoy::MutexTracer*, mutexTracer, ()); MOCK_METHOD(const Options&, options, ()); MOCK_METHOD(OverloadManager&, overloadManager, ()); - MOCK_METHOD(Random::RandomGenerator&, random, ()); MOCK_METHOD(Runtime::Loader&, runtime, ()); MOCK_METHOD(void, shutdown, ()); MOCK_METHOD(bool, isShutdown, ()); @@ -108,7 +107,6 @@ class MockInstance : public Instance { testing::NiceMock access_log_manager_; testing::NiceMock hot_restart_; testing::NiceMock options_; - testing::NiceMock random_; testing::NiceMock lifecycle_notifier_; testing::NiceMock local_info_; testing::NiceMock init_manager_; @@ -134,7 +132,6 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); MOCK_METHOD(const Network::DrainDecision&, drainDecision, ()); MOCK_METHOD(const LocalInfo::LocalInfo&, localInfo, (), (const)); - MOCK_METHOD(Envoy::Random::RandomGenerator&, random, ()); MOCK_METHOD(Envoy::Runtime::Loader&, runtime, ()); MOCK_METHOD(Stats::Scope&, scope, ()); MOCK_METHOD(Singleton::Manager&, singletonManager, ()); @@ -155,7 +152,6 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { testing::NiceMock dispatcher_; testing::NiceMock drain_manager_; testing::NiceMock local_info_; - testing::NiceMock random_; testing::NiceMock runtime_loader_; testing::NiceMock scope_; testing::NiceMock thread_local_; diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index e4578654d3fa..02c318b6b6d5 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -69,7 +69,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/watchdog/profile_action:84.9" "source/extensions/watchdog/abort_action:42.9" # Death tests don't report LCOV "source/server:94.6" -"source/server/config_validation:76.8" +"source/server/config_validation:76.6" "source/server/admin:95.3" ) diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index 36f3a0443ebb..1318d192f14f 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -48,8 +48,8 @@ TEST(ValidationClusterManagerTest, MockedMethods) { Singleton::ManagerImpl singleton_manager{Thread::threadFactoryForTest()}; ValidationClusterManagerFactory factory( - admin, runtime, stats_store, tls, random, dns_resolver, ssl_context_manager, dispatcher, - local_info, secret_manager, validation_context, *api, http_context, grpc_context, log_manager, + admin, runtime, stats_store, tls, dns_resolver, ssl_context_manager, dispatcher, local_info, + secret_manager, validation_context, *api, http_context, grpc_context, log_manager, singleton_manager, time_system); const envoy::config::bootstrap::v3::Bootstrap bootstrap; diff --git a/test/server/configuration_impl_test.cc b/test/server/configuration_impl_test.cc index 2681825740a9..fdb1a0ba2341 100644 --- a/test/server/configuration_impl_test.cc +++ b/test/server/configuration_impl_test.cc @@ -60,10 +60,10 @@ class ConfigurationImplTest : public testing::Test { : api_(Api::createApiForTest()), cluster_manager_factory_( server_.admin(), server_.runtime(), server_.stats(), server_.threadLocal(), - server_.random(), server_.dnsResolver(), server_.sslContextManager(), - server_.dispatcher(), server_.localInfo(), server_.secretManager(), - server_.messageValidationContext(), *api_, server_.httpContext(), server_.grpcContext(), - server_.accessLogManager(), server_.singletonManager()) {} + server_.dnsResolver(), server_.sslContextManager(), server_.dispatcher(), + server_.localInfo(), server_.secretManager(), server_.messageValidationContext(), *api_, + server_.httpContext(), server_.grpcContext(), server_.accessLogManager(), + server_.singletonManager()) {} void addStatsdFakeClusterConfig(envoy::config::metrics::v3::StatsSink& sink) { envoy::config::metrics::v3::StatsdSink statsd_sink; diff --git a/test/server/drain_manager_impl_test.cc b/test/server/drain_manager_impl_test.cc index 160080b34b1b..f5a5687af366 100644 --- a/test/server/drain_manager_impl_test.cc +++ b/test/server/drain_manager_impl_test.cc @@ -80,13 +80,13 @@ TEST_P(DrainManagerImplTest, DrainDeadline) { // Ensure drainClose() behaviour is determined by the deadline. drain_manager.startDrainSequence([] {}); EXPECT_CALL(server_, healthCheckFailed()).WillRepeatedly(Return(false)); - ON_CALL(server_.random_, random()).WillByDefault(Return(DrainTimeSeconds * 2 - 1)); + ON_CALL(server_.api_.random_, random()).WillByDefault(Return(DrainTimeSeconds * 2 - 1)); ON_CALL(server_.options_, drainTime()) .WillByDefault(Return(std::chrono::seconds(DrainTimeSeconds))); if (drain_gradually) { // random() should be called when elapsed time < drain timeout - EXPECT_CALL(server_.random_, random()).Times(2); + EXPECT_CALL(server_.api_.random_, random()).Times(2); EXPECT_FALSE(drain_manager.drainClose()); simTime().advanceTimeWait(std::chrono::seconds(DrainTimeSeconds - 1)); EXPECT_FALSE(drain_manager.drainClose()); @@ -99,7 +99,7 @@ TEST_P(DrainManagerImplTest, DrainDeadline) { simTime().advanceTimeWait(std::chrono::seconds(500)); EXPECT_TRUE(drain_manager.drainClose()); } else { - EXPECT_CALL(server_.random_, random()).Times(0); + EXPECT_CALL(server_.api_.random_, random()).Times(0); EXPECT_TRUE(drain_manager.drainClose()); simTime().advanceTimeWait(std::chrono::seconds(DrainTimeSeconds - 1)); EXPECT_TRUE(drain_manager.drainClose()); @@ -117,7 +117,7 @@ TEST_P(DrainManagerImplTest, DrainDeadlineProbability) { ON_CALL(server_.options_, drainStrategy()) .WillByDefault(Return(drain_gradually ? Server::DrainStrategy::Gradual : Server::DrainStrategy::Immediate)); - ON_CALL(server_.random_, random()).WillByDefault(Return(4)); + ON_CALL(server_.api_.random_, random()).WillByDefault(Return(4)); ON_CALL(server_.options_, drainTime()).WillByDefault(Return(std::chrono::seconds(3))); DrainManagerImpl drain_manager(server_, envoy::config::listener::v3::Listener::DEFAULT); @@ -133,7 +133,7 @@ TEST_P(DrainManagerImplTest, DrainDeadlineProbability) { if (drain_gradually) { // random() should be called when elapsed time < drain timeout - EXPECT_CALL(server_.random_, random()).Times(2); + EXPECT_CALL(server_.api_.random_, random()).Times(2); // Current elapsed time is 0 // drainClose() will return true when elapsed time > (4 % 3 == 1). EXPECT_FALSE(drain_manager.drainClose()); @@ -142,7 +142,7 @@ TEST_P(DrainManagerImplTest, DrainDeadlineProbability) { simTime().advanceTimeWait(std::chrono::seconds(1)); EXPECT_TRUE(drain_manager.drainClose()); } else { - EXPECT_CALL(server_.random_, random()).Times(0); + EXPECT_CALL(server_.api_.random_, random()).Times(0); EXPECT_TRUE(drain_manager.drainClose()); simTime().advanceTimeWait(std::chrono::seconds(2)); EXPECT_TRUE(drain_manager.drainClose()); diff --git a/test/server/listener_manager_impl_quic_only_test.cc b/test/server/listener_manager_impl_quic_only_test.cc index 3a2283fa4cc7..891949861423 100644 --- a/test/server/listener_manager_impl_quic_only_test.cc +++ b/test/server/listener_manager_impl_quic_only_test.cc @@ -62,7 +62,7 @@ reuse_port: true envoy::config::listener::v3::Listener listener_proto = parseListenerFromV3Yaml(yaml); ON_CALL(udp_gso_syscall_, supportsUdpGso()).WillByDefault(Return(true)); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); expectCreateListenSocket(envoy::config::core::v3::SocketOption::STATE_PREBIND, #ifdef SO_RXQ_OVFL // SO_REUSEPORT is on as configured /* expected_num_options */ diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index b5f6cef747fa..22d682666a0b 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -163,7 +163,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, EmptyFilter) { - filters: [] )EOF"; - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -336,7 +336,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, UdpAddress) { envoy::config::listener::v3::Listener listener_proto; EXPECT_TRUE(Protobuf::TextFormat::ParseFromString(proto_text, &listener_proto)); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(*worker_, addListener(_, _, _)); EXPECT_CALL(listener_factory_, createListenSocket(_, Network::Socket::Type::Datagram, _, {{true, false}})) @@ -2144,7 +2144,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithDestinationP )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2190,7 +2190,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithDestinationI )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2236,7 +2236,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithServerNamesM )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2283,7 +2283,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithTransportPro )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2326,7 +2326,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithApplicationP )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2372,7 +2372,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithSourceTypeMa )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2433,7 +2433,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithSourceIpMatc )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2493,7 +2493,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithSourceIpv6Ma )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2532,7 +2532,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithSourcePortMa )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2599,7 +2599,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainWithSourceType )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2686,7 +2686,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithDestinati )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2772,7 +2772,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithDestinati )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2867,7 +2867,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithServerNam )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2939,7 +2939,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithTransport )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2984,7 +2984,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithApplicati )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3034,7 +3034,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithMultipleR )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3109,7 +3109,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithDifferent )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3150,7 +3150,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3272,7 +3272,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, TlsFilterChainWithoutTlsInspector )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3305,7 +3305,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3333,7 +3333,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SniFilterChainWithoutTlsInspector )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3361,7 +3361,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, AlpnFilterChainWithoutTlsInspecto )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3390,7 +3390,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, CustomTransportProtocolWithSniWit )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3429,7 +3429,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, TlsCertificateInline) { absl::CEscape(ca), R"EOF(" } )EOF"); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3454,7 +3454,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, TlsCertificateChainInlinePrivateK )EOF"), Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3627,7 +3627,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstFilter) { )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3704,7 +3704,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstTestFilter) { )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3778,7 +3778,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstTestFilterIPv6) { )EOF", Network::Address::IpVersion::v6); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3976,7 +3976,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, CRLFilename) { )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -4004,7 +4004,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, CRLInline) { )EOF"), Network::Address::IpVersion::v4); - EXPECT_CALL(server_.random_, uuid()); + EXPECT_CALL(server_.api_.random_, uuid()); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); diff --git a/test/server/listener_manager_impl_test.h b/test/server/listener_manager_impl_test.h index 747859f669a1..9b22f3109bf8 100644 --- a/test/server/listener_manager_impl_test.h +++ b/test/server/listener_manager_impl_test.h @@ -53,7 +53,7 @@ class ListenerHandle { class ListenerManagerImplTest : public testing::Test { protected: - ListenerManagerImplTest() : api_(Api::createApiForTest()) {} + ListenerManagerImplTest() : api_(Api::createApiForTest(server_.api_.random_)) {} void SetUp() override { ON_CALL(server_, api()).WillByDefault(ReturnRef(*api_)); From fb844b2d5c9e50c55794ae42200644cba3113617 Mon Sep 17 00:00:00 2001 From: Sunjay Bhatia <5337253+sunjayBhatia@users.noreply.github.com> Date: Tue, 6 Oct 2020 12:32:06 -0400 Subject: [PATCH 04/27] flaky test: //test/extensions/transport_sockets/tls:handshaker_test (#13407) Signed-off-by: Sunjay Bhatia --- test/extensions/transport_sockets/tls/BUILD | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/extensions/transport_sockets/tls/BUILD b/test/extensions/transport_sockets/tls/BUILD index 3da044f7f590..48a456162df3 100644 --- a/test/extensions/transport_sockets/tls/BUILD +++ b/test/extensions/transport_sockets/tls/BUILD @@ -176,6 +176,9 @@ envoy_cc_test( "//test/extensions/transport_sockets/tls/test_data:certs", ], external_deps = ["ssl"], + # TODO(sunjayBhatia): Diagnose openssl DLL load issue on Windows + # See: https://github.com/envoyproxy/envoy/pull/13276 + tags = ["flaky_on_windows"], deps = [ ":ssl_socket_test", ":ssl_test_utils", From 641b34a0b114ae670920c995efe00a72680420a6 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 6 Oct 2020 12:40:00 -0400 Subject: [PATCH 05/27] test: some hcm test clean up (#13359) Risk Level: n/a Testing: n/a Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- test/common/http/conn_manager_impl_test.cc | 292 +++++------ test/common/http/conn_manager_impl_test_2.cc | 452 +++++------------- .../http/conn_manager_impl_test_base.cc | 19 +- .../common/http/conn_manager_impl_test_base.h | 8 +- 4 files changed, 258 insertions(+), 513 deletions(-) diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index e3589452866b..a022f18c2cd8 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -47,21 +47,20 @@ TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponse) { // When dispatch is called on the codec, we pretend to get a new stream and then fire a headers // only request into it. Then we respond into the filter. - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .Times(2) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); // Test not charging stats on the second call. if (data.length() == 4) { RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); } else { RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", "/healthcheck"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); } ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; @@ -108,15 +107,14 @@ TEST_F(HttpConnectionManagerImplTest, 100ContinueResponse) { // When dispatch is called on the codec, we pretend to get a new stream and then fire a headers // only request into it. Then we respond into the filter. - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); // Test not charging stats on the second call. RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); ResponseHeaderMapPtr continue_headers{new TestResponseHeaderMapImpl{{":status", "100"}}}; filter->callbacks_->encode100ContinueHeaders(std::move(continue_headers)); @@ -254,24 +252,23 @@ TEST_F(HttpConnectionManagerImplTest, 100ContinueResponseWithDecoderPause) { EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); // Test not charging stats on the second call. RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); // Allow the decode pipeline to pause. - decoder->decodeData(data, false); + decoder_->decodeData(data, false); ResponseHeaderMapPtr continue_headers{new TestResponseHeaderMapImpl{{":status", "100"}}}; filter->callbacks_->encode100ContinueHeaders(std::move(continue_headers)); // Resume decode pipeline after encoding 100 continue headers, we're now // ready to trigger #10923. - decoder->decodeData(data, true); + decoder_->decodeData(data, true); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -367,10 +364,10 @@ TEST_F(HttpConnectionManagerImplTest, InvalidPathWithDualFilter) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", "http://api.lyft.com/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); @@ -406,12 +403,12 @@ TEST_F(HttpConnectionManagerImplTest, PathFailedtoSanitize) { normalize_path_ = true; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", "/ab%00c"}, // "%00" is not valid in path according to RFC {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); @@ -464,10 +461,10 @@ TEST_F(HttpConnectionManagerImplTest, FilterShouldUseSantizedPath) { EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", original_path}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); @@ -490,10 +487,10 @@ TEST_F(HttpConnectionManagerImplTest, RouteShouldUseSantizedPath) { const std::string normalized_path = "/z"; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", original_path}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); @@ -525,10 +522,10 @@ TEST_F(HttpConnectionManagerImplTest, RouteOverride) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); @@ -745,10 +742,10 @@ TEST_F(HttpConnectionManagerImplTest, FilterShouldUseNormalizedHost) { EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", original_host}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); @@ -772,10 +769,10 @@ TEST_F(HttpConnectionManagerImplTest, RouteShouldUseNormalizedHost) { const std::string normalized_host = "host"; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", original_host}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); @@ -1010,17 +1007,16 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlow) { use_remote_address_ = false; EXPECT_CALL(random_, uuid()).Times(0); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -1031,7 +1027,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlow) { })); // Should be no 'x-envoy-decorator-operation' response header. - EXPECT_CALL(encoder, encodeHeaders(_, true)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ(nullptr, headers.EnvoyDecoratorOperation()); })); @@ -1080,17 +1076,16 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat use_remote_address_ = false; EXPECT_CALL(random_, uuid()).Times(0); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -1101,7 +1096,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat })); // Verify decorator operation response header has been defined. - EXPECT_CALL(encoder, encodeHeaders(_, true)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ("testOp", headers.getEnvoyDecoratorOperationValue()); })); @@ -1148,17 +1143,16 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat use_remote_address_ = false; EXPECT_CALL(random_, uuid()).Times(0); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -1169,7 +1163,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat })); // Verify decorator operation response header has NOT been defined (i.e. not propagated). - EXPECT_CALL(encoder, encodeHeaders(_, true)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ(nullptr, headers.EnvoyDecoratorOperation()); })); @@ -1214,10 +1208,9 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat use_remote_address_ = false; EXPECT_CALL(random_, uuid()).Times(0); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, @@ -1225,7 +1218,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}, {"x-envoy-decorator-operation", "testOp"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -1238,7 +1231,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat // Should be no 'x-envoy-decorator-operation' response header, as decorator // was overridden by request header. - EXPECT_CALL(encoder, encodeHeaders(_, true)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ(nullptr, headers.EnvoyDecoratorOperation()); })); @@ -1297,17 +1290,16 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecorato use_remote_address_ = false; EXPECT_CALL(random_, uuid()).Times(0); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -1381,17 +1373,16 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecorato use_remote_address_ = false; EXPECT_CALL(random_, uuid()).Times(0); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); filter->callbacks_->streamInfo().setResponseCodeDetails(""); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; @@ -1465,17 +1456,16 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecorato use_remote_address_ = false; EXPECT_CALL(random_, uuid()).Times(0); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); filter->callbacks_->streamInfo().setResponseCodeDetails(""); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{ @@ -1523,17 +1513,16 @@ TEST_F(HttpConnectionManagerImplTest, use_remote_address_ = false; EXPECT_CALL(random_, uuid()).Times(0); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); filter->callbacks_->streamInfo().setResponseCodeDetails(""); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{ @@ -1581,10 +1570,9 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLog) { local_address); })); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, @@ -1592,7 +1580,7 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLog) { {":path", "/"}, {"x-forwarded-for", xff_address}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); filter->callbacks_->streamInfo().setResponseCodeDetails(""); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; @@ -1676,14 +1664,13 @@ TEST_F(HttpConnectionManagerImplTest, TestDownstreamDisconnectAccessLog) { EXPECT_EQ("downstream_remote_disconnect", stream_info.responseCodeDetails().value()); })); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":method", "GET"}, {":authority", "host"}, {":path", "/"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); @@ -1718,17 +1705,16 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLogWithTrailers) { EXPECT_NE(nullptr, stream_info.routeEntry()); })); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); filter->callbacks_->streamInfo().setResponseCodeDetails(""); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; @@ -1769,15 +1755,14 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLogWithInvalidRequest) { EXPECT_EQ(nullptr, stream_info.routeEntry()); })); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); // These request headers are missing the necessary ":host" RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":path", "/"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(0); return Http::okStatus(); })); @@ -1793,12 +1778,12 @@ class StreamErrorOnInvalidHttpMessageTest : public HttpConnectionManagerImplTest EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); // These request headers are missing the necessary ":host" RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":path", "/"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(0); return Http::okStatus(); })); @@ -1868,17 +1853,16 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLogSsl) { EXPECT_NE(nullptr, stream_info.routeEntry()); })); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); filter->callbacks_->streamInfo().setResponseCodeDetails(""); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; @@ -1913,17 +1897,16 @@ TEST_F(HttpConnectionManagerImplTest, DoNotStartSpanIfTracingIsNotEnabled) { callbacks.addStreamDecoderFilter(filter); })); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}, {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); filter->callbacks_->streamInfo().setResponseCodeDetails(""); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; @@ -1940,17 +1923,16 @@ TEST_F(HttpConnectionManagerImplTest, DoNotStartSpanIfTracingIsNotEnabled) { TEST_F(HttpConnectionManagerImplTest, NoPath) { setup(false, ""); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":method", "NOT_CONNECT"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); - EXPECT_CALL(encoder, encodeHeaders(_, true)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ("404", headers.getStatusValue()); })); @@ -1968,11 +1950,11 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutNotConfigured) { EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, createTimer_(_)).Times(0); EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); data.drain(4); return Http::okStatus(); @@ -2073,7 +2055,6 @@ TEST_F(HttpConnectionManagerImplTest, TestStreamIdleAccessLog) { stream_idle_timeout_ = std::chrono::milliseconds(10); setup(false, ""); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* idle_timer = setUpTimer(); EXPECT_CALL(*idle_timer, enableTimer(std::chrono::milliseconds(10), _)); @@ -2318,13 +2299,13 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutRouteOverride) { .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { Event::MockTimer* idle_timer = setUpTimer(); EXPECT_CALL(*idle_timer, enableTimer(std::chrono::milliseconds(10), _)); - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; EXPECT_CALL(*idle_timer, enableTimer(std::chrono::milliseconds(30), _)); EXPECT_CALL(*idle_timer, disableTimer()); - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); data.drain(4); return Http::okStatus(); @@ -2348,12 +2329,12 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutRouteZeroOverride) { .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { Event::MockTimer* idle_timer = setUpTimer(); EXPECT_CALL(*idle_timer, enableTimer(std::chrono::milliseconds(10), _)); - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; EXPECT_CALL(*idle_timer, disableTimer()); - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); data.drain(4); return Http::okStatus(); @@ -2375,13 +2356,13 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterDownstreamHeaders // Codec sends downstream request headers. EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); Event::MockTimer* idle_timer = setUpTimer(); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; EXPECT_CALL(*idle_timer, enableTimer(_, _)); - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); // Expect resetIdleTimer() to be called for the response // encodeHeaders()/encodeData(). @@ -2417,12 +2398,12 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutNormalTermination) { // Codec sends downstream request headers. Event::MockTimer* idle_timer = setUpTimer(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; EXPECT_CALL(*idle_timer, enableTimer(_, _)); - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); data.drain(4); return Http::okStatus(); @@ -2446,16 +2427,16 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterDownstreamHeaders // Codec sends downstream request headers. EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); Event::MockTimer* idle_timer = setUpTimer(); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; EXPECT_CALL(*idle_timer, enableTimer(_, _)); - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); EXPECT_CALL(*idle_timer, enableTimer(_, _)); - decoder->decodeData(data, false); + decoder_->decodeData(data, false); // Expect resetIdleTimer() to be called for the response // encodeHeaders()/encodeData(). @@ -2500,13 +2481,13 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterUpstreamHeaders) // Codec sends downstream request headers, upstream response headers are // encoded. EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); Event::MockTimer* idle_timer = setUpTimer(); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; EXPECT_CALL(*idle_timer, enableTimer(_, _)); - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; EXPECT_CALL(*idle_timer, enableTimer(_, _)); @@ -2550,13 +2531,12 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterBidiData) { // Codec sends downstream request headers, upstream response headers are // encoded, data events happen in various directions. Event::MockTimer* idle_timer = setUpTimer(); - RequestDecoder* decoder; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; EXPECT_CALL(*idle_timer, enableTimer(_, _)); - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); ResponseHeaderMapPtr response_continue_headers{ new TestResponseHeaderMapImpl{{":status", "100"}}}; @@ -2569,11 +2549,11 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterBidiData) { filter->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); EXPECT_CALL(*idle_timer, enableTimer(_, _)); - decoder->decodeData(data, false); + decoder_->decodeData(data, false); RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}}; EXPECT_CALL(*idle_timer, enableTimer(_, _)); - decoder->decodeTrailers(std::move(trailers)); + decoder_->decodeTrailers(std::move(trailers)); Buffer::OwnedImpl fake_response("world"); EXPECT_CALL(*idle_timer, enableTimer(_, _)); @@ -2694,12 +2674,12 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsNotDisarmedOnIncompleteReq EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); EXPECT_CALL(*request_timer, disableTimer()).Times(1); - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; // the second parameter 'false' leaves the stream open - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); return Http::okStatus(); })); @@ -2720,12 +2700,12 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestW Event::MockTimer* request_timer = setUpTimer(); EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; EXPECT_CALL(*request_timer, disableTimer()).Times(2); - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); @@ -2746,13 +2726,13 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestW Event::MockTimer* request_timer = setUpTimer(); EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "POST"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); EXPECT_CALL(*request_timer, disableTimer()).Times(2); - decoder->decodeData(data, true); + decoder_->decodeData(data, true); return Http::okStatus(); })); @@ -2772,16 +2752,16 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestW EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); - decoder->decodeData(data, false); + decoder_->decodeHeaders(std::move(headers), false); + decoder_->decodeData(data, false); EXPECT_CALL(*request_timer, disableTimer()).Times(2); RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}}; - decoder->decodeTrailers(std::move(trailers)); + decoder_->decodeTrailers(std::move(trailers)); return Http::okStatus(); })); @@ -2808,11 +2788,11 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnEncodeHeaders) { Event::MockTimer* request_timer = setUpTimer(); EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); EXPECT_CALL(*request_timer, disableTimer()).Times(2); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; @@ -2835,11 +2815,11 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnConnectionTermin Event::MockTimer* request_timer = setUpTimer(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); return Http::okStatus(); })); @@ -2913,19 +2893,17 @@ TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationCallbackResetStream) { TEST_F(HttpConnectionManagerImplTest, Http10Rejected) { setup(false, ""); - RequestDecoder* decoder = nullptr; - NiceMock encoder; EXPECT_CALL(*codec_, protocol()).Times(AnyNumber()).WillRepeatedly(Return(Protocol::Http10)); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":method", "GET"}, {":path", "/"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); - EXPECT_CALL(encoder, encodeHeaders(_, true)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ("426", headers.getStatusValue()); EXPECT_EQ("close", headers.getConnectionValue()); @@ -2941,19 +2919,17 @@ TEST_F(HttpConnectionManagerImplTest, Http10ConnCloseLegacy) { Runtime::LoaderSingleton::getExisting()->mergeValues( {{"envoy.reloadable_features.fixed_connection_close", "false"}}); setup(false, ""); - RequestDecoder* decoder = nullptr; - NiceMock encoder; EXPECT_CALL(*codec_, protocol()).Times(AnyNumber()).WillRepeatedly(Return(Protocol::Http10)); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host:80"}, {":method", "CONNECT"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); - EXPECT_CALL(encoder, encodeHeaders(_, true)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ("close", headers.getConnectionValue()); })); @@ -2967,18 +2943,16 @@ TEST_F(HttpConnectionManagerImplTest, ProxyConnectLegacyClose) { Runtime::LoaderSingleton::getExisting()->mergeValues( {{"envoy.reloadable_features.fixed_connection_close", "false"}}); setup(false, ""); - RequestDecoder* decoder = nullptr; - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host:80"}, {":method", "CONNECT"}, {"proxy-connection", "close"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); - EXPECT_CALL(encoder, encodeHeaders(_, true)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ("close", headers.getConnectionValue()); })); @@ -2992,18 +2966,16 @@ TEST_F(HttpConnectionManagerImplTest, ConnectLegacyClose) { Runtime::LoaderSingleton::getExisting()->mergeValues( {{"envoy.reloadable_features.fixed_connection_close", "false"}}); setup(false, ""); - RequestDecoder* decoder = nullptr; - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":method", "CONNECT"}, {"connection", "close"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); - EXPECT_CALL(encoder, encodeHeaders(_, true)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ("close", headers.getConnectionValue()); })); @@ -3035,25 +3007,24 @@ TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationCallbackNotCalledIfResetS TEST_F(HttpConnectionManagerImplTest, RejectWebSocketOnNonWebSocketRoute) { setup(false, ""); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, {":method", "GET"}, {":path", "/"}, {"connection", "Upgrade"}, {"upgrade", "websocket"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); // Try sending trailers after the headers which will be rejected, just to // test the HCM logic that further decoding will not be passed to the // filters once the early response path is kicked off. RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"bazzz", "bar"}}}; - decoder->decodeTrailers(std::move(trailers)); + decoder_->decodeTrailers(std::move(trailers)); data.drain(4); return Http::okStatus(); })); - EXPECT_CALL(encoder, encodeHeaders(_, true)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ("403", headers.getStatusValue()); })); @@ -3081,8 +3052,7 @@ TEST_F(HttpConnectionManagerImplTest, FooUpgradeDrainClose) { .WillRepeatedly(Invoke( [&](HeaderMap&, bool) -> FilterHeadersStatus { return FilterHeadersStatus::Continue; })); - NiceMock encoder; - EXPECT_CALL(encoder, encodeHeaders(_, false)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { EXPECT_NE(nullptr, headers.Connection()); EXPECT_EQ("upgrade", headers.getConnectionValue()); @@ -3102,14 +3072,14 @@ TEST_F(HttpConnectionManagerImplTest, FooUpgradeDrainClose) { // only request into it. Then we respond into the filter. EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, {":method", "GET"}, {":path", "/"}, {"connection", "Upgrade"}, {"upgrade", "foo"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); filter->decoder_callbacks_->streamInfo().setResponseCodeDetails(""); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{ @@ -3133,17 +3103,15 @@ TEST_F(HttpConnectionManagerImplTest, FooUpgradeDrainClose) { TEST_F(HttpConnectionManagerImplTest, ConnectAsUpgrade) { setup(false, "envoy-custom-server", false); - NiceMock encoder; - EXPECT_CALL(filter_factory_, createUpgradeFilterChain("CONNECT", _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":method", "CONNECT"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); data.drain(4); return Http::okStatus(); })); @@ -3159,17 +3127,15 @@ TEST_F(HttpConnectionManagerImplTest, ConnectAsUpgrade) { TEST_F(HttpConnectionManagerImplTest, ConnectWithEmptyPath) { setup(false, "envoy-custom-server", false); - NiceMock encoder; - EXPECT_CALL(filter_factory_, createUpgradeFilterChain("CONNECT", _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", ""}, {":method", "CONNECT"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); data.drain(4); return Http::okStatus(); })); @@ -3189,23 +3155,20 @@ TEST_F(HttpConnectionManagerImplTest, ConnectLegacy) { setup(false, "envoy-custom-server", false); - NiceMock encoder; - RequestDecoder* decoder = nullptr; - EXPECT_CALL(filter_factory_, createUpgradeFilterChain("CONNECT", _, _)) .WillRepeatedly(Return(false)); EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":method", "CONNECT"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); data.drain(4); return Http::okStatus(); })); - EXPECT_CALL(encoder, encodeHeaders(_, _)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, _)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ("403", headers.getStatusValue()); })); @@ -3222,12 +3185,11 @@ TEST_F(HttpConnectionManagerImplTest, DrainCloseRaceWithClose) { InSequence s; setup(false, ""); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); @@ -3267,12 +3229,11 @@ TEST_F(HttpConnectionManagerImplTest, InSequence s; setup(false, ""); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); @@ -3329,12 +3290,11 @@ TEST_F(HttpConnectionManagerImplTest, DrainClose) { return FilterHeadersStatus::StopIteration; })); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); diff --git a/test/common/http/conn_manager_impl_test_2.cc b/test/common/http/conn_manager_impl_test_2.cc index 5cd2f150c2d7..7d601824544f 100644 --- a/test/common/http/conn_manager_impl_test_2.cc +++ b/test/common/http/conn_manager_impl_test_2.cc @@ -18,24 +18,12 @@ namespace Envoy { namespace Http { TEST_F(HttpConnectionManagerImplTest, ResponseBeforeRequestComplete) { - InSequence s; setup(false, "envoy-server-test"); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); - return Http::okStatus(); - })); - setupFilterChain(1, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) .WillOnce(Return(FilterHeadersStatus::StopIteration)); - - Buffer::OwnedImpl fake_input; - conn_manager_->onData(fake_input, false); + startRequest(); EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { @@ -53,24 +41,14 @@ TEST_F(HttpConnectionManagerImplTest, ResponseBeforeRequestComplete) { } TEST_F(HttpConnectionManagerImplTest, DisconnectOnProxyConnectionDisconnect) { - InSequence s; setup(false, "envoy-server-test"); - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/"}, {":method", "GET"}, {"proxy-connection", "close"}}}; - decoder->decodeHeaders(std::move(headers), false); - return Http::okStatus(); - })); - setupFilterChain(1, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) .WillOnce(Return(FilterHeadersStatus::StopIteration)); - Buffer::OwnedImpl fake_input; - conn_manager_->onData(fake_input, false); + startRequest(); EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { @@ -103,13 +81,11 @@ TEST_F(HttpConnectionManagerImplTest, ResponseStartBeforeRequestComplete) { .WillOnce(Return(FilterHeadersStatus::StopIteration)); // Start the request - NiceMock encoder; - RequestDecoder* decoder = nullptr; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); return Http::okStatus(); })); @@ -118,7 +94,7 @@ TEST_F(HttpConnectionManagerImplTest, ResponseStartBeforeRequestComplete) { // Start the response ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - EXPECT_CALL(encoder, encodeHeaders(_, false)) + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { EXPECT_NE(nullptr, headers.Server()); EXPECT_EQ("", headers.getServerValue()); @@ -129,7 +105,7 @@ TEST_F(HttpConnectionManagerImplTest, ResponseStartBeforeRequestComplete) { // Finish the request. EXPECT_CALL(*filter, decodeData(_, true)); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder->decodeData(data, true); + decoder_->decodeData(data, true); return Http::okStatus(); })); @@ -148,9 +124,8 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamDisconnect) { InSequence s; setup(false, ""); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - conn_manager_->newStream(encoder); + conn_manager_->newStream(response_encoder_); data.drain(2); return Http::okStatus(); })); @@ -201,9 +176,8 @@ TEST_F(HttpConnectionManagerImplTest, TestDownstreamProtocolErrorAccessLog) { EXPECT_TRUE(stream_info.hasResponseFlag(StreamInfo::ResponseFlag::DownstreamProtocolError)); })); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { - conn_manager_->newStream(encoder); + conn_manager_->newStream(response_encoder_); return codecProtocolError("protocol error"); })); @@ -231,13 +205,12 @@ TEST_F(HttpConnectionManagerImplTest, TestDownstreamProtocolErrorAfterHeadersAcc EXPECT_TRUE(stream_info.hasResponseFlag(StreamInfo::ResponseFlag::DownstreamProtocolError)); })); - NiceMock encoder; EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":method", "GET"}, {":authority", "host"}, {":path", "/"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return codecProtocolError("protocol error"); })); @@ -309,27 +282,13 @@ TEST_F(HttpConnectionManagerImplTest, IdleTimeout) { callbacks.addStreamDecoderFilter(StreamDecoderFilterSharedPtr{filter}); })); - NiceMock encoder; - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); - - Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, true); - return Http::okStatus(); - })); - EXPECT_CALL(*idle_timer, disableTimer()); EXPECT_CALL(*filter, decodeHeaders(_, false)) .WillOnce(Return(FilterHeadersStatus::StopIteration)); EXPECT_CALL(*filter, decodeData(_, true)) .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + startRequest(true, "hello"); EXPECT_CALL(*idle_timer, enableTimer(_, _)); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; @@ -379,26 +338,13 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDuration) { callbacks.addStreamDecoderFilter(StreamDecoderFilterSharedPtr{filter}); })); - NiceMock encoder; - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(encoder); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); - - Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, true); - return Http::okStatus(); - })); - EXPECT_CALL(*filter, decodeHeaders(_, false)) .WillOnce(Return(FilterHeadersStatus::StopIteration)); EXPECT_CALL(*filter, decodeData(_, true)) .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + startRequest(true, "hello"); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -419,20 +365,8 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDuration) { } TEST_F(HttpConnectionManagerImplTest, IntermediateBufferingEarlyResponse) { - InSequence s; setup(false, ""); - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); - - Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, true); - return Http::okStatus(); - })); - setupFilterChain(2, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) @@ -441,9 +375,8 @@ TEST_F(HttpConnectionManagerImplTest, IntermediateBufferingEarlyResponse) { .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + // Kick off the request. + startRequest(true, "hello"); // Mimic a decoder filter that trapped data and now sends on the headers. EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) @@ -465,21 +398,7 @@ TEST_F(HttpConnectionManagerImplTest, IntermediateBufferingEarlyResponse) { } TEST_F(HttpConnectionManagerImplTest, DoubleBuffering) { - InSequence s; setup(false, ""); - - // The data will get moved so we need to have a copy to compare against. - Buffer::OwnedImpl fake_data("hello"); - Buffer::OwnedImpl fake_data_copy("hello"); - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); - decoder->decodeData(fake_data, true); - return Http::okStatus(); - })); - setupFilterChain(3, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) @@ -489,8 +408,8 @@ TEST_F(HttpConnectionManagerImplTest, DoubleBuffering) { EXPECT_CALL(*decoder_filters_[0], decodeComplete()); // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + Buffer::OwnedImpl fake_data_copy("hello"); + startRequest(true, "hello"); // Continue iteration and stop and buffer on the 2nd filter. EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) @@ -514,26 +433,13 @@ TEST_F(HttpConnectionManagerImplTest, DoubleBuffering) { } TEST_F(HttpConnectionManagerImplTest, ZeroByteDataFiltering) { - InSequence s; setup(false, ""); - - RequestDecoder* decoder = nullptr; - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); - return Http::okStatus(); - })); - setupFilterChain(2, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) .WillOnce(Return(FilterHeadersStatus::StopIteration)); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + startRequest(); // Continue headers only of filter 1. EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) @@ -545,7 +451,7 @@ TEST_F(HttpConnectionManagerImplTest, ZeroByteDataFiltering) { .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); EXPECT_CALL(*decoder_filters_[0], decodeComplete()); Buffer::OwnedImpl zero; - decoder->decodeData(zero, true); + decoder_->decodeData(zero, true); // Continue. EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) @@ -562,16 +468,16 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddTrailersInTrailersCallback) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, false); + decoder_->decodeData(fake_data, false); RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"bazzz", "bar"}}}; - decoder->decodeTrailers(std::move(trailers)); + decoder_->decodeTrailers(std::move(trailers)); return Http::okStatus(); })); @@ -646,20 +552,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddTrailersInTrailersCallback) { } TEST_F(HttpConnectionManagerImplTest, FilterAddTrailersInDataCallbackNoTrailers) { - InSequence s; setup(false, ""); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); - - Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, true); - return Http::okStatus(); - })); - setupFilterChain(2, 2); std::string trailers_data("trailers"); @@ -690,9 +583,8 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddTrailersInDataCallbackNoTrailers) })); EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + // Kick off the request. + startRequest(true, "hello"); // set up encodeHeaders expectations EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) @@ -743,16 +635,16 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInTrailersCallback) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, false); + decoder_->decodeData(fake_data, false); RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}}; - decoder->decodeTrailers(std::move(trailers)); + decoder_->decodeTrailers(std::move(trailers)); return Http::okStatus(); })); @@ -826,13 +718,13 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInTrailersCallback_NoDataFram setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}}; - decoder->decodeTrailers(std::move(trailers)); + decoder_->decodeTrailers(std::move(trailers)); return Http::okStatus(); })); @@ -887,13 +779,13 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInTrailersCallback_ContinueAf setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}}; - decoder->decodeTrailers(std::move(trailers)); + decoder_->decodeTrailers(std::move(trailers)); return Http::okStatus(); })); @@ -954,16 +846,16 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyDuringDecodeData) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); Buffer::OwnedImpl data1("hello"); - decoder->decodeData(data1, false); + decoder_->decodeData(data1, false); Buffer::OwnedImpl data2("world"); - decoder->decodeData(data2, true); + decoder_->decodeData(data2, true); return Http::okStatus(); })); @@ -1020,17 +912,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyDuringDecodeData) { } TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInline) { - InSequence s; setup(false, ""); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); - return Http::okStatus(); - })); - setupFilterChain(2, 2); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -1047,8 +929,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInline) { EXPECT_CALL(*decoder_filters_[1], decodeComplete()); // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + startRequest(true); EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { @@ -1075,14 +956,6 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInline) { TEST_F(HttpConnectionManagerImplTest, Filter) { setup(false, ""); - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); - return Http::okStatus(); - })); - setupFilterChain(3, 2); const std::string fake_cluster1_name = "fake_cluster1"; const std::string fake_cluster2_name = "fake_cluster2"; @@ -1132,8 +1005,7 @@ TEST_F(HttpConnectionManagerImplTest, Filter) { EXPECT_CALL(*decoder_filters_[2], decodeComplete()); // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + startRequest(true); expectOnDestroy(); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); @@ -1195,11 +1067,10 @@ TEST_F(HttpConnectionManagerImplTest, UnderlyingConnectionWatermarksPassedOnWith // Create the stream. Defer the creation of the filter chain by not sending // complete headers. - RequestDecoder* decoder; { setUpBufferLimits(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); // Call the high buffer callbacks as the codecs do. stream_callbacks_->onAboveWriteBufferHighWatermark(); return Http::okStatus(); @@ -1218,7 +1089,7 @@ TEST_F(HttpConnectionManagerImplTest, UnderlyingConnectionWatermarksPassedOnWith EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -1258,11 +1129,10 @@ TEST_F(HttpConnectionManagerImplTest, UnderlyingConnectionWatermarksUnwoundWithL // Create the stream. Defer the creation of the filter chain by not sending // complete headers. - RequestDecoder* decoder; { setUpBufferLimits(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); // Call the high buffer callbacks as the codecs do. stream_callbacks_->onAboveWriteBufferHighWatermark(); return Http::okStatus(); @@ -1288,7 +1158,7 @@ TEST_F(HttpConnectionManagerImplTest, UnderlyingConnectionWatermarksUnwoundWithL EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -1429,16 +1299,16 @@ TEST_F(HttpConnectionManagerImplTest, HitRequestBufferLimitsIntermediateFilter) setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, false); + decoder_->decodeData(fake_data, false); Buffer::OwnedImpl fake_data2("world world"); - decoder->decodeData(fake_data2, true); + decoder_->decodeData(fake_data2, true); return Http::okStatus(); })); @@ -1547,10 +1417,10 @@ TEST_F(HttpConnectionManagerImplTest, FilterHeadReply) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "HEAD"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); @@ -1584,19 +1454,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterHeadReply) { // up resetting the stream in the doEndStream() path (e.g., via filter reset due to timeout, etc.), // we emit a reset to the codec. TEST_F(HttpConnectionManagerImplTest, ResetWithStoppedFilter) { - InSequence s; setup(false, ""); - - EXPECT_CALL(*codec_, dispatch(_)) - .WillOnce(Invoke([&](Buffer::Instance& data) -> Envoy::Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); - data.drain(4); - return Http::okStatus(); - })); - setupFilterChain(1, 1); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -1621,9 +1479,8 @@ TEST_F(HttpConnectionManagerImplTest, ResetWithStoppedFilter) { EXPECT_CALL(*encoder_filters_[0], encodeComplete()); EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + // Kick off the request + startRequest(true); EXPECT_CALL(response_encoder_.stream_, resetStream(_)); expectOnDestroy(); @@ -1631,18 +1488,7 @@ TEST_F(HttpConnectionManagerImplTest, ResetWithStoppedFilter) { } TEST_F(HttpConnectionManagerImplTest, FilterContinueAndEndStreamHeaders) { - InSequence s; setup(false, ""); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - auto headers = std::make_unique( - std::initializer_list>( - {{":authority", "host"}, {":path", "/"}, {":method", "GET"}})); - decoder->decodeHeaders(std::move(headers), false); - return Http::okStatus(); - })); - setupFilterChain(2, 2); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) @@ -1653,8 +1499,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterContinueAndEndStreamHeaders) { EXPECT_CALL(*decoder_filters_[1], decodeComplete()); // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, true); + startRequest(); EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) .WillOnce(Return(FilterHeadersStatus::ContinueAndEndStream)); @@ -1675,20 +1520,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterContinueAndEndStreamHeaders) { } TEST_F(HttpConnectionManagerImplTest, FilterContinueAndEndStreamData) { - InSequence s; setup(false, ""); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - auto headers = makeHeaderMap( - {{":authority", "host"}, {":path", "/"}, {":method", "GET"}}); - decoder->decodeHeaders(std::move(headers), false); - - Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, true); - return Http::okStatus(); - })); - setupFilterChain(2, 2); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) @@ -1698,9 +1530,8 @@ TEST_F(HttpConnectionManagerImplTest, FilterContinueAndEndStreamData) { .WillOnce(Return(FilterHeadersStatus::Continue)); EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + // Kick off the request + startRequest(true, "hello"); EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) .WillOnce(Return(FilterHeadersStatus::ContinueAndEndStream)); @@ -1725,16 +1556,16 @@ TEST_F(HttpConnectionManagerImplTest, FilterContinueAndEndStreamTrailers) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); auto headers = makeHeaderMap( {{":authority", "host"}, {":path", "/"}, {":method", "GET"}}); - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, false); + decoder_->decodeData(fake_data, false); auto trailers = makeHeaderMap({{"foo", "bar"}}); - decoder->decodeTrailers(std::move(trailers)); + decoder_->decodeTrailers(std::move(trailers)); return Http::okStatus(); })); @@ -1774,17 +1605,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterContinueAndEndStreamTrailers) { // Filter continues headers iteration without ending the stream, then injects a body later. TEST_F(HttpConnectionManagerImplTest, FilterContinueDontEndStreamInjectBody) { - InSequence s; setup(false, ""); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - auto headers = makeHeaderMap( - {{":authority", "host"}, {":path", "/"}, {":method", "GET"}}); - decoder->decodeHeaders(std::move(headers), true); - return Http::okStatus(); - })); - setupFilterChain(2, 2); // Decode filter 0 changes end_stream to false. @@ -1795,8 +1616,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterContinueDontEndStreamInjectBody) { .WillOnce(Return(FilterHeadersStatus::Continue)); // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, true); + startRequest(true); EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) .WillOnce(Return(FilterDataStatus::Continue)); @@ -1830,26 +1650,15 @@ TEST_F(HttpConnectionManagerImplTest, FilterContinueDontEndStreamInjectBody) { } TEST_F(HttpConnectionManagerImplTest, FilterAddBodyContinuation) { - InSequence s; setup(false, ""); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); - return Http::okStatus(); - })); - setupFilterChain(2, 2); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) .WillOnce(Return(FilterHeadersStatus::StopIteration)); EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + // Kick off the incoming request. + startRequest(true); EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) .WillOnce(Return(FilterHeadersStatus::Continue)); @@ -1917,17 +1726,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyContinuation) { // filter1->encodeData(, true) is NOT called. // TEST_F(HttpConnectionManagerImplTest, AddDataWithAllContinue) { - InSequence s; setup(false, ""); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); - return Http::okStatus(); - })); - setupFilterChain(3, 3); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -1952,8 +1751,7 @@ TEST_F(HttpConnectionManagerImplTest, AddDataWithAllContinue) { EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)).Times(0); // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, true); + startRequest(true); // For encode direction EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, true)) @@ -2021,26 +1819,16 @@ TEST_F(HttpConnectionManagerImplTest, AddDataWithAllContinue) { // filter1->encodeData(, true) is NOT called. // TEST_F(HttpConnectionManagerImplTest, AddDataWithStopAndContinue) { - InSequence s; setup(false, ""); - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); - return Http::okStatus(); - })); - setupFilterChain(3, 3); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) .WillOnce(Return(FilterHeadersStatus::StopIteration)); EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, true); + // Kick off the request. + startRequest(true); EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { @@ -2098,20 +1886,7 @@ TEST_F(HttpConnectionManagerImplTest, AddDataWithStopAndContinue) { // Use filter direct decode/encodeData() calls without trailers. TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataNoTrailers) { - InSequence s; setup(false, ""); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); - - Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, true); - return Http::okStatus(); - })); - EXPECT_CALL(*route_config_provider_.route_config_, route(_, _, _, _)); setupFilterChain(2, 2); @@ -2128,10 +1903,9 @@ TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataNoTrailers) { })); EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); + // Kick off the request. EXPECT_CALL(filter_callbacks_.connection_.stream_info_, protocol(Envoy::Http::Protocol::Http11)); - conn_manager_->onData(fake_input, false); + startRequest(true, "hello"); Buffer::OwnedImpl decoded_data_to_forward; decoded_data_to_forward.move(decode_buffer, 2); @@ -2184,16 +1958,16 @@ TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataTrailers) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, false); + decoder_->decodeData(fake_data, false); RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}}; - decoder->decodeTrailers(std::move(trailers)); + decoder_->decodeTrailers(std::move(trailers)); return Http::okStatus(); })); @@ -2280,16 +2054,16 @@ TEST_F(HttpConnectionManagerImplTest, MultipleFilters) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); Buffer::OwnedImpl fake_data("hello"); - decoder->decodeData(fake_data, false); + decoder_->decodeData(fake_data, false); Buffer::OwnedImpl fake_data2("world"); - decoder->decodeData(fake_data2, true); + decoder_->decodeData(fake_data2, true); return Http::okStatus(); })); @@ -2397,14 +2171,6 @@ TEST_F(HttpConnectionManagerImplTest, NoNewStreamWhenOverloaded) { setup(false, ""); - EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); - return Http::okStatus(); - })); - // 503 direct response when overloaded. EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { @@ -2413,8 +2179,7 @@ TEST_F(HttpConnectionManagerImplTest, NoNewStreamWhenOverloaded) { std::string response_body; EXPECT_CALL(response_encoder_, encodeData(_, true)).WillOnce(AddBufferToString(&response_body)); - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + startRequest(); EXPECT_EQ("envoy overloaded", response_body); EXPECT_EQ(1U, stats_.named_.downstream_rq_overload_close_.value()); @@ -2437,12 +2202,12 @@ TEST_F(HttpConnectionManagerImplTest, DisableHttp1KeepAliveWhenOverloaded) { EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}, {"connection", "keep-alive"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -2496,12 +2261,12 @@ TEST_P(DrainH2HttpConnectionManagerImplTest, DisableHttp2KeepAliveWhenOverloaded EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}, {"connection", "keep-alive"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -2626,12 +2391,12 @@ TEST_F(HttpConnectionManagerImplTest, DisableKeepAliveWhenDraining) { EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}, {"connection", "keep-alive"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -2665,7 +2430,7 @@ TEST_F(HttpConnectionManagerImplTest, TestSessionTrace) { setupFilterChain(1, 1); // Create a new stream - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); // Send headers to that stream, and verify we both set and clear the tracked object. { @@ -2688,7 +2453,7 @@ TEST_F(HttpConnectionManagerImplTest, TestSessionTrace) { .WillOnce(Invoke([](HeaderMap&, bool) -> FilterHeadersStatus { return FilterHeadersStatus::StopIteration; })); - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); } // Send trailers to that stream, and verify by this point headers are in logged state. @@ -2710,7 +2475,7 @@ TEST_F(HttpConnectionManagerImplTest, TestSessionTrace) { EXPECT_CALL(*decoder_filters_[0], decodeComplete()); EXPECT_CALL(*decoder_filters_[0], decodeTrailers(_)) .WillOnce(Return(FilterTrailersStatus::StopIteration)); - decoder->decodeTrailers(std::move(trailers)); + decoder_->decodeTrailers(std::move(trailers)); } expectOnDestroy(); @@ -2728,10 +2493,10 @@ TEST_F(HttpConnectionManagerImplTest, TestSrdsRouteNotFound) { .Times(2) .WillRepeatedly(Return(nullptr)); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":method", "GET"}, {":path", "/foo"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); @@ -2762,10 +2527,10 @@ TEST_F(HttpConnectionManagerImplTest, TestSrdsUpdate) { .WillOnce(Return(nullptr)) // refreshCachedRoute first time. .WillOnce(Return(route_config_)); // triggered by callbacks_->route(), SRDS now updated. EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":method", "GET"}, {":path", "/foo"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); @@ -2829,10 +2594,10 @@ TEST_F(HttpConnectionManagerImplTest, TestSrdsCrossScopeReroute) { return route_config2; })); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":method", "GET"}, {"scope_key", "foo"}, {":path", "/foo"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); data.drain(4); return Http::okStatus(); })); @@ -2885,10 +2650,10 @@ TEST_F(HttpConnectionManagerImplTest, TestSrdsRouteFound) { route(_, _, _, _)) .WillOnce(Return(route1)); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":method", "GET"}, {":path", "/foo"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); data.drain(4); return Http::okStatus(); })); @@ -2930,10 +2695,10 @@ TEST_F(HttpConnectionManagerImplTest, TestUpstreamRequestHeadersSize) { // Test with Headers only request, No Data, No response. setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); @@ -2966,13 +2731,13 @@ TEST_F(HttpConnectionManagerImplTest, TestUpstreamRequestBodySize) { // Test Request with Headers and Data, No response. setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); Buffer::OwnedImpl fake_data("12345"); - decoder->decodeData(fake_data, true); + decoder_->decodeData(fake_data, true); return Http::okStatus(); })); @@ -3009,13 +2774,13 @@ TEST_F(HttpConnectionManagerImplTest, TestUpstreamResponseHeadersSize) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); Buffer::OwnedImpl fake_data("1234"); - decoder->decodeData(fake_data, true); + decoder_->decodeData(fake_data, true); return Http::okStatus(); })); @@ -3065,13 +2830,13 @@ TEST_F(HttpConnectionManagerImplTest, TestUpstreamResponseBodySize) { setup(false, ""); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), false); + decoder_->decodeHeaders(std::move(headers), false); Buffer::OwnedImpl fake_data("1234"); - decoder->decodeData(fake_data, true); + decoder_->decodeData(fake_data, true); return Http::okStatus(); })); @@ -3144,8 +2909,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponseUsingHttp3) { // Pretend to get a new stream and then fire a headers only request into it. Then we respond into // the filter. - NiceMock encoder; - RequestDecoder& decoder = conn_manager_->newStream(encoder); + RequestDecoder& decoder = conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; decoder.decodeHeaders(std::move(headers), true); @@ -3188,10 +2952,10 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionFilterState) { EXPECT_CALL(*codec_, dispatch(_)) .Times(2) .WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { - RequestDecoder* decoder = &conn_manager_->newStream(response_encoder_); + decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder->decodeHeaders(std::move(headers), true); + decoder_->decodeHeaders(std::move(headers), true); return Http::okStatus(); })); { @@ -3313,4 +3077,4 @@ TEST_F(HttpConnectionManagerImplDeathTest, InvalidConnectionManagerConfig) { } } // namespace Http -} // namespace Envoy \ No newline at end of file +} // 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 17983aea4b11..2d8278d4ad75 100644 --- a/test/common/http/conn_manager_impl_test_base.cc +++ b/test/common/http/conn_manager_impl_test_base.cc @@ -166,6 +166,23 @@ void HttpConnectionManagerImplTest::setUpEncoderAndDecoder(bool request_with_dat EXPECT_CALL(*decoder_filters_[0], decodeComplete()); } +void HttpConnectionManagerImplTest::startRequest(bool end_stream, + absl::optional body) { + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), end_stream && !body.has_value()); + if (body.has_value()) { + Buffer::OwnedImpl fake_data(body.value()); + decoder_->decodeData(fake_data, end_stream); + } + return Http::okStatus(); + })); + Buffer::OwnedImpl fake_input; + conn_manager_->onData(fake_input, false); +} + Event::MockTimer* HttpConnectionManagerImplTest::setUpTimer() { // this timer belongs to whatever by whatever next creates a timer. // See Envoy::Event::MockTimer for details. @@ -236,4 +253,4 @@ void HttpConnectionManagerImplTest::doRemoteClose(bool deferred) { } } // namespace Http -} // namespace Envoy \ No newline at end of file +} // 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 854bb7d77839..067ca0a1f369 100644 --- a/test/common/http/conn_manager_impl_test_base.h +++ b/test/common/http/conn_manager_impl_test_base.h @@ -53,6 +53,9 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan // StopAllIterationAndBuffer. void setUpEncoderAndDecoder(bool request_with_data_and_trailers, bool decode_headers_stop_all); + // Sends request headers, and stashes the new stream in decoder_; + void startRequest(bool end_stream = false, absl::optional body = absl::nullopt); + Event::MockTimer* setUpTimer(); void sendRequestHeadersAndData(); ResponseHeaderMap* sendResponseHeaders(ResponseHeaderMapPtr&& response_headers); @@ -172,6 +175,7 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan NiceMock random_; NiceMock local_info_; NiceMock factory_context_; + RequestDecoder* decoder_{}; std::shared_ptr ssl_connection_; std::shared_ptr> tracer_{ std::make_shared>()}; @@ -200,11 +204,11 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan const LocalReply::LocalReplyPtr local_reply_; // TODO(mattklein123): Not all tests have been converted over to better setup. Convert the rest. - MockResponseEncoder response_encoder_; + NiceMock response_encoder_; std::vector decoder_filters_; std::vector encoder_filters_; std::shared_ptr log_handler_; }; } // namespace Http -} // namespace Envoy \ No newline at end of file +} // namespace Envoy From b18f57e265a172cb0ae324acc67861c2f8a101f3 Mon Sep 17 00:00:00 2001 From: Weston Carlson Date: Tue, 6 Oct 2020 10:42:03 -0600 Subject: [PATCH 06/27] transport sockets: expose proxy protocol socket (#12762) Signed-off-by: Weston Carlson --- .../v3/upstream_proxy_protocol.proto | 4 +- .../other_features/ip_transparency.rst | 24 ++- docs/root/version_history/current.rst | 1 + .../v3/upstream_proxy_protocol.proto | 4 +- source/common/tcp_proxy/BUILD | 1 + source/common/tcp_proxy/tcp_proxy.cc | 13 ++ source/extensions/extensions_build_config.bzl | 2 +- .../transport_sockets/proxy_protocol/BUILD | 19 +- .../proxy_protocol/config.cc | 46 ++++ .../transport_sockets/proxy_protocol/config.h | 29 +++ .../proxy_protocol/proxy_protocol.cc | 27 ++- .../proxy_protocol/proxy_protocol.h | 17 ++ .../transport_socket_options_impl_test.cc | 2 + .../transport_sockets/proxy_protocol/BUILD | 14 ++ .../proxy_protocol_integration_test.cc | 204 ++++++++++++++++++ .../proxy_protocol/proxy_protocol_test.cc | 52 +++++ 16 files changed, 448 insertions(+), 11 deletions(-) create mode 100644 source/extensions/transport_sockets/proxy_protocol/config.cc create mode 100644 source/extensions/transport_sockets/proxy_protocol/config.h create mode 100644 test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc diff --git a/api/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto b/api/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto index c6c2ee9798d6..687226574d29 100644 --- a/api/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto +++ b/api/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto @@ -6,7 +6,6 @@ import "envoy/config/core/v3/base.proto"; import "envoy/config/core/v3/proxy_protocol.proto"; import "udpa/annotations/status.proto"; -import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.proxy_protocol.v3"; @@ -16,9 +15,10 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Upstream Proxy Protocol] // [#extension: envoy.transport_sockets.upstream_proxy_protocol] -// [#not-implemented-hide:] + // Configuration for PROXY protocol socket message ProxyProtocolUpstreamTransport { + // The PROXY protocol settings config.core.v3.ProxyProtocolConfig config = 1; // The underlying transport socket being wrapped. diff --git a/docs/root/intro/arch_overview/other_features/ip_transparency.rst b/docs/root/intro/arch_overview/other_features/ip_transparency.rst index 095ec54bc124..06e07b5b82f4 100644 --- a/docs/root/intro/arch_overview/other_features/ip_transparency.rst +++ b/docs/root/intro/arch_overview/other_features/ip_transparency.rst @@ -45,13 +45,33 @@ metadata includes the source IP. Envoy supports consuming this information using the downstream remote address for propagation into an :ref:`x-forwarded-for ` header. It can also be used in conjunction with the -:ref:`Original Src Listener Filter `. +:ref:`Original Src Listener Filter `. Finally, +Envoy supports generating this header using the :ref:`Proxy Protocol Transport Socket `. +Here is an example config for setting up the socket: + +.. code-block:: yaml + + clusters: + - name: service1 + connect_timeout: 0.25s + type: strict_dns + lb_policy: round_robin + transport_socket: + name: envoy.transport_sockets.upstream_proxy_protocol + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.proxy_protocol.v3.ProxyProtocolUpstreamTransport + config: + version: V1 + transport_socket: + name: envoy.transport_sockets.raw_buffer + ... + +Note: If you are wrapping a TLS socket, the header will be sent before the TLS handshake occurs. Some drawbacks to Proxy Protocol: * It only supports TCP protocols. * It requires upstream host support. -* Envoy cannot yet send it to the upstream. .. _arch_overview_ip_transparency_original_src_listener: diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index f31dcf28d919..eb5be4cd44a5 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -124,6 +124,7 @@ New Features * lua: added new :ref:`source_code ` field to support the dispatching of inline Lua code in per route configuration of Lua filter. * overload management: add :ref:`scaling ` trigger for OverloadManager actions. * postgres network filter: :ref:`metadata ` is produced based on SQL query. +* proxy protocol: added support for generating the header upstream using :ref:`Proxy Protocol Transport Socket `. * ratelimit: added :ref:`enable_x_ratelimit_headers ` option to enable `X-RateLimit-*` headers as defined in `draft RFC `_. * ratelimit: added :ref:`per route config ` for rate limit filter. * ratelimit: added support for optional :ref:`descriptor_key ` to Generic Key action. diff --git a/generated_api_shadow/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto b/generated_api_shadow/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto index c6c2ee9798d6..687226574d29 100644 --- a/generated_api_shadow/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto +++ b/generated_api_shadow/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto @@ -6,7 +6,6 @@ import "envoy/config/core/v3/base.proto"; import "envoy/config/core/v3/proxy_protocol.proto"; import "udpa/annotations/status.proto"; -import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.proxy_protocol.v3"; @@ -16,9 +15,10 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Upstream Proxy Protocol] // [#extension: envoy.transport_sockets.upstream_proxy_protocol] -// [#not-implemented-hide:] + // Configuration for PROXY protocol socket message ProxyProtocolUpstreamTransport { + // The PROXY protocol settings config.core.v3.ProxyProtocolConfig config = 1; // The underlying transport socket being wrapped. diff --git a/source/common/tcp_proxy/BUILD b/source/common/tcp_proxy/BUILD index 328aca0a23e9..f75138d14134 100644 --- a/source/common/tcp_proxy/BUILD +++ b/source/common/tcp_proxy/BUILD @@ -44,6 +44,7 @@ envoy_cc_library( "//source/common/network:cidr_range_lib", "//source/common/network:filter_lib", "//source/common/network:hash_policy_lib", + "//source/common/network:proxy_protocol_filter_state_lib", "//source/common/network:transport_socket_options_lib", "//source/common/network:upstream_server_name_lib", "//source/common/network:utility_lib", diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index 48d6994a6028..5ca0ea661d83 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -22,6 +22,7 @@ #include "common/common/utility.h" #include "common/config/well_known_names.h" #include "common/network/application_protocol.h" +#include "common/network/proxy_protocol_filter_state.h" #include "common/network/transport_socket_options_impl.h" #include "common/network/upstream_server_name.h" #include "common/router/metadatamatchcriteria_impl.h" @@ -414,6 +415,18 @@ Network::FilterStatus Filter::initializeUpstreamConnection() { } if (downstreamConnection()) { + if (!read_callbacks_->connection() + .streamInfo() + .filterState() + ->hasData( + Network::ProxyProtocolFilterState::key())) { + read_callbacks_->connection().streamInfo().filterState()->setData( + Network::ProxyProtocolFilterState::key(), + std::make_unique(Network::ProxyProtocolData{ + downstreamConnection()->remoteAddress(), downstreamConnection()->localAddress()}), + StreamInfo::FilterState::StateType::ReadOnly, + StreamInfo::FilterState::LifeSpan::Connection); + } transport_socket_options_ = Network::TransportSocketOptionsUtility::fromFilterState( downstreamConnection()->streamInfo().filterState()); } diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index c9ffb4859fd1..54d6cb5a4a71 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -162,7 +162,7 @@ EXTENSIONS = { # "envoy.transport_sockets.alts": "//source/extensions/transport_sockets/alts:config", - "envoy.transport_sockets.upstream_proxy_protocol": "//source/extensions/transport_sockets/proxy_protocol:upstream_proxy_protocol", + "envoy.transport_sockets.upstream_proxy_protocol": "//source/extensions/transport_sockets/proxy_protocol:upstream_config", "envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config", "envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config", "envoy.transport_sockets.quic": "//source/extensions/quic_listeners/quiche:quic_factory_lib", diff --git a/source/extensions/transport_sockets/proxy_protocol/BUILD b/source/extensions/transport_sockets/proxy_protocol/BUILD index 251721adfbb4..397626c3c6ae 100644 --- a/source/extensions/transport_sockets/proxy_protocol/BUILD +++ b/source/extensions/transport_sockets/proxy_protocol/BUILD @@ -1,6 +1,7 @@ load( "//bazel:envoy_build_system.bzl", "envoy_cc_extension", + "envoy_cc_library", "envoy_extension_package", ) @@ -9,11 +10,25 @@ licenses(["notice"]) # Apache 2 envoy_extension_package() envoy_cc_extension( + name = "upstream_config", + srcs = ["config.cc"], + hdrs = ["config.h"], + security_posture = "robust_to_untrusted_downstream_and_upstream", # header generated in Envoy, so can't be faked + deps = [ + ":upstream_proxy_protocol", + "//include/envoy/network:transport_socket_interface", + "//include/envoy/registry", + "//include/envoy/server:transport_socket_config_interface", + "//source/common/config:utility_lib", + "//source/extensions/transport_sockets:well_known_names", + "@envoy_api//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg_cc_proto", + ], +) + +envoy_cc_library( name = "upstream_proxy_protocol", srcs = ["proxy_protocol.cc"], hdrs = ["proxy_protocol.h"], - security_posture = "robust_to_untrusted_downstream", - undocumented = True, deps = [ "//include/envoy/network:connection_interface", "//include/envoy/network:transport_socket_interface", diff --git a/source/extensions/transport_sockets/proxy_protocol/config.cc b/source/extensions/transport_sockets/proxy_protocol/config.cc new file mode 100644 index 000000000000..e037263c8a4a --- /dev/null +++ b/source/extensions/transport_sockets/proxy_protocol/config.cc @@ -0,0 +1,46 @@ +#include "extensions/transport_sockets/proxy_protocol/config.h" + +#include "envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.pb.h" +#include "envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.pb.validate.h" +#include "envoy/registry/registry.h" + +#include "common/config/utility.h" + +#include "extensions/transport_sockets/proxy_protocol/proxy_protocol.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace ProxyProtocol { + +Network::TransportSocketFactoryPtr +UpstreamProxyProtocolSocketConfigFactory::createTransportSocketFactory( + const Protobuf::Message& message, + Server::Configuration::TransportSocketFactoryContext& context) { + const auto& outer_config = + MessageUtil::downcastAndValidate( + message, context.messageValidationVisitor()); + auto& inner_config_factory = Config::Utility::getAndCheckFactory< + Server::Configuration::UpstreamTransportSocketConfigFactory>(outer_config.transport_socket()); + ProtobufTypes::MessagePtr inner_factory_config = Config::Utility::translateToFactoryConfig( + outer_config.transport_socket(), context.messageValidationVisitor(), inner_config_factory); + auto inner_transport_factory = + inner_config_factory.createTransportSocketFactory(*inner_factory_config, context); + return std::make_unique(std::move(inner_transport_factory), + outer_config.config()); +} + +ProtobufTypes::MessagePtr UpstreamProxyProtocolSocketConfigFactory::createEmptyConfigProto() { + return std::make_unique< + envoy::extensions::transport_sockets::proxy_protocol::v3::ProxyProtocolUpstreamTransport>(); + ; +} + +REGISTER_FACTORY(UpstreamProxyProtocolSocketConfigFactory, + Server::Configuration::UpstreamTransportSocketConfigFactory); + +} // namespace ProxyProtocol +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/proxy_protocol/config.h b/source/extensions/transport_sockets/proxy_protocol/config.h new file mode 100644 index 000000000000..9ab05d870b40 --- /dev/null +++ b/source/extensions/transport_sockets/proxy_protocol/config.h @@ -0,0 +1,29 @@ +#pragma once + +#include "envoy/server/transport_socket_config.h" + +#include "extensions/transport_sockets/well_known_names.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace ProxyProtocol { + +/** + * Config registration for the proxy protocol wrapper for transport socket factory. + * @see TransportSocketConfigFactory. + */ +class UpstreamProxyProtocolSocketConfigFactory + : public Server::Configuration::UpstreamTransportSocketConfigFactory { +public: + std::string name() const override { return TransportSocketNames::get().UpstreamProxyProtocol; } + ProtobufTypes::MessagePtr createEmptyConfigProto() override; + Network::TransportSocketFactoryPtr createTransportSocketFactory( + const Protobuf::Message& config, + Server::Configuration::TransportSocketFactoryContext& context) override; +}; + +} // namespace ProxyProtocol +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc index d1427b7aaa9d..a32b5c7bdd70 100644 --- a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc +++ b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc @@ -10,6 +10,7 @@ #include "extensions/common/proxy_protocol/proxy_protocol_header.h" +using envoy::config::core::v3::ProxyProtocolConfig; using envoy::config::core::v3::ProxyProtocolConfig_Version; namespace Envoy { @@ -26,7 +27,6 @@ void UpstreamProxyProtocolSocket::setTransportSocketCallbacks( Network::TransportSocketCallbacks& callbacks) { transport_socket_->setTransportSocketCallbacks(callbacks); callbacks_ = &callbacks; - generateHeader(); } Network::IoResult UpstreamProxyProtocolSocket::doWrite(Buffer::Instance& buffer, bool end_stream) { @@ -51,7 +51,7 @@ void UpstreamProxyProtocolSocket::generateHeader() { } void UpstreamProxyProtocolSocket::generateHeaderV1() { - // Default to local addresses + // Default to local addresses (used if no downstream connection exists e.g. health checks) auto src_addr = callbacks_->connection().localAddress(); auto dst_addr = callbacks_->connection().remoteAddress(); @@ -100,6 +100,29 @@ Network::IoResult UpstreamProxyProtocolSocket::writeHeader() { return {action, bytes_written, false}; } +void UpstreamProxyProtocolSocket::onConnected() { + generateHeader(); + transport_socket_->onConnected(); +} + +UpstreamProxyProtocolSocketFactory::UpstreamProxyProtocolSocketFactory( + Network::TransportSocketFactoryPtr transport_socket_factory, ProxyProtocolConfig config) + : transport_socket_factory_(std::move(transport_socket_factory)), config_(config) {} + +Network::TransportSocketPtr UpstreamProxyProtocolSocketFactory::createTransportSocket( + Network::TransportSocketOptionsSharedPtr options) const { + auto inner_socket = transport_socket_factory_->createTransportSocket(options); + if (inner_socket == nullptr) { + return nullptr; + } + return std::make_unique(std::move(inner_socket), options, + config_.version()); +} + +bool UpstreamProxyProtocolSocketFactory::implementsSecureTransport() const { + return transport_socket_factory_->implementsSecureTransport(); +} + } // namespace ProxyProtocol } // namespace TransportSockets } // namespace Extensions diff --git a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h index 3b0996e20882..4a191ebf539d 100644 --- a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h +++ b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h @@ -9,6 +9,7 @@ #include "extensions/transport_sockets/common/passthrough.h" +using envoy::config::core::v3::ProxyProtocolConfig; using envoy::config::core::v3::ProxyProtocolConfig_Version; namespace Envoy { @@ -25,6 +26,7 @@ class UpstreamProxyProtocolSocket : public TransportSockets::PassthroughSocket, void setTransportSocketCallbacks(Network::TransportSocketCallbacks& callbacks) override; Network::IoResult doWrite(Buffer::Instance& buffer, bool end_stream) override; + void onConnected() override; private: void generateHeader(); @@ -38,6 +40,21 @@ class UpstreamProxyProtocolSocket : public TransportSockets::PassthroughSocket, ProxyProtocolConfig_Version version_{ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1}; }; +class UpstreamProxyProtocolSocketFactory : public Network::TransportSocketFactory { +public: + UpstreamProxyProtocolSocketFactory(Network::TransportSocketFactoryPtr transport_socket_factory, + ProxyProtocolConfig config); + + // Network::TransportSocketFactory + Network::TransportSocketPtr + createTransportSocket(Network::TransportSocketOptionsSharedPtr options) const override; + bool implementsSecureTransport() const override; + +private: + Network::TransportSocketFactoryPtr transport_socket_factory_; + ProxyProtocolConfig config_; +}; + } // namespace ProxyProtocol } // namespace TransportSockets } // namespace Extensions diff --git a/test/common/network/transport_socket_options_impl_test.cc b/test/common/network/transport_socket_options_impl_test.cc index a96fbc53bdd3..213c6778b037 100644 --- a/test/common/network/transport_socket_options_impl_test.cc +++ b/test/common/network/transport_socket_options_impl_test.cc @@ -44,6 +44,8 @@ TEST_F(TransportSocketOptionsImplTest, UpstreamServer) { auto transport_socket_options = TransportSocketOptionsUtility::fromFilterState(filter_state_); EXPECT_EQ(absl::make_optional("www.example.com"), transport_socket_options->serverNameOverride()); + EXPECT_EQ("202.168.0.13:52000", + transport_socket_options->proxyProtocolOptions()->src_addr_->asStringView()); EXPECT_TRUE(transport_socket_options->applicationProtocolListOverride().empty()); } diff --git a/test/extensions/transport_sockets/proxy_protocol/BUILD b/test/extensions/transport_sockets/proxy_protocol/BUILD index dbbdb719f507..666da2fdb1d7 100644 --- a/test/extensions/transport_sockets/proxy_protocol/BUILD +++ b/test/extensions/transport_sockets/proxy_protocol/BUILD @@ -23,5 +23,19 @@ envoy_extension_cc_test( "//test/mocks/network:io_handle_mocks", "//test/mocks/network:network_mocks", "//test/mocks/network:transport_socket_mocks", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + ], +) + +envoy_extension_cc_test( + name = "proxy_protocol_integration_test", + srcs = ["proxy_protocol_integration_test.cc"], + extension_name = "envoy.transport_sockets.upstream_proxy_protocol", + deps = [ + "//source/extensions/filters/network/tcp_proxy:config", + "//source/extensions/transport_sockets/proxy_protocol:upstream_config", + "//test/integration:integration_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg_cc_proto", ], ) diff --git a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc new file mode 100644 index 000000000000..99e04a348746 --- /dev/null +++ b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc @@ -0,0 +1,204 @@ +#include "envoy/config/core/v3/base.pb.h" +#include "envoy/config/core/v3/health_check.pb.h" +#include "envoy/config/core/v3/proxy_protocol.pb.h" +#include "envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.pb.h" + +#include "test/integration/integration.h" + +namespace Envoy { +namespace { + +class ProxyProtocolIntegrationTest : public testing::TestWithParam, + public BaseIntegrationTest { +public: + ProxyProtocolIntegrationTest() + : BaseIntegrationTest(GetParam(), ConfigHelper::tcpProxyConfig()) {} + + void TearDown() override { + test_server_.reset(); + fake_upstreams_.clear(); + } + + void setup(envoy::config::core::v3::ProxyProtocolConfig_Version version, bool health_checks, + std::string inner_socket) { + version_ = version; + health_checks_ = health_checks; + inner_socket_ = inner_socket; + } + + void initialize() override { + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto* transport_socket = + bootstrap.mutable_static_resources()->mutable_clusters(0)->mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.upstream_proxy_protocol"); + envoy::config::core::v3::TransportSocket inner_socket; + inner_socket.set_name(inner_socket_); + envoy::config::core::v3::ProxyProtocolConfig proxy_proto_config; + proxy_proto_config.set_version(version_); + envoy::extensions::transport_sockets::proxy_protocol::v3::ProxyProtocolUpstreamTransport + proxy_proto_transport; + proxy_proto_transport.mutable_transport_socket()->MergeFrom(inner_socket); + proxy_proto_transport.mutable_config()->MergeFrom(proxy_proto_config); + transport_socket->mutable_typed_config()->PackFrom(proxy_proto_transport); + + if (health_checks_) { + auto* cluster = bootstrap.mutable_static_resources()->mutable_clusters(0); + cluster->set_close_connections_on_host_health_failure(false); + cluster->mutable_common_lb_config()->mutable_healthy_panic_threshold()->set_value(0); + cluster->add_health_checks()->mutable_timeout()->set_seconds(20); + cluster->mutable_health_checks(0)->mutable_reuse_connection()->set_value(true); + cluster->mutable_health_checks(0)->mutable_interval()->set_seconds(1); + cluster->mutable_health_checks(0)->mutable_no_traffic_interval()->set_seconds(1); + cluster->mutable_health_checks(0)->mutable_unhealthy_threshold()->set_value(1); + cluster->mutable_health_checks(0)->mutable_healthy_threshold()->set_value(1); + cluster->mutable_health_checks(0)->mutable_tcp_health_check(); + cluster->mutable_health_checks(0)->mutable_tcp_health_check()->mutable_send()->set_text( + "50696E67"); + cluster->mutable_health_checks(0)->mutable_tcp_health_check()->add_receive()->set_text( + "506F6E67"); + } + }); + BaseIntegrationTest::initialize(); + } + + FakeRawConnectionPtr fake_upstream_connection_; + +private: + envoy::config::core::v3::ProxyProtocolConfig_Version version_; + bool health_checks_; + std::string inner_socket_; +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, ProxyProtocolIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +// Test sending proxy protocol v1 +TEST_P(ProxyProtocolIntegrationTest, TestV1ProxyProtocol) { + setup(envoy::config::core::v3::ProxyProtocolConfig::V1, false, + "envoy.transport_sockets.raw_buffer"); + initialize(); + + auto listener_port = lookupPort("listener_0"); + auto tcp_client = makeTcpConnection(listener_port); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); + + std::string observed_data; + ASSERT_TRUE(tcp_client->write("data")); + if (GetParam() == Network::Address::IpVersion::v4) { + ASSERT_TRUE(fake_upstream_connection_->waitForData(48, &observed_data)); + EXPECT_THAT(observed_data, testing::StartsWith("PROXY TCP4 127.0.0.1 127.0.0.1 ")); + } else if (GetParam() == Network::Address::IpVersion::v6) { + ASSERT_TRUE(fake_upstream_connection_->waitForData(36, &observed_data)); + EXPECT_THAT(observed_data, testing::StartsWith("PROXY TCP6 ::1 ::1 ")); + } + EXPECT_THAT(observed_data, testing::EndsWith(absl::StrCat(" ", listener_port, "\r\ndata"))); + + auto previous_data = observed_data; + observed_data.clear(); + ASSERT_TRUE(tcp_client->write(" more data")); + ASSERT_TRUE(fake_upstream_connection_->waitForData(previous_data.length() + 10, &observed_data)); + EXPECT_EQ(previous_data + " more data", observed_data); + + tcp_client->close(); + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); +} + +// Test header is sent unencrypted using a TLS inner socket +TEST_P(ProxyProtocolIntegrationTest, TestTLSSocket) { + setup(envoy::config::core::v3::ProxyProtocolConfig::V1, false, "envoy.transport_sockets.tls"); + initialize(); + + auto listener_port = lookupPort("listener_0"); + auto tcp_client = makeTcpConnection(listener_port); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); + + ASSERT_TRUE(tcp_client->write("data")); + if (GetParam() == Network::Address::IpVersion::v4) { + ASSERT_TRUE(fake_upstream_connection_->waitForData( + fake_upstream_connection_->waitForInexactMatch("PROXY TCP4 127.0.0.1 127.0.0.1 "))); + } else if (GetParam() == Network::Address::IpVersion::v6) { + ASSERT_TRUE(fake_upstream_connection_->waitForData( + fake_upstream_connection_->waitForInexactMatch("PROXY TCP6 ::1 ::1 "))); + } + + tcp_client->close(); + ASSERT_TRUE(fake_upstream_connection_->close()); + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); +} + +// Test sending proxy protocol health check +TEST_P(ProxyProtocolIntegrationTest, TestProxyProtocolHealthCheck) { + setup(envoy::config::core::v3::ProxyProtocolConfig::V1, true, + "envoy.transport_sockets.raw_buffer"); + FakeRawConnectionPtr fake_upstream_health_connection; + on_server_init_function_ = [&](void) -> void { + std::string observed_data; + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_health_connection)); + if (GetParam() == Network::Address::IpVersion::v4) { + ASSERT_TRUE(fake_upstream_health_connection->waitForData(48, &observed_data)); + EXPECT_THAT(observed_data, testing::StartsWith("PROXY TCP4 127.0.0.1 127.0.0.1 ")); + } else if (GetParam() == Network::Address::IpVersion::v6) { + ASSERT_TRUE(fake_upstream_health_connection->waitForData(36, &observed_data)); + EXPECT_THAT(observed_data, testing::StartsWith("PROXY TCP6 ::1 ::1 ")); + } + ASSERT_TRUE(fake_upstream_health_connection->write("Pong")); + }; + + initialize(); + + ASSERT_TRUE(fake_upstream_health_connection->close()); + ASSERT_TRUE(fake_upstream_health_connection->waitForDisconnect()); +} + +// Test sending proxy protocol v2 +TEST_P(ProxyProtocolIntegrationTest, TestV2ProxyProtocol) { + setup(envoy::config::core::v3::ProxyProtocolConfig::V2, false, + "envoy.transport_sockets.raw_buffer"); + initialize(); + + auto listener_port = lookupPort("listener_0"); + auto tcp_client = makeTcpConnection(listener_port); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); + + std::string observed_data; + ASSERT_TRUE(tcp_client->write("data")); + if (GetParam() == Envoy::Network::Address::IpVersion::v4) { + ASSERT_TRUE(fake_upstream_connection_->waitForData(32, &observed_data)); + // - signature + // - version and command type, address family and protocol, length of addresses + // - src address, dest address + auto header_start = "\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\ + \x21\x11\x00\x0c\ + \x7f\x00\x00\x01\x7f\x00\x00\x01"; + EXPECT_THAT(observed_data, testing::StartsWith(header_start)); + EXPECT_EQ(static_cast(observed_data[26]), listener_port >> 8); + EXPECT_EQ(static_cast(observed_data[27]), listener_port & 0xFF); + } else if (GetParam() == Envoy::Network::Address::IpVersion::v6) { + ASSERT_TRUE(fake_upstream_connection_->waitForData(56, &observed_data)); + // - signature + // - version and command type, address family and protocol, length of addresses + // - src address + // - dest address + auto header_start = "\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\ + \x21\x21\x00\x24\ + \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\ + \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"; + EXPECT_THAT(observed_data, testing::StartsWith(header_start)); + EXPECT_EQ(static_cast(observed_data[50]), listener_port >> 8); + EXPECT_EQ(static_cast(observed_data[51]), listener_port & 0xFF); + } + EXPECT_THAT(observed_data, testing::EndsWith("data")); + + auto previous_data = observed_data; + observed_data.clear(); + ASSERT_TRUE(tcp_client->write(" more data")); + ASSERT_TRUE(fake_upstream_connection_->waitForData(previous_data.length() + 10, &observed_data)); + EXPECT_EQ(previous_data + " more data", observed_data); + + tcp_client->close(); + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); +} + +} // namespace +} // namespace Envoy \ No newline at end of file diff --git a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc index 2823d218c992..141bba9703a2 100644 --- a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc @@ -1,3 +1,4 @@ +#include "envoy/config/core/v3/proxy_protocol.pb.h" #include "envoy/network/proxy_protocol.h" #include "common/buffer/buffer_impl.h" @@ -20,8 +21,10 @@ using testing::_; using testing::InSequence; using testing::NiceMock; using testing::Return; +using testing::ReturnNull; using testing::ReturnRef; +using envoy::config::core::v3::ProxyProtocolConfig; using envoy::config::core::v3::ProxyProtocolConfig_Version; namespace Envoy { @@ -42,6 +45,7 @@ class ProxyProtocolTest : public testing::Test { proxy_protocol_socket_ = std::make_unique(std::move(inner_socket), socket_options, version); proxy_protocol_socket_->setTransportSocketCallbacks(transport_callbacks_); + proxy_protocol_socket_->onConnected(); } NiceMock* inner_socket_; @@ -391,6 +395,54 @@ TEST_F(ProxyProtocolTest, V2IPV6DownstreamAddresses) { proxy_protocol_socket_->doWrite(msg, false); } +// Test onConnected calls inner onConnected +TEST_F(ProxyProtocolTest, OnConnectedCallsInnerOnConnected) { + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv6Instance("1:2:3::4", 8)); + auto dst_addr = Network::Address::InstanceConstSharedPtr( + new Network::Address::Ipv6Instance("1:100:200:3::", 2)); + Network::TransportSocketOptionsSharedPtr socket_options = + std::make_shared( + "", std::vector{}, std::vector{}, absl::nullopt, + absl::optional( + Network::ProxyProtocolData{src_addr, dst_addr})); + transport_callbacks_.connection_.local_address_ = + Network::Utility::resolveUrl("tcp://[1:100:200:3::]:50000"); + transport_callbacks_.connection_.remote_address_ = + Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080"); + initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, socket_options); + + EXPECT_CALL(*inner_socket_, onConnected()).Times(1); + proxy_protocol_socket_->onConnected(); +} + +class ProxyProtocolSocketFactoryTest : public testing::Test { +public: + void initialize() { + auto inner_factory = std::make_unique>(); + inner_factory_ = inner_factory.get(); + factory_ = std::make_unique(std::move(inner_factory), + ProxyProtocolConfig()); + } + + NiceMock* inner_factory_; + std::unique_ptr factory_; +}; + +// Test createTransportSocket returns nullptr if inner call returns nullptr +TEST_F(ProxyProtocolSocketFactoryTest, CreateSocketReturnsNullWhenInnerFactoryReturnsNull) { + initialize(); + EXPECT_CALL(*inner_factory_, createTransportSocket(_)).WillOnce(ReturnNull()); + ASSERT_EQ(nullptr, factory_->createTransportSocket(nullptr)); +} + +// Test implementsSecureTransport calls inner factory +TEST_F(ProxyProtocolSocketFactoryTest, ImplementsSecureTransportCallInnerFactory) { + initialize(); + EXPECT_CALL(*inner_factory_, implementsSecureTransport()).WillOnce(Return(true)); + ASSERT_TRUE(factory_->implementsSecureTransport()); +} + } // namespace } // namespace ProxyProtocol } // namespace TransportSockets From b50af650301e90eab60d14fa438a018edde31f0a Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 6 Oct 2020 13:21:05 -0400 Subject: [PATCH 07/27] stats: remove references to fake symbol tables. (#13392) Signed-off-by: Joshua Marantz --- docs/root/version_history/current.rst | 1 + source/common/stats/BUILD | 19 - source/common/stats/fake_symbol_table_impl.h | 144 -------- source/common/stats/isolated_store_impl.cc | 4 +- source/common/stats/symbol_table_creator.cc | 24 -- source/common/stats/symbol_table_creator.h | 57 --- source/docs/stats.md | 34 +- source/exe/BUILD | 2 - source/exe/main_common.cc | 5 +- source/exe/main_common.h | 4 +- source/server/BUILD | 1 - source/server/options_impl.cc | 5 + test/common/grpc/BUILD | 1 - test/common/grpc/context_impl_test.cc | 2 +- .../grpc_client_integration_test_harness.h | 2 +- test/common/http/BUILD | 1 - test/common/http/codes_speed_test.cc | 22 +- test/common/http/codes_test.cc | 1 - .../http/conn_manager_impl_fuzz_test.cc | 5 +- test/common/stats/BUILD | 6 - test/common/stats/allocator_impl_test.cc | 11 +- test/common/stats/isolated_store_impl_test.cc | 8 +- test/common/stats/metric_impl_test.cc | 9 +- test/common/stats/stat_merger_fuzz_test.cc | 2 - test/common/stats/stat_merger_test.cc | 38 +- test/common/stats/stat_test_utility.h | 13 - test/common/stats/stat_test_utility_test.cc | 8 +- test/common/stats/symbol_table_fuzz_test.cc | 11 - test/common/stats/symbol_table_impl_test.cc | 329 ++++++++---------- .../stats/thread_local_store_speed_test.cc | 12 +- test/common/stats/thread_local_store_test.cc | 119 +++---- test/common/stats/utility_fuzz_test.cc | 12 +- test/common/stats/utility_test.cc | 3 +- .../filters/http/grpc_http1_bridge/BUILD | 1 - .../http1_bridge_filter_test.cc | 2 +- test/extensions/filters/http/grpc_web/BUILD | 1 - .../http/grpc_web/grpc_web_filter_test.cc | 2 +- test/extensions/tracers/lightstep/BUILD | 1 - .../lightstep/lightstep_tracer_impl_test.cc | 2 +- test/integration/BUILD | 2 - test/integration/hotrestart_test.sh | 14 + test/integration/integration_admin_test.cc | 2 - test/integration/server.cc | 8 +- test/integration/server.h | 2 +- test/integration/stats_integration_test.cc | 107 +----- test/mocks/router/BUILD | 1 - test/mocks/router/mocks.h | 2 +- test/mocks/server/instance.h | 2 +- test/mocks/stats/BUILD | 2 - test/mocks/stats/mocks.cc | 2 +- test/mocks/stats/mocks.h | 10 +- test/mocks/upstream/host.h | 2 +- test/server/admin/BUILD | 1 - test/server/admin/stats_handler_test.cc | 5 +- test/server/options_impl_test.cc | 7 + test/server/server_test.cc | 12 +- test/test_common/BUILD | 1 - test/test_common/utility.h | 2 +- test/tools/router_check/router.h | 2 +- 59 files changed, 274 insertions(+), 836 deletions(-) delete mode 100644 source/common/stats/fake_symbol_table_impl.h delete mode 100644 source/common/stats/symbol_table_creator.cc delete mode 100644 source/common/stats/symbol_table_creator.h diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index eb5be4cd44a5..f19993ed5ebc 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -49,6 +49,7 @@ Minor Behavior Changes * postgres: changed log format to tokenize fields of Postgres messages. * router: added transport failure reason to response body when upstream reset happens. After this change, the response body will be of the form `upstream connect error or disconnect/reset before headers. reset reason:{}, transport failure reason:{}`.This behavior may be reverted by setting runtime feature `envoy.reloadable_features.http_transport_failure_reason_in_body` to false. * router: now consumes all retry related headers to prevent them from being propagated to the upstream. This behavior may be reverted by setting runtime feature `envoy.reloadable_features.consume_all_retry_headers` to false. +* stats: the fake symbol table implemention has been removed from the binary, and the option "--use-fake-symbol-table" is now a no-op with a warning. * thrift_proxy: special characters {'\0', '\r', '\n'} will be stripped from thrift headers. * watchdog: replaced single watchdog with separate watchdog configuration for worker threads and for the main thread :ref:`Watchdogs`. It works with :ref:`watchdog` by having the worker thread and main thread watchdogs have same config. diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index bc5c41f6e9e2..fb8528063934 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -46,7 +46,6 @@ envoy_cc_library( srcs = ["isolated_store_impl.cc"], hdrs = ["isolated_store_impl.h"], deps = [ - ":fake_symbol_table_lib", ":histogram_lib", ":null_counter_lib", ":null_gauge_lib", @@ -54,7 +53,6 @@ envoy_cc_library( ":scope_prefixer_lib", ":stats_lib", ":store_impl_lib", - ":symbol_table_creator_lib", ":tag_utility_lib", "//include/envoy/stats:stats_macros", "//source/common/stats:allocator_lib", @@ -191,23 +189,6 @@ envoy_cc_library( ], ) -envoy_cc_library( - name = "symbol_table_creator_lib", - srcs = ["symbol_table_creator.cc"], - hdrs = ["symbol_table_creator.h"], - external_deps = ["abseil_base"], - deps = [ - ":fake_symbol_table_lib", - ":symbol_table_lib", - ], -) - -envoy_cc_library( - name = "fake_symbol_table_lib", - hdrs = ["fake_symbol_table_impl.h"], - deps = [":symbol_table_lib"], -) - envoy_cc_library( name = "tag_extractor_lib", srcs = ["tag_extractor_impl.cc"], diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h deleted file mode 100644 index 19bfa00daa79..000000000000 --- a/source/common/stats/fake_symbol_table_impl.h +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "envoy/common/exception.h" -#include "envoy/stats/symbol_table.h" - -#include "common/common/assert.h" -#include "common/common/hash.h" -#include "common/common/lock_guard.h" -#include "common/common/non_copyable.h" -#include "common/common/thread.h" -#include "common/common/utility.h" -#include "common/stats/symbol_table_impl.h" - -#include "absl/strings/str_join.h" -#include "absl/strings/str_split.h" - -namespace Envoy { -namespace Stats { - -/** - * Implements the SymbolTable interface without taking locks or saving memory. - * This implementation is intended as a transient state for the Envoy codebase - * to allow incremental conversion of Envoy stats call-sites to use the - * SymbolTable interface, pre-allocating symbols during construction time for - * all stats tokens. - * - * Once all stat tokens are symbolized at construction time, this - * FakeSymbolTable implementation can be deleted, and real-symbol tables can be - * used, thereby reducing memory and improving stat construction time. - * - * Note that it is not necessary to pre-allocate all elaborated stat names - * because multiple StatNames can be joined together without taking locks, - * even in SymbolTableImpl. - * - * This implementation simply stores the characters directly in the uint8_t[] - * that backs each StatName, so there is no sharing or memory savings, but also - * no state associated with the SymbolTable, and thus no locks needed. - * - * TODO(#6307): delete this class once SymbolTable is fully deployed in the - * Envoy codebase. - */ -class FakeSymbolTableImpl : public SymbolTable { -public: - // SymbolTable - void populateList(const StatName* names, uint32_t num_names, StatNameList& list) override { - // This implementation of populateList is similar to - // SymbolTableImpl::populateList. This variant is more efficient for - // FakeSymbolTableImpl, because it avoid "encoding" each name in names. The - // strings are laid out abutting each other with 2-byte length prefixes, so - // encoding isn't needed, and doing a dummy encoding step would cost one - // memory allocation per element, adding significant overhead as measured by - // thread_local_store_speed_test. - - // We encode the number of names in a single byte, thus there must be less - // than 256 of them. - RELEASE_ASSERT(num_names < 256, "Maximum number elements in a StatNameList exceeded"); - - size_t total_size_bytes = 1; /* one byte for holding the number of names */ - for (uint32_t i = 0; i < num_names; ++i) { - total_size_bytes += names[i].size(); - } - - // Now allocate the exact number of bytes required and move the encodings - // into storage. - MemBlockBuilder mem_block(total_size_bytes); - mem_block.appendOne(num_names); - for (uint32_t i = 0; i < num_names; ++i) { - SymbolTableImpl::Encoding::appendToMemBlock(names[i], mem_block); - } - - // This assertion double-checks the arithmetic where we computed - // total_size_bytes. After appending all the encoded data into the - // allocated byte array, we should have exhausted all the memory - // we though we needed. - ASSERT(mem_block.capacityRemaining() == 0); - list.moveStorageIntoList(mem_block.release()); - } - - std::string toString(const StatName& stat_name) const override { - return std::string(toStringView(stat_name)); - } - uint64_t numSymbols() const override { return 0; } - bool lessThan(const StatName& a, const StatName& b) const override { - return toStringView(a) < toStringView(b); - } - void free(const StatName&) override {} - void incRefCount(const StatName&) override {} - StoragePtr encode(absl::string_view name) override { return encodeHelper(name); } - StoragePtr makeDynamicStorage(absl::string_view name) override { return encodeHelper(name); } - SymbolTable::StoragePtr join(const StatNameVec& names) const override { - std::vector strings; - for (StatName name : names) { - if (!name.empty()) { - strings.push_back(toStringView(name)); - } - } - return encodeHelper(absl::StrJoin(strings, ".")); - } - -#ifndef ENVOY_CONFIG_COVERAGE - void debugPrint() const override {} -#endif - - void callWithStringView(StatName stat_name, - const std::function& fn) const override { - fn(toStringView(stat_name)); - } - - StatNameSetPtr makeSet(absl::string_view name) override { - // make_unique does not work with private ctor, even though FakeSymbolTableImpl is a friend. - return StatNameSetPtr(new StatNameSet(*this, name)); - } - uint64_t getRecentLookups(const RecentLookupsFn&) const override { return 0; } - void clearRecentLookups() override {} - void setRecentLookupCapacity(uint64_t) override {} - uint64_t recentLookupCapacity() const override { return 0; } - DynamicSpans getDynamicSpans(StatName) const override { return DynamicSpans(); } - -private: - absl::string_view toStringView(const StatName& stat_name) const { - return {reinterpret_cast(stat_name.data()), - static_cast(stat_name.dataSize())}; - } - - StoragePtr encodeHelper(absl::string_view name) const { - name = StringUtil::removeTrailingCharacters(name, '.'); - MemBlockBuilder mem_block(SymbolTableImpl::Encoding::totalSizeBytes(name.size())); - SymbolTableImpl::Encoding::appendEncoding(name.size(), mem_block); - mem_block.appendData( - absl::MakeSpan(reinterpret_cast(name.data()), name.size())); - ASSERT(mem_block.capacityRemaining() == 0); - return mem_block.release(); - } -}; - -} // namespace Stats -} // namespace Envoy diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index d9511916e844..bbc2a267c918 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -5,16 +5,14 @@ #include #include "common/common/utility.h" -#include "common/stats/fake_symbol_table_impl.h" #include "common/stats/histogram_impl.h" #include "common/stats/scope_prefixer.h" -#include "common/stats/symbol_table_creator.h" #include "common/stats/utility.h" namespace Envoy { namespace Stats { -IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(SymbolTableCreator::makeSymbolTable()) {} +IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_unique()) {} IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr&& symbol_table) : IsolatedStoreImpl(*symbol_table) { diff --git a/source/common/stats/symbol_table_creator.cc b/source/common/stats/symbol_table_creator.cc deleted file mode 100644 index 755c8fcce2e4..000000000000 --- a/source/common/stats/symbol_table_creator.cc +++ /dev/null @@ -1,24 +0,0 @@ -#include "common/stats/symbol_table_creator.h" - -namespace Envoy { -namespace Stats { - -bool SymbolTableCreator::initialized_ = false; -bool SymbolTableCreator::use_fake_symbol_tables_ = false; - -SymbolTablePtr SymbolTableCreator::initAndMakeSymbolTable(bool use_fake) { - ASSERT(!initialized_ || (use_fake_symbol_tables_ == use_fake)); - use_fake_symbol_tables_ = use_fake; - return makeSymbolTable(); -} - -SymbolTablePtr SymbolTableCreator::makeSymbolTable() { - initialized_ = true; - if (use_fake_symbol_tables_) { - return std::make_unique(); - } - return std::make_unique(); -} - -} // namespace Stats -} // namespace Envoy diff --git a/source/common/stats/symbol_table_creator.h b/source/common/stats/symbol_table_creator.h deleted file mode 100644 index 4b51468890ce..000000000000 --- a/source/common/stats/symbol_table_creator.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "common/stats/fake_symbol_table_impl.h" -#include "common/stats/symbol_table_impl.h" - -namespace Envoy { -namespace Stats { - -namespace TestUtil { -class SymbolTableCreatorTestPeer; -} - -class SymbolTableCreator { -public: - /** - * Initializes the symbol-table creation system. Once this is called, it is a - * runtime assertion to call this again in production code, changing the - * use_fakes setting. However, tests can change the setting via - * TestUtil::SymbolTableCreatorTestPeer::setUseFakeSymbolTables(use_fakes). - * - * @param use_fakes Whether to use fake symbol tables; typically from a command-line option. - * @return a SymbolTable. - */ - static SymbolTablePtr initAndMakeSymbolTable(bool use_fakes); - - /** - * Factory method to create SymbolTables. This is needed to help make it - * possible to flag-flip use of real symbol tables, and ultimately should be - * removed. - * - * @return a SymbolTable. - */ - static SymbolTablePtr makeSymbolTable(); - - /** - * @return whether the system is initialized to use fake symbol tables. - */ - static bool useFakeSymbolTables() { return use_fake_symbol_tables_; } - -private: - friend class TestUtil::SymbolTableCreatorTestPeer; - - /** - * Sets whether fake or real symbol tables should be used. Tests that alter - * this should restore previous value at the end of the test. This must be - * called via TestUtil::SymbolTableCreatorTestPeer. - * - * *param use_fakes whether to use fake symbol tables. - */ - static void setUseFakeSymbolTables(bool use_fakes) { use_fake_symbol_tables_ = use_fakes; } - - static bool initialized_; - static bool use_fake_symbol_tables_; -}; - -} // namespace Stats -} // namespace Envoy diff --git a/source/docs/stats.md b/source/docs/stats.md index f80d1b46932f..418a04f628d7 100644 --- a/source/docs/stats.md +++ b/source/docs/stats.md @@ -180,23 +180,10 @@ showing the memory layout for a few scenarios of constructing and joining symbol ![Symbol Table Memory Diagram](symtab.png) -### Current State and Strategy To Deploy Symbol Tables - -As of September 5, 2019, the symbol table API has been integrated into the -production code, using a temporary ["fake" symbol table -implementation](https://github.com/envoyproxy/envoy/blob/master/source/common/stats/fake_symbol_table_impl.h). This -fake has enabled us to incrementally transform the codebase to pre-symbolize -names as much as possible, avoiding contention in the hot-path. - -There are no longer any explicit production calls to create counters -or gauges directly from a string via `Stats::Scope::counter(const -std::string&)`, though they are ubiquitous in tests. There is also a -`check_format` protection against reintroducting production calls to -`counter()`. - -However, there are still several ways to create hot-path contention -looking up stats by name, and there is no bulletproof way to prevent it from -occurring. +### Symbol Contention Risk + +There are several ways to create hot-path contention looking up stats by name, +and there is no bulletproof way to prevent it from occurring. * The [stats macros](https://github.com/envoyproxy/envoy/blob/master/include/envoy/stats/stats_macros.h) may be used in a data structure which is constructed in response to requests. * An explicit symbol-table lookup, via `StatNamePool` or `StatNameSet` can be made in the hot path. @@ -204,22 +191,21 @@ occurring. It is difficult to search for those scenarios in the source code or prevent them with a format-check, but we can determine whether symbol-table lookups are occurring during via an admin endpoint that shows 20 recent lookups by name, at -`ENVOY_HOST:ADMIN_PORT/stats?recentlookups`. This works only when real symbol -tables are enabled, via command-line option `--use-fake-symbol-table 0`. +`ENVOY_HOST:ADMIN_PORT/stats?recentlookups`. -Once we are confident we've removed all hot-path symbol-table lookups, ideally -through usage of real symbol tables in production, examining that endpoint, we -can enable real symbol tables by default. +As of October 6, 2020, the "fake" symbol table implementation has been removed +from the system, and the "--use-fake-symbol-table" option is now a no-op, +triggering a warning if set to "1". The option will be removed in a later +release. ### Symbol Table Class Overview Class | Superclass | Description -----| ---------- | --------- SymbolTable | | Abstract class providing an interface for symbol tables -FakeSymbolTableImpl | SymbolTable | Implementation of SymbolTable API where StatName is represented as a flat string SymbolTableImpl | SymbolTable | Implementation of SymbolTable API where StatName share symbols held in a table SymbolTableImpl::Encoding | | Helper class for incrementally encoding strings into symbols -StatName | | Provides an API and a view into a StatName (dynamic, symbolized, or fake). Like absl::string_view, the backing store must be separately maintained. +StatName | | Provides an API and a view into a StatName (dynamic orsymbolized). Like absl::string_view, the backing store must be separately maintained. StatNameStorageBase | | Holds storage (an array of bytes) for a dynamic or symbolized StatName StatNameStorage | StatNameStorageBase | Holds storage for a symbolized StatName. Must be explicitly freed (not just destructed). StatNameManagedStorage | StatNameStorage | Like StatNameStorage, but is 8 bytes larger, and can be destructed without free(). diff --git a/source/exe/BUILD b/source/exe/BUILD index 4fc1048ef2d1..3edb3dd917c2 100644 --- a/source/exe/BUILD +++ b/source/exe/BUILD @@ -67,7 +67,6 @@ envoy_cc_library( "//source/common/common:compiler_requirements_lib", "//source/common/common:perf_annotation_lib", "//source/common/grpc:google_grpc_context_lib", - "//source/common/stats:symbol_table_creator_lib", "//source/server:hot_restart_lib", "//source/server:hot_restart_nop_lib", "//source/server/config_validation:server_lib", @@ -114,7 +113,6 @@ envoy_cc_library( "//source/common/common:compiler_requirements_lib", "//source/common/common:perf_annotation_lib", "//source/common/grpc:google_grpc_context_lib", - "//source/common/stats:symbol_table_creator_lib", "//source/server:hot_restart_lib", "//source/server:hot_restart_nop_lib", "//source/server/config_validation:server_lib", diff --git a/source/exe/main_common.cc b/source/exe/main_common.cc index 1bf80b72c7a1..a9e478871ad7 100644 --- a/source/exe/main_common.cc +++ b/source/exe/main_common.cc @@ -11,7 +11,6 @@ #include "common/common/logger.h" #include "common/common/perf_annotation.h" #include "common/network/utility.h" -#include "common/stats/symbol_table_creator.h" #include "common/stats/thread_local_store.h" #include "server/config_validation/server.h" @@ -51,9 +50,7 @@ MainCommonBase::MainCommonBase(const OptionsImpl& options, Event::TimeSystem& ti Filesystem::Instance& file_system, std::unique_ptr process_context) : options_(options), component_factory_(component_factory), thread_factory_(thread_factory), - file_system_(file_system), symbol_table_(Stats::SymbolTableCreator::initAndMakeSymbolTable( - options_.fakeSymbolTableEnabled())), - stats_allocator_(*symbol_table_) { + file_system_(file_system), stats_allocator_(symbol_table_) { // Process the option to disable extensions as early as possible, // before we do any configuration loading. OptionsImpl::disableExtensions(options.disabledExtensions()); diff --git a/source/exe/main_common.h b/source/exe/main_common.h index 91ea197def3c..a986d502fb42 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -6,7 +6,7 @@ #include "common/common/thread.h" #include "common/event/real_time_system.h" #include "common/grpc/google_grpc_context.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "common/stats/thread_local_store.h" #include "common/thread_local/thread_local_impl.h" @@ -75,7 +75,7 @@ class MainCommonBase { Server::ComponentFactory& component_factory_; Thread::ThreadFactory& thread_factory_; Filesystem::Instance& file_system_; - Stats::SymbolTablePtr symbol_table_; + Stats::SymbolTableImpl symbol_table_; Stats::AllocatorImpl stats_allocator_; ThreadLocal::InstanceImplPtr tls_; diff --git a/source/server/BUILD b/source/server/BUILD index bc938e92819e..6c587d9d95d7 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -441,7 +441,6 @@ envoy_cc_library( "//source/common/runtime:runtime_lib", "//source/common/secret:secret_manager_impl_lib", "//source/common/singleton:manager_impl_lib", - "//source/common/stats:symbol_table_creator_lib", "//source/common/stats:thread_local_store_lib", "//source/common/upstream:cluster_manager_lib", "//source/common/upstream:health_discovery_service_lib", diff --git a/source/server/options_impl.cc b/source/server/options_impl.cc index 9ba46d373a6e..b7c031e914a2 100644 --- a/source/server/options_impl.cc +++ b/source/server/options_impl.cc @@ -187,6 +187,11 @@ OptionsImpl::OptionsImpl(std::vector args, hot_restart_disabled_ = disable_hot_restart.getValue(); mutex_tracing_enabled_ = enable_mutex_tracing.getValue(); fake_symbol_table_enabled_ = use_fake_symbol_table.getValue(); + if (fake_symbol_table_enabled_) { + ENVOY_LOG(warn, "Fake symbol tables have been removed. Please remove references to " + "--use-fake-symbol-table"); + } + cpuset_threads_ = cpuset_threads.getValue(); if (log_level.isSet()) { diff --git a/test/common/grpc/BUILD b/test/common/grpc/BUILD index 16c3d937ff69..eac8fbefa8a5 100644 --- a/test/common/grpc/BUILD +++ b/test/common/grpc/BUILD @@ -82,7 +82,6 @@ envoy_cc_test( "//source/common/grpc:common_lib", "//source/common/grpc:context_lib", "//source/common/http:headers_lib", - "//source/common/stats:fake_symbol_table_lib", "//test/mocks/upstream:cluster_info_mocks", "//test/test_common:global_lib", ], diff --git a/test/common/grpc/context_impl_test.cc b/test/common/grpc/context_impl_test.cc index 64ed11321215..745ddb797f49 100644 --- a/test/common/grpc/context_impl_test.cc +++ b/test/common/grpc/context_impl_test.cc @@ -5,7 +5,7 @@ #include "common/http/headers.h" #include "common/http/message_impl.h" #include "common/http/utility.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "test/mocks/upstream/cluster_info.h" #include "test/test_common/global.h" diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index a4a7cf65fc8b..d610a221f8b7 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -20,7 +20,7 @@ #include "common/http/async_client_impl.h" #include "common/http/codes.h" #include "common/http/http2/conn_pool.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "common/network/connection_impl.h" #include "common/network/raw_buffer_socket.h" diff --git a/test/common/http/BUILD b/test/common/http/BUILD index ff06ef4f916c..a9877ae24883 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -173,7 +173,6 @@ envoy_cc_fuzz_test( "//source/common/http:request_id_extension_lib", "//source/common/network:address_lib", "//source/common/network:utility_lib", - "//source/common/stats:symbol_table_creator_lib", "//source/server:drain_manager_lib", "//test/fuzz:utility_lib", "//test/mocks/access_log:access_log_mocks", diff --git a/test/common/http/codes_speed_test.cc b/test/common/http/codes_speed_test.cc index 0aa91791a20f..f6730603abc6 100644 --- a/test/common/http/codes_speed_test.cc +++ b/test/common/http/codes_speed_test.cc @@ -9,8 +9,8 @@ #include "envoy/stats/stats.h" #include "common/http/codes.h" -#include "common/stats/fake_symbol_table_impl.h" #include "common/stats/isolated_store_impl.h" +#include "common/stats/symbol_table_impl.h" #include "benchmark/benchmark.h" @@ -76,24 +76,7 @@ template class CodeUtilitySpeedTest { } // namespace Http } // namespace Envoy -static void BM_AddResponsesFakeSymtab(benchmark::State& state) { - Envoy::Http::CodeUtilitySpeedTest context; - - for (auto _ : state) { - context.addResponses(); - } -} -BENCHMARK(BM_AddResponsesFakeSymtab); - -static void BM_ResponseTimingFakeSymtab(benchmark::State& state) { - Envoy::Http::CodeUtilitySpeedTest context; - - for (auto _ : state) { - context.responseTiming(); - } -} -BENCHMARK(BM_ResponseTimingFakeSymtab); - +// NOLINTNEXTLINE(readability-identifier-naming) static void BM_AddResponsesRealSymtab(benchmark::State& state) { Envoy::Http::CodeUtilitySpeedTest context; @@ -103,6 +86,7 @@ static void BM_AddResponsesRealSymtab(benchmark::State& state) { } BENCHMARK(BM_AddResponsesRealSymtab); +// NOLINTNEXTLINE(readability-identifier-naming) static void BM_ResponseTimingRealSymtab(benchmark::State& state) { Envoy::Http::CodeUtilitySpeedTest context; diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index 9a071f8c122f..bcbcf4820489 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -8,7 +8,6 @@ #include "common/common/empty_string.h" #include "common/http/codes.h" #include "common/http/header_map_impl.h" -#include "common/stats/symbol_table_creator.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/printers.h" diff --git a/test/common/http/conn_manager_impl_fuzz_test.cc b/test/common/http/conn_manager_impl_fuzz_test.cc index 65deb28a0293..7ba69a0dec4f 100644 --- a/test/common/http/conn_manager_impl_fuzz_test.cc +++ b/test/common/http/conn_manager_impl_fuzz_test.cc @@ -23,7 +23,6 @@ #include "common/http/request_id_extension_impl.h" #include "common/network/address_impl.h" #include "common/network/utility.h" -#include "common/stats/symbol_table_creator.h" #include "test/common/http/conn_manager_impl_fuzz.pb.validate.h" #include "test/fuzz/fuzz_runner.h" @@ -560,8 +559,8 @@ DEFINE_PROTO_FUZZER(const test::common::http::ConnManagerImplTestCase& input) { FuzzConfig config(input.forward_client_cert()); NiceMock drain_close; NiceMock random; - Stats::SymbolTablePtr symbol_table(Stats::SymbolTableCreator::makeSymbolTable()); - Http::ContextImpl http_context(*symbol_table); + Stats::SymbolTableImpl symbol_table; + Http::ContextImpl http_context(symbol_table); NiceMock runtime; NiceMock local_info; NiceMock cluster_manager; diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 8e04b273263f..0493f5127b17 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -18,7 +18,6 @@ envoy_cc_test( srcs = ["allocator_impl_test.cc"], deps = [ "//source/common/stats:allocator_lib", - "//source/common/stats:symbol_table_creator_lib", "//test/test_common:logging_lib", "//test/test_common:thread_factory_for_test_lib", ], @@ -29,7 +28,6 @@ envoy_cc_test( srcs = ["isolated_store_impl_test.cc"], deps = [ "//source/common/stats:isolated_store_lib", - "//source/common/stats:symbol_table_creator_lib", ], ) @@ -47,7 +45,6 @@ envoy_cc_test( srcs = ["metric_impl_test.cc"], deps = [ "//source/common/stats:allocator_lib", - "//source/common/stats:symbol_table_creator_lib", "//source/common/stats:utility_lib", "//test/test_common:logging_lib", ], @@ -85,7 +82,6 @@ envoy_cc_test( ":stat_test_utility_lib", "//source/common/stats:isolated_store_lib", "//source/common/stats:stat_merger_lib", - "//source/common/stats:symbol_table_creator_lib", "//source/common/stats:thread_local_store_lib", "//test/test_common:utility_lib", ], @@ -103,7 +99,6 @@ envoy_cc_test_library( "//source/common/common:assert_lib", "//source/common/memory:stats_lib", "//source/common/stats:isolated_store_lib", - "//source/common/stats:symbol_table_creator_lib", ], ) @@ -142,7 +137,6 @@ envoy_cc_test( ":stat_test_utility_lib", "//source/common/common:mutex_tracer_lib", "//source/common/memory:stats_lib", - "//source/common/stats:fake_symbol_table_lib", "//source/common/stats:symbol_table_lib", "//test/mocks/stats:stats_mocks", "//test/test_common:logging_lib", diff --git a/test/common/stats/allocator_impl_test.cc b/test/common/stats/allocator_impl_test.cc index b6579fdae696..571e53fd6810 100644 --- a/test/common/stats/allocator_impl_test.cc +++ b/test/common/stats/allocator_impl_test.cc @@ -1,7 +1,6 @@ #include #include "common/stats/allocator_impl.h" -#include "common/stats/symbol_table_creator.h" #include "test/test_common/logging.h" #include "test/test_common/thread_factory_for_test.h" @@ -15,23 +14,21 @@ namespace { class AllocatorImplTest : public testing::Test { protected: - AllocatorImplTest() - : symbol_table_(SymbolTableCreator::makeSymbolTable()), alloc_(*symbol_table_), - pool_(*symbol_table_) {} + AllocatorImplTest() : alloc_(symbol_table_), pool_(symbol_table_) {} ~AllocatorImplTest() override { clearStorage(); } StatNameStorage makeStatStorage(absl::string_view name) { - return StatNameStorage(name, *symbol_table_); + return StatNameStorage(name, symbol_table_); } StatName makeStat(absl::string_view name) { return pool_.add(name); } void clearStorage() { pool_.clear(); - EXPECT_EQ(0, symbol_table_->numSymbols()); + EXPECT_EQ(0, symbol_table_.numSymbols()); } - SymbolTablePtr symbol_table_; + SymbolTableImpl symbol_table_; AllocatorImpl alloc_; StatNamePool pool_; }; diff --git a/test/common/stats/isolated_store_impl_test.cc b/test/common/stats/isolated_store_impl_test.cc index ee618669cb2c..b3a82ad8773e 100644 --- a/test/common/stats/isolated_store_impl_test.cc +++ b/test/common/stats/isolated_store_impl_test.cc @@ -5,7 +5,6 @@ #include "common/stats/isolated_store_impl.h" #include "common/stats/null_counter.h" #include "common/stats/null_gauge.h" -#include "common/stats/symbol_table_creator.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -17,17 +16,16 @@ namespace Stats { class StatsIsolatedStoreImplTest : public testing::Test { protected: StatsIsolatedStoreImplTest() - : symbol_table_(SymbolTableCreator::makeSymbolTable()), - store_(std::make_unique(*symbol_table_)), pool_(*symbol_table_) {} + : store_(std::make_unique(symbol_table_)), pool_(symbol_table_) {} ~StatsIsolatedStoreImplTest() override { pool_.clear(); store_.reset(); - EXPECT_EQ(0, symbol_table_->numSymbols()); + EXPECT_EQ(0, symbol_table_.numSymbols()); } StatName makeStatName(absl::string_view name) { return pool_.add(name); } - SymbolTablePtr symbol_table_; + SymbolTableImpl symbol_table_; std::unique_ptr store_; StatNamePool pool_; }; diff --git a/test/common/stats/metric_impl_test.cc b/test/common/stats/metric_impl_test.cc index 75652e21f989..30f20a61ee3a 100644 --- a/test/common/stats/metric_impl_test.cc +++ b/test/common/stats/metric_impl_test.cc @@ -1,7 +1,6 @@ #include #include "common/stats/allocator_impl.h" -#include "common/stats/symbol_table_creator.h" #include "common/stats/utility.h" #include "test/test_common/logging.h" @@ -14,19 +13,17 @@ namespace { class MetricImplTest : public testing::Test { protected: - MetricImplTest() - : symbol_table_(SymbolTableCreator::makeSymbolTable()), alloc_(*symbol_table_), - pool_(*symbol_table_) {} + MetricImplTest() : alloc_(symbol_table_), pool_(symbol_table_) {} ~MetricImplTest() override { clearStorage(); } StatName makeStat(absl::string_view name) { return pool_.add(name); } void clearStorage() { pool_.clear(); - EXPECT_EQ(0, symbol_table_->numSymbols()); + EXPECT_EQ(0, symbol_table_.numSymbols()); } - SymbolTablePtr symbol_table_; + SymbolTableImpl symbol_table_; AllocatorImpl alloc_; StatNamePool pool_; }; diff --git a/test/common/stats/stat_merger_fuzz_test.cc b/test/common/stats/stat_merger_fuzz_test.cc index 70579f378676..c35c7fd9ea35 100644 --- a/test/common/stats/stat_merger_fuzz_test.cc +++ b/test/common/stats/stat_merger_fuzz_test.cc @@ -69,11 +69,9 @@ void testDynamicEncoding(absl::string_view data, SymbolTable& symbol_table) { // Fuzzer for symbol tables. DEFINE_FUZZER(const uint8_t* buf, size_t len) { - FakeSymbolTableImpl fake_symbol_table; SymbolTableImpl symbol_table; absl::string_view data(reinterpret_cast(buf), len); - testDynamicEncoding(data, fake_symbol_table); testDynamicEncoding(data, symbol_table); } diff --git a/test/common/stats/stat_merger_test.cc b/test/common/stats/stat_merger_test.cc index ee8b5bf65ae8..092fc531597b 100644 --- a/test/common/stats/stat_merger_test.cc +++ b/test/common/stats/stat_merger_test.cc @@ -2,7 +2,6 @@ #include "common/stats/isolated_store_impl.h" #include "common/stats/stat_merger.h" -#include "common/stats/symbol_table_creator.h" #include "common/stats/thread_local_store.h" #include "test/test_common/utility.h" @@ -303,43 +302,10 @@ TEST_F(StatMergerDynamicTest, DynamicsWithRealSymbolTable) { EXPECT_EQ(1, dynamicEncodeDecodeTest("D:hello,,,world")); } -TEST_F(StatMergerDynamicTest, DynamicsWithFakeSymbolTable) { - init(std::make_unique()); - - for (uint32_t i = 1; i < 256; ++i) { - char ch = static_cast(i); - absl::string_view one_char(&ch, 1); - EXPECT_EQ(0, dynamicEncodeDecodeTest(absl::StrCat("D:", one_char))) << "dynamic=" << one_char; - EXPECT_EQ(0, dynamicEncodeDecodeTest(one_char)) << "symbolic=" << one_char; - } - EXPECT_EQ(0, dynamicEncodeDecodeTest("normal")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("D:dynamic")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("hello.world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("hello..world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("hello...world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("D:hello.world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("hello.D:world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("D:hello.D:world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("D:hello,world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("one.D:two.three.D:four.D:five.six.D:seven,eight.nine")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("D:one,two,three")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("hello..world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("D:hello..world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("hello..D:world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("D:hello..D:world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("D:hello.D:.D:world")); - EXPECT_EQ(0, dynamicEncodeDecodeTest("aV.D:,b")); - - // TODO(#10008): these tests fail because fake/real symbol tables - // deal with empty components differently. - // EXPECT_EQ(0, dynamicEncodeDecodeTest("D:hello,,world")); - // EXPECT_EQ(0, dynamicEncodeDecodeTest("D:hello,,,world")); -} - class StatMergerThreadLocalTest : public testing::Test { protected: - SymbolTablePtr symbol_table_{SymbolTableCreator::makeSymbolTable()}; - AllocatorImpl alloc_{*symbol_table_}; + SymbolTableImpl symbol_table_; + AllocatorImpl alloc_{symbol_table_}; ThreadLocalStoreImpl store_{alloc_}; }; diff --git a/test/common/stats/stat_test_utility.h b/test/common/stats/stat_test_utility.h index 6b46a0f05aea..1d0eaa3c66cf 100644 --- a/test/common/stats/stat_test_utility.h +++ b/test/common/stats/stat_test_utility.h @@ -5,7 +5,6 @@ #include "common/common/logger.h" #include "common/memory/stats.h" #include "common/stats/isolated_store_impl.h" -#include "common/stats/symbol_table_creator.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" @@ -164,18 +163,6 @@ class TestStore : public IsolatedStoreImpl { } \ } while (false) -class SymbolTableCreatorTestPeer { -public: - ~SymbolTableCreatorTestPeer() { SymbolTableCreator::setUseFakeSymbolTables(save_use_fakes_); } - - void setUseFakeSymbolTables(bool use_fakes) { - SymbolTableCreator::setUseFakeSymbolTables(use_fakes); - } - -private: - const bool save_use_fakes_{SymbolTableCreator::useFakeSymbolTables()}; -}; - // Serializes a number into a uint8_t array, and check that it de-serializes to // the same number. The serialized number is also returned, which can be // checked in unit tests, but ignored in fuzz tests. diff --git a/test/common/stats/stat_test_utility_test.cc b/test/common/stats/stat_test_utility_test.cc index a395adff8847..7a526d4d6f28 100644 --- a/test/common/stats/stat_test_utility_test.cc +++ b/test/common/stats/stat_test_utility_test.cc @@ -11,13 +11,9 @@ namespace { class StatTestUtilityTest : public testing::Test { protected: StatTestUtilityTest() - : symbol_table_(SymbolTableCreator::makeSymbolTable()), test_store_(*symbol_table_), - dynamic_(*symbol_table_), symbolic_(*symbol_table_) { - symbol_table_creator_test_peer_.setUseFakeSymbolTables(false); - } + : test_store_(symbol_table_), dynamic_(symbol_table_), symbolic_(symbol_table_) {} - TestUtil::SymbolTableCreatorTestPeer symbol_table_creator_test_peer_; - SymbolTablePtr symbol_table_; + SymbolTableImpl symbol_table_; TestUtil::TestStore test_store_; StatNameDynamicPool dynamic_; StatNamePool symbolic_; diff --git a/test/common/stats/symbol_table_fuzz_test.cc b/test/common/stats/symbol_table_fuzz_test.cc index 4284f05c7359..3d5ac4df99bc 100644 --- a/test/common/stats/symbol_table_fuzz_test.cc +++ b/test/common/stats/symbol_table_fuzz_test.cc @@ -15,19 +15,14 @@ namespace Fuzz { // Fuzzer for symbol tables. DEFINE_FUZZER(const uint8_t* buf, size_t len) { FuzzedDataProvider provider(buf, len); - FakeSymbolTableImpl fake_symbol_table; SymbolTableImpl symbol_table; StatNamePool pool(symbol_table); - StatNamePool fake_pool(fake_symbol_table); StatNameDynamicPool dynamic_pool(symbol_table); - StatNameDynamicPool fake_dynamic_pool(fake_symbol_table); while (provider.remaining_bytes() != 0) { std::string next_data = provider.ConsumeRandomLengthString(provider.remaining_bytes()); StatName stat_name = pool.add(next_data); - StatName fake_stat_name = fake_pool.add(next_data); StatName dynamic_stat_name = dynamic_pool.add(next_data); - StatName fake_dynamic_stat_name = fake_dynamic_pool.add(next_data); // Encode the string directly first. TestUtil::serializeDeserializeString(next_data); @@ -49,9 +44,7 @@ DEFINE_FUZZER(const uint8_t* buf, size_t len) { // string before comparing. absl::string_view trimmed_fuzz_data = StringUtil::removeTrailingCharacters(next_data, '.'); FUZZ_ASSERT(trimmed_fuzz_data == symbol_table.toString(stat_name)); - FUZZ_ASSERT(trimmed_fuzz_data == fake_symbol_table.toString(fake_stat_name)); FUZZ_ASSERT(trimmed_fuzz_data == symbol_table.toString(dynamic_stat_name)); - FUZZ_ASSERT(trimmed_fuzz_data == fake_symbol_table.toString(fake_dynamic_stat_name)); // The 'join' tests only work if the trimmed fuzz data is not empty. if (trimmed_fuzz_data.empty()) { @@ -84,10 +77,6 @@ DEFINE_FUZZER(const uint8_t* buf, size_t len) { FUZZ_ASSERT(join(symbol_table, stat_name, dynamic_stat_name)); FUZZ_ASSERT(join(symbol_table, dynamic_stat_name, dynamic_stat_name)); FUZZ_ASSERT(join(symbol_table, dynamic_stat_name, stat_name)); - FUZZ_ASSERT(join(fake_symbol_table, fake_stat_name, fake_stat_name)); - FUZZ_ASSERT(join(fake_symbol_table, fake_stat_name, fake_dynamic_stat_name)); - FUZZ_ASSERT(join(fake_symbol_table, fake_dynamic_stat_name, fake_dynamic_stat_name)); - FUZZ_ASSERT(join(fake_symbol_table, fake_dynamic_stat_name, fake_stat_name)); } } diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 5913b47b4be6..d8d8db14d999 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -3,7 +3,6 @@ #include "common/common/macros.h" #include "common/common/mutex_tracer_impl.h" #include "common/memory/stats.h" -#include "common/stats/fake_symbol_table_impl.h" #include "common/stats/symbol_table_impl.h" #include "test/common/stats/stat_test_utility.h" @@ -18,69 +17,35 @@ namespace Envoy { namespace Stats { -// See comments in fake_symbol_table_impl.h: we need to test two implementations -// of SymbolTable, which we'll do with a test parameterized on this enum. -// -// Note that some of the tests cover behavior that is specific to the real -// SymbolTableImpl, and thus early-exit when the param is Fake. -// -// TODO(jmarantz): un-parameterize this test once SymbolTable is fully deployed -// and FakeSymbolTableImpl can be deleted. -enum class SymbolTableType { - Real, - Fake, -}; - -class StatNameTest : public testing::TestWithParam { +class StatNameTest : public testing::Test { protected: - StatNameTest() { - switch (GetParam()) { - case SymbolTableType::Real: { - auto table = std::make_unique(); - real_symbol_table_ = table.get(); - table_ = std::move(table); - break; - } - case SymbolTableType::Fake: - auto table = std::make_unique(); - fake_symbol_table_ = table.get(); - table_ = std::move(table); - break; - } - pool_ = std::make_unique(*table_); - } - + StatNameTest() : pool_(table_) {} ~StatNameTest() override { clearStorage(); } void clearStorage() { - pool_->clear(); - EXPECT_EQ(0, table_->numSymbols()); + pool_.clear(); + EXPECT_EQ(0, table_.numSymbols()); } SymbolVec getSymbols(StatName stat_name) { return SymbolTableImpl::Encoding::decodeSymbols(stat_name.data(), stat_name.dataSize()); } - Symbol monotonicCounter() { return real_symbol_table_->monotonicCounter(); } + Symbol monotonicCounter() { return table_.monotonicCounter(); } std::string encodeDecode(absl::string_view stat_name) { - return table_->toString(makeStat(stat_name)); + return table_.toString(makeStat(stat_name)); } - StatName makeStat(absl::string_view name) { return pool_->add(name); } + StatName makeStat(absl::string_view name) { return pool_.add(name); } std::vector serializeDeserialize(uint64_t number) { return TestUtil::serializeDeserializeNumber(number); } - FakeSymbolTableImpl* fake_symbol_table_{nullptr}; - SymbolTableImpl* real_symbol_table_{nullptr}; - std::unique_ptr table_; - std::unique_ptr pool_; + SymbolTableImpl table_; + StatNamePool pool_; }; -INSTANTIATE_TEST_SUITE_P(StatNameTest, StatNameTest, - testing::ValuesIn({SymbolTableType::Real, SymbolTableType::Fake})); - -TEST_P(StatNameTest, SerializeBytes) { +TEST_F(StatNameTest, SerializeBytes) { EXPECT_EQ(std::vector{1}, serializeDeserialize(1)); EXPECT_EQ(std::vector{127}, serializeDeserialize(127)); EXPECT_EQ((std::vector{128, 1}), serializeDeserialize(128)); @@ -114,7 +79,7 @@ TEST_P(StatNameTest, SerializeBytes) { } } -TEST_P(StatNameTest, SerializeStrings) { +TEST_F(StatNameTest, SerializeStrings) { TestUtil::serializeDeserializeString(""); TestUtil::serializeDeserializeString("Hello, world!"); TestUtil::serializeDeserializeString("embedded\0\nul"); @@ -126,22 +91,22 @@ TEST_P(StatNameTest, SerializeStrings) { TestUtil::serializeDeserializeString(std::string(20000000, 'a')); } -TEST_P(StatNameTest, AllocFree) { encodeDecode("hello.world"); } +TEST_F(StatNameTest, AllocFree) { encodeDecode("hello.world"); } -TEST_P(StatNameTest, TestArbitrarySymbolRoundtrip) { +TEST_F(StatNameTest, TestArbitrarySymbolRoundtrip) { const std::vector stat_names = {"", " ", " ", ",", "\t", "$", "%", "`", ".x"}; for (auto& stat_name : stat_names) { EXPECT_EQ(stat_name, encodeDecode(stat_name)); } } -TEST_P(StatNameTest, TestEmpty) { +TEST_F(StatNameTest, TestEmpty) { EXPECT_TRUE(makeStat("").empty()); EXPECT_FALSE(makeStat("x").empty()); EXPECT_TRUE(StatName().empty()); } -TEST_P(StatNameTest, TestDynamic100k) { +TEST_F(StatNameTest, TestDynamic100k) { // Tests a variety different sizes of dynamic stat ranging to 500k, covering // potential corner cases of spilling over into multi-byte lengths. std::string stat_str("dyn.x"); @@ -154,11 +119,11 @@ TEST_P(StatNameTest, TestDynamic100k) { for (uint32_t i = stat_str.size(); i < size; ++i, ++ch) { stat_str += (ch == '.') ? 'x' : ch; } - StatNameDynamicStorage storage(stat_str, *table_); + StatNameDynamicStorage storage(stat_str, table_); StatName dynamic = storage.statName(); - EXPECT_EQ(stat_str, table_->toString(dynamic)); - SymbolTable::StoragePtr joined = table_->join({ab, dynamic, cd}); - EXPECT_EQ(absl::StrCat("a.b.", stat_str, ".c.d"), table_->toString(StatName(joined.get()))); + EXPECT_EQ(stat_str, table_.toString(dynamic)); + SymbolTable::StoragePtr joined = table_.join({ab, dynamic, cd}); + EXPECT_EQ(absl::StrCat("a.b.", stat_str, ".c.d"), table_.toString(StatName(joined.get()))); } }; @@ -174,21 +139,21 @@ TEST_P(StatNameTest, TestDynamic100k) { } } -TEST_P(StatNameTest, TestDynamicPools) { +TEST_F(StatNameTest, TestDynamicPools) { // Same test for a dynamically allocated name. The only difference between // the behavior with a remembered vs dynamic name is that when looking // up a remembered name, a mutex is not taken. But we have no easy way // to test for that. So we'll at least cover the code. - StatNameDynamicPool d1(*table_); + StatNameDynamicPool d1(table_); const StatName dynamic = d1.add("dynamic"); - EXPECT_EQ("dynamic", table_->toString(dynamic)); + EXPECT_EQ("dynamic", table_.toString(dynamic)); // The nature of the StatNameDynamicPool is that there is no sharing (and also no locks). EXPECT_NE(dynamic.data(), d1.add("dynamic").data()); // Make sure blanks are always the same. const StatName blank = d1.add(""); - EXPECT_EQ("", table_->toString(blank)); + EXPECT_EQ("", table_.toString(blank)); EXPECT_NE(blank.data(), d1.add("").data()); EXPECT_NE(blank.data(), d1.add("").data()); EXPECT_NE(blank.data(), d1.add(absl::string_view()).data()); @@ -197,29 +162,29 @@ TEST_P(StatNameTest, TestDynamicPools) { // different set. Here we will get a different StatName object // out of the second set, though it will share the same underlying // symbol-table symbol. - StatNameDynamicPool d2(*table_); + StatNameDynamicPool d2(table_); const StatName dynamic2 = d2.add("dynamic"); - EXPECT_EQ("dynamic", table_->toString(dynamic2)); + EXPECT_EQ("dynamic", table_.toString(dynamic2)); EXPECT_NE(dynamic2.data(), d2.add("dynamic").data()); // No storage sharing. EXPECT_NE(dynamic2.data(), dynamic.data()); } -TEST_P(StatNameTest, TestDynamicHash) { - StatNameDynamicPool dynamic(*table_); +TEST_F(StatNameTest, TestDynamicHash) { + StatNameDynamicPool dynamic(table_); const StatName d1 = dynamic.add("dynamic"); const StatName d2 = dynamic.add("dynamic"); EXPECT_EQ(d1, d2); EXPECT_EQ(d1.hash(), d2.hash()); } -TEST_P(StatNameTest, Test100KSymbolsRoundtrip) { +TEST_F(StatNameTest, Test100KSymbolsRoundtrip) { for (int i = 0; i < 100 * 1000; ++i) { const std::string stat_name = absl::StrCat("symbol_", i); EXPECT_EQ(stat_name, encodeDecode(stat_name)); } } -TEST_P(StatNameTest, TwoHundredTwoLevel) { +TEST_F(StatNameTest, TwoHundredTwoLevel) { for (int i = 0; i < 200; ++i) { const std::string stat_name = absl::StrCat("symbol_", i); EXPECT_EQ(stat_name, encodeDecode(stat_name)); @@ -227,12 +192,12 @@ TEST_P(StatNameTest, TwoHundredTwoLevel) { EXPECT_EQ("http.foo", encodeDecode("http.foo")); } -TEST_P(StatNameTest, TestLongSymbolName) { +TEST_F(StatNameTest, TestLongSymbolName) { std::string long_name(100000, 'a'); EXPECT_EQ(long_name, encodeDecode(long_name)); } -TEST_P(StatNameTest, TestLongSequence) { +TEST_F(StatNameTest, TestLongSequence) { std::string long_name("a"); for (int i = 0; i < 100000; ++i) { absl::StrAppend(&long_name, ".a"); @@ -241,7 +206,7 @@ TEST_P(StatNameTest, TestLongSequence) { EXPECT_EQ(long_name, encodeDecode(long_name)); } -TEST_P(StatNameTest, TestUnusualDelimitersRoundtrip) { +TEST_F(StatNameTest, TestUnusualDelimitersRoundtrip) { const std::vector stat_names = {".x", "..x", "...x", "foo", "foo.x", ".foo", ".foo.x", ".foo..x", "..foo.x", "..foo..x"}; for (auto& stat_name : stat_names) { @@ -249,37 +214,31 @@ TEST_P(StatNameTest, TestUnusualDelimitersRoundtrip) { } } -TEST_P(StatNameTest, TestSuccessfulDoubleLookup) { +TEST_F(StatNameTest, TestSuccessfulDoubleLookup) { StatName stat_name_1(makeStat("foo.bar.baz")); StatName stat_name_2(makeStat("foo.bar.baz")); EXPECT_EQ(stat_name_1, stat_name_2); } -TEST_P(StatNameTest, TestSuccessfulDecode) { +TEST_F(StatNameTest, TestSuccessfulDecode) { std::string stat_name = "foo.bar.baz"; StatName stat_name_1(makeStat(stat_name)); StatName stat_name_2(makeStat(stat_name)); - EXPECT_EQ(table_->toString(stat_name_1), table_->toString(stat_name_2)); - EXPECT_EQ(table_->toString(stat_name_1), stat_name); + EXPECT_EQ(table_.toString(stat_name_1), table_.toString(stat_name_2)); + EXPECT_EQ(table_.toString(stat_name_1), stat_name); } class StatNameDeathTest : public StatNameTest { public: void decodeSymbolVec(const SymbolVec& symbol_vec) { - Thread::LockGuard lock(real_symbol_table_->lock_); + Thread::LockGuard lock(table_.lock_); for (Symbol symbol : symbol_vec) { - real_symbol_table_->fromSymbol(symbol); + table_.fromSymbol(symbol); } } }; -INSTANTIATE_TEST_SUITE_P(StatNameDeathTest, StatNameDeathTest, - testing::ValuesIn({SymbolTableType::Real})); - -TEST_P(StatNameDeathTest, TestBadDecodes) { - if (GetParam() == SymbolTableType::Fake) { - return; - } +TEST_F(StatNameDeathTest, TestBadDecodes) { { // If a symbol doesn't exist, decoding it should trigger an ASSERT() and crash. SymbolVec bad_symbol_vec = {1}; // symbol 0 is the empty symbol. @@ -297,17 +256,14 @@ TEST_P(StatNameDeathTest, TestBadDecodes) { } } -TEST_P(StatNameTest, TestDifferentStats) { +TEST_F(StatNameTest, TestDifferentStats) { StatName stat_name_1(makeStat("foo.bar")); StatName stat_name_2(makeStat("bar.foo")); - EXPECT_NE(table_->toString(stat_name_1), table_->toString(stat_name_2)); + EXPECT_NE(table_.toString(stat_name_1), table_.toString(stat_name_2)); EXPECT_NE(stat_name_1, stat_name_2); } -TEST_P(StatNameTest, TestSymbolConsistency) { - if (GetParam() == SymbolTableType::Fake) { - return; - } +TEST_F(StatNameTest, TestSymbolConsistency) { StatName stat_name_1(makeStat("foo.bar")); StatName stat_name_2(makeStat("bar.foo")); // We expect the encoding of "foo" in one context to be the same as another. @@ -317,20 +273,20 @@ TEST_P(StatNameTest, TestSymbolConsistency) { EXPECT_EQ(vec_2[0], vec_1[1]); } -TEST_P(StatNameTest, TestIgnoreTrailingDots) { +TEST_F(StatNameTest, TestIgnoreTrailingDots) { EXPECT_EQ("foo.bar", encodeDecode("foo.bar.")); EXPECT_EQ("foo.bar", encodeDecode("foo.bar...")); EXPECT_EQ("", encodeDecode(".")); EXPECT_EQ("", encodeDecode("..")); } -TEST_P(StatNameTest, TestSameValueOnPartialFree) { +TEST_F(StatNameTest, TestSameValueOnPartialFree) { // This should hold true for components as well. Since "foo" persists even when "foo.bar" is // freed, we expect both instances of "foo" to have the same symbol. makeStat("foo"); - StatNameStorage stat_foobar_1("foo.bar", *table_); + StatNameStorage stat_foobar_1("foo.bar", table_); SymbolVec stat_foobar_1_symbols = getSymbols(stat_foobar_1.statName()); - stat_foobar_1.free(*table_); + stat_foobar_1.free(table_); StatName stat_foobar_2(makeStat("foo.bar")); SymbolVec stat_foobar_2_symbols = getSymbols(stat_foobar_2); @@ -339,11 +295,7 @@ TEST_P(StatNameTest, TestSameValueOnPartialFree) { // And we have no expectation for the "bar" components, because of the free pool. } -TEST_P(StatNameTest, FreePoolTest) { - if (GetParam() == SymbolTableType::Fake) { - return; - } - +TEST_F(StatNameTest, FreePoolTest) { // To ensure that the free pool is being used, we should be able to cycle through a large number // of stats while validating that: // a) the size of the table has not increased, and @@ -357,11 +309,11 @@ TEST_P(StatNameTest, FreePoolTest) { makeStat("4a"); makeStat("5a"); EXPECT_EQ(monotonicCounter(), 6); - EXPECT_EQ(table_->numSymbols(), 5); + EXPECT_EQ(table_.numSymbols(), 5); clearStorage(); } EXPECT_EQ(monotonicCounter(), 6); - EXPECT_EQ(table_->numSymbols(), 0); + EXPECT_EQ(table_.numSymbols(), 0); // These are different strings being encoded, but they should recycle through the same symbols as // the stats above. @@ -371,59 +323,59 @@ TEST_P(StatNameTest, FreePoolTest) { makeStat("4b"); makeStat("5b"); EXPECT_EQ(monotonicCounter(), 6); - EXPECT_EQ(table_->numSymbols(), 5); + EXPECT_EQ(table_.numSymbols(), 5); makeStat("6"); EXPECT_EQ(monotonicCounter(), 7); - EXPECT_EQ(table_->numSymbols(), 6); + EXPECT_EQ(table_.numSymbols(), 6); } -TEST_P(StatNameTest, TestShrinkingExpectation) { +TEST_F(StatNameTest, TestShrinkingExpectation) { // We expect that as we free stat names, the memory used to store those underlying symbols will // be freed. // ::size() is a public function, but should only be used for testing. - size_t table_size_0 = table_->numSymbols(); + size_t table_size_0 = table_.numSymbols(); auto make_stat_storage = [this](absl::string_view name) -> StatNameStorage { - return StatNameStorage(name, *table_); + return StatNameStorage(name, table_); }; StatNameStorage stat_a(make_stat_storage("a")); - size_t table_size_1 = table_->numSymbols(); + size_t table_size_1 = table_.numSymbols(); StatNameStorage stat_aa(make_stat_storage("a.a")); - EXPECT_EQ(table_size_1, table_->numSymbols()); + EXPECT_EQ(table_size_1, table_.numSymbols()); StatNameStorage stat_ab(make_stat_storage("a.b")); - size_t table_size_2 = table_->numSymbols(); + size_t table_size_2 = table_.numSymbols(); StatNameStorage stat_ac(make_stat_storage("a.c")); - size_t table_size_3 = table_->numSymbols(); + size_t table_size_3 = table_.numSymbols(); StatNameStorage stat_acd(make_stat_storage("a.c.d")); - size_t table_size_4 = table_->numSymbols(); + size_t table_size_4 = table_.numSymbols(); StatNameStorage stat_ace(make_stat_storage("a.c.e")); - size_t table_size_5 = table_->numSymbols(); + size_t table_size_5 = table_.numSymbols(); EXPECT_GE(table_size_5, table_size_4); - stat_ace.free(*table_); - EXPECT_EQ(table_size_4, table_->numSymbols()); + stat_ace.free(table_); + EXPECT_EQ(table_size_4, table_.numSymbols()); - stat_acd.free(*table_); - EXPECT_EQ(table_size_3, table_->numSymbols()); + stat_acd.free(table_); + EXPECT_EQ(table_size_3, table_.numSymbols()); - stat_ac.free(*table_); - EXPECT_EQ(table_size_2, table_->numSymbols()); + stat_ac.free(table_); + EXPECT_EQ(table_size_2, table_.numSymbols()); - stat_ab.free(*table_); - EXPECT_EQ(table_size_1, table_->numSymbols()); + stat_ab.free(table_); + EXPECT_EQ(table_size_1, table_.numSymbols()); - stat_aa.free(*table_); - EXPECT_EQ(table_size_1, table_->numSymbols()); + stat_aa.free(table_); + EXPECT_EQ(table_size_1, table_.numSymbols()); - stat_a.free(*table_); - EXPECT_EQ(table_size_0, table_->numSymbols()); + stat_a.free(table_); + EXPECT_EQ(table_size_0, table_.numSymbols()); } // In the tests above we use the StatNameStorage abstraction which is not the @@ -433,33 +385,33 @@ TEST_P(StatNameTest, TestShrinkingExpectation) { // safety-net here in terms of leaks is that SymbolTable will assert-fail if // you don't free all the StatNames you've allocated bytes for. StatNameList // provides this capability. -TEST_P(StatNameTest, List) { +TEST_F(StatNameTest, List) { StatName names[] = {makeStat("hello.world"), makeStat("goodbye.world")}; StatNameList name_list; EXPECT_FALSE(name_list.populated()); - table_->populateList(names, ARRAY_SIZE(names), name_list); + table_.populateList(names, ARRAY_SIZE(names), name_list); EXPECT_TRUE(name_list.populated()); // First, decode only the first name. name_list.iterate([this](StatName stat_name) -> bool { - EXPECT_EQ("hello.world", table_->toString(stat_name)); + EXPECT_EQ("hello.world", table_.toString(stat_name)); return false; }); // Decode all the names. std::vector decoded_strings; name_list.iterate([this, &decoded_strings](StatName stat_name) -> bool { - decoded_strings.push_back(table_->toString(stat_name)); + decoded_strings.push_back(table_.toString(stat_name)); return true; }); ASSERT_EQ(2, decoded_strings.size()); EXPECT_EQ("hello.world", decoded_strings[0]); EXPECT_EQ("goodbye.world", decoded_strings[1]); - name_list.clear(*table_); + name_list.clear(table_); EXPECT_FALSE(name_list.populated()); } -TEST_P(StatNameTest, HashTable) { +TEST_F(StatNameTest, HashTable) { StatName ac = makeStat("a.c"); StatName ab = makeStat("a.b"); StatName de = makeStat("d.e"); @@ -477,65 +429,64 @@ TEST_P(StatNameTest, HashTable) { EXPECT_EQ(3, name_int_map[de]); } -TEST_P(StatNameTest, Sort) { +TEST_F(StatNameTest, Sort) { StatNameVec names{makeStat("a.c"), makeStat("a.b"), makeStat("d.e"), makeStat("d.a.a"), makeStat("d.a"), makeStat("a.c")}; const StatNameVec sorted_names{makeStat("a.b"), makeStat("a.c"), makeStat("a.c"), makeStat("d.a"), makeStat("d.a.a"), makeStat("d.e")}; EXPECT_NE(names, sorted_names); - std::sort(names.begin(), names.end(), StatNameLessThan(*table_)); + std::sort(names.begin(), names.end(), StatNameLessThan(table_)); EXPECT_EQ(names, sorted_names); } -TEST_P(StatNameTest, Concat2) { - SymbolTable::StoragePtr joined = table_->join({makeStat("a.b"), makeStat("c.d")}); - EXPECT_EQ("a.b.c.d", table_->toString(StatName(joined.get()))); +TEST_F(StatNameTest, Concat2) { + SymbolTable::StoragePtr joined = table_.join({makeStat("a.b"), makeStat("c.d")}); + EXPECT_EQ("a.b.c.d", table_.toString(StatName(joined.get()))); } -TEST_P(StatNameTest, ConcatFirstEmpty) { - SymbolTable::StoragePtr joined = table_->join({makeStat(""), makeStat("c.d")}); - EXPECT_EQ("c.d", table_->toString(StatName(joined.get()))); +TEST_F(StatNameTest, ConcatFirstEmpty) { + SymbolTable::StoragePtr joined = table_.join({makeStat(""), makeStat("c.d")}); + EXPECT_EQ("c.d", table_.toString(StatName(joined.get()))); } -TEST_P(StatNameTest, ConcatSecondEmpty) { - SymbolTable::StoragePtr joined = table_->join({makeStat("a.b"), makeStat("")}); - EXPECT_EQ("a.b", table_->toString(StatName(joined.get()))); +TEST_F(StatNameTest, ConcatSecondEmpty) { + SymbolTable::StoragePtr joined = table_.join({makeStat("a.b"), makeStat("")}); + EXPECT_EQ("a.b", table_.toString(StatName(joined.get()))); } -TEST_P(StatNameTest, ConcatAllEmpty) { - SymbolTable::StoragePtr joined = table_->join({makeStat(""), makeStat("")}); - EXPECT_EQ("", table_->toString(StatName(joined.get()))); +TEST_F(StatNameTest, ConcatAllEmpty) { + SymbolTable::StoragePtr joined = table_.join({makeStat(""), makeStat("")}); + EXPECT_EQ("", table_.toString(StatName(joined.get()))); } -TEST_P(StatNameTest, Join3) { - SymbolTable::StoragePtr joined = - table_->join({makeStat("a.b"), makeStat("c.d"), makeStat("e.f")}); - EXPECT_EQ("a.b.c.d.e.f", table_->toString(StatName(joined.get()))); +TEST_F(StatNameTest, Join3) { + SymbolTable::StoragePtr joined = table_.join({makeStat("a.b"), makeStat("c.d"), makeStat("e.f")}); + EXPECT_EQ("a.b.c.d.e.f", table_.toString(StatName(joined.get()))); } -TEST_P(StatNameTest, Join3FirstEmpty) { - SymbolTable::StoragePtr joined = table_->join({makeStat(""), makeStat("c.d"), makeStat("e.f")}); - EXPECT_EQ("c.d.e.f", table_->toString(StatName(joined.get()))); +TEST_F(StatNameTest, Join3FirstEmpty) { + SymbolTable::StoragePtr joined = table_.join({makeStat(""), makeStat("c.d"), makeStat("e.f")}); + EXPECT_EQ("c.d.e.f", table_.toString(StatName(joined.get()))); } -TEST_P(StatNameTest, Join3SecondEmpty) { - SymbolTable::StoragePtr joined = table_->join({makeStat("a.b"), makeStat(""), makeStat("e.f")}); - EXPECT_EQ("a.b.e.f", table_->toString(StatName(joined.get()))); +TEST_F(StatNameTest, Join3SecondEmpty) { + SymbolTable::StoragePtr joined = table_.join({makeStat("a.b"), makeStat(""), makeStat("e.f")}); + EXPECT_EQ("a.b.e.f", table_.toString(StatName(joined.get()))); } -TEST_P(StatNameTest, Join3ThirdEmpty) { - SymbolTable::StoragePtr joined = table_->join({makeStat("a.b"), makeStat("c.d"), makeStat("")}); - EXPECT_EQ("a.b.c.d", table_->toString(StatName(joined.get()))); +TEST_F(StatNameTest, Join3ThirdEmpty) { + SymbolTable::StoragePtr joined = table_.join({makeStat("a.b"), makeStat("c.d"), makeStat("")}); + EXPECT_EQ("a.b.c.d", table_.toString(StatName(joined.get()))); } -TEST_P(StatNameTest, JoinAllEmpty) { - SymbolTable::StoragePtr joined = table_->join({makeStat(""), makeStat(""), makeStat("")}); - EXPECT_EQ("", table_->toString(StatName(joined.get()))); +TEST_F(StatNameTest, JoinAllEmpty) { + SymbolTable::StoragePtr joined = table_.join({makeStat(""), makeStat(""), makeStat("")}); + EXPECT_EQ("", table_.toString(StatName(joined.get()))); } // Validates that we don't get tsan or other errors when concurrently creating // a large number of stats. -TEST_P(StatNameTest, RacingSymbolCreation) { +TEST_F(StatNameTest, RacingSymbolCreation) { Thread::ThreadFactory& thread_factory = Thread::threadFactoryForTest(); MutexTracerImpl& mutex_tracer = MutexTracerImpl::getOrCreateTracer(); @@ -558,11 +509,11 @@ TEST_P(StatNameTest, RacingSymbolCreation) { // Block each thread on waking up a common condition variable, // so we make it likely to race on creation. creation.wait(); - StatNameManagedStorage initial(stat_name_string, *table_); + StatNameManagedStorage initial(stat_name_string, table_); creates.DecrementCount(); access.wait(); - StatNameManagedStorage second(stat_name_string, *table_); + StatNameManagedStorage second(stat_name_string, table_); accesses.DecrementCount(); wait.wait(); @@ -601,7 +552,7 @@ TEST_P(StatNameTest, RacingSymbolCreation) { } } -TEST_P(StatNameTest, MutexContentionOnExistingSymbols) { +TEST_F(StatNameTest, MutexContentionOnExistingSymbols) { Thread::ThreadFactory& thread_factory = Thread::threadFactoryForTest(); MutexTracerImpl& mutex_tracer = MutexTracerImpl::getOrCreateTracer(); @@ -624,11 +575,11 @@ TEST_P(StatNameTest, MutexContentionOnExistingSymbols) { // Block each thread on waking up a common condition variable, // so we make it likely to race on creation. creation.wait(); - StatNameManagedStorage initial(stat_name_string, *table_); + StatNameManagedStorage initial(stat_name_string, table_); creates.DecrementCount(); access.wait(); - StatNameManagedStorage second(stat_name_string, *table_); + StatNameManagedStorage second(stat_name_string, table_); accesses.DecrementCount(); wait.wait(); @@ -669,62 +620,55 @@ TEST_P(StatNameTest, MutexContentionOnExistingSymbols) { } } -TEST_P(StatNameTest, SharedStatNameStorageSetInsertAndFind) { +TEST_F(StatNameTest, SharedStatNameStorageSetInsertAndFind) { StatNameStorageSet set; const int iters = 10; for (int i = 0; i < iters; ++i) { std::string foo = absl::StrCat("foo", i); - auto insertion = set.insert(StatNameStorage(foo, *table_)); - StatNameManagedStorage temp_foo(foo, *table_); + auto insertion = set.insert(StatNameStorage(foo, table_)); + StatNameManagedStorage temp_foo(foo, table_); auto found = set.find(temp_foo.statName()); EXPECT_EQ(found->statName().data(), insertion.first->statName().data()); } - StatNameManagedStorage bar("bar", *table_); + StatNameManagedStorage bar("bar", table_); EXPECT_EQ(set.end(), set.find(bar.statName())); EXPECT_EQ(iters, set.size()); - set.free(*table_); + set.free(table_); } -TEST_P(StatNameTest, StatNameSet) { - StatNameSetPtr set(table_->makeSet("set")); +TEST_F(StatNameTest, StatNameSet) { + StatNameSetPtr set(table_.makeSet("set")); // Test that we get a consistent StatName object from a remembered name. set->rememberBuiltin("remembered"); const StatName fallback = set->add("fallback"); const Stats::StatName remembered = set->getBuiltin("remembered", fallback); - EXPECT_EQ("remembered", table_->toString(remembered)); + EXPECT_EQ("remembered", table_.toString(remembered)); EXPECT_EQ(remembered.data(), set->getBuiltin("remembered", fallback).data()); EXPECT_EQ(fallback.data(), set->getBuiltin("not_remembered", fallback).data()); } -TEST_P(StatNameTest, StorageCopy) { - StatName a = pool_->add("stat.name"); - StatNameStorage b_storage(a, *table_); +TEST_F(StatNameTest, StorageCopy) { + StatName a = pool_.add("stat.name"); + StatNameStorage b_storage(a, table_); StatName b = b_storage.statName(); EXPECT_EQ(a, b); EXPECT_NE(a.data(), b.data()); - b_storage.free(*table_); + b_storage.free(table_); } -TEST_P(StatNameTest, RecentLookups) { - if (GetParam() == SymbolTableType::Fake) { - // Touch these for coverage of fake symbol tables, but they'll have no effect. - table_->clearRecentLookups(); - table_->setRecentLookupCapacity(0); - return; - } - - StatNameSetPtr set1(table_->makeSet("set1")); - table_->setRecentLookupCapacity(10); - StatNameSetPtr set2(table_->makeSet("set2")); - StatNameDynamicPool d1(*table_); +TEST_F(StatNameTest, RecentLookups) { + StatNameSetPtr set1(table_.makeSet("set1")); + table_.setRecentLookupCapacity(10); + StatNameSetPtr set2(table_.makeSet("set2")); + StatNameDynamicPool d1(table_); d1.add("dynamic.stat1"); - StatNameDynamicPool d2(*table_); + StatNameDynamicPool d2(table_); d2.add("dynamic.stat2"); encodeDecode("direct.stat"); std::vector accum; - uint64_t total = table_->getRecentLookups([&accum](absl::string_view name, uint64_t count) { + uint64_t total = table_.getRecentLookups([&accum](absl::string_view name, uint64_t count) { accum.emplace_back(absl::StrCat(count, ": ", name)); }); EXPECT_EQ(1, total); // Dynamic pool adds don't count as recent lookups. @@ -732,14 +676,13 @@ TEST_P(StatNameTest, RecentLookups) { EXPECT_EQ("1: direct.stat", recent_lookups_str); // No dynamic-pool lookups take locks. - table_->clearRecentLookups(); + table_.clearRecentLookups(); uint32_t num_calls = 0; - EXPECT_EQ(0, - table_->getRecentLookups([&num_calls](absl::string_view, uint64_t) { ++num_calls; })); + EXPECT_EQ(0, table_.getRecentLookups([&num_calls](absl::string_view, uint64_t) { ++num_calls; })); EXPECT_EQ(0, num_calls); } -TEST_P(StatNameTest, StatNameEmptyEquivalent) { +TEST_F(StatNameTest, StatNameEmptyEquivalent) { StatName empty1; StatName empty2 = makeStat(""); StatName non_empty = makeStat("a"); @@ -751,7 +694,7 @@ TEST_P(StatNameTest, StatNameEmptyEquivalent) { EXPECT_NE(empty2.hash(), non_empty.hash()); } -TEST_P(StatNameTest, SupportsAbslHash) { +TEST_F(StatNameTest, SupportsAbslHash) { EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ StatName(), makeStat(""), diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 6e2c62ace9ef..3eff52a78d97 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -7,7 +7,7 @@ #include "common/common/thread.h" #include "common/event/dispatcher_impl.h" #include "common/stats/allocator_impl.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "common/stats/tag_producer_impl.h" #include "common/stats/thread_local_store.h" #include "common/thread_local/thread_local_impl.h" @@ -24,18 +24,18 @@ namespace Envoy { class ThreadLocalStorePerf { public: ThreadLocalStorePerf() - : symbol_table_(Stats::SymbolTableCreator::makeSymbolTable()), heap_alloc_(*symbol_table_), - store_(heap_alloc_), api_(Api::createApiForTest(store_, time_system_)) { + : heap_alloc_(symbol_table_), store_(heap_alloc_), + api_(Api::createApiForTest(store_, time_system_)) { store_.setTagProducer(std::make_unique(stats_config_)); Stats::TestUtil::forEachSampleStat(1000, [this](absl::string_view name) { - stat_names_.push_back(std::make_unique(name, *symbol_table_)); + stat_names_.push_back(std::make_unique(name, symbol_table_)); }); } ~ThreadLocalStorePerf() { for (auto& stat_name_storage : stat_names_) { - stat_name_storage->free(*symbol_table_); + stat_name_storage->free(symbol_table_); } store_.shutdownThreading(); if (tls_) { @@ -64,7 +64,7 @@ class ThreadLocalStorePerf { } private: - Stats::SymbolTablePtr symbol_table_; + Stats::SymbolTableImpl symbol_table_; Event::SimulatedTimeSystem time_system_; Stats::AllocatorImpl heap_alloc_; Event::DispatcherPtr dispatcher_; diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 5944f3bc5c5a..18004e4ce5ec 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -68,8 +68,7 @@ class ThreadLocalStoreTestingPeer { class StatsThreadLocalStoreTest : public testing::Test { public: StatsThreadLocalStoreTest() - : symbol_table_(SymbolTableCreator::makeSymbolTable()), alloc_(*symbol_table_), - store_(std::make_unique(alloc_)) { + : alloc_(symbol_table_), store_(std::make_unique(alloc_)) { store_->addSink(sink_); } @@ -93,7 +92,7 @@ class StatsThreadLocalStoreTest : public testing::Test { return num_tls_histograms; } - SymbolTablePtr symbol_table_; + SymbolTableImpl symbol_table_; NiceMock main_thread_dispatcher_; NiceMock tls_; AllocatorImpl alloc_; @@ -123,7 +122,7 @@ class HistogramTest : public testing::Test { public: using NameHistogramMap = std::map; - HistogramTest() : symbol_table_(SymbolTableCreator::makeSymbolTable()), alloc_(*symbol_table_) {} + HistogramTest() : alloc_(symbol_table_) {} void SetUp() override { store_ = std::make_unique(alloc_); @@ -211,7 +210,7 @@ class HistogramTest : public testing::Test { } } - SymbolTablePtr symbol_table_; + SymbolTableImpl symbol_table_; NiceMock main_thread_dispatcher_; NiceMock tls_; AllocatorImpl alloc_; @@ -227,7 +226,7 @@ TEST_F(StatsThreadLocalStoreTest, NoTls) { Counter& c1 = store_->counterFromString("c1"); EXPECT_EQ(&c1, &store_->counterFromString("c1")); - StatNameManagedStorage c1_name("c1", *symbol_table_); + StatNameManagedStorage c1_name("c1", symbol_table_); c1.add(100); auto found_counter = store_->findCounter(c1_name.statName()); ASSERT_TRUE(found_counter.has_value()); @@ -238,7 +237,7 @@ TEST_F(StatsThreadLocalStoreTest, NoTls) { Gauge& g1 = store_->gaugeFromString("g1", Gauge::ImportMode::Accumulate); EXPECT_EQ(&g1, &store_->gaugeFromString("g1", Gauge::ImportMode::Accumulate)); - StatNameManagedStorage g1_name("g1", *symbol_table_); + StatNameManagedStorage g1_name("g1", symbol_table_); g1.set(100); auto found_gauge = store_->findGauge(g1_name.statName()); ASSERT_TRUE(found_gauge.has_value()); @@ -249,7 +248,7 @@ TEST_F(StatsThreadLocalStoreTest, NoTls) { Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); EXPECT_EQ(&h1, &store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified)); - StatNameManagedStorage h1_name("h1", *symbol_table_); + StatNameManagedStorage h1_name("h1", symbol_table_); auto found_histogram = store_->findHistogram(h1_name.statName()); ASSERT_TRUE(found_histogram.has_value()); EXPECT_EQ(&h1, &found_histogram->get()); @@ -281,7 +280,7 @@ TEST_F(StatsThreadLocalStoreTest, Tls) { Counter& c1 = store_->counterFromString("c1"); EXPECT_EQ(&c1, &store_->counterFromString("c1")); - StatNameManagedStorage c1_name("c1", *symbol_table_); + StatNameManagedStorage c1_name("c1", symbol_table_); c1.add(100); auto found_counter = store_->findCounter(c1_name.statName()); ASSERT_TRUE(found_counter.has_value()); @@ -292,7 +291,7 @@ TEST_F(StatsThreadLocalStoreTest, Tls) { Gauge& g1 = store_->gaugeFromString("g1", Gauge::ImportMode::Accumulate); EXPECT_EQ(&g1, &store_->gaugeFromString("g1", Gauge::ImportMode::Accumulate)); - StatNameManagedStorage g1_name("g1", *symbol_table_); + StatNameManagedStorage g1_name("g1", symbol_table_); g1.set(100); auto found_gauge = store_->findGauge(g1_name.statName()); ASSERT_TRUE(found_gauge.has_value()); @@ -303,7 +302,7 @@ TEST_F(StatsThreadLocalStoreTest, Tls) { Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); EXPECT_EQ(&h1, &store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified)); - StatNameManagedStorage h1_name("h1", *symbol_table_); + StatNameManagedStorage h1_name("h1", symbol_table_); auto found_histogram = store_->findHistogram(h1_name.statName()); ASSERT_TRUE(found_histogram.has_value()); EXPECT_EQ(&h1, &found_histogram->get()); @@ -345,11 +344,11 @@ TEST_F(StatsThreadLocalStoreTest, BasicScope) { Counter& c2 = scope1->counterFromString("c2"); EXPECT_EQ("c1", c1.name()); EXPECT_EQ("scope1.c2", c2.name()); - StatNameManagedStorage c1_name("c1", *symbol_table_); + StatNameManagedStorage c1_name("c1", symbol_table_); auto found_counter = store_->findCounter(c1_name.statName()); ASSERT_TRUE(found_counter.has_value()); EXPECT_EQ(&c1, &found_counter->get()); - StatNameManagedStorage c2_name("scope1.c2", *symbol_table_); + StatNameManagedStorage c2_name("scope1.c2", symbol_table_); auto found_counter2 = store_->findCounter(c2_name.statName()); ASSERT_TRUE(found_counter2.has_value()); EXPECT_EQ(&c2, &found_counter2->get()); @@ -358,11 +357,11 @@ TEST_F(StatsThreadLocalStoreTest, BasicScope) { Gauge& g2 = scope1->gaugeFromString("g2", Gauge::ImportMode::Accumulate); EXPECT_EQ("g1", g1.name()); EXPECT_EQ("scope1.g2", g2.name()); - StatNameManagedStorage g1_name("g1", *symbol_table_); + StatNameManagedStorage g1_name("g1", symbol_table_); auto found_gauge = store_->findGauge(g1_name.statName()); ASSERT_TRUE(found_gauge.has_value()); EXPECT_EQ(&g1, &found_gauge->get()); - StatNameManagedStorage g2_name("scope1.g2", *symbol_table_); + StatNameManagedStorage g2_name("scope1.g2", symbol_table_); auto found_gauge2 = store_->findGauge(g2_name.statName()); ASSERT_TRUE(found_gauge2.has_value()); EXPECT_EQ(&g2, &found_gauge2->get()); @@ -375,11 +374,11 @@ TEST_F(StatsThreadLocalStoreTest, BasicScope) { h1.recordValue(100); EXPECT_CALL(sink_, onHistogramComplete(Ref(h2), 200)); h2.recordValue(200); - StatNameManagedStorage h1_name("h1", *symbol_table_); + StatNameManagedStorage h1_name("h1", symbol_table_); auto found_histogram = store_->findHistogram(h1_name.statName()); ASSERT_TRUE(found_histogram.has_value()); EXPECT_EQ(&h1, &found_histogram->get()); - StatNameManagedStorage h2_name("scope1.h2", *symbol_table_); + StatNameManagedStorage h2_name("scope1.h2", symbol_table_); auto found_histogram2 = store_->findHistogram(h2_name.statName()); ASSERT_TRUE(found_histogram2.has_value()); EXPECT_EQ(&h2, &found_histogram2->get()); @@ -389,20 +388,20 @@ TEST_F(StatsThreadLocalStoreTest, BasicScope) { EXPECT_EQ("t1", t1.name()); EXPECT_EQ("scope1.t2", t2.name()); - StatNameManagedStorage tag_key("a", *symbol_table_); - StatNameManagedStorage tag_value("b", *symbol_table_); + StatNameManagedStorage tag_key("a", symbol_table_); + StatNameManagedStorage tag_value("b", symbol_table_); StatNameTagVector tags{{StatName(tag_key.statName()), StatName(tag_value.statName())}}; const TagVector expectedTags = {Tag{"a", "b"}}; { - StatNameManagedStorage storage("c3", *symbol_table_); + StatNameManagedStorage storage("c3", symbol_table_); Counter& counter = scope1->counterFromStatNameWithTags(StatName(storage.statName()), tags); EXPECT_EQ(expectedTags, counter.tags()); EXPECT_EQ(&counter, &scope1->counterFromStatNameWithTags(StatName(storage.statName()), tags)); } { - StatNameManagedStorage storage("g3", *symbol_table_); + StatNameManagedStorage storage("g3", symbol_table_); Gauge& gauge = scope1->gaugeFromStatNameWithTags(StatName(storage.statName()), tags, Gauge::ImportMode::Accumulate); EXPECT_EQ(expectedTags, gauge.tags()); @@ -410,7 +409,7 @@ TEST_F(StatsThreadLocalStoreTest, BasicScope) { Gauge::ImportMode::Accumulate)); } { - StatNameManagedStorage storage("h3", *symbol_table_); + StatNameManagedStorage storage("h3", symbol_table_); Histogram& histogram = scope1->histogramFromStatNameWithTags( StatName(storage.statName()), tags, Stats::Histogram::Unit::Unspecified); EXPECT_EQ(expectedTags, histogram.tags()); @@ -521,7 +520,7 @@ TEST_F(StatsThreadLocalStoreTest, NestedScopes) { ScopePtr scope1 = store_->createScope("scope1."); Counter& c1 = scope1->counterFromString("foo.bar"); EXPECT_EQ("scope1.foo.bar", c1.name()); - StatNameManagedStorage c1_name("scope1.foo.bar", *symbol_table_); + StatNameManagedStorage c1_name("scope1.foo.bar", symbol_table_); auto found_counter = store_->findCounter(c1_name.statName()); ASSERT_TRUE(found_counter.has_value()); EXPECT_EQ(&c1, &found_counter->get()); @@ -530,7 +529,7 @@ TEST_F(StatsThreadLocalStoreTest, NestedScopes) { Counter& c2 = scope2->counterFromString("bar"); EXPECT_EQ(&c1, &c2); EXPECT_EQ("scope1.foo.bar", c2.name()); - StatNameManagedStorage c2_name("scope1.foo.bar", *symbol_table_); + StatNameManagedStorage c2_name("scope1.foo.bar", symbol_table_); auto found_counter2 = store_->findCounter(c2_name.statName()); ASSERT_TRUE(found_counter2.has_value()); @@ -661,8 +660,8 @@ TEST_F(StatsThreadLocalStoreTest, TextReadoutAllLengths) { class ThreadLocalStoreNoMocksTestBase : public testing::Test { public: ThreadLocalStoreNoMocksTestBase() - : symbol_table_(SymbolTableCreator::makeSymbolTable()), alloc_(*symbol_table_), - store_(std::make_unique(alloc_)), pool_(*symbol_table_) {} + : alloc_(symbol_table_), store_(std::make_unique(alloc_)), + pool_(symbol_table_) {} ~ThreadLocalStoreNoMocksTestBase() override { if (store_ != nullptr) { store_->shutdownThreading(); @@ -671,7 +670,7 @@ class ThreadLocalStoreNoMocksTestBase : public testing::Test { StatName makeStatName(absl::string_view name) { return pool_.add(name); } - SymbolTablePtr symbol_table_; + SymbolTableImpl symbol_table_; AllocatorImpl alloc_; ThreadLocalStoreImplPtr store_; StatNamePool pool_; @@ -934,8 +933,7 @@ TEST_F(StatsMatcherTLSTest, TestExclusionRegex) { class RememberStatsMatcherTest : public testing::TestWithParam { public: RememberStatsMatcherTest() - : symbol_table_(SymbolTableCreator::makeSymbolTable()), heap_alloc_(*symbol_table_), - store_(heap_alloc_), scope_(store_.createScope("scope.")) { + : heap_alloc_(symbol_table_), store_(heap_alloc_), scope_(store_.createScope("scope.")) { if (GetParam()) { store_.initializeThreading(main_thread_dispatcher_, tls_); } @@ -1032,7 +1030,7 @@ class RememberStatsMatcherTest : public testing::TestWithParam { }; } - Stats::SymbolTablePtr symbol_table_; + SymbolTableImpl symbol_table_; NiceMock main_thread_dispatcher_; NiceMock tls_; AllocatorImpl heap_alloc_; @@ -1137,78 +1135,51 @@ TEST_F(StatsThreadLocalStoreTest, NonHotRestartNoTruncation) { class StatsThreadLocalStoreTestNoFixture : public testing::Test { protected: + StatsThreadLocalStoreTestNoFixture() : alloc_(symbol_table_), store_(alloc_) { + store_.addSink(sink_); + + // Use a tag producer that will produce tags. + envoy::config::metrics::v3::StatsConfig stats_config; + store_.setTagProducer(std::make_unique(stats_config)); + } + ~StatsThreadLocalStoreTestNoFixture() override { if (threading_enabled_) { - store_->shutdownThreading(); + store_.shutdownThreading(); tls_.shutdownThread(); } } - void init(bool use_fakes) { - symbol_table_creator_test_peer_.setUseFakeSymbolTables(use_fakes); - symbol_table_ = SymbolTableCreator::makeSymbolTable(); - alloc_ = std::make_unique(*symbol_table_); - store_ = std::make_unique(*alloc_); - store_->addSink(sink_); - - // Use a tag producer that will produce tags. - envoy::config::metrics::v3::StatsConfig stats_config; - store_->setTagProducer(std::make_unique(stats_config)); - } - void initThreading() { threading_enabled_ = true; - store_->initializeThreading(main_thread_dispatcher_, tls_); + store_.initializeThreading(main_thread_dispatcher_, tls_); } static constexpr size_t million_ = 1000 * 1000; MockSink sink_; - SymbolTablePtr symbol_table_; - std::unique_ptr alloc_; - ThreadLocalStoreImplPtr store_; + SymbolTableImpl symbol_table_; + AllocatorImpl alloc_; + ThreadLocalStoreImpl store_; NiceMock main_thread_dispatcher_; NiceMock tls_; - TestUtil::SymbolTableCreatorTestPeer symbol_table_creator_test_peer_; bool threading_enabled_{false}; }; -// Tests how much memory is consumed allocating 100k stats. -TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTlsFakeSymbolTable) { - init(true); - TestUtil::MemoryTest memory_test; - TestUtil::forEachSampleStat( - 100, [this](absl::string_view name) { store_->counterFromString(std::string(name)); }); - EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 1358576); // Jan 23, 2020 - EXPECT_MEMORY_LE(memory_test.consumedBytes(), 1.4 * million_); -} - -TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithTlsFakeSymbolTable) { - init(true); - initThreading(); - TestUtil::MemoryTest memory_test; - TestUtil::forEachSampleStat( - 100, [this](absl::string_view name) { store_->counterFromString(std::string(name)); }); - EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 1498128); // July 30, 2020 - EXPECT_MEMORY_LE(memory_test.consumedBytes(), 1.6 * million_); -} - // Tests how much memory is consumed allocating 100k stats. TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTlsRealSymbolTable) { - init(false); TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( - 100, [this](absl::string_view name) { store_->counterFromString(std::string(name)); }); + 100, [this](absl::string_view name) { store_.counterFromString(std::string(name)); }); EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 688080); // July 2, 2020 EXPECT_MEMORY_LE(memory_test.consumedBytes(), 0.75 * million_); } TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithTlsRealSymbolTable) { - init(false); initThreading(); TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( - 100, [this](absl::string_view name) { store_->counterFromString(std::string(name)); }); + 100, [this](absl::string_view name) { store_.counterFromString(std::string(name)); }); EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 827632); // July 20, 2020 EXPECT_MEMORY_LE(memory_test.consumedBytes(), 0.9 * million_); } @@ -1265,11 +1236,11 @@ TEST_F(StatsThreadLocalStoreTest, MergeDuringShutDown) { } TEST(ThreadLocalStoreThreadTest, ConstructDestruct) { - SymbolTablePtr symbol_table(SymbolTableCreator::makeSymbolTable()); + SymbolTableImpl symbol_table; Api::ApiPtr api = Api::createApiForTest(); Event::DispatcherPtr dispatcher = api->allocateDispatcher("test_thread"); NiceMock tls; - AllocatorImpl alloc(*symbol_table); + AllocatorImpl alloc(symbol_table); ThreadLocalStoreImpl store(alloc); store.initializeThreading(*dispatcher, tls); diff --git a/test/common/stats/utility_fuzz_test.cc b/test/common/stats/utility_fuzz_test.cc index e61257e2d191..eebdceab396d 100644 --- a/test/common/stats/utility_fuzz_test.cc +++ b/test/common/stats/utility_fuzz_test.cc @@ -2,7 +2,6 @@ #include #include "common/stats/isolated_store_impl.h" -#include "common/stats/symbol_table_creator.h" #include "common/stats/utility.h" #include "test/fuzz/fuzz_runner.h" @@ -41,15 +40,10 @@ DEFINE_FUZZER(const uint8_t* buf, size_t len) { // model common/stats/utility_test.cc, initialize those objects to create random elements as // input - Stats::SymbolTablePtr symbol_table; - if (provider.ConsumeBool()) { - symbol_table = std::make_unique(); - } else { - symbol_table = std::make_unique(); - } + Stats::SymbolTableImpl symbol_table; std::unique_ptr store = - std::make_unique(*symbol_table); - Stats::StatNamePool pool(*symbol_table); + std::make_unique(symbol_table); + Stats::StatNamePool pool(symbol_table); Stats::ScopePtr scope = store->createScope(provider.ConsumeRandomLengthString(max_len)); Stats::ElementVec ele_vec; Stats::StatNameVec sn_vec; diff --git a/test/common/stats/utility_test.cc b/test/common/stats/utility_test.cc index bf1643ff4ed5..3c4bda7d122d 100644 --- a/test/common/stats/utility_test.cc +++ b/test/common/stats/utility_test.cc @@ -5,7 +5,6 @@ #include "common/stats/isolated_store_impl.h" #include "common/stats/null_counter.h" #include "common/stats/null_gauge.h" -#include "common/stats/symbol_table_creator.h" #include "common/stats/thread_local_store.h" #include "absl/strings/str_cat.h" @@ -32,7 +31,7 @@ class StatsUtilityTest : public testing::TestWithParam { using MakeStatFn = std::function; StatsUtilityTest() - : symbol_table_(SymbolTableCreator::makeSymbolTable()), pool_(*symbol_table_), + : symbol_table_(std::make_unique()), pool_(*symbol_table_), tags_( {{pool_.add("tag1"), pool_.add("value1")}, {pool_.add("tag2"), pool_.add("value2")}}) { switch (GetParam()) { diff --git a/test/extensions/filters/http/grpc_http1_bridge/BUILD b/test/extensions/filters/http/grpc_http1_bridge/BUILD index fbf5798cf07c..a68ebdec1e39 100644 --- a/test/extensions/filters/http/grpc_http1_bridge/BUILD +++ b/test/extensions/filters/http/grpc_http1_bridge/BUILD @@ -18,7 +18,6 @@ envoy_extension_cc_test( deps = [ "//source/common/buffer:buffer_lib", "//source/common/http:header_map_lib", - "//source/common/stats:fake_symbol_table_lib", "//source/extensions/filters/http/grpc_http1_bridge:http1_bridge_filter_lib", "//test/mocks/http:http_mocks", "//test/test_common:global_lib", diff --git a/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc b/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc index b3d0e7898bc2..d37bd313b090 100644 --- a/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc +++ b/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc @@ -1,7 +1,7 @@ #include "common/buffer/buffer_impl.h" #include "common/grpc/common.h" #include "common/http/header_map_impl.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "extensions/filters/http/grpc_http1_bridge/http1_bridge_filter.h" diff --git a/test/extensions/filters/http/grpc_web/BUILD b/test/extensions/filters/http/grpc_web/BUILD index bb92594cd687..831e596ffb14 100644 --- a/test/extensions/filters/http/grpc_web/BUILD +++ b/test/extensions/filters/http/grpc_web/BUILD @@ -16,7 +16,6 @@ envoy_extension_cc_test( srcs = ["grpc_web_filter_test.cc"], extension_name = "envoy.filters.http.grpc_web", deps = [ - "//source/common/stats:fake_symbol_table_lib", "//source/extensions/filters/http/grpc_web:grpc_web_filter_lib", "//test/mocks/http:http_mocks", "//test/test_common:global_lib", diff --git a/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc b/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc index 42f75c32be19..2a35daa2d94a 100644 --- a/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc +++ b/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc @@ -7,7 +7,7 @@ #include "common/http/codes.h" #include "common/http/header_map_impl.h" #include "common/http/headers.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "extensions/filters/http/grpc_web/grpc_web_filter.h" diff --git a/test/extensions/tracers/lightstep/BUILD b/test/extensions/tracers/lightstep/BUILD index 078d5d43825a..d0d866e61c4f 100644 --- a/test/extensions/tracers/lightstep/BUILD +++ b/test/extensions/tracers/lightstep/BUILD @@ -25,7 +25,6 @@ envoy_extension_cc_test( "//source/common/http:headers_lib", "//source/common/http:message_lib", "//source/common/runtime:runtime_lib", - "//source/common/stats:fake_symbol_table_lib", "//source/extensions/tracers/lightstep:lightstep_tracer_lib", "//test/mocks/http:http_mocks", "//test/mocks/local_info:local_info_mocks", diff --git a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc index 8e13014efcb2..84802031580b 100644 --- a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc +++ b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc @@ -11,7 +11,7 @@ #include "common/http/headers.h" #include "common/http/message_impl.h" #include "common/runtime/runtime_impl.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "common/tracing/http_tracer_impl.h" #include "extensions/tracers/lightstep/lightstep_tracer_impl.h" diff --git a/test/integration/BUILD b/test/integration/BUILD index 2829ce5682fe..e4715ab94b30 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -807,7 +807,6 @@ envoy_cc_test_library( "//source/common/network:utility_lib", "//source/common/runtime:runtime_lib", "//source/common/stats:isolated_store_lib", - "//source/common/stats:symbol_table_creator_lib", "//source/common/stats:thread_local_store_lib", "//source/common/thread_local:thread_local_lib", "//source/common/upstream:upstream_includes", @@ -946,7 +945,6 @@ envoy_cc_test( deps = [ ":integration_lib", "//source/common/memory:stats_lib", - "//source/common/stats:symbol_table_creator_lib", "//source/extensions/filters/http/router:config", "//source/extensions/filters/network/http_connection_manager:config", "//test/common/stats:stat_test_utility_lib", diff --git a/test/integration/hotrestart_test.sh b/test/integration/hotrestart_test.sh index 69d7ccac336e..d16a5a0bc59e 100755 --- a/test/integration/hotrestart_test.sh +++ b/test/integration/hotrestart_test.sh @@ -144,6 +144,7 @@ function run_testsuite() { start_test "Checking for consistency of /hot_restart_version with --use-fake-symbol-table ${FAKE_SYMBOL_TABLE}" CLI_HOT_RESTART_VERSION=$("${ENVOY_BIN}" --hot-restart-version --base-id "${BASE_ID}" \ --use-fake-symbol-table "$FAKE_SYMBOL_TABLE" 2>&1) + CLI_HOT_RESTART_VERSION=$(strip_fake_symbol_table_warning "$CLI_HOT_RESTART_VERSION" "$FAKE_SYMBOL_TABLE") EXPECTED_CLI_HOT_RESTART_VERSION="11.${SHARED_MEMORY_SIZE}" check [ "${CLI_HOT_RESTART_VERSION}" = "${EXPECTED_CLI_HOT_RESTART_VERSION}" ] @@ -154,6 +155,7 @@ function run_testsuite() { echo "Fetched ADMIN_HOT_RESTART_VERSION is ${ADMIN_HOT_RESTART_VERSION}" CLI_HOT_RESTART_VERSION=$("${ENVOY_BIN}" --hot-restart-version --base-id "${BASE_ID}" \ --use-fake-symbol-table "$FAKE_SYMBOL_TABLE" 2>&1) + CLI_HOT_RESTART_VERSION=$(strip_fake_symbol_table_warning "$CLI_HOT_RESTART_VERSION" "$FAKE_SYMBOL_TABLE") check [ "${ADMIN_HOT_RESTART_VERSION}" = "${CLI_HOT_RESTART_VERSION}" ] start_test "Checking server.hot_restart_generation 1" @@ -265,6 +267,18 @@ function run_testsuite() { wait "${SERVER_2_PID}" } +# TODO(#13399): remove this helper function and the references to it, as long as +# the references to $FAKE_SYMBOL_TABLE. +function strip_fake_symbol_table_warning() { + local INPUT="$1" + local FAKE_SYMBOL_TABLE="$2" + if [ "$FAKE_SYMBOL_TABLE" = "1" ]; then + echo "$INPUT" | grep -v "Fake symbol tables have been removed" + else + echo "$INPUT" + fi +} + # Hotrestart in abstract namespace for HOT_RESTART_JSON in "${JSON_TEST_ARRAY[@]}" do diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index b5640bfcbac5..c01716d33c24 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -118,8 +118,6 @@ std::string ContentType(const BufferingStreamDecoderPtr& response) { } // namespace TEST_P(IntegrationAdminTest, Admin) { - Stats::TestUtil::SymbolTableCreatorTestPeer symbol_table_creator_test_peer; - symbol_table_creator_test_peer.setUseFakeSymbolTables(false); initialize(); BufferingStreamDecoderPtr response; diff --git a/test/integration/server.cc b/test/integration/server.cc index 77868cd2c9f2..e6991ec0a71a 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -9,7 +9,6 @@ #include "common/common/thread.h" #include "common/local_info/local_info_impl.h" #include "common/network/utility.h" -#include "common/stats/symbol_table_creator.h" #include "common/stats/thread_local_store.h" #include "common/thread_local/thread_local_impl.h" @@ -190,11 +189,10 @@ void IntegrationTestServer::threadRoutine(const Network::Address::IpVersion vers IntegrationTestServerImpl::IntegrationTestServerImpl(Event::TestTimeSystem& time_system, Api::Api& api, const std::string& config_path, bool use_real_stats) - : IntegrationTestServer(time_system, api, config_path), - symbol_table_(Stats::SymbolTableCreator::makeSymbolTable()) { + : IntegrationTestServer(time_system, api, config_path) { stats_allocator_ = - (use_real_stats ? std::make_unique(*symbol_table_) - : std::make_unique(*symbol_table_)); + (use_real_stats ? std::make_unique(symbol_table_) + : std::make_unique(symbol_table_)); } void IntegrationTestServerImpl::createAndRunEnvoyServer( diff --git a/test/integration/server.h b/test/integration/server.h index 65cad7f5ea88..48995e8cb0b7 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -574,7 +574,7 @@ class IntegrationTestServerImpl : public IntegrationTestServer { Stats::Store* stat_store_{}; Network::Address::InstanceConstSharedPtr admin_address_; absl::Notification server_gone_; - Stats::SymbolTablePtr symbol_table_; + Stats::SymbolTableImpl symbol_table_; std::unique_ptr stats_allocator_; }; diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index a5600628a402..08e8019111a4 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -7,7 +7,6 @@ #include "common/config/well_known_names.h" #include "common/memory/stats.h" -#include "common/stats/symbol_table_creator.h" #include "test/common/stats/stat_test_utility.h" #include "test/config/utility.h" @@ -206,8 +205,6 @@ class ClusterMemoryTestRunner : public testing::TestWithParam::GetParam()) {} - Stats::TestUtil::SymbolTableCreatorTestPeer symbol_table_creator_test_peer_; - Network::Address::IpVersion ip_version_; }; @@ -215,107 +212,7 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, ClusterMemoryTestRunner, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), TestUtility::ipTestParamsToString); -TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithFakeSymbolTable) { - symbol_table_creator_test_peer_.setUseFakeSymbolTables(true); - - // A unique instance of ClusterMemoryTest allows for multiple runs of Envoy with - // differing configuration. This is necessary for measuring the memory consumption - // between the different instances within the same test. - const size_t m100 = ClusterMemoryTestHelper::computeMemoryDelta(1, 0, 101, 0, true); - const size_t m_per_cluster = (m100) / 100; - - // Note: if you are increasing this golden value because you are adding a - // stat, please confirm that this will be generally useful to most Envoy - // users. Otherwise you are adding to the per-cluster memory overhead, which - // will be significant for Envoy installations that are massively - // multi-tenant. - // - // History of golden values: - // - // Date PR Bytes Per Cluster Notes - // exact upper-bound - // ---------- ----- ----------------- ----- - // 2019/03/20 6329 59015 Initial version - // 2019/04/12 6477 59576 Implementing Endpoint lease... - // 2019/04/23 6659 59512 Reintroduce dispatcher stats... - // 2019/04/24 6161 49415 Pack tags and tag extracted names - // 2019/05/07 6794 49957 Stats for excluded hosts in cluster - // 2019/04/27 6733 50213 Use SymbolTable API for HTTP codes - // 2019/05/31 6866 50157 libstdc++ upgrade in CI - // 2019/06/03 7199 49393 absl update - // 2019/06/06 7208 49650 make memory targets approximate - // 2019/06/17 7243 49412 49700 macros for exact/upper-bound memory checks - // 2019/06/29 7364 45685 46000 combine 2 levels of stat ref-counting into 1 - // 2019/06/30 7428 42742 43000 remove stats multiple inheritance, inline HeapStatData - // 2019/07/06 7477 42742 43000 fork gauge representation to drop pending_increment_ - // 2019/07/15 7555 42806 43000 static link libstdc++ in tests - // 2019/07/24 7503 43030 44000 add upstream filters to clusters - // 2019/08/13 7877 42838 44000 skip EdfScheduler creation if all host weights equal - // 2019/09/02 8118 42830 43000 Share symbol-tables in cluster/host stats. - // 2019/09/16 8100 42894 43000 Add transport socket matcher in cluster. - // 2019/09/25 8226 43022 44000 dns: enable dns failure refresh rate configuration - // 2019/09/30 8354 43310 44000 Implement transport socket match. - // 2019/10/17 8537 43308 44000 add new enum value HTTP3 - // 2019/10/17 8484 43340 44000 stats: add unit support to histogram - // 2019/11/01 8859 43563 44000 build: switch to libc++ by default - // 2019/11/15 9040 43371 44000 build: update protobuf to 3.10.1 - // 2019/11/15 9031 43403 44000 upstream: track whether cluster is local - // 2019/12/10 8779 42919 43500 use var-length coding for name length - // 2020/01/07 9069 43413 44000 upstream: Implement retry concurrency budgets - // 2020/01/07 9564 43445 44000 use RefcountPtr for CentralCache. - // 2020/01/09 8889 43509 44000 api: add UpstreamHttpProtocolOptions message - // 2020/01/09 9227 43637 44000 router: per-cluster histograms w/ timeout budget - // 2020/01/12 9633 43797 44104 config: support recovery of original message when - // upgrading. - // 2020/02/13 10042 43797 44136 Metadata: Metadata are shared across different - // clusters and hosts. - // 2020/03/16 9964 44085 44600 http2: support custom SETTINGS parameters. - // 2020/03/24 10501 44261 44600 upstream: upstream_rq_retry_limit_exceeded. - // 2020/04/02 10624 43356 44000 Use 100 clusters rather than 1000 to avoid timeouts - // 2020/04/07 10661 43349 44000 fix clang tidy on master - // 2020/04/23 10531 44169 44600 http: max stream duration upstream support. - // 2020/04/23 10661 44425 46000 per-listener connection limits - // 2020/05/05 10908 44233 44600 router: add InternalRedirectPolicy and predicate - // 2020/05/13 10531 44425 44600 Refactor resource manager - // 2020/05/20 11223 44491 44600 Add primary clusters tracking to cluster manager. - // 2020/06/10 11561 44491 44811 Make upstreams pluggable - // 2020/06/29 11751 44715 46000 Improve time complexity of removing callback handle - // in callback manager. - // 2020/07/07 11252 44971 46000 Introduce Least Request LB active request bias config - // 2020/07/15 11748 45003 46000 Stream error on invalid messaging - // 2020/07/20 11559 44747 46000 stats: add histograms for request/response headers - // and body sizes. - // 2020/07/21 12034 44811 46000 Add configurable histogram buckets. - // 2020/07/31 12035 45002 46000 Init manager store unready targets in hash map. - // 2020/08/10 12275 44949 46000 Re-organize tls histogram maps to improve continuity. - // 2020/08/11 12202 44949 46500 router: add new retry back-off strategy - // 2020/09/11 12973 47500 upstream: predictive prefetch - - // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI - // 'release' builds, where we control the platform and tool-chain. So you - // will need to find the correct value only after failing CI and looking - // at the logs. - // - // On a local clang8/libstdc++/linux flow, the memory usage was observed in - // June 2019 to be 64 bytes higher than it is in CI/release. Your mileage may - // vary. - // - // If you encounter a failure here, please see - // https://github.com/envoyproxy/envoy/blob/master/source/docs/stats.md#stats-memory-tests - // for details on how to fix. - // - // We only run the exact test for ipv6 because ipv4 in some cases may allocate a - // different number of bytes. We still run the approximate test. - if (ip_version_ != Network::Address::IpVersion::v6) { - // https://github.com/envoyproxy/envoy/issues/12209 - // EXPECT_MEMORY_EQ(m_per_cluster, 44949); - } - EXPECT_MEMORY_LE(m_per_cluster, 47500); // Round up to allow platform variations. -} - -TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { - symbol_table_creator_test_peer_.setUseFakeSymbolTables(false); - +TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSize) { // A unique instance of ClusterMemoryTest allows for multiple runs of Envoy with // differing configuration. This is necessary for measuring the memory consumption // between the different instances within the same test. @@ -394,8 +291,6 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { } TEST_P(ClusterMemoryTestRunner, MemoryLargeHostSizeWithStats) { - symbol_table_creator_test_peer_.setUseFakeSymbolTables(false); - // A unique instance of ClusterMemoryTest allows for multiple runs of Envoy with // differing configuration. This is necessary for measuring the memory consumption // between the different instances within the same test. diff --git a/test/mocks/router/BUILD b/test/mocks/router/BUILD index 7044a84c1edb..4bb335ea67a8 100644 --- a/test/mocks/router/BUILD +++ b/test/mocks/router/BUILD @@ -26,7 +26,6 @@ envoy_cc_mock( "//include/envoy/stream_info:stream_info_interface", "//include/envoy/thread_local:thread_local_interface", "//include/envoy/upstream:cluster_manager_interface", - "//source/common/stats:fake_symbol_table_lib", "//test/mocks:common_lib", "//test/mocks/stats:stats_mocks", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index 77cd57786ba6..b015647a791d 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -29,7 +29,7 @@ #include "envoy/type/v3/percent.pb.h" #include "envoy/upstream/cluster_manager.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/global.h" diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index 8de5491105fc..cf9f8bf3e885 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -4,7 +4,7 @@ #include "common/grpc/context_impl.h" #include "common/http/context_impl.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "extensions/transport_sockets/tls/context_manager_impl.h" diff --git a/test/mocks/stats/BUILD b/test/mocks/stats/BUILD index 6d4ddd19a050..55b3c0a19474 100644 --- a/test/mocks/stats/BUILD +++ b/test/mocks/stats/BUILD @@ -17,12 +17,10 @@ envoy_cc_mock( "//include/envoy/stats:timespan_interface", "//include/envoy/thread_local:thread_local_interface", "//include/envoy/upstream:cluster_manager_interface", - "//source/common/stats:fake_symbol_table_lib", "//source/common/stats:histogram_lib", "//source/common/stats:isolated_store_lib", "//source/common/stats:stats_lib", "//source/common/stats:store_impl_lib", - "//source/common/stats:symbol_table_creator_lib", "//source/common/stats:timespan_lib", "//test/common/stats:stat_test_utility_lib", "//test/mocks:common_lib", diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index adeed55eeb5e..cd5e01fb5b0c 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -2,7 +2,7 @@ #include -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "gmock/gmock.h" #include "gtest/gtest.h" diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 6266ae6e2aec..cc43bd084e10 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -14,11 +14,10 @@ #include "envoy/thread_local/thread_local.h" #include "envoy/upstream/cluster_manager.h" -#include "common/stats/fake_symbol_table_impl.h" #include "common/stats/histogram_impl.h" #include "common/stats/isolated_store_impl.h" #include "common/stats/store_impl.h" -#include "common/stats/symbol_table_creator.h" +#include "common/stats/symbol_table_impl.h" #include "common/stats/timespan_impl.h" #include "test/common/stats/stat_test_utility.h" @@ -31,12 +30,11 @@ namespace Stats { class TestSymbolTableHelper { public: - TestSymbolTableHelper() : symbol_table_(SymbolTableCreator::makeSymbolTable()) {} - SymbolTable& symbolTable() { return *symbol_table_; } - const SymbolTable& constSymbolTable() const { return *symbol_table_; } + SymbolTable& symbolTable() { return symbol_table_; } + const SymbolTable& constSymbolTable() const { return symbol_table_; } private: - SymbolTablePtr symbol_table_; + SymbolTableImpl symbol_table_; }; class TestSymbolTable { diff --git a/test/mocks/upstream/host.h b/test/mocks/upstream/host.h index 3c927b0208aa..95183622dbb7 100644 --- a/test/mocks/upstream/host.h +++ b/test/mocks/upstream/host.h @@ -9,7 +9,7 @@ #include "envoy/data/cluster/v2alpha/outlier_detection_event.pb.h" #include "envoy/upstream/upstream.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "test/mocks/network/transport_socket.h" #include "test/mocks/upstream/cluster_info.h" diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index fdc7e58e3500..1952d5f6007f 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -33,7 +33,6 @@ envoy_cc_test( "//source/common/json:json_loader_lib", "//source/common/protobuf", "//source/common/protobuf:utility_lib", - "//source/common/stats:symbol_table_creator_lib", "//source/common/stats:thread_local_store_lib", "//source/server/admin:admin_lib", "//test/mocks/runtime:runtime_mocks", diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 623438013b97..1059d642edbd 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -19,8 +19,7 @@ namespace Server { class AdminStatsTest : public testing::TestWithParam { public: - AdminStatsTest() - : symbol_table_(Stats::SymbolTableCreator::makeSymbolTable()), alloc_(*symbol_table_) { + AdminStatsTest() : alloc_(symbol_table_) { store_ = std::make_unique(alloc_); store_->addSink(sink_); } @@ -34,7 +33,7 @@ class AdminStatsTest : public testing::TestWithParam main_thread_dispatcher_; NiceMock tls_; Stats::AllocatorImpl alloc_; diff --git a/test/server/options_impl_test.cc b/test/server/options_impl_test.cc index 1faa77a06425..3ab6be0e647c 100644 --- a/test/server/options_impl_test.cc +++ b/test/server/options_impl_test.cc @@ -129,6 +129,13 @@ TEST_F(OptionsImplTest, All) { EXPECT_EQ(Server::Mode::InitOnly, options->mode()); } +// TODO(#13399): remove this test once we remove the option. +TEST_F(OptionsImplTest, FakeSymtabWarning) { + EXPECT_LOG_CONTAINS("warning", "Fake symbol tables have been removed", + createOptionsImpl("envoy --use-fake-symbol-table 1")); + EXPECT_NO_LOGS(createOptionsImpl("envoy --use-fake-symbol-table 0")); +} + // Either variants of allow-unknown-[static-]-fields works. TEST_F(OptionsImplTest, AllowUnknownFields) { { diff --git a/test/server/server_test.cc b/test/server/server_test.cc index b2b8b61d429f..a26da0f646e6 100644 --- a/test/server/server_test.cc +++ b/test/server/server_test.cc @@ -505,18 +505,8 @@ TEST_P(ServerInstanceImplTest, Stats) { #endif } -class TestWithSimTimeAndRealSymbolTables : public Event::TestUsingSimulatedTime { -protected: - TestWithSimTimeAndRealSymbolTables() { - symbol_table_creator_test_peer_.setUseFakeSymbolTables(false); - } - -private: - Stats::TestUtil::SymbolTableCreatorTestPeer symbol_table_creator_test_peer_; -}; - class ServerStatsTest - : public TestWithSimTimeAndRealSymbolTables, + : public Event::TestUsingSimulatedTime, public ServerInstanceImplTestBase, public testing::TestWithParam> { protected: diff --git a/test/test_common/BUILD b/test/test_common/BUILD index fbfcba40ff14..4305cf7e4def 100644 --- a/test/test_common/BUILD +++ b/test/test_common/BUILD @@ -137,7 +137,6 @@ envoy_cc_test_library( "//source/common/network:address_lib", "//source/common/network:utility_lib", "//source/common/protobuf:utility_lib", - "//source/common/stats:fake_symbol_table_lib", "//source/common/stats:stats_lib", "//test/mocks/stats:stats_mocks", "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 3a3d0237acda..389b95f65929 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -25,7 +25,7 @@ #include "common/http/header_map_impl.h" #include "common/protobuf/message_validator_impl.h" #include "common/protobuf/utility.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "test/test_common/file_system_for_test.h" #include "test/test_common/printers.h" diff --git a/test/tools/router_check/router.h b/test/tools/router_check/router.h index bb3e50064cc4..a566b0bb7224 100644 --- a/test/tools/router_check/router.h +++ b/test/tools/router_check/router.h @@ -12,7 +12,7 @@ #include "common/http/headers.h" #include "common/json/json_loader.h" #include "common/router/config_impl.h" -#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" #include "common/stream_info/stream_info_impl.h" #include "test/mocks/server/instance.h" From fb7bdbe45cc04ef504b1092f037b45a45cad2714 Mon Sep 17 00:00:00 2001 From: Snow Pettersen Date: Tue, 6 Oct 2020 15:03:31 -0400 Subject: [PATCH 08/27] hcm: fix sending local reply during encode + grpc request classification (#13256) Fixes two issues that surfaced when adding an integration test for the gRPC reverse bridge: 1) when sending a local reply during the encoding path, local_complete_ is set to true which results in endStream being called twice. We fix this by not calling endStream during the header/body callback when local_complete_ is true 2) the direct reply function was not using the state variable to detect is_grpc_request, which meant that for the reverse bridge is was using the modified headers which no longer have a gRPC content type. Signed-off-by: Snow Pettersen --- include/envoy/http/filter.h | 21 +++ source/common/http/filter_manager.cc | 15 +- source/common/http/filter_manager.h | 4 + test/common/http/BUILD | 12 ++ test/common/http/filter_manager_test.cc | 144 ++++++++++++++++++ .../reverse_bridge_integration_test.cc | 29 ++++ test/integration/BUILD | 1 + test/integration/filters/BUILD | 15 ++ .../local_reply_during_encoding_filter.cc | 30 ++++ test/integration/protocol_integration_test.cc | 24 +++ test/mocks/http/BUILD | 1 + test/mocks/http/mocks.cc | 11 ++ test/mocks/http/mocks.h | 55 +++++++ test/mocks/local_reply/BUILD | 16 ++ test/mocks/local_reply/mocks.cc | 8 + test/mocks/local_reply/mocks.h | 19 +++ 16 files changed, 400 insertions(+), 5 deletions(-) create mode 100644 test/common/http/filter_manager_test.cc create mode 100644 test/integration/filters/local_reply_during_encoding_filter.cc create mode 100644 test/mocks/local_reply/BUILD create mode 100644 test/mocks/local_reply/mocks.cc create mode 100644 test/mocks/local_reply/mocks.h diff --git a/include/envoy/http/filter.h b/include/envoy/http/filter.h index 85c1d68b315a..26e58c65858b 100644 --- a/include/envoy/http/filter.h +++ b/include/envoy/http/filter.h @@ -717,6 +717,27 @@ class StreamEncoderFilterCallbacks : public virtual StreamFilterCallbacks { */ virtual ResponseTrailerMap& addEncodedTrailers() PURE; + /** + * Attempts to create a locally generated response using the provided response_code and body_text + * parameters. If the request was a gRPC request the local reply will be encoded as a gRPC + * response with a 200 HTTP response code and grpc-status and grpc-message headers mapped from the + * provided parameters. + * + * If a response has already started (e.g. if the router calls sendSendLocalReply after encoding + * headers) this will either ship the reply directly to the downstream codec, or reset the stream. + * + * @param response_code supplies the HTTP response code. + * @param body_text supplies the optional body text which is sent using the text/plain content + * type, or encoded in the grpc-message header. + * @param modify_headers supplies an optional callback function that can modify the + * response headers. + * @param grpc_status the gRPC status code to override the httpToGrpcStatus mapping with. + * @param details a string detailing why this local reply was sent. + */ + virtual void sendLocalReply(Code response_code, absl::string_view body_text, + std::function modify_headers, + const absl::optional grpc_status, + absl::string_view details) PURE; /** * Adds new metadata to be encoded. * diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index ebc51191db5b..1a6cc22520f4 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -880,17 +880,14 @@ void FilterManager::sendDirectLocalReply( state_.non_100_response_headers_encoded_ = true; filter_manager_callbacks_.encodeHeaders(*filter_manager_callbacks_.responseHeaders(), end_stream); + maybeEndEncode(end_stream); }, [&](Buffer::Instance& data, bool end_stream) -> void { filter_manager_callbacks_.encodeData(data, end_stream); maybeEndEncode(end_stream); }}, - Utility::LocalReplyData{ - filter_manager_callbacks_.requestHeaders().has_value() && - Grpc::Common::hasGrpcContentType(filter_manager_callbacks_.requestHeaders()->get()), - code, body, grpc_status, is_head_request}); - maybeEndEncode(state_.local_complete_); + Utility::LocalReplyData{state_.is_grpc_request_, code, body, grpc_status, is_head_request}); } void FilterManager::encode100ContinueHeaders(ActiveStreamEncoderFilter* filter, @@ -1439,6 +1436,14 @@ void ActiveStreamEncoderFilter::modifyEncodingBuffer( callback(*parent_.buffered_response_data_.get()); } +void ActiveStreamEncoderFilter::sendLocalReply( + Code code, absl::string_view body, + std::function modify_headers, + const absl::optional grpc_status, absl::string_view details) { + parent_.sendLocalReply(parent_.state_.is_grpc_request_, code, body, modify_headers, grpc_status, + details); +} + Http1StreamEncoderOptionsOptRef ActiveStreamEncoderFilter::http1StreamEncoderOptions() { // TODO(mattklein123): At some point we might want to actually wrap this interface but for now // we give the filter direct access to the encoder options. diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index 844cd3900ecf..cf0a8b112eb6 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -255,6 +255,10 @@ struct ActiveStreamEncoderFilter : public ActiveStreamFilterBase, void continueEncoding() override; const Buffer::Instance* encodingBuffer() override; void modifyEncodingBuffer(std::function callback) override; + void sendLocalReply(Code code, absl::string_view body, + std::function modify_headers, + const absl::optional grpc_status, + absl::string_view details) override; Http1StreamEncoderOptionsOptRef http1StreamEncoderOptions() override; void responseDataTooLarge(); diff --git a/test/common/http/BUILD b/test/common/http/BUILD index a9877ae24883..c5bd17d29fd8 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -95,6 +95,18 @@ envoy_cc_fuzz_test( ], ) +envoy_cc_test( + name = "filter_manager_test", + srcs = ["filter_manager_test.cc"], + deps = [ + "//source/common/http:filter_manager_lib", + "//test/mocks/event:event_mocks", + "//test/mocks/http:http_mocks", + "//test/mocks/local_reply:local_reply_mocks", + "//test/mocks/network:network_mocks", + ], +) + envoy_cc_test( name = "codec_wrappers_test", srcs = ["codec_wrappers_test.cc"], diff --git a/test/common/http/filter_manager_test.cc b/test/common/http/filter_manager_test.cc new file mode 100644 index 000000000000..fdc6e6a4b6be --- /dev/null +++ b/test/common/http/filter_manager_test.cc @@ -0,0 +1,144 @@ +#include "envoy/stream_info/filter_state.h" + +#include "common/http/filter_manager.h" +#include "common/stream_info/filter_state_impl.h" +#include "common/stream_info/stream_info_impl.h" + +#include "test/mocks/event/mocks.h" +#include "test/mocks/http/mocks.h" +#include "test/mocks/local_reply/mocks.h" +#include "test/mocks/network/mocks.h" + +#include "gtest/gtest.h" + +using testing::Return; + +namespace Envoy { +namespace Http { +namespace { +class FilterManagerTest : public testing::Test { +public: + void initialize() { + filter_manager_ = std::make_unique( + filter_manager_callbacks_, dispatcher_, connection_, 0, true, 10000, filter_factory_, + local_reply_, protocol_, time_source_, filter_state_, + StreamInfo::FilterState::LifeSpan::Connection); + } + + std::unique_ptr filter_manager_; + NiceMock filter_manager_callbacks_; + Event::MockDispatcher dispatcher_; + NiceMock connection_; + Envoy::Http::MockFilterChainFactory filter_factory_; + LocalReply::MockLocalReply local_reply_; + Protocol protocol_{Protocol::Http2}; + NiceMock time_source_; + StreamInfo::FilterStateSharedPtr filter_state_ = + std::make_shared(StreamInfo::FilterState::LifeSpan::Connection); +}; + +// Verifies that the local reply persists the gRPC classification even if the request headers are +// modified. +TEST_F(FilterManagerTest, SendLocalReplyDuringDecodingGrpcClassiciation) { + initialize(); + + std::shared_ptr filter(new NiceMock()); + + EXPECT_CALL(*filter, decodeHeaders(_, true)) + .WillRepeatedly(Invoke([&](RequestHeaderMap& headers, bool) -> FilterHeadersStatus { + headers.setContentType("text/plain"); + + filter->callbacks_->sendLocalReply(Code::InternalServerError, "", nullptr, absl::nullopt, + ""); + + return FilterHeadersStatus::StopIteration; + })); + + RequestHeaderMapPtr grpc_headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {"content-type", "application/grpc"}}}; + + ON_CALL(filter_manager_callbacks_, requestHeaders()) + .WillByDefault(Return(absl::make_optional(std::ref(*grpc_headers)))); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillRepeatedly(Invoke([&](FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addStreamDecoderFilter(filter); + })); + + filter_manager_->createFilterChain(); + + filter_manager_->requestHeadersInitialized(); + EXPECT_CALL(local_reply_, rewrite(_, _, _, _, _, _)); + EXPECT_CALL(filter_manager_callbacks_, setResponseHeaders_(_)) + .WillOnce(Invoke([](auto& response_headers) { + EXPECT_THAT(response_headers, + HeaderHasValueRef(Http::Headers::get().ContentType, "application/grpc")); + })); + EXPECT_CALL(filter_manager_callbacks_, resetIdleTimer()); + EXPECT_CALL(filter_manager_callbacks_, encodeHeaders(_, _)); + EXPECT_CALL(filter_manager_callbacks_, endStream()); + filter_manager_->decodeHeaders(*grpc_headers, true); + filter_manager_->destroyFilters(); +} + +// Verifies that the local reply persists the gRPC classification even if the request headers are +// modified when directly encoding a response. +TEST_F(FilterManagerTest, SendLocalReplyDuringEncodingGrpcClassiciation) { + initialize(); + + std::shared_ptr decoder_filter(new NiceMock()); + + EXPECT_CALL(*decoder_filter, decodeHeaders(_, true)) + .WillRepeatedly(Invoke([&](RequestHeaderMap& headers, bool) -> FilterHeadersStatus { + headers.setContentType("text/plain"); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filter->callbacks_->encodeHeaders(std::move(response_headers), true, "test"); + + return FilterHeadersStatus::StopIteration; + })); + + std::shared_ptr encoder_filter(new NiceMock()); + + EXPECT_CALL(*encoder_filter, encodeHeaders(_, true)) + .WillRepeatedly(Invoke([&](auto&, bool) -> FilterHeadersStatus { + encoder_filter->encoder_callbacks_->sendLocalReply(Code::InternalServerError, "", nullptr, + absl::nullopt, ""); + return FilterHeadersStatus::StopIteration; + })); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillRepeatedly(Invoke([&](FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addStreamDecoderFilter(decoder_filter); + callbacks.addStreamFilter(encoder_filter); + })); + + RequestHeaderMapPtr grpc_headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {"content-type", "application/grpc"}}}; + + ON_CALL(filter_manager_callbacks_, requestHeaders()) + .WillByDefault(Return(absl::make_optional(std::ref(*grpc_headers)))); + filter_manager_->createFilterChain(); + + filter_manager_->requestHeadersInitialized(); + EXPECT_CALL(local_reply_, rewrite(_, _, _, _, _, _)); + EXPECT_CALL(filter_manager_callbacks_, setResponseHeaders_(_)) + .WillOnce(Invoke([](auto&) {})) + .WillOnce(Invoke([](auto& response_headers) { + EXPECT_THAT(response_headers, + HeaderHasValueRef(Http::Headers::get().ContentType, "application/grpc")); + })); + EXPECT_CALL(filter_manager_callbacks_, encodeHeaders(_, _)); + EXPECT_CALL(filter_manager_callbacks_, endStream()); + filter_manager_->decodeHeaders(*grpc_headers, true); + filter_manager_->destroyFilters(); +} +} // namespace +} // namespace Http +} // namespace Envoy \ No newline at end of file diff --git a/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_integration_test.cc b/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_integration_test.cc index febd3d40a3ed..c6c2b4d43e7e 100644 --- a/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_integration_test.cc +++ b/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_integration_test.cc @@ -173,5 +173,34 @@ TEST_P(ReverseBridgeIntegrationTest, EnabledRoute) { ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); } +TEST_P(ReverseBridgeIntegrationTest, EnabledRouteBadContentType) { + upstream_protocol_ = FakeHttpConnection::Type::HTTP1; + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + + Http::TestRequestHeaderMapImpl request_headers({{":scheme", "http"}, + {":method", "POST"}, + {":authority", "foo"}, + {":path", "/testing.ExampleService/Print"}, + {"content-type", "application/grpc"}}); + + Http::TestResponseHeaderMapImpl response_headers; + response_headers.setStatus(200); + response_headers.setContentType("application/x-not-protobuf"); + + auto response = sendRequestAndWaitForResponse(request_headers, 5, response_headers, 5); + + EXPECT_TRUE(response->complete()); + + // The response should indicate an error. + EXPECT_THAT(response->headers(), + HeaderValueOf(Http::Headers::get().ContentType, "application/grpc")); + EXPECT_THAT(response->headers(), HeaderValueOf(Http::Headers::get().GrpcStatus, "2")); + + codec_client_->close(); + ASSERT_TRUE(fake_upstream_connection_->close()); + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); +} } // namespace } // namespace Envoy diff --git a/test/integration/BUILD b/test/integration/BUILD index e4715ab94b30..c9736305617a 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -420,6 +420,7 @@ envoy_cc_test( "//source/extensions/filters/http/health_check:config", "//test/integration/filters:continue_headers_only_inject_body", "//test/integration/filters:encoder_decoder_buffer_filter_lib", + "//test/integration/filters:local_reply_during_encoding_filter_lib", "//test/integration/filters:random_pause_filter_lib", "//test/test_common:utility_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", diff --git a/test/integration/filters/BUILD b/test/integration/filters/BUILD index 1746a3952626..01fe327a3ead 100644 --- a/test/integration/filters/BUILD +++ b/test/integration/filters/BUILD @@ -24,6 +24,21 @@ envoy_cc_test_library( ], ) +envoy_cc_test_library( + name = "local_reply_during_encoding_filter_lib", + srcs = [ + "local_reply_during_encoding_filter.cc", + ], + deps = [ + ":common_lib", + "//include/envoy/http:filter_interface", + "//include/envoy/registry", + "//include/envoy/server:filter_config_interface", + "//source/extensions/filters/http/common:pass_through_filter_lib", + "//test/extensions/filters/http/common:empty_http_filter_config_lib", + ], +) + envoy_cc_test_library( name = "continue_headers_only_inject_body", srcs = [ diff --git a/test/integration/filters/local_reply_during_encoding_filter.cc b/test/integration/filters/local_reply_during_encoding_filter.cc new file mode 100644 index 000000000000..54a6fadce8e9 --- /dev/null +++ b/test/integration/filters/local_reply_during_encoding_filter.cc @@ -0,0 +1,30 @@ +#include + +#include "envoy/http/filter.h" +#include "envoy/registry/registry.h" +#include "envoy/server/filter_config.h" + +#include "extensions/filters/http/common/pass_through_filter.h" + +#include "test/extensions/filters/http/common/empty_http_filter_config.h" +#include "test/integration/filters/common.h" + +namespace Envoy { + +class LocalReplyDuringEncode : public Http::PassThroughFilter { +public: + constexpr static char name[] = "local-reply-during-encode"; + + Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override { + encoder_callbacks_->sendLocalReply(Http::Code::InternalServerError, "", nullptr, absl::nullopt, + ""); + return Http::FilterHeadersStatus::StopIteration; + } +}; + +constexpr char LocalReplyDuringEncode::name[]; +static Registry::RegisterFactory, + Server::Configuration::NamedHttpFilterConfigFactory> + register_; + +} // namespace Envoy diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 381c968c75d2..0228f29b056f 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1415,6 +1415,30 @@ name: decode-headers-only EXPECT_EQ(0, upstream_request_->body().length()); } +TEST_P(DownstreamProtocolIntegrationTest, LocalReplyDuringEncoding) { + config_helper_.addFilter(R"EOF( +name: local-reply-during-encode +)EOF"); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + + auto response = codec_client_->makeHeaderOnlyRequest( + Http::TestRequestHeaderMapImpl{{":method", "GET"}, + {":path", "/test/long/url"}, + {":scheme", "http"}, + {":authority", "host"}}); + + // Wait for the upstream request and begin sending a response with end_stream = false. + waitForNextUpstreamRequest(); + upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "503"}}, true); + + response->waitForEndStream(); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("500", response->headers().getStatusValue()); + EXPECT_EQ(0, upstream_request_->body().length()); +} + TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlRejected) { // Send one 95 kB URL with limit 60 kB headers. testLargeRequestUrl(95, 60); diff --git a/test/mocks/http/BUILD b/test/mocks/http/BUILD index d169464f6c26..ca7705d8881f 100644 --- a/test/mocks/http/BUILD +++ b/test/mocks/http/BUILD @@ -50,6 +50,7 @@ envoy_cc_mock( "//include/envoy/http:filter_interface", "//include/envoy/ssl:connection_interface", "//include/envoy/tracing:http_tracer_interface", + "//source/common/http:filter_manager_lib", "//source/common/http:header_map_lib", "//test/mocks/event:event_mocks", "//test/mocks/router:router_mocks", diff --git a/test/mocks/http/mocks.cc b/test/mocks/http/mocks.cc index 9befd7cf8a91..639295ae16d3 100644 --- a/test/mocks/http/mocks.cc +++ b/test/mocks/http/mocks.cc @@ -2,6 +2,7 @@ #include "envoy/buffer/buffer.h" #include "envoy/event/dispatcher.h" +#include "envoy/http/header_map.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -20,6 +21,16 @@ MockConnectionCallbacks::~MockConnectionCallbacks() = default; MockServerConnectionCallbacks::MockServerConnectionCallbacks() = default; MockServerConnectionCallbacks::~MockServerConnectionCallbacks() = default; +MockFilterManagerCallbacks::MockFilterManagerCallbacks() { + ON_CALL(*this, responseHeaders()).WillByDefault(Invoke([this]() -> ResponseHeaderMapOptRef { + if (response_headers_) { + return absl::make_optional(std::ref(*response_headers_)); + } + return absl::nullopt; + })); +} +MockFilterManagerCallbacks::~MockFilterManagerCallbacks() = default; + MockStreamCallbacks::MockStreamCallbacks() = default; MockStreamCallbacks::~MockStreamCallbacks() = default; diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 6fba43711f17..ce9eb19fb646 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -14,6 +14,7 @@ #include "envoy/http/filter.h" #include "envoy/ssl/connection.h" +#include "common/http/filter_manager.h" #include "common/http/header_map_impl.h" #include "common/http/utility.h" @@ -47,6 +48,55 @@ class MockConnectionCallbacks : public virtual ConnectionCallbacks { MOCK_METHOD(void, onGoAway, (GoAwayErrorCode error_code)); }; +class MockFilterManagerCallbacks : public FilterManagerCallbacks { +public: + MockFilterManagerCallbacks(); + ~MockFilterManagerCallbacks() override; + + MOCK_METHOD(void, encodeHeaders, (ResponseHeaderMap&, bool)); + MOCK_METHOD(void, encode100ContinueHeaders, (ResponseHeaderMap&)); + MOCK_METHOD(void, encodeData, (Buffer::Instance&, bool)); + MOCK_METHOD(void, encodeTrailers, (ResponseTrailerMap&)); + MOCK_METHOD(void, encodeMetadata, (MetadataMapVector&)); + MOCK_METHOD(void, setRequestTrailers, (RequestTrailerMapPtr &&)); + MOCK_METHOD(void, setContinueHeaders, (ResponseHeaderMapPtr &&)); + MOCK_METHOD(void, setResponseHeaders_, (ResponseHeaderMap&)); + void setResponseHeaders(ResponseHeaderMapPtr&& response_headers) override { + // TODO(snowp): Repeat this pattern for all setters. + response_headers_ = std::move(response_headers); + setResponseHeaders_(*response_headers_); + } + MOCK_METHOD(void, setResponseTrailers, (ResponseTrailerMapPtr &&)); + MOCK_METHOD(RequestHeaderMapOptRef, requestHeaders, ()); + MOCK_METHOD(RequestTrailerMapOptRef, requestTrailers, ()); + MOCK_METHOD(ResponseHeaderMapOptRef, continueHeaders, ()); + MOCK_METHOD(ResponseHeaderMapOptRef, responseHeaders, ()); + MOCK_METHOD(ResponseTrailerMapOptRef, responseTrailers, ()); + MOCK_METHOD(void, endStream, ()); + MOCK_METHOD(void, onDecoderFilterBelowWriteBufferLowWatermark, ()); + MOCK_METHOD(void, onDecoderFilterAboveWriteBufferHighWatermark, ()); + MOCK_METHOD(void, upgradeFilterChainCreated, ()); + MOCK_METHOD(void, disarmRequestTimeout, ()); + MOCK_METHOD(void, resetIdleTimer, ()); + MOCK_METHOD(void, recreateStream, (StreamInfo::FilterStateSharedPtr filter_state)); + MOCK_METHOD(void, resetStream, ()); + MOCK_METHOD(const Router::RouteEntry::UpgradeMap*, upgradeMap, ()); + MOCK_METHOD(Upstream::ClusterInfoConstSharedPtr, clusterInfo, ()); + MOCK_METHOD(Router::RouteConstSharedPtr, route, (const Router::RouteCallback& cb)); + MOCK_METHOD(void, clearRouteCache, ()); + MOCK_METHOD(absl::optional, routeConfig, ()); + MOCK_METHOD(void, requestRouteConfigUpdate, (Http::RouteConfigUpdatedCallbackSharedPtr)); + MOCK_METHOD(Tracing::Span&, activeSpan, ()); + MOCK_METHOD(void, onResponseDataTooLarge, ()); + MOCK_METHOD(void, onRequestDataTooLarge, ()); + MOCK_METHOD(Http1StreamEncoderOptionsOptRef, http1StreamEncoderOptions, ()); + MOCK_METHOD(void, onLocalReply, (Code code)); + MOCK_METHOD(Tracing::Config&, tracingConfig, ()); + MOCK_METHOD(const ScopeTrackedObject&, scope, ()); + + ResponseHeaderMapPtr response_headers_; +}; + class MockServerConnectionCallbacks : public ServerConnectionCallbacks, public MockConnectionCallbacks { public: @@ -242,6 +292,11 @@ class MockStreamEncoderFilterCallbacks : public StreamEncoderFilterCallbacks, MOCK_METHOD(void, continueEncoding, ()); MOCK_METHOD(const Buffer::Instance*, encodingBuffer, ()); MOCK_METHOD(void, modifyEncodingBuffer, (std::function)); + MOCK_METHOD(void, sendLocalReply, + (Code code, absl::string_view body, + std::function modify_headers, + const absl::optional grpc_status, + absl::string_view details)); MOCK_METHOD(Http1StreamEncoderOptionsOptRef, http1StreamEncoderOptions, ()); Buffer::InstancePtr buffer_; diff --git a/test/mocks/local_reply/BUILD b/test/mocks/local_reply/BUILD new file mode 100644 index 000000000000..7467186cb044 --- /dev/null +++ b/test/mocks/local_reply/BUILD @@ -0,0 +1,16 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_mock", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_mock( + name = "local_reply_mocks", + srcs = ["mocks.cc"], + hdrs = ["mocks.h"], + deps = ["//source/common/local_reply:local_reply_lib"], +) diff --git a/test/mocks/local_reply/mocks.cc b/test/mocks/local_reply/mocks.cc new file mode 100644 index 000000000000..f6219934def1 --- /dev/null +++ b/test/mocks/local_reply/mocks.cc @@ -0,0 +1,8 @@ +#include "test/mocks/local_reply/mocks.h" + +namespace Envoy { +namespace LocalReply { +MockLocalReply::MockLocalReply() = default; +MockLocalReply::~MockLocalReply() = default; +} // namespace LocalReply +} // namespace Envoy \ No newline at end of file diff --git a/test/mocks/local_reply/mocks.h b/test/mocks/local_reply/mocks.h new file mode 100644 index 000000000000..3d0a7ddeab88 --- /dev/null +++ b/test/mocks/local_reply/mocks.h @@ -0,0 +1,19 @@ +#include "common/local_reply/local_reply.h" + +#include "gmock/gmock.h" + +namespace Envoy { +namespace LocalReply { +class MockLocalReply : public LocalReply { +public: + MockLocalReply(); + ~MockLocalReply() override; + + MOCK_METHOD(void, rewrite, + (const Http::RequestHeaderMap* request_headers, + Http::ResponseHeaderMap& response_headers, StreamInfo::StreamInfoImpl& stream_info, + Http::Code& code, std::string& body, absl::string_view& content_type), + (const)); +}; +} // namespace LocalReply +} // namespace Envoy \ No newline at end of file From 62ef51de84e5de061d87bc50c0e3f83cfca19a30 Mon Sep 17 00:00:00 2001 From: asraa Date: Tue, 6 Oct 2020 18:17:59 -0400 Subject: [PATCH 09/27] [api] fix address configuration bugs (#13090) Signed-off-by: Asra Ali --- api/envoy/config/core/v3/address.proto | 1 + api/envoy/config/core/v4alpha/address.proto | 1 + .../envoy/config/core/v3/address.proto | 1 + .../envoy/config/core/v4alpha/address.proto | 1 + source/common/network/resolver_impl.cc | 11 +- test/common/network/resolver_impl_test.cc | 2 +- ...inimized-server_fuzz_test-5084029869883392 | 9 + ...inimized-server_fuzz_test-5733243234811904 | 204 ++++++++++++++++++ .../not_implemented_envoy_internal | 103 +++++++++ test/server/server_fuzz_test.cc | 7 +- 10 files changed, 330 insertions(+), 10 deletions(-) create mode 100644 test/server/server_corpus/clusterfuzz-testcase-minimized-server_fuzz_test-5084029869883392 create mode 100644 test/server/server_corpus/clusterfuzz-testcase-minimized-server_fuzz_test-5733243234811904 create mode 100644 test/server/server_corpus/not_implemented_envoy_internal diff --git a/api/envoy/config/core/v3/address.proto b/api/envoy/config/core/v3/address.proto index bf8f84c25ba0..8228450eb93c 100644 --- a/api/envoy/config/core/v3/address.proto +++ b/api/envoy/config/core/v3/address.proto @@ -32,6 +32,7 @@ message Pipe { // [#not-implemented-hide:] The address represents an envoy internal listener. // TODO(lambdai): Make this address available for listener and endpoint. +// TODO(asraa): When address available, remove workaround from test/server/server_fuzz_test.cc:30. message EnvoyInternalAddress { oneof address_name_specifier { option (validate.required) = true; diff --git a/api/envoy/config/core/v4alpha/address.proto b/api/envoy/config/core/v4alpha/address.proto index a16059468769..6ae82359504e 100644 --- a/api/envoy/config/core/v4alpha/address.proto +++ b/api/envoy/config/core/v4alpha/address.proto @@ -32,6 +32,7 @@ message Pipe { // [#not-implemented-hide:] The address represents an envoy internal listener. // TODO(lambdai): Make this address available for listener and endpoint. +// TODO(asraa): When address available, remove workaround from test/server/server_fuzz_test.cc:30. message EnvoyInternalAddress { option (udpa.annotations.versioning).previous_message_type = "envoy.config.core.v3.EnvoyInternalAddress"; diff --git a/generated_api_shadow/envoy/config/core/v3/address.proto b/generated_api_shadow/envoy/config/core/v3/address.proto index bf8f84c25ba0..8228450eb93c 100644 --- a/generated_api_shadow/envoy/config/core/v3/address.proto +++ b/generated_api_shadow/envoy/config/core/v3/address.proto @@ -32,6 +32,7 @@ message Pipe { // [#not-implemented-hide:] The address represents an envoy internal listener. // TODO(lambdai): Make this address available for listener and endpoint. +// TODO(asraa): When address available, remove workaround from test/server/server_fuzz_test.cc:30. message EnvoyInternalAddress { oneof address_name_specifier { option (validate.required) = true; diff --git a/generated_api_shadow/envoy/config/core/v4alpha/address.proto b/generated_api_shadow/envoy/config/core/v4alpha/address.proto index a16059468769..6ae82359504e 100644 --- a/generated_api_shadow/envoy/config/core/v4alpha/address.proto +++ b/generated_api_shadow/envoy/config/core/v4alpha/address.proto @@ -32,6 +32,7 @@ message Pipe { // [#not-implemented-hide:] The address represents an envoy internal listener. // TODO(lambdai): Make this address available for listener and endpoint. +// TODO(asraa): When address available, remove workaround from test/server/server_fuzz_test.cc:30. message EnvoyInternalAddress { option (udpa.annotations.versioning).previous_message_type = "envoy.config.core.v3.EnvoyInternalAddress"; diff --git a/source/common/network/resolver_impl.cc b/source/common/network/resolver_impl.cc index f31dde68b7d1..8aa49b8bb8bc 100644 --- a/source/common/network/resolver_impl.cc +++ b/source/common/network/resolver_impl.cc @@ -55,15 +55,12 @@ InstanceConstSharedPtr resolveProtoAddress(const envoy::config::core::v3::Addres kServerListenerName: return std::make_shared( address.envoy_internal_address().server_listener_name()); - case envoy::config::core::v3::EnvoyInternalAddress::AddressNameSpecifierCase:: - ADDRESS_NAME_SPECIFIER_NOT_SET: - break; + default: + NOT_REACHED_GCOVR_EXCL_LINE; } - break; - case envoy::config::core::v3::Address::AddressCase::ADDRESS_NOT_SET: - break; + default: + throw EnvoyException("Address must be set: " + address.DebugString()); } - NOT_REACHED_GCOVR_EXCL_LINE; } InstanceConstSharedPtr diff --git a/test/common/network/resolver_impl_test.cc b/test/common/network/resolver_impl_test.cc index 5d3707f5abb3..c9d9b3fce901 100644 --- a/test/common/network/resolver_impl_test.cc +++ b/test/common/network/resolver_impl_test.cc @@ -170,7 +170,7 @@ TEST(ResolverTest, NonStandardResolver) { TEST(ResolverTest, UninitializedAddress) { envoy::config::core::v3::Address address; - EXPECT_DEATH(resolveProtoAddress(address), "panic"); + EXPECT_THROW_WITH_MESSAGE(resolveProtoAddress(address), EnvoyException, "Address must be set: "); } TEST(ResolverTest, NoSuchResolver) { diff --git a/test/server/server_corpus/clusterfuzz-testcase-minimized-server_fuzz_test-5084029869883392 b/test/server/server_corpus/clusterfuzz-testcase-minimized-server_fuzz_test-5084029869883392 new file mode 100644 index 000000000000..948e34d2ce4f --- /dev/null +++ b/test/server/server_corpus/clusterfuzz-testcase-minimized-server_fuzz_test-5084029869883392 @@ -0,0 +1,9 @@ +static_resources { + listeners { + address { + envoy_internal_address { + server_listener_name: "ipv6" + } + } + } +} \ No newline at end of file diff --git a/test/server/server_corpus/clusterfuzz-testcase-minimized-server_fuzz_test-5733243234811904 b/test/server/server_corpus/clusterfuzz-testcase-minimized-server_fuzz_test-5733243234811904 new file mode 100644 index 000000000000..0b3fd14152e2 --- /dev/null +++ b/test/server/server_corpus/clusterfuzz-testcase-minimized-server_fuzz_test-5733243234811904 @@ -0,0 +1,204 @@ +node { +} +static_resources { + clusters { + name: ";" + connect_timeout { + seconds: 8 + nanos: 132 + } + per_connection_buffer_limit_bytes { + value: 1728053248 + } + health_checks { + timeout { + seconds: 8 + nanos: 25 + } + interval { + seconds: 2559 + nanos: 67154560 + } + unhealthy_threshold { + value: 1728053248 + } + healthy_threshold { + value: 1728053248 + } + alt_port { + value: 4 + } + http_health_check { + path: ":" + receive { + text: "@B\017\000\000\000\000\000" + } + request_headers_to_add { + header { + key: "\361\211\211\211\t\341\211\211\tt" + value: "\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\337\205\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177" + } + } + request_headers_to_remove: ";" + } + unhealthy_interval { + seconds: 8 + nanos: 132 + } + unhealthy_edge_interval { + seconds: 2299 + nanos: 16384 + } + event_log_path: "%" + always_log_health_check_failures: true + } + max_requests_per_connection { + value: 842473504 + } + http2_protocol_options { + max_inbound_priority_frames_per_stream { + value: 1701737468 + } + } + alt_stat_name: "=" + common_http_protocol_options { + max_headers_count { + value: 842473504 + } + } + load_assignment { + cluster_name: "domains" + endpoints { + locality { + zone: "6" + } + load_balancing_weight { + value: 842473504 + } + priority: 122 + } + endpoints { + lb_endpoints { + endpoint { + address { + socket_address { + address: "0.0.0.0" + port_value: 0 + } + } + } + health_status: TIMEOUT + } + priority: 122 + proximity { + value: 28732523 + } + } + endpoints { + locality { + sub_zone: "|" + } + lb_endpoints { + endpoint { + address { + pipe { + path: "$node {\n}\ns" + } + } + } + health_status: UNHEALTHY + } + load_balancing_weight { + value: 122 + } + priority: 122 + } + endpoints { + locality { + sub_zone: "|" + } + lb_endpoints { + endpoint { + address { + socket_address { + address: "0.0.0.0" + port_value: 0 + } + } + } + health_status: DEGRADED + } + priority: 122 + } + endpoints { + lb_endpoints { + health_status: HEALTHY + endpoint_name: "\021\000\000\000\000\000\000\000" + } + lb_endpoints { + endpoint { + address { + envoy_internal_address { + server_listener_name: "\001\000\000\001" + } + } + } + health_status: TIMEOUT + } + priority: 106 + } + policy { + drop_overloads { + category: "U" + } + } + } + dns_failure_refresh_rate { + base_interval { + seconds: 8 + nanos: 132 + } + } + track_cluster_stats { + } + } +} +stats_sinks { + name: "type.googleapis.com/envoy.api.v2.route.Route" + typed_config { + type_url: "IIIIIIIIIIIIIIII" + } +} +stats_sinks { + name: "=" + typed_config { + value: "\000\037" + } +} +stats_sinks { +} +stats_sinks { +} +stats_sinks { + name: "[" +} +stats_sinks { +} +stats_sinks { + name: "z" +} +stats_sinks { + name: "z" +} +stats_sinks { + name: "z" +} +stats_sinks { + name: "z" +} +stats_sinks { + name: "z" +} +stats_sinks { + name: "z" +} \ No newline at end of file diff --git a/test/server/server_corpus/not_implemented_envoy_internal b/test/server/server_corpus/not_implemented_envoy_internal new file mode 100644 index 000000000000..2fd02520683c --- /dev/null +++ b/test/server/server_corpus/not_implemented_envoy_internal @@ -0,0 +1,103 @@ +static_resources { + clusters { + name: "ser" + connect_timeout { + nanos: 813 + } + lb_policy: RING_HASH + health_checks { + timeout { + seconds: 1000000 + nanos: 262239 + } + interval { + seconds: 10838081697 + nanos: 95 + } + unhealthy_threshold { + } + healthy_threshold { + } + http_health_check { + host: "\037\037\037\037\037\037\037\037\037\037\037\037\037\037\037\037\037\037\037\037\037\037\037\001\037\037\037\037\037\037\037\037\037\037\037\037\037f\037\037\037\037" + path: "&" + } + healthy_edge_interval { + nanos: 95 + } + } + circuit_breakers { + } + http_protocol_options { + allow_absolute_url { + value: true + } + } + load_assignment { + cluster_name: "." + endpoints { + } + endpoints { + lb_endpoints { + endpoint { + address { + pipe { + path: "f" + } + } + } + health_status: DRAINING + } + } + endpoints { + lb_endpoints { + endpoint { + address { + envoy_internal_address { + server_listener_name: "\000\000\000\003" + } + } + } + } + priority: 16 + } + endpoints { + proximity { + value: 10240 + } + } + endpoints { + lb_endpoints { + endpoint { + address { + socket_address { + address: "127.0.0.1" + port_value: 9901 + } + } + } + health_status: HEALTHY + } + priority: 16 + } + } + use_tcp_for_dns_lookups: true + } +} +stats_flush_interval { + nanos: 16777216 +} +admin { + access_log_path: "f" + address { + socket_address { + address: "\024" + } + } + socket_options { + description: "=" + level: 4702337453602635775 + int_value: 4702337453602635775 + } +} +use_tcp_for_dns_lookups: true \ No newline at end of file diff --git a/test/server/server_fuzz_test.cc b/test/server/server_fuzz_test.cc index ed3e1507578d..f96f3b17c8d6 100644 --- a/test/server/server_fuzz_test.cc +++ b/test/server/server_fuzz_test.cc @@ -27,7 +27,9 @@ void makePortHermetic(Fuzz::PerTestEnvironment& test_env, envoy::config::core::v3::Address& address) { if (address.has_socket_address()) { address.mutable_socket_address()->set_port_value(0); - } else if (address.has_pipe()) { + } else if (address.has_pipe() || address.has_envoy_internal_address()) { + // TODO(asraa): Remove this work-around to replace EnvoyInternalAddress when implemented and + // remove condition at line 74. address.mutable_pipe()->set_path("@" + test_env.testId() + address.pipe().path()); } } @@ -70,7 +72,8 @@ makeHermeticPathsAndPorts(Fuzz::PerTestEnvironment& test_env, auto* locality_lb = cluster.mutable_load_assignment()->mutable_endpoints(j); for (int k = 0; k < locality_lb->lb_endpoints_size(); ++k) { auto* lb_endpoint = locality_lb->mutable_lb_endpoints(k); - if (lb_endpoint->endpoint().address().has_socket_address()) { + if (lb_endpoint->endpoint().address().has_socket_address() || + lb_endpoint->endpoint().address().has_envoy_internal_address()) { makePortHermetic(test_env, *lb_endpoint->mutable_endpoint()->mutable_address()); } } From 06604048eb8dd40294370cb35d650c2869626eaa Mon Sep 17 00:00:00 2001 From: Yangmin Zhu Date: Tue, 6 Oct 2020 15:18:18 -0700 Subject: [PATCH 10/27] ext_authz: add metadata matcher (#13404) Signed-off-by: Yangmin Zhu --- .../filters/http/ext_authz/v3/ext_authz.proto | 7 +- .../http/ext_authz/v4alpha/ext_authz.proto | 7 +- .../filters/network/ext_authz/v3/BUILD | 1 + .../network/ext_authz/v3/ext_authz.proto | 7 +- .../filters/network/ext_authz/v4alpha/BUILD | 14 ++ .../network/ext_authz/v4alpha/ext_authz.proto | 59 ++++++ .../http/http_filters/ext_authz_filter.rst | 1 + .../network_filters/ext_authz_filter.rst | 1 + docs/root/version_history/current.rst | 1 + .../filters/http/ext_authz/v3/ext_authz.proto | 7 +- .../http/ext_authz/v4alpha/ext_authz.proto | 7 +- .../filters/network/ext_authz/v3/BUILD | 1 + .../network/ext_authz/v3/ext_authz.proto | 7 +- .../filters/network/ext_authz/v4alpha/BUILD | 14 ++ .../network/ext_authz/v4alpha/ext_authz.proto | 59 ++++++ .../filters/http/ext_authz/ext_authz.cc | 10 +- .../filters/http/ext_authz/ext_authz.h | 13 +- .../filters/network/ext_authz/BUILD | 1 + .../filters/network/ext_authz/ext_authz.cc | 5 + .../filters/network/ext_authz/ext_authz.h | 17 +- .../ext_authz/ext_authz_integration_test.cc | 40 +++- .../filters/http/ext_authz/ext_authz_test.cc | 197 +++++++++++++++++ .../network/ext_authz/ext_authz_test.cc | 200 +++++++++++++----- 23 files changed, 599 insertions(+), 77 deletions(-) create mode 100644 api/envoy/extensions/filters/network/ext_authz/v4alpha/BUILD create mode 100644 api/envoy/extensions/filters/network/ext_authz/v4alpha/ext_authz.proto create mode 100644 generated_api_shadow/envoy/extensions/filters/network/ext_authz/v4alpha/BUILD create mode 100644 generated_api_shadow/envoy/extensions/filters/network/ext_authz/v4alpha/ext_authz.proto diff --git a/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto b/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto index 07f883315187..395258802f56 100644 --- a/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto +++ b/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto @@ -6,6 +6,7 @@ import "envoy/config/core/v3/base.proto"; import "envoy/config/core/v3/config_source.proto"; import "envoy/config/core/v3/grpc_service.proto"; import "envoy/config/core/v3/http_uri.proto"; +import "envoy/type/matcher/v3/metadata.proto"; import "envoy/type/matcher/v3/string.proto"; import "envoy/type/v3/http_status.proto"; @@ -23,7 +24,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // External Authorization :ref:`configuration overview `. // [#extension: envoy.filters.http.ext_authz] -// [#next-free-field: 14] +// [#next-free-field: 15] message ExtAuthz { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.ext_authz.v2.ExtAuthz"; @@ -103,6 +104,10 @@ message ExtAuthz { // If this field is not specified, the filter will be enabled for all requests. config.core.v3.RuntimeFractionalPercent filter_enabled = 9; + // Specifies if the filter is enabled with metadata matcher. + // If this field is not specified, the filter will be enabled for all requests. + type.matcher.v3.MetadataMatcher filter_enabled_metadata = 14; + // Specifies whether to deny the requests, when the filter is disabled. // If :ref:`runtime_key ` is specified, // Envoy will lookup the runtime key to determine whether to deny request for diff --git a/api/envoy/extensions/filters/http/ext_authz/v4alpha/ext_authz.proto b/api/envoy/extensions/filters/http/ext_authz/v4alpha/ext_authz.proto index 72b5002379ae..ec8854f5d1be 100644 --- a/api/envoy/extensions/filters/http/ext_authz/v4alpha/ext_authz.proto +++ b/api/envoy/extensions/filters/http/ext_authz/v4alpha/ext_authz.proto @@ -6,6 +6,7 @@ import "envoy/config/core/v4alpha/base.proto"; import "envoy/config/core/v4alpha/config_source.proto"; import "envoy/config/core/v4alpha/grpc_service.proto"; import "envoy/config/core/v4alpha/http_uri.proto"; +import "envoy/type/matcher/v4alpha/metadata.proto"; import "envoy/type/matcher/v4alpha/string.proto"; import "envoy/type/v3/http_status.proto"; @@ -23,7 +24,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // External Authorization :ref:`configuration overview `. // [#extension: envoy.filters.http.ext_authz] -// [#next-free-field: 14] +// [#next-free-field: 15] message ExtAuthz { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.filters.http.ext_authz.v3.ExtAuthz"; @@ -103,6 +104,10 @@ message ExtAuthz { // If this field is not specified, the filter will be enabled for all requests. config.core.v4alpha.RuntimeFractionalPercent filter_enabled = 9; + // Specifies if the filter is enabled with metadata matcher. + // If this field is not specified, the filter will be enabled for all requests. + type.matcher.v4alpha.MetadataMatcher filter_enabled_metadata = 14; + // Specifies whether to deny the requests, when the filter is disabled. // If :ref:`runtime_key ` is specified, // Envoy will lookup the runtime key to determine whether to deny request for diff --git a/api/envoy/extensions/filters/network/ext_authz/v3/BUILD b/api/envoy/extensions/filters/network/ext_authz/v3/BUILD index a4e298b42619..a5c5b57b7227 100644 --- a/api/envoy/extensions/filters/network/ext_authz/v3/BUILD +++ b/api/envoy/extensions/filters/network/ext_authz/v3/BUILD @@ -8,6 +8,7 @@ api_proto_package( deps = [ "//envoy/config/core/v3:pkg", "//envoy/config/filter/network/ext_authz/v2:pkg", + "//envoy/type/matcher/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto b/api/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto index 4499b80cb9a5..78f4167ccc33 100644 --- a/api/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto +++ b/api/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto @@ -4,6 +4,7 @@ package envoy.extensions.filters.network.ext_authz.v3; import "envoy/config/core/v3/config_source.proto"; import "envoy/config/core/v3/grpc_service.proto"; +import "envoy/type/matcher/v3/metadata.proto"; import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; @@ -23,7 +24,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // gRPC Authorization API defined by // :ref:`CheckRequest `. // A failed check will cause this filter to close the TCP connection. -// [#next-free-field: 6] +// [#next-free-field: 7] message ExtAuthz { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.network.ext_authz.v2.ExtAuthz"; @@ -51,4 +52,8 @@ message ExtAuthz { // version of Check{Request,Response} used on the wire. config.core.v3.ApiVersion transport_api_version = 5 [(validate.rules).enum = {defined_only: true}]; + + // Specifies if the filter is enabled with metadata matcher. + // If this field is not specified, the filter will be enabled for all requests. + type.matcher.v3.MetadataMatcher filter_enabled_metadata = 6; } diff --git a/api/envoy/extensions/filters/network/ext_authz/v4alpha/BUILD b/api/envoy/extensions/filters/network/ext_authz/v4alpha/BUILD new file mode 100644 index 000000000000..6d146b1c64d1 --- /dev/null +++ b/api/envoy/extensions/filters/network/ext_authz/v4alpha/BUILD @@ -0,0 +1,14 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/core/v4alpha:pkg", + "//envoy/extensions/filters/network/ext_authz/v3:pkg", + "//envoy/type/matcher/v4alpha:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/filters/network/ext_authz/v4alpha/ext_authz.proto b/api/envoy/extensions/filters/network/ext_authz/v4alpha/ext_authz.proto new file mode 100644 index 000000000000..f877a3ed8502 --- /dev/null +++ b/api/envoy/extensions/filters/network/ext_authz/v4alpha/ext_authz.proto @@ -0,0 +1,59 @@ +syntax = "proto3"; + +package envoy.extensions.filters.network.ext_authz.v4alpha; + +import "envoy/config/core/v4alpha/config_source.proto"; +import "envoy/config/core/v4alpha/grpc_service.proto"; +import "envoy/type/matcher/v4alpha/metadata.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.network.ext_authz.v4alpha"; +option java_outer_classname = "ExtAuthzProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Network External Authorization ] +// The network layer external authorization service configuration +// :ref:`configuration overview `. +// [#extension: envoy.filters.network.ext_authz] + +// External Authorization filter calls out to an external service over the +// gRPC Authorization API defined by +// :ref:`CheckRequest `. +// A failed check will cause this filter to close the TCP connection. +// [#next-free-field: 7] +message ExtAuthz { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.network.ext_authz.v3.ExtAuthz"; + + // The prefix to use when emitting statistics. + string stat_prefix = 1 [(validate.rules).string = {min_len: 1}]; + + // The external authorization gRPC service configuration. + // The default timeout is set to 200ms by this filter. + config.core.v4alpha.GrpcService grpc_service = 2; + + // The filter's behaviour in case the external authorization service does + // not respond back. When it is set to true, Envoy will also allow traffic in case of + // communication failure between authorization service and the proxy. + // Defaults to false. + bool failure_mode_allow = 3; + + // Specifies if the peer certificate is sent to the external service. + // + // When this field is true, Envoy will include the peer X.509 certificate, if available, in the + // :ref:`certificate`. + bool include_peer_certificate = 4; + + // API version for ext_authz transport protocol. This describes the ext_authz gRPC endpoint and + // version of Check{Request,Response} used on the wire. + config.core.v4alpha.ApiVersion transport_api_version = 5 + [(validate.rules).enum = {defined_only: true}]; + + // Specifies if the filter is enabled with metadata matcher. + // If this field is not specified, the filter will be enabled for all requests. + type.matcher.v4alpha.MetadataMatcher filter_enabled_metadata = 6; +} diff --git a/docs/root/configuration/http/http_filters/ext_authz_filter.rst b/docs/root/configuration/http/http_filters/ext_authz_filter.rst index 8dff0ce46950..269789a4be66 100644 --- a/docs/root/configuration/http/http_filters/ext_authz_filter.rst +++ b/docs/root/configuration/http/http_filters/ext_authz_filter.rst @@ -168,6 +168,7 @@ The HTTP filter outputs statistics in the *cluster..ext_au error, Counter, Total errors (including timeouts) contacting the external service. timeout, Counter, Total timeouts contacting the external service (only counted when timeout is measured when check request is created). denied, Counter, Total responses from the authorizations service that were to deny the traffic. + disabled, Counter, Total requests that are allowed without calling external services due to the filter is disabled. failure_mode_allowed, Counter, "Total requests that were error(s) but were allowed through because of failure_mode_allow set to true." diff --git a/docs/root/configuration/listeners/network_filters/ext_authz_filter.rst b/docs/root/configuration/listeners/network_filters/ext_authz_filter.rst index eaa6ca5bae4c..441da8ec5c37 100644 --- a/docs/root/configuration/listeners/network_filters/ext_authz_filter.rst +++ b/docs/root/configuration/listeners/network_filters/ext_authz_filter.rst @@ -66,6 +66,7 @@ The network filter outputs statistics in the *config.ext_authz.* namespace. total, Counter, Total responses from the filter. error, Counter, Total errors contacting the external service. denied, Counter, Total responses from the authorizations service that were to deny the traffic. + disabled, Counter, Total requests that are allowed without calling external services due to the filter is disabled. failure_mode_allowed, Counter, "Total requests that were error(s) but were allowed through because of failure_mode_allow set to true." ok, Counter, Total responses from the authorization service that were to allow the traffic. diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index f19993ed5ebc..5173c659dc9c 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -103,6 +103,7 @@ New Features * ext_authz filter: added :ref:`stat_prefix ` as an optional additional prefix for the statistics emitted from `ext_authz` HTTP filter. * ext_authz filter: added support for letting the authorization server instruct Envoy to remove headers from the original request by setting the new field :ref:`headers_to_remove ` before forwarding it to the upstream. * ext_authz filter: added support for sending :ref:`raw bytes as request body ` of a gRPC check request by setting :ref:`pack_as_bytes ` to true. +* ext_authz filter: added support for enabling the filter based on :ref:`dynamic metadata `. * grpc-json: support specifying `response_body` field in for `google.api.HttpBody` message. * hds: added :ref:`cluster_endpoints_health ` to HDS responses, keeping endpoints in the same groupings as they were configured in the HDS specifier by cluster and locality instead of as a flat list. * hds: added :ref:`transport_socket_matches ` to HDS cluster health check specifier, so the existing match filter :ref:`transport_socket_match_criteria ` in the repeated field :ref:`health_checks ` has context to match against. This unblocks support for health checks over HTTPS and HTTP/2. diff --git a/generated_api_shadow/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto b/generated_api_shadow/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto index ae46303e207f..b1ab3989f20e 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto @@ -6,6 +6,7 @@ import "envoy/config/core/v3/base.proto"; import "envoy/config/core/v3/config_source.proto"; import "envoy/config/core/v3/grpc_service.proto"; import "envoy/config/core/v3/http_uri.proto"; +import "envoy/type/matcher/v3/metadata.proto"; import "envoy/type/matcher/v3/string.proto"; import "envoy/type/v3/http_status.proto"; @@ -23,7 +24,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // External Authorization :ref:`configuration overview `. // [#extension: envoy.filters.http.ext_authz] -// [#next-free-field: 14] +// [#next-free-field: 15] message ExtAuthz { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.ext_authz.v2.ExtAuthz"; @@ -99,6 +100,10 @@ message ExtAuthz { // If this field is not specified, the filter will be enabled for all requests. config.core.v3.RuntimeFractionalPercent filter_enabled = 9; + // Specifies if the filter is enabled with metadata matcher. + // If this field is not specified, the filter will be enabled for all requests. + type.matcher.v3.MetadataMatcher filter_enabled_metadata = 14; + // Specifies whether to deny the requests, when the filter is disabled. // If :ref:`runtime_key ` is specified, // Envoy will lookup the runtime key to determine whether to deny request for diff --git a/generated_api_shadow/envoy/extensions/filters/http/ext_authz/v4alpha/ext_authz.proto b/generated_api_shadow/envoy/extensions/filters/http/ext_authz/v4alpha/ext_authz.proto index 72b5002379ae..ec8854f5d1be 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/ext_authz/v4alpha/ext_authz.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/ext_authz/v4alpha/ext_authz.proto @@ -6,6 +6,7 @@ import "envoy/config/core/v4alpha/base.proto"; import "envoy/config/core/v4alpha/config_source.proto"; import "envoy/config/core/v4alpha/grpc_service.proto"; import "envoy/config/core/v4alpha/http_uri.proto"; +import "envoy/type/matcher/v4alpha/metadata.proto"; import "envoy/type/matcher/v4alpha/string.proto"; import "envoy/type/v3/http_status.proto"; @@ -23,7 +24,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // External Authorization :ref:`configuration overview `. // [#extension: envoy.filters.http.ext_authz] -// [#next-free-field: 14] +// [#next-free-field: 15] message ExtAuthz { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.filters.http.ext_authz.v3.ExtAuthz"; @@ -103,6 +104,10 @@ message ExtAuthz { // If this field is not specified, the filter will be enabled for all requests. config.core.v4alpha.RuntimeFractionalPercent filter_enabled = 9; + // Specifies if the filter is enabled with metadata matcher. + // If this field is not specified, the filter will be enabled for all requests. + type.matcher.v4alpha.MetadataMatcher filter_enabled_metadata = 14; + // Specifies whether to deny the requests, when the filter is disabled. // If :ref:`runtime_key ` is specified, // Envoy will lookup the runtime key to determine whether to deny request for diff --git a/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v3/BUILD b/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v3/BUILD index a4e298b42619..a5c5b57b7227 100644 --- a/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v3/BUILD +++ b/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v3/BUILD @@ -8,6 +8,7 @@ api_proto_package( deps = [ "//envoy/config/core/v3:pkg", "//envoy/config/filter/network/ext_authz/v2:pkg", + "//envoy/type/matcher/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto b/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto index 4499b80cb9a5..78f4167ccc33 100644 --- a/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto +++ b/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto @@ -4,6 +4,7 @@ package envoy.extensions.filters.network.ext_authz.v3; import "envoy/config/core/v3/config_source.proto"; import "envoy/config/core/v3/grpc_service.proto"; +import "envoy/type/matcher/v3/metadata.proto"; import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; @@ -23,7 +24,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // gRPC Authorization API defined by // :ref:`CheckRequest `. // A failed check will cause this filter to close the TCP connection. -// [#next-free-field: 6] +// [#next-free-field: 7] message ExtAuthz { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.network.ext_authz.v2.ExtAuthz"; @@ -51,4 +52,8 @@ message ExtAuthz { // version of Check{Request,Response} used on the wire. config.core.v3.ApiVersion transport_api_version = 5 [(validate.rules).enum = {defined_only: true}]; + + // Specifies if the filter is enabled with metadata matcher. + // If this field is not specified, the filter will be enabled for all requests. + type.matcher.v3.MetadataMatcher filter_enabled_metadata = 6; } diff --git a/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v4alpha/BUILD b/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v4alpha/BUILD new file mode 100644 index 000000000000..6d146b1c64d1 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v4alpha/BUILD @@ -0,0 +1,14 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/core/v4alpha:pkg", + "//envoy/extensions/filters/network/ext_authz/v3:pkg", + "//envoy/type/matcher/v4alpha:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v4alpha/ext_authz.proto b/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v4alpha/ext_authz.proto new file mode 100644 index 000000000000..f877a3ed8502 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/network/ext_authz/v4alpha/ext_authz.proto @@ -0,0 +1,59 @@ +syntax = "proto3"; + +package envoy.extensions.filters.network.ext_authz.v4alpha; + +import "envoy/config/core/v4alpha/config_source.proto"; +import "envoy/config/core/v4alpha/grpc_service.proto"; +import "envoy/type/matcher/v4alpha/metadata.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.network.ext_authz.v4alpha"; +option java_outer_classname = "ExtAuthzProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Network External Authorization ] +// The network layer external authorization service configuration +// :ref:`configuration overview `. +// [#extension: envoy.filters.network.ext_authz] + +// External Authorization filter calls out to an external service over the +// gRPC Authorization API defined by +// :ref:`CheckRequest `. +// A failed check will cause this filter to close the TCP connection. +// [#next-free-field: 7] +message ExtAuthz { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.network.ext_authz.v3.ExtAuthz"; + + // The prefix to use when emitting statistics. + string stat_prefix = 1 [(validate.rules).string = {min_len: 1}]; + + // The external authorization gRPC service configuration. + // The default timeout is set to 200ms by this filter. + config.core.v4alpha.GrpcService grpc_service = 2; + + // The filter's behaviour in case the external authorization service does + // not respond back. When it is set to true, Envoy will also allow traffic in case of + // communication failure between authorization service and the proxy. + // Defaults to false. + bool failure_mode_allow = 3; + + // Specifies if the peer certificate is sent to the external service. + // + // When this field is true, Envoy will include the peer X.509 certificate, if available, in the + // :ref:`certificate`. + bool include_peer_certificate = 4; + + // API version for ext_authz transport protocol. This describes the ext_authz gRPC endpoint and + // version of Check{Request,Response} used on the wire. + config.core.v4alpha.ApiVersion transport_api_version = 5 + [(validate.rules).enum = {defined_only: true}]; + + // Specifies if the filter is enabled with metadata matcher. + // If this field is not specified, the filter will be enabled for all requests. + type.matcher.v4alpha.MetadataMatcher filter_enabled_metadata = 6; +} diff --git a/source/extensions/filters/http/ext_authz/ext_authz.cc b/source/extensions/filters/http/ext_authz/ext_authz.cc index 173e6ca7ef99..cb3bd5c491ad 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.cc +++ b/source/extensions/filters/http/ext_authz/ext_authz.cc @@ -4,6 +4,7 @@ #include "common/common/assert.h" #include "common/common/enum_to_int.h" +#include "common/common/matchers.h" #include "common/http/utility.h" #include "common/router/config_impl.h" @@ -80,11 +81,12 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, Router::RouteConstSharedPtr route = callbacks_->route(); const auto per_route_flags = getPerRouteFlags(route); skip_check_ = per_route_flags.skip_check_; + if (skip_check_) { + return Http::FilterHeadersStatus::Continue; + } - if (!config_->filterEnabled() || skip_check_) { - if (skip_check_) { - return Http::FilterHeadersStatus::Continue; - } + if (!config_->filterEnabled(callbacks_->streamInfo().dynamicMetadata())) { + stats_.disabled_.inc(); if (config_->denyAtDisable()) { ENVOY_STREAM_LOG(trace, "ext_authz filter is disabled. Deny the request.", *callbacks_); callbacks_->streamInfo().setResponseFlag( diff --git a/source/extensions/filters/http/ext_authz/ext_authz.h b/source/extensions/filters/http/ext_authz/ext_authz.h index 5a906913fb01..6dd15619df42 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.h +++ b/source/extensions/filters/http/ext_authz/ext_authz.h @@ -38,6 +38,7 @@ namespace ExtAuthz { COUNTER(denied) \ COUNTER(error) \ COUNTER(timeout) \ + COUNTER(disabled) \ COUNTER(failure_mode_allowed) /** @@ -66,6 +67,10 @@ class FilterConfig { ? absl::optional( Runtime::FractionalPercent(config.filter_enabled(), runtime_)) : absl::nullopt), + filter_enabled_metadata_( + config.has_filter_enabled_metadata() + ? absl::optional(config.filter_enabled_metadata()) + : absl::nullopt), deny_at_disable_(config.has_deny_at_disable() ? absl::optional( Runtime::FeatureFlag(config.deny_at_disable(), runtime_)) @@ -96,7 +101,12 @@ class FilterConfig { Http::Code statusOnError() const { return status_on_error_; } - bool filterEnabled() { return filter_enabled_.has_value() ? filter_enabled_->enabled() : true; } + bool filterEnabled(const envoy::config::core::v3::Metadata& metadata) { + const bool enabled = filter_enabled_.has_value() ? filter_enabled_->enabled() : true; + const bool enabled_metadata = + filter_enabled_metadata_.has_value() ? filter_enabled_metadata_->match(metadata) : true; + return enabled && enabled_metadata; + } bool denyAtDisable() { return deny_at_disable_.has_value() ? deny_at_disable_->enabled() : false; @@ -154,6 +164,7 @@ class FilterConfig { Http::Context& http_context_; const absl::optional filter_enabled_; + const absl::optional filter_enabled_metadata_; const absl::optional deny_at_disable_; // TODO(nezdolik): stop using pool as part of deprecating cluster scope stats. diff --git a/source/extensions/filters/network/ext_authz/BUILD b/source/extensions/filters/network/ext_authz/BUILD index ebc6847e28f6..3ef4a4738a3d 100644 --- a/source/extensions/filters/network/ext_authz/BUILD +++ b/source/extensions/filters/network/ext_authz/BUILD @@ -23,6 +23,7 @@ envoy_cc_library( "//include/envoy/stats:stats_macros", "//include/envoy/upstream:cluster_manager_interface", "//source/common/common:assert_lib", + "//source/common/common:matchers_lib", "//source/common/tracing:http_tracer_lib", "//source/extensions/filters/common/ext_authz:ext_authz_grpc_lib", "//source/extensions/filters/common/ext_authz:ext_authz_interface", diff --git a/source/extensions/filters/network/ext_authz/ext_authz.cc b/source/extensions/filters/network/ext_authz/ext_authz.cc index bf0fa8fef154..0ea178650d84 100644 --- a/source/extensions/filters/network/ext_authz/ext_authz.cc +++ b/source/extensions/filters/network/ext_authz/ext_authz.cc @@ -37,6 +37,11 @@ void Filter::callCheck() { } Network::FilterStatus Filter::onData(Buffer::Instance&, bool /* end_stream */) { + if (!filterEnabled(filter_callbacks_->connection().streamInfo().dynamicMetadata())) { + config_->stats().disabled_.inc(); + return Network::FilterStatus::Continue; + } + if (status_ == Status::NotStarted) { // By waiting to invoke the check at onData() the call to authorization service will have // sufficient information to fill out the checkRequest_. diff --git a/source/extensions/filters/network/ext_authz/ext_authz.h b/source/extensions/filters/network/ext_authz/ext_authz.h index 18e30ba197f6..73fa9c0df5d8 100644 --- a/source/extensions/filters/network/ext_authz/ext_authz.h +++ b/source/extensions/filters/network/ext_authz/ext_authz.h @@ -14,6 +14,8 @@ #include "envoy/stats/stats_macros.h" #include "envoy/upstream/cluster_manager.h" +#include "common/common/matchers.h" + #include "extensions/filters/common/ext_authz/ext_authz.h" #include "extensions/filters/common/ext_authz/ext_authz_grpc_impl.h" @@ -33,6 +35,7 @@ namespace ExtAuthz { COUNTER(failure_mode_allowed) \ COUNTER(ok) \ COUNTER(total) \ + COUNTER(disabled) \ GAUGE(active, Accumulate) /** @@ -51,18 +54,26 @@ class Config { Stats::Scope& scope) : stats_(generateStats(config.stat_prefix(), scope)), failure_mode_allow_(config.failure_mode_allow()), - include_peer_certificate_(config.include_peer_certificate()) {} + include_peer_certificate_(config.include_peer_certificate()), + filter_enabled_metadata_( + config.has_filter_enabled_metadata() + ? absl::optional(config.filter_enabled_metadata()) + : absl::nullopt) {} const InstanceStats& stats() { return stats_; } bool failureModeAllow() const { return failure_mode_allow_; } void setFailModeAllow(bool value) { failure_mode_allow_ = value; } bool includePeerCertificate() const { return include_peer_certificate_; } + bool filterEnabledMetadata(const envoy::config::core::v3::Metadata& metadata) const { + return filter_enabled_metadata_.has_value() ? filter_enabled_metadata_->match(metadata) : true; + } private: static InstanceStats generateStats(const std::string& name, Stats::Scope& scope); const InstanceStats stats_; bool failure_mode_allow_; const bool include_peer_certificate_; + const absl::optional filter_enabled_metadata_; }; using ConfigSharedPtr = std::shared_ptr; @@ -108,6 +119,10 @@ class Filter : public Network::ReadFilter, enum class FilterReturn { Stop, Continue }; void callCheck(); + bool filterEnabled(const envoy::config::core::v3::Metadata& metadata) { + return config_->filterEnabledMetadata(metadata); + } + ConfigSharedPtr config_; Filters::Common::ExtAuthz::ClientPtr client_; Network::ReadFilterCallbacks* filter_callbacks_{}; diff --git a/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc index 91b7623d4d54..9828b56e5b22 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc @@ -44,8 +44,8 @@ class ExtAuthzGrpcIntegrationTest : public Grpc::VersionedGrpcClientIntegrationP addFakeUpstream(FakeHttpConnection::Type::HTTP2); } - void initializeConfig(bool with_timeout = false) { - config_helper_.addConfigModifier([this, with_timeout]( + void initializeConfig(bool with_timeout = false, bool disable_with_metadata = false) { + config_helper_.addConfigModifier([this, with_timeout, disable_with_metadata]( envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto* ext_authz_cluster = bootstrap.mutable_static_resources()->add_clusters(); ext_authz_cluster->MergeFrom(bootstrap.static_resources().clusters()[0]); @@ -63,6 +63,13 @@ class ExtAuthzGrpcIntegrationTest : public Grpc::VersionedGrpcClientIntegrationP proto_config_.mutable_filter_enabled()->set_runtime_key("envoy.ext_authz.enable"); proto_config_.mutable_filter_enabled()->mutable_default_value()->set_numerator(100); + if (disable_with_metadata) { + // Disable the ext_authz filter with metadata matcher that never matches. + auto* metadata = proto_config_.mutable_filter_enabled_metadata(); + metadata->set_filter("xyz.abc"); + metadata->add_path()->set_key("k1"); + metadata->mutable_value()->mutable_string_match()->set_exact("never_matched"); + } proto_config_.mutable_deny_at_disable()->set_runtime_key("envoy.ext_authz.deny_at_disable"); proto_config_.mutable_deny_at_disable()->mutable_default_value()->set_value(false); proto_config_.set_transport_api_version(apiVersion()); @@ -74,8 +81,10 @@ class ExtAuthzGrpcIntegrationTest : public Grpc::VersionedGrpcClientIntegrationP }); } - void setDenyAtDisableRuntimeConfig(bool deny_at_disable) { - config_helper_.addRuntimeOverride("envoy.ext_authz.enable", "numerator: 0"); + void setDenyAtDisableRuntimeConfig(bool deny_at_disable, bool disable_with_metadata) { + if (!disable_with_metadata) { + config_helper_.addRuntimeOverride("envoy.ext_authz.enable", "numerator: 0"); + } if (deny_at_disable) { config_helper_.addRuntimeOverride("envoy.ext_authz.deny_at_disable", "true"); } else { @@ -391,9 +400,10 @@ class ExtAuthzGrpcIntegrationTest : public Grpc::VersionedGrpcClientIntegrationP cleanup(); } - void expectFilterDisableCheck(bool deny_at_disable, const std::string& expected_status) { - initializeConfig(); - setDenyAtDisableRuntimeConfig(deny_at_disable); + void expectFilterDisableCheck(bool deny_at_disable, bool disable_with_metadata, + const std::string& expected_status) { + initializeConfig(false, disable_with_metadata); + setDenyAtDisableRuntimeConfig(deny_at_disable, disable_with_metadata); setDownstreamProtocol(Http::CodecClient::Type::HTTP2); HttpIntegrationTest::initialize(); initiateClientConnection(4); @@ -663,9 +673,21 @@ TEST_P(ExtAuthzGrpcIntegrationTest, CheckTimesOutFromCheckCreation) { expectCheckRequestTimedout(true); } -TEST_P(ExtAuthzGrpcIntegrationTest, AllowAtDisable) { expectFilterDisableCheck(false, "200"); } +TEST_P(ExtAuthzGrpcIntegrationTest, AllowAtDisable) { + expectFilterDisableCheck(/*deny_at_disable=*/false, /*disable_with_metadata=*/false, "200"); +} -TEST_P(ExtAuthzGrpcIntegrationTest, DenyAtDisable) { expectFilterDisableCheck(true, "403"); } +TEST_P(ExtAuthzGrpcIntegrationTest, AllowAtDisableWithMetadata) { + expectFilterDisableCheck(/*deny_at_disable=*/false, /*disable_with_metadata=*/true, "200"); +} + +TEST_P(ExtAuthzGrpcIntegrationTest, DenyAtDisable) { + expectFilterDisableCheck(/*deny_at_disable=*/true, /*disable_with_metadata=*/false, "403"); +} + +TEST_P(ExtAuthzGrpcIntegrationTest, DenyAtDisableWithMetadata) { + expectFilterDisableCheck(/*deny_at_disable=*/true, /*disable_with_metadata=*/true, "403"); +} INSTANTIATE_TEST_SUITE_P(IpVersions, ExtAuthzHttpIntegrationTest, ValuesIn(TestEnvironment::getIpVersionsForTest()), diff --git a/test/extensions/filters/http/ext_authz/ext_authz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_test.cc index d9f259a04dba..fdedea7546f1 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -1157,6 +1157,203 @@ TEST_F(HttpFilterTest, FilterEnabled) { filter_->decodeHeaders(request_headers_, false)); } +// Test that filter can be disabled via the filter_enabled_metadata field. +TEST_F(HttpFilterTest, MetadataDisabled) { + initialize(R"EOF( + grpc_service: + envoy_grpc: + cluster_name: "ext_authz_server" + filter_enabled_metadata: + filter: "abc.xyz" + path: + - key: "k1" + value: + string_match: + exact: "check" + )EOF"); + + // Disable in filter_enabled. + const std::string yaml = R"EOF( + filter_metadata: + abc.xyz: + k1: skip + )EOF"; + envoy::config::core::v3::Metadata metadata; + TestUtility::loadFromYaml(yaml, metadata); + ON_CALL(filter_callbacks_.stream_info_, dynamicMetadata()).WillByDefault(ReturnRef(metadata)); + + // Make sure check is not called. + EXPECT_CALL(*client_, check(_, _, _, _, _)).Times(0); + // Engage the filter. + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); +} + +// Test that filter can be enabled via the filter_enabled_metadata field. +TEST_F(HttpFilterTest, MetadataEnabled) { + initialize(R"EOF( + grpc_service: + envoy_grpc: + cluster_name: "ext_authz_server" + filter_enabled_metadata: + filter: "abc.xyz" + path: + - key: "k1" + value: + string_match: + exact: "check" + )EOF"); + + // Enable in filter_enabled. + const std::string yaml = R"EOF( + filter_metadata: + abc.xyz: + k1: check + )EOF"; + envoy::config::core::v3::Metadata metadata; + TestUtility::loadFromYaml(yaml, metadata); + ON_CALL(filter_callbacks_.stream_info_, dynamicMetadata()).WillByDefault(ReturnRef(metadata)); + + prepareCheck(); + + // Make sure check is called once. + EXPECT_CALL(*client_, check(_, _, _, _, _)).Times(1); + // Engage the filter. + EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, + filter_->decodeHeaders(request_headers_, false)); +} + +// Test that the filter is disabled if one of the filter_enabled and filter_enabled_metadata field +// is disabled. +TEST_F(HttpFilterTest, FilterEnabledButMetadataDisabled) { + initialize(R"EOF( + grpc_service: + envoy_grpc: + cluster_name: "ext_authz_server" + filter_enabled: + runtime_key: "http.ext_authz.enabled" + default_value: + numerator: 100 + denominator: HUNDRED + filter_enabled_metadata: + filter: "abc.xyz" + path: + - key: "k1" + value: + string_match: + exact: "check" + )EOF"); + + // Enable in filter_enabled. + ON_CALL(runtime_.snapshot_, + featureEnabled("http.ext_authz.enabled", + testing::Matcher(Percent(100)))) + .WillByDefault(Return(true)); + + // Disable in filter_enabled_metadata. + const std::string yaml = R"EOF( + filter_metadata: + abc.xyz: + k1: skip + )EOF"; + envoy::config::core::v3::Metadata metadata; + TestUtility::loadFromYaml(yaml, metadata); + ON_CALL(filter_callbacks_.stream_info_, dynamicMetadata()).WillByDefault(ReturnRef(metadata)); + + // Make sure check is not called. + EXPECT_CALL(*client_, check(_, _, _, _, _)).Times(0); + // Engage the filter. + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); +} + +// Test that the filter is disabled if one of the filter_enabled and filter_enabled_metadata field +// is disabled. +TEST_F(HttpFilterTest, FilterDisabledButMetadataEnabled) { + initialize(R"EOF( + grpc_service: + envoy_grpc: + cluster_name: "ext_authz_server" + filter_enabled: + runtime_key: "http.ext_authz.enabled" + default_value: + numerator: 0 + denominator: HUNDRED + filter_enabled_metadata: + filter: "abc.xyz" + path: + - key: "k1" + value: + string_match: + exact: "check" + )EOF"); + + // Disable in filter_enabled. + ON_CALL(runtime_.snapshot_, + featureEnabled("http.ext_authz.enabled", + testing::Matcher(Percent(0)))) + .WillByDefault(Return(false)); + + // Enable in filter_enabled_metadata. + const std::string yaml = R"EOF( + filter_metadata: + abc.xyz: + k1: check + )EOF"; + envoy::config::core::v3::Metadata metadata; + TestUtility::loadFromYaml(yaml, metadata); + ON_CALL(filter_callbacks_.stream_info_, dynamicMetadata()).WillByDefault(ReturnRef(metadata)); + + // Make sure check is not called. + EXPECT_CALL(*client_, check(_, _, _, _, _)).Times(0); + // Engage the filter. + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); +} + +// Test that the filter is enabled if both the filter_enabled and filter_enabled_metadata field +// is enabled. +TEST_F(HttpFilterTest, FilterEnabledAndMetadataEnabled) { + initialize(R"EOF( + grpc_service: + envoy_grpc: + cluster_name: "ext_authz_server" + filter_enabled: + runtime_key: "http.ext_authz.enabled" + default_value: + numerator: 100 + denominator: HUNDRED + filter_enabled_metadata: + filter: "abc.xyz" + path: + - key: "k1" + value: + string_match: + exact: "check" + )EOF"); + + // Enable in filter_enabled. + ON_CALL(runtime_.snapshot_, + featureEnabled("http.ext_authz.enabled", + testing::Matcher(Percent(100)))) + .WillByDefault(Return(true)); + + // Enable in filter_enabled_metadata. + const std::string yaml = R"EOF( + filter_metadata: + abc.xyz: + k1: check + )EOF"; + envoy::config::core::v3::Metadata metadata; + TestUtility::loadFromYaml(yaml, metadata); + ON_CALL(filter_callbacks_.stream_info_, dynamicMetadata()).WillByDefault(ReturnRef(metadata)); + + prepareCheck(); + + // Make sure check is called once. + EXPECT_CALL(*client_, check(_, _, _, _, _)).Times(1); + // Engage the filter. + EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, + filter_->decodeHeaders(request_headers_, false)); +} + // Test that filter can deny for protected path when filter is disabled via filter_enabled field. TEST_F(HttpFilterTest, FilterDenyAtDisable) { initialize(R"EOF( diff --git a/test/extensions/filters/network/ext_authz/ext_authz_test.cc b/test/extensions/filters/network/ext_authz/ext_authz_test.cc index b8fd75767e21..e3dab5cdd2f2 100644 --- a/test/extensions/filters/network/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/network/ext_authz/ext_authz_test.cc @@ -37,11 +37,9 @@ namespace ExtAuthz { class ExtAuthzFilterTest : public testing::Test { public: - ExtAuthzFilterTest() { initialize(); } - - void initialize() { + void initialize(std::string yaml) { envoy::extensions::filters::network::ext_authz::v3::ExtAuthz proto_config{}; - TestUtility::loadFromYaml(default_yaml_string_, proto_config); + TestUtility::loadFromYaml(yaml, proto_config); config_ = std::make_shared(proto_config, stats_store_); client_ = new Filters::Common::ExtAuthz::MockClient(); filter_ = std::make_unique(config_, Filters::Common::ExtAuthz::ClientPtr{client_}); @@ -67,6 +65,63 @@ class ExtAuthzFilterTest : public testing::Test { } } + void expectOKWithOnData() { + EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); + EXPECT_CALL(filter_callbacks_.connection_, localAddress()).WillOnce(ReturnRef(addr_)); + EXPECT_CALL(*client_, check(_, _, _, testing::A(), _)) + .WillOnce( + WithArgs<0>(Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks) -> void { + request_callbacks_ = &callbacks; + }))); + + EXPECT_EQ(Network::FilterStatus::Continue, filter_->onNewConnection()); + // Confirm that the invocation of onNewConnection did NOT increment the active or total count! + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.total").value()); + EXPECT_EQ( + 0U, + stats_store_.gauge("ext_authz.name.active", Stats::Gauge::ImportMode::Accumulate).value()); + Buffer::OwnedImpl data("hello"); + EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data, false)); + // Confirm that the invocation of onData does increment the active and total count! + EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); + EXPECT_EQ( + 1U, + stats_store_.gauge("ext_authz.name.active", Stats::Gauge::ImportMode::Accumulate).value()); + + Filters::Common::ExtAuthz::Response response{}; + response.status = Filters::Common::ExtAuthz::CheckStatus::OK; + response.headers_to_set = Http::HeaderVector{{Http::LowerCaseString{"foo"}, "bar"}}; + + auto* fields = response.dynamic_metadata.mutable_fields(); + (*fields)["foo"] = ValueUtil::stringValue("ok"); + (*fields)["bar"] = ValueUtil::numberValue(1); + + EXPECT_CALL(filter_callbacks_.connection_.stream_info_, setDynamicMetadata(_, _)) + .WillOnce(Invoke([&response](const std::string& ns, + const ProtobufWkt::Struct& returned_dynamic_metadata) { + EXPECT_EQ(ns, NetworkFilterNames::get().ExtAuthorization); + EXPECT_TRUE( + TestUtility::protoEqual(returned_dynamic_metadata, response.dynamic_metadata)); + })); + + EXPECT_CALL(filter_callbacks_, continueReading()); + request_callbacks_->onComplete(std::make_unique(response)); + + EXPECT_EQ(Network::FilterStatus::Continue, filter_->onData(data, false)); + + EXPECT_CALL(*client_, cancel()).Times(0); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); + + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); + EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.error").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.failure_mode_allowed").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.denied").value()); + EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.ok").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.cx_closed").value()); + } + Stats::TestUtil::TestStore stats_store_; ConfigSharedPtr config_; Filters::Common::ExtAuthz::MockClient* client_; @@ -82,6 +137,20 @@ class ExtAuthzFilterTest : public testing::Test { failure_mode_allow: true stat_prefix: name )EOF"; + const std::string metadata_yaml_string_ = R"EOF( +grpc_service: + envoy_grpc: + cluster_name: ext_authz_server +failure_mode_allow: true +stat_prefix: name +filter_enabled_metadata: + filter: "abc.xyz" + path: + - key: "k1" + value: + string_match: + exact: "check" + )EOF"; }; TEST_F(ExtAuthzFilterTest, BadExtAuthzConfig) { @@ -100,61 +169,12 @@ stat_prefix: name } TEST_F(ExtAuthzFilterTest, OKWithOnData) { - EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); - EXPECT_CALL(filter_callbacks_.connection_, localAddress()).WillOnce(ReturnRef(addr_)); - EXPECT_CALL(*client_, check(_, _, _, testing::A(), _)) - .WillOnce( - WithArgs<0>(Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks) -> void { - request_callbacks_ = &callbacks; - }))); - - EXPECT_EQ(Network::FilterStatus::Continue, filter_->onNewConnection()); - // Confirm that the invocation of onNewConnection did NOT increment the active or total count! - EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.total").value()); - EXPECT_EQ( - 0U, - stats_store_.gauge("ext_authz.name.active", Stats::Gauge::ImportMode::Accumulate).value()); - Buffer::OwnedImpl data("hello"); - EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data, false)); - // Confirm that the invocation of onData does increment the active and total count! - EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); - EXPECT_EQ( - 1U, - stats_store_.gauge("ext_authz.name.active", Stats::Gauge::ImportMode::Accumulate).value()); - - Filters::Common::ExtAuthz::Response response{}; - response.status = Filters::Common::ExtAuthz::CheckStatus::OK; - response.headers_to_set = Http::HeaderVector{{Http::LowerCaseString{"foo"}, "bar"}}; - - auto* fields = response.dynamic_metadata.mutable_fields(); - (*fields)["foo"] = ValueUtil::stringValue("ok"); - (*fields)["bar"] = ValueUtil::numberValue(1); - - EXPECT_CALL(filter_callbacks_.connection_.stream_info_, setDynamicMetadata(_, _)) - .WillOnce(Invoke([&response](const std::string& ns, - const ProtobufWkt::Struct& returned_dynamic_metadata) { - EXPECT_EQ(ns, NetworkFilterNames::get().ExtAuthorization); - EXPECT_TRUE(TestUtility::protoEqual(returned_dynamic_metadata, response.dynamic_metadata)); - })); - - EXPECT_CALL(filter_callbacks_, continueReading()); - request_callbacks_->onComplete(std::make_unique(response)); - - EXPECT_EQ(Network::FilterStatus::Continue, filter_->onData(data, false)); - - EXPECT_CALL(*client_, cancel()).Times(0); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); - - EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); - EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.error").value()); - EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); - EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.failure_mode_allowed").value()); - EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.denied").value()); - EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.ok").value()); - EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.cx_closed").value()); + initialize(default_yaml_string_); + expectOKWithOnData(); } TEST_F(ExtAuthzFilterTest, DeniedWithOnData) { + initialize(default_yaml_string_); InSequence s; EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); @@ -185,6 +205,7 @@ TEST_F(ExtAuthzFilterTest, DeniedWithOnData) { EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data, false)); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.error").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); @@ -195,6 +216,7 @@ TEST_F(ExtAuthzFilterTest, DeniedWithOnData) { } TEST_F(ExtAuthzFilterTest, FailOpen) { + initialize(default_yaml_string_); InSequence s; EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); @@ -216,6 +238,7 @@ TEST_F(ExtAuthzFilterTest, FailOpen) { EXPECT_EQ(Network::FilterStatus::Continue, filter_->onData(data, false)); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.error").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); @@ -226,6 +249,7 @@ TEST_F(ExtAuthzFilterTest, FailOpen) { } TEST_F(ExtAuthzFilterTest, FailClose) { + initialize(default_yaml_string_); InSequence s; // Explicitly set the failure_mode_allow to false. config_->setFailModeAllow(false); @@ -246,6 +270,7 @@ TEST_F(ExtAuthzFilterTest, FailClose) { EXPECT_CALL(filter_callbacks_, continueReading()).Times(0); request_callbacks_->onComplete(makeAuthzResponse(Filters::Common::ExtAuthz::CheckStatus::Error)); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.error").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); @@ -258,6 +283,7 @@ TEST_F(ExtAuthzFilterTest, FailClose) { // Test to verify that when callback from the authorization service has completed the filter // does not invoke Cancel on RemoteClose event. TEST_F(ExtAuthzFilterTest, DoNotCallCancelonRemoteClose) { + initialize(default_yaml_string_); InSequence s; EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); @@ -280,6 +306,7 @@ TEST_F(ExtAuthzFilterTest, DoNotCallCancelonRemoteClose) { EXPECT_CALL(*client_, cancel()).Times(0); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.error").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); @@ -292,6 +319,7 @@ TEST_F(ExtAuthzFilterTest, DoNotCallCancelonRemoteClose) { // Test to verify that Cancel is invoked when a RemoteClose event occurs while the call // to the authorization service was in progress. TEST_F(ExtAuthzFilterTest, VerifyCancelOnRemoteClose) { + initialize(default_yaml_string_); InSequence s; EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); @@ -309,6 +337,7 @@ TEST_F(ExtAuthzFilterTest, VerifyCancelOnRemoteClose) { EXPECT_CALL(*client_, cancel()); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.error").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); @@ -321,6 +350,7 @@ TEST_F(ExtAuthzFilterTest, VerifyCancelOnRemoteClose) { // Test to verify that on stack response from the authorization service does NOT // result in calling cancel. TEST_F(ExtAuthzFilterTest, ImmediateOK) { + initialize(default_yaml_string_); InSequence s; EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); @@ -340,6 +370,7 @@ TEST_F(ExtAuthzFilterTest, ImmediateOK) { EXPECT_CALL(*client_, cancel()).Times(0); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.error").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); @@ -352,6 +383,7 @@ TEST_F(ExtAuthzFilterTest, ImmediateOK) { // Test to verify that on stack denied response from the authorization service does // result in stoppage of the filter chain. TEST_F(ExtAuthzFilterTest, ImmediateNOK) { + initialize(default_yaml_string_); InSequence s; EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); @@ -367,6 +399,7 @@ TEST_F(ExtAuthzFilterTest, ImmediateNOK) { Buffer::OwnedImpl data("hello"); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data, false)); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.error").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); @@ -379,6 +412,7 @@ TEST_F(ExtAuthzFilterTest, ImmediateNOK) { // Test to verify that on stack Error response when failure_mode_allow is configured // result in request being allowed. TEST_F(ExtAuthzFilterTest, ImmediateErrorFailOpen) { + initialize(default_yaml_string_); InSequence s; EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); @@ -398,6 +432,7 @@ TEST_F(ExtAuthzFilterTest, ImmediateErrorFailOpen) { EXPECT_CALL(*client_, cancel()).Times(0); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.error").value()); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); @@ -409,6 +444,7 @@ TEST_F(ExtAuthzFilterTest, ImmediateErrorFailOpen) { // Test to verify that timeout the proper stat is incremented. TEST_F(ExtAuthzFilterTest, TimeoutError) { + initialize(default_yaml_string_); InSequence s; EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); @@ -430,6 +466,7 @@ TEST_F(ExtAuthzFilterTest, TimeoutError) { EXPECT_CALL(*client_, cancel()).Times(0); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.total").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.error").value()); EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.timeout").value()); @@ -439,6 +476,57 @@ TEST_F(ExtAuthzFilterTest, TimeoutError) { EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.cx_closed").value()); } +// Test to verify the filter is disabled with metadata. +TEST_F(ExtAuthzFilterTest, DisabledWithMetadata) { + initialize(metadata_yaml_string_); + + // Disable in filter_enabled_metadata. + const std::string yaml = R"EOF( + filter_metadata: + abc.xyz: + k1: skip + )EOF"; + envoy::config::core::v3::Metadata metadata; + TestUtility::loadFromYaml(yaml, metadata); + ON_CALL(filter_callbacks_.connection_.stream_info_, dynamicMetadata()) + .WillByDefault(ReturnRef(metadata)); + + EXPECT_EQ(Network::FilterStatus::Continue, filter_->onNewConnection()); + Buffer::OwnedImpl data("hello"); + EXPECT_EQ(Network::FilterStatus::Continue, filter_->onData(data, false)); + + EXPECT_CALL(*client_, check(_, _, _, _, _)).Times(0); + EXPECT_CALL(filter_callbacks_.connection_, close(_)).Times(0); + EXPECT_CALL(*client_, cancel()).Times(0); + + EXPECT_EQ(1U, stats_store_.counter("ext_authz.name.disabled").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.total").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.error").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.timeout").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.failure_mode_allowed").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.denied").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.ok").value()); + EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.cx_closed").value()); +} + +// Test to verify the filter is enabled with metadata. +TEST_F(ExtAuthzFilterTest, EnabledWithMetadata) { + initialize(metadata_yaml_string_); + + // Enable in filter_enabled_metadata. + const std::string yaml = R"EOF( + filter_metadata: + abc.xyz: + k1: check + )EOF"; + envoy::config::core::v3::Metadata metadata; + TestUtility::loadFromYaml(yaml, metadata); + ON_CALL(filter_callbacks_.connection_.stream_info_, dynamicMetadata()) + .WillByDefault(ReturnRef(metadata)); + + expectOKWithOnData(); +} + } // namespace ExtAuthz } // namespace NetworkFilters } // namespace Extensions From 3fac88f4a05d5abf794c818c9f7ba9b2ded09ed8 Mon Sep 17 00:00:00 2001 From: Teju Nareddy Date: Tue, 6 Oct 2020 18:26:09 -0400 Subject: [PATCH 11/27] router: add benchmarks for route matching (#13320) Signed-off-by: Teju Nareddy --- test/benchmark/BUILD | 1 + test/benchmark/main.cc | 6 + test/common/router/BUILD | 23 +++ test/common/router/config_impl_speed_test.cc | 149 +++++++++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 test/common/router/config_impl_speed_test.cc diff --git a/test/benchmark/BUILD b/test/benchmark/BUILD index 28f9d26d032f..7d14fd5f4cb7 100644 --- a/test/benchmark/BUILD +++ b/test/benchmark/BUILD @@ -18,6 +18,7 @@ envoy_cc_test_library( ], deps = [ "//source/common/common:minimal_logger_lib", + "//source/common/common:thread_lib", "//test/test_common:environment_lib", "//test/test_common:printers_lib", "//test/test_common:test_runtime_lib", diff --git a/test/benchmark/main.cc b/test/benchmark/main.cc index 8c37a64836a3..ee248c84cd85 100644 --- a/test/benchmark/main.cc +++ b/test/benchmark/main.cc @@ -3,6 +3,7 @@ #include "test/benchmark/main.h" #include "common/common/logger.h" +#include "common/common/thread.h" #include "test/test_common/environment.h" #include "test/test_common/test_runtime.h" @@ -46,6 +47,11 @@ int main(int argc, char** argv) { return 0; } + // Reduce logs so benchmark output is readable. + Thread::MutexBasicLockable lock; + Logger::Context logging_context{spdlog::level::warn, Logger::Context::getFancyLogFormat(), lock, + false}; + skip_expensive_benchmarks = skip_switch.getValue(); // Initialize scoped_runtime if a runtime_feature argument is present. This diff --git a/test/common/router/BUILD b/test/common/router/BUILD index 0e75ddc0fbd4..51121e73b8a4 100644 --- a/test/common/router/BUILD +++ b/test/common/router/BUILD @@ -1,5 +1,6 @@ load( "//bazel:envoy_build_system.bzl", + "envoy_benchmark_test", "envoy_cc_benchmark_binary", "envoy_cc_fuzz_test", "envoy_cc_test", @@ -402,3 +403,25 @@ envoy_proto_library( "@envoy_api//envoy/extensions/filters/http/router/v3:pkg", ], ) + +envoy_cc_benchmark_binary( + name = "config_impl_speed_test", + srcs = ["config_impl_speed_test.cc"], + external_deps = [ + "benchmark", + ], + deps = [ + "//source/common/common:assert_lib", + "//source/common/router:config_lib", + "//test/mocks/server:instance_mocks", + "//test/mocks/stream_info:stream_info_mocks", + "//test/test_common:test_runtime_lib", + "//test/test_common:utility_lib", + "@envoy_api//envoy/config/route/v3:pkg_cc_proto", + ], +) + +envoy_benchmark_test( + name = "config_impl_benchmark_test", + benchmark_binary = "config_impl_speed_test", +) diff --git a/test/common/router/config_impl_speed_test.cc b/test/common/router/config_impl_speed_test.cc new file mode 100644 index 000000000000..a4192eb487f2 --- /dev/null +++ b/test/common/router/config_impl_speed_test.cc @@ -0,0 +1,149 @@ +#include "envoy/config/route/v3/route.pb.h" +#include "envoy/config/route/v3/route.pb.validate.h" + +#include "common/common/assert.h" +#include "common/router/config_impl.h" + +#include "test/mocks/server/instance.h" +#include "test/mocks/stream_info/mocks.h" +#include "test/test_common/test_runtime.h" +#include "test/test_common/utility.h" + +#include "benchmark/benchmark.h" +#include "gmock/gmock.h" + +namespace Envoy { +namespace Router { +namespace { + +using envoy::config::route::v3::DirectResponseAction; +using envoy::config::route::v3::Route; +using envoy::config::route::v3::RouteConfiguration; +using envoy::config::route::v3::RouteMatch; +using envoy::config::route::v3::VirtualHost; +using testing::NiceMock; +using testing::ReturnRef; + +/** + * Generates a request with the path: + * - /shelves/shelf_x/route_x + */ +static Http::TestRequestHeaderMapImpl genRequestHeaders(int route_num) { + return Http::TestRequestHeaderMapImpl{ + {":authority", "www.google.com"}, + {":method", "GET"}, + {":path", absl::StrCat("/shelves/shelf_", route_num, "/route_", route_num)}, + {"x-forwarded-proto", "http"}}; +} + +/** + * Generates the route config for the type of matcher being tested. + */ +static RouteConfiguration genRouteConfig(benchmark::State& state, + RouteMatch::PathSpecifierCase match_type) { + // Create the base route config. + RouteConfiguration route_config; + VirtualHost* v_host = route_config.add_virtual_hosts(); + v_host->set_name("default"); + v_host->add_domains("*"); + + // Create `n` regex routes. The last route will be the only one matched. + for (int i = 0; i < state.range(0); ++i) { + Route* route = v_host->add_routes(); + DirectResponseAction* direct_response = route->mutable_direct_response(); + direct_response->set_status(200); + RouteMatch* match = route->mutable_match(); + + switch (match_type) { + case RouteMatch::PathSpecifierCase::kPrefix: { + match->set_prefix(absl::StrCat("/shelves/shelf_", i, "/")); + break; + } + case RouteMatch::PathSpecifierCase::kPath: { + match->set_prefix(absl::StrCat("/shelves/shelf_", i, "/route_", i)); + break; + } + case RouteMatch::PathSpecifierCase::kSafeRegex: { + envoy::type::matcher::v3::RegexMatcher* regex = match->mutable_safe_regex(); + regex->mutable_google_re2(); + regex->set_regex(absl::StrCat("^/shelves/[^\\\\/]+/route_", i, "$")); + break; + } + default: + NOT_REACHED_GCOVR_EXCL_LINE; + } + } + + return route_config; +} + +/** + * Measure the speed of doing a route match against a route table of varying sizes. + * Why? Currently, route matching is linear in first-to-win ordering. + * + * We construct the first `n - 1` items in the route table so they are not + * matched by the incoming request. Only the last route will be matched. + * We then time how long it takes for the request to be matched against the + * last route. + */ +static void bmRouteTableSize(benchmark::State& state, RouteMatch::PathSpecifierCase match_type) { + // Setup router for benchmarking. + TestScopedRuntime scoped_runtime; + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.preserve_query_string_in_path_redirects", "false"}}); + Api::ApiPtr api = Api::createApiForTest(); + NiceMock factory_context; + NiceMock stream_info; + ON_CALL(factory_context, api()).WillByDefault(ReturnRef(*api)); + + // Create router config. + ConfigImpl config(genRouteConfig(state, match_type), factory_context, + ProtobufMessage::getNullValidationVisitor(), true); + + for (auto _ : state) { // NOLINT + // Do the actual timing here. + // Single request that will match the last route in the config. + int last_route_num = state.range(0) - 1; + config.route(genRequestHeaders(last_route_num), stream_info, 0); + } +} + +/** + * Benchmark a route table with path prefix matchers in the form of: + * - /shelves/shelf_1/... + * - /shelves/shelf_2/... + * - etc. + */ +static void bmRouteTableSizeWithPathPrefixMatch(benchmark::State& state) { + bmRouteTableSize(state, RouteMatch::PathSpecifierCase::kPrefix); +} + +/** + * Benchmark a route table with exact path matchers in the form of: + * - /shelves/shelf_1/route_1 + * - /shelves/shelf_2/route_2 + * - etc. + */ +static void bmRouteTableSizeWithExactPathMatch(benchmark::State& state) { + bmRouteTableSize(state, RouteMatch::PathSpecifierCase::kPath); +} + +/** + * Benchmark a route table with regex path matchers in the form of: + * - /shelves/{shelf_id}/route_1 + * - /shelves/{shelf_id}/route_2 + * - etc. + * + * This represents common OpenAPI path templating. + */ +static void bmRouteTableSizeWithRegexMatch(benchmark::State& state) { + bmRouteTableSize(state, RouteMatch::PathSpecifierCase::kSafeRegex); +} + +BENCHMARK(bmRouteTableSizeWithPathPrefixMatch)->RangeMultiplier(2)->Ranges({{1, 2 << 13}}); +BENCHMARK(bmRouteTableSizeWithExactPathMatch)->RangeMultiplier(2)->Ranges({{1, 2 << 13}}); +BENCHMARK(bmRouteTableSizeWithRegexMatch)->RangeMultiplier(2)->Ranges({{1, 2 << 13}}); + +} // namespace +} // namespace Router +} // namespace Envoy From 602dfeaaa5e6c45d883b1c0d271defbbc9a3dec4 Mon Sep 17 00:00:00 2001 From: Snow Pettersen Date: Tue, 6 Oct 2020 19:20:34 -0400 Subject: [PATCH 12/27] reverse bridge: use local reply instead of ContinueAndEndStream (#13205) (#13414) Instead of returning ContinueAndEndStream we can respond with a local reply. This removes the only use case of ContinueAndEndStream in non-test code. Signed-off-by: Snow Pettersen --- docs/root/version_history/current.rst | 1 + .../http/grpc_http1_reverse_bridge/filter.cc | 14 ++----- .../reverse_bridge_test.cc | 39 ++++++++----------- 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 5173c659dc9c..a3b4de11bbf8 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -26,6 +26,7 @@ Minor Behavior Changes This behavior can be reverted by setting runtime feature ``envoy.reloadable_features.ext_authz_measure_timeout_on_check_created`` to false. When enabled, a new `ext_authz.timeout` stat is counted when timeout occurs. See :ref:`stats // `. * ext_authz_filter: added :ref:`disable_request_body_buffering ` to disable request data buffering per-route. +* grpc reverse bridge: upstream headers will no longer be propagated when the response is missing or contains an unexpected content-type. * http: added :ref:`contains ` a new string matcher type which matches if the value of the string has the substring mentioned in contains matcher. * http: added :ref:`contains ` a new header matcher type which matches if the value of the header has the substring mentioned in contains matcher. * http: added :ref:`headers_to_add ` to :ref:`local reply mapper ` to allow its users to add/append/override response HTTP headers to local replies. diff --git a/source/extensions/filters/http/grpc_http1_reverse_bridge/filter.cc b/source/extensions/filters/http/grpc_http1_reverse_bridge/filter.cc index d13b360ee07f..b8119639e8c1 100644 --- a/source/extensions/filters/http/grpc_http1_reverse_bridge/filter.cc +++ b/source/extensions/filters/http/grpc_http1_reverse_bridge/filter.cc @@ -136,17 +136,11 @@ Http::FilterHeadersStatus Filter::encodeHeaders(Http::ResponseHeaderMap& headers // If the response from upstream does not have the correct content-type, // perform an early return with a useful error message in grpc-message. if (content_type != upstream_content_type_) { - headers.setGrpcMessage(badContentTypeMessage(headers)); - headers.setGrpcStatus(Envoy::Grpc::Status::WellKnownGrpcStatus::Unknown); - headers.setStatus(enumToInt(Http::Code::OK)); - - if (!content_type.empty()) { - headers.setContentType(content_type_); - } + decoder_callbacks_->sendLocalReply(Http::Code::OK, badContentTypeMessage(headers), nullptr, + Grpc::Status::WellKnownGrpcStatus::Unknown, + RcDetails::get().GrpcBridgeFailedContentType); - decoder_callbacks_->streamInfo().setResponseCodeDetails( - RcDetails::get().GrpcBridgeFailedContentType); - return Http::FilterHeadersStatus::ContinueAndEndStream; + return Http::FilterHeadersStatus::StopIteration; } // Restore the content-type to match what the downstream sent. diff --git a/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_test.cc b/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_test.cc index 87bad1121e1e..599e16a845f3 100644 --- a/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_test.cc +++ b/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_test.cc @@ -532,9 +532,6 @@ TEST_F(ReverseBridgeTest, GrpcRequestBadResponseNoContentType) { buffer.add("abcdefgh", 8); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(buffer, false)); EXPECT_EQ("fgh", buffer.toString()); - EXPECT_CALL(decoder_callbacks_, streamInfo()); - EXPECT_CALL(decoder_callbacks_.stream_info_, - setResponseCodeDetails(absl::string_view("grpc_bridge_content_type_wrong"))); } { @@ -549,15 +546,14 @@ TEST_F(ReverseBridgeTest, GrpcRequestBadResponseNoContentType) { EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(trailers)); Http::TestResponseHeaderMapImpl headers({{":status", "400"}}); - EXPECT_EQ(Http::FilterHeadersStatus::ContinueAndEndStream, - filter_->encodeHeaders(headers, false)); - EXPECT_THAT(headers, HeaderValueOf(Http::Headers::get().Status, "200")); - EXPECT_THAT(headers, HeaderValueOf(Http::Headers::get().GrpcStatus, "2")); - EXPECT_THAT( - headers, - HeaderValueOf( - Http::Headers::get().GrpcMessage, - "envoy reverse bridge: upstream responded with no content-type header, status code 400")); + EXPECT_CALL( + decoder_callbacks_, + sendLocalReply( + Http::Code::OK, + "envoy reverse bridge: upstream responded with no content-type header, status code 400", + _, absl::make_optional(static_cast(Grpc::Status::Unknown)), _)); + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(_, _)); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->encodeHeaders(headers, false)); } // Tests that a gRPC is downgraded to application/x-protobuf and that if the response @@ -583,9 +579,6 @@ TEST_F(ReverseBridgeTest, GrpcRequestBadResponse) { buffer.add("abcdefgh", 8); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(buffer, false)); EXPECT_EQ("fgh", buffer.toString()); - EXPECT_CALL(decoder_callbacks_, streamInfo()); - EXPECT_CALL(decoder_callbacks_.stream_info_, - setResponseCodeDetails(absl::string_view("grpc_bridge_content_type_wrong"))); } { @@ -601,13 +594,15 @@ TEST_F(ReverseBridgeTest, GrpcRequestBadResponse) { Http::TestResponseHeaderMapImpl headers( {{":status", "400"}, {"content-type", "application/json"}}); - EXPECT_EQ(Http::FilterHeadersStatus::ContinueAndEndStream, - filter_->encodeHeaders(headers, false)); - EXPECT_THAT(headers, HeaderValueOf(Http::Headers::get().Status, "200")); - EXPECT_THAT(headers, HeaderValueOf(Http::Headers::get().GrpcStatus, "2")); - EXPECT_THAT(headers, HeaderValueOf(Http::Headers::get().GrpcMessage, - "envoy reverse bridge: upstream responded with unsupported " - "content-type application/json, status code 400")); + EXPECT_CALL( + decoder_callbacks_, + sendLocalReply( + Http::Code::OK, + "envoy reverse bridge: upstream responded with unsupported " + "content-type application/json, status code 400", + _, absl::make_optional(static_cast(Grpc::Status::Unknown)), _)); + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(_, _)); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->encodeHeaders(headers, false)); } // Tests that the filter passes a GRPC request through without modification because it is disabled From 03f46bbdafae57a510d3a7fd8aa912efb9c71db3 Mon Sep 17 00:00:00 2001 From: Spencer Lewis Date: Tue, 6 Oct 2020 20:18:56 -0400 Subject: [PATCH 13/27] api: rename connection pool proto messages (#13378) The java_outer_classname must not collide with a type defined within a proto file in order to compile protos to Java. Additionally, this commit introduces a format check to prevent this from happening again. Risk Level: low Testing: none Docs Changes:none Release Notes: none Fixes #13368 Signed-off-by: Spencer Lewis --- .../http/generic/v3/generic_connection_pool.proto | 2 +- .../upstreams/http/http/v3/http_connection_pool.proto | 2 +- .../upstreams/http/tcp/v3/tcp_connection_pool.proto | 2 +- .../http/generic/v3/generic_connection_pool.proto | 2 +- .../upstreams/http/http/v3/http_connection_pool.proto | 2 +- .../upstreams/http/tcp/v3/tcp_connection_pool.proto | 2 +- tools/protoxform/protoprint.py | 9 +++++++++ 7 files changed, 15 insertions(+), 6 deletions(-) diff --git a/api/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto b/api/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto index c6b02364aa2d..44e207172c9b 100644 --- a/api/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto +++ b/api/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto @@ -5,7 +5,7 @@ package envoy.extensions.upstreams.http.generic.v3; import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.generic.v3"; -option java_outer_classname = "GenericConnectionPoolProto"; +option java_outer_classname = "GenericConnectionPoolProtoOuterClass"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto b/api/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto index e4c2d6ff9b84..8318f3c666d9 100644 --- a/api/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto +++ b/api/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto @@ -5,7 +5,7 @@ package envoy.extensions.upstreams.http.http.v3; import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.http.v3"; -option java_outer_classname = "HttpConnectionPoolProto"; +option java_outer_classname = "HttpConnectionPoolProtoOuterClass"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto b/api/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto index 5bc8734cb3f7..7c1d633432e9 100644 --- a/api/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto +++ b/api/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto @@ -5,7 +5,7 @@ package envoy.extensions.upstreams.http.tcp.v3; import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.tcp.v3"; -option java_outer_classname = "TcpConnectionPoolProto"; +option java_outer_classname = "TcpConnectionPoolProtoOuterClass"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/generated_api_shadow/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto b/generated_api_shadow/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto index c6b02364aa2d..44e207172c9b 100644 --- a/generated_api_shadow/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto +++ b/generated_api_shadow/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto @@ -5,7 +5,7 @@ package envoy.extensions.upstreams.http.generic.v3; import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.generic.v3"; -option java_outer_classname = "GenericConnectionPoolProto"; +option java_outer_classname = "GenericConnectionPoolProtoOuterClass"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/generated_api_shadow/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto b/generated_api_shadow/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto index e4c2d6ff9b84..8318f3c666d9 100644 --- a/generated_api_shadow/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto +++ b/generated_api_shadow/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto @@ -5,7 +5,7 @@ package envoy.extensions.upstreams.http.http.v3; import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.http.v3"; -option java_outer_classname = "HttpConnectionPoolProto"; +option java_outer_classname = "HttpConnectionPoolProtoOuterClass"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/generated_api_shadow/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto b/generated_api_shadow/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto index 5bc8734cb3f7..7c1d633432e9 100644 --- a/generated_api_shadow/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto +++ b/generated_api_shadow/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto @@ -5,7 +5,7 @@ package envoy.extensions.upstreams.http.tcp.v3; import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.tcp.v3"; -option java_outer_classname = "TcpConnectionPoolProto"; +option java_outer_classname = "TcpConnectionPoolProtoOuterClass"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/tools/protoxform/protoprint.py b/tools/protoxform/protoprint.py index 092c86d6bca7..95a72b0ea51b 100755 --- a/tools/protoxform/protoprint.py +++ b/tools/protoxform/protoprint.py @@ -204,7 +204,16 @@ def CamelCase(s): file_block = '\n'.join(['syntax = "proto3";\n', package_line]) options = descriptor_pb2.FileOptions() + options.java_outer_classname = CamelCase(os.path.basename(file_proto.name)) + for msg in file_proto.message_type: + if msg.name == options.java_outer_classname: + # This is a workaround for Java outer class names that would otherwise + # conflict with types defined within the same proto file, see + # https://github.com/envoyproxy/envoy/pull/13378. + # TODO: in next major version, make this consistent. + options.java_outer_classname += "OuterClass" + options.java_multiple_files = True options.java_package = 'io.envoyproxy.' + file_proto.package From 3d5866fba1bb99681ad5133c970ad7bb204c19a6 Mon Sep 17 00:00:00 2001 From: Zach Reyes <39203661+zasweq@users.noreply.github.com> Date: Wed, 7 Oct 2020 11:14:07 -0400 Subject: [PATCH 14/27] [fuzz] Refactor health check fuzz (#13385) Refactor health check fuzz tests Signed-off-by: Zach --- test/common/upstream/health_check_fuzz.cc | 143 +++++------------- test/common/upstream/health_check_fuzz.h | 89 ++++++----- .../common/upstream/health_check_fuzz_test.cc | 22 ++- 3 files changed, 103 insertions(+), 151 deletions(-) diff --git a/test/common/upstream/health_check_fuzz.cc b/test/common/upstream/health_check_fuzz.cc index 7697040a6078..fa21636cb335 100644 --- a/test/common/upstream/health_check_fuzz.cc +++ b/test/common/upstream/health_check_fuzz.cc @@ -122,7 +122,7 @@ void HttpHealthCheckFuzz::initialize(test::common::upstream::HealthCheckTestCase PROTOBUF_GET_WRAPPED_OR_DEFAULT(input.health_check_config(), reuse_connection, true); } -void HttpHealthCheckFuzz::respond(const test::fuzz::Headers& headers, uint64_t status) { +void HttpHealthCheckFuzz::respond(test::common::upstream::Respond respond, bool last_action) { // Timeout timer needs to be explicitly enabled, usually by onIntervalBase() (Callback on interval // timer). if (!test_sessions_[0]->timeout_timer_->enabled_) { @@ -130,6 +130,9 @@ void HttpHealthCheckFuzz::respond(const test::fuzz::Headers& headers, uint64_t s return; } + const test::fuzz::Headers& headers = respond.http_respond().headers(); + uint64_t status = respond.http_respond().status(); + std::unique_ptr response_headers = std::make_unique( Fuzz::fromHeaders(headers, {}, {})); @@ -152,7 +155,7 @@ void HttpHealthCheckFuzz::respond(const test::fuzz::Headers& headers, uint64_t s test_sessions_[0]->stream_response_callbacks_->decodeHeaders(std::move(response_headers), true); // Interval timer gets turned on from decodeHeaders() - if (!reuse_connection_ || client_will_close) { + if ((!reuse_connection_ || client_will_close) && !last_action) { ENVOY_LOG_MISC(trace, "Creating client and stream because shouldClose() is true"); triggerIntervalTimer(true); } @@ -229,7 +232,8 @@ void TcpHealthCheckFuzz::initialize(test::common::upstream::HealthCheckTestCase } } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) -void TcpHealthCheckFuzz::respond(std::string data, bool last_action) { +void TcpHealthCheckFuzz::respond(test::common::upstream::Respond respond, bool last_action) { + std::string data = respond.tcp_respond().data(); if (!timeout_timer_->enabled_) { ENVOY_LOG_MISC(trace, "Timeout timer is disabled. Skipping response."); return; @@ -245,17 +249,20 @@ void TcpHealthCheckFuzz::respond(std::string data, bool last_action) { // turn on interval and turn off timeout, but for tcp it doesn't if the data doesn't match. If the // response doesn't match, it only sets the host to unhealthy. If it does match, it will turn // timeout off and interval on. - if (!reuse_connection_ && !last_action && interval_timer_->enabled_) { - expectClientCreate(); - interval_timer_->invokeCallback(); + if (!reuse_connection_ && interval_timer_->enabled_ && !last_action) { + triggerIntervalTimer(true); } } -void TcpHealthCheckFuzz::triggerIntervalTimer() { +void TcpHealthCheckFuzz::triggerIntervalTimer(bool expect_client_create) { if (!interval_timer_->enabled_) { ENVOY_LOG_MISC(trace, "Interval timer is disabled. Skipping trigger interval timer."); return; } + if (expect_client_create) { + ENVOY_LOG_MISC(trace, "Creating client"); + expectClientCreate(); + } ENVOY_LOG_MISC(trace, "Triggered interval timer"); interval_timer_->invokeCallback(); } @@ -269,9 +276,8 @@ void TcpHealthCheckFuzz::triggerTimeoutTimer(bool last_action) { timeout_timer_->invokeCallback(); // This closes the client, turns off timeout // and enables interval if (!last_action) { - ENVOY_LOG_MISC(trace, "Creating client and stream from network timeout"); - expectClientCreate(); - interval_timer_->invokeCallback(); + ENVOY_LOG_MISC(trace, "Will create client and stream from network timeout"); + triggerIntervalTimer(true); } } @@ -285,18 +291,16 @@ void TcpHealthCheckFuzz::raiseEvent(const Network::ConnectionEvent& event_type, if (!interval_timer_->enabled_) { return; } - ENVOY_LOG_MISC(trace, "Creating client from close event"); - expectClientCreate(); - interval_timer_->invokeCallback(); + ENVOY_LOG_MISC(trace, "Will create client from close event"); + triggerIntervalTimer(true); } // In the specific case of: // https://github.com/envoyproxy/envoy/blob/master/source/common/upstream/health_checker_impl.cc#L489 // This blows away client, should create a new one if (event_type == Network::ConnectionEvent::Connected && empty_response_) { - ENVOY_LOG_MISC(trace, "Creating client from connected event and empty response."); - expectClientCreate(); - interval_timer_->invokeCallback(); + ENVOY_LOG_MISC(trace, "Will create client from connected event and empty response."); + triggerIntervalTimer(true); } } @@ -327,7 +331,8 @@ void GrpcHealthCheckFuzz::initialize(test::common::upstream::HealthCheckTestCase } // Logic from respondResponseSpec() in unit tests -void GrpcHealthCheckFuzz::respond(test::common::upstream::GrpcRespond grpc_respond) { +void GrpcHealthCheckFuzz::respond(test::common::upstream::Respond respond, bool last_action) { + const test::common::upstream::GrpcRespond& grpc_respond = respond.grpc_respond(); if (!test_sessions_[0]->timeout_timer_->enabled_) { ENVOY_LOG_MISC(trace, "Timeout timer is disabled. Skipping response."); return; @@ -398,10 +403,11 @@ void GrpcHealthCheckFuzz::respond(test::common::upstream::GrpcRespond grpc_respo // Once it gets here the health checker will have called onRpcComplete(), logically representing a // completed rpc call, which blows away client if reuse connection is set to false or the health // checker had a goaway event with no error flag. - ENVOY_LOG_MISC(trace, "Triggering interval timer after response"); - triggerIntervalTimer(!reuse_connection_ || received_no_error_goaway_); - - received_no_error_goaway_ = false; // from resetState() + if (!last_action) { + ENVOY_LOG_MISC(trace, "Triggering interval timer after response"); + triggerIntervalTimer(!reuse_connection_ || received_no_error_goaway_); + received_no_error_goaway_ = false; // from resetState() + } } void GrpcHealthCheckFuzz::triggerIntervalTimer(bool expect_client_create) { @@ -477,29 +483,7 @@ HealthCheckFuzz::getEventTypeFromProto(const test::common::upstream::RaiseEvent& void HealthCheckFuzz::initializeAndReplay(test::common::upstream::HealthCheckTestCase input) { try { - switch (input.health_check_config().health_checker_case()) { - case envoy::config::core::v3::HealthCheck::kHttpHealthCheck: { - type_ = HealthCheckFuzz::Type::HTTP; - http_fuzz_test_ = std::make_unique(); - http_fuzz_test_->initialize(input); - break; - } - case envoy::config::core::v3::HealthCheck::kTcpHealthCheck: { - type_ = HealthCheckFuzz::Type::TCP; - tcp_fuzz_test_ = std::make_unique(); - tcp_fuzz_test_->initialize(input); - break; - } - case envoy::config::core::v3::HealthCheck::kGrpcHealthCheck: { - type_ = HealthCheckFuzz::Type::GRPC; - grpc_fuzz_test_ = std::make_unique(); - grpc_fuzz_test_->initialize(input); - break; - } - default: // Handles custom health checkers - ENVOY_LOG_MISC(trace, "Custom Health Checker currently unsupported, skipping"); - return; - } + initialize(input); } catch (EnvoyException& e) { ENVOY_LOG_MISC(debug, "EnvoyException: {}", e.what()); return; @@ -511,84 +495,25 @@ void HealthCheckFuzz::replay(const test::common::upstream::HealthCheckTestCase& constexpr auto max_actions = 64; for (int i = 0; i < std::min(max_actions, input.actions().size()); ++i) { const auto& event = input.actions(i); + // The last_action boolean prevents final actions from creating a client and stream that will + // never be used. const bool last_action = i == std::min(max_actions, input.actions().size()) - 1; ENVOY_LOG_MISC(trace, "Action: {}", event.DebugString()); switch (event.action_selector_case()) { case test::common::upstream::Action::kRespond: { - switch (type_) { - case HealthCheckFuzz::Type::HTTP: { - http_fuzz_test_->respond(event.respond().http_respond().headers(), - event.respond().http_respond().status()); - break; - } - case HealthCheckFuzz::Type::TCP: { - tcp_fuzz_test_->respond(event.respond().tcp_respond().data(), last_action); - break; - } - case HealthCheckFuzz::Type::GRPC: { - grpc_fuzz_test_->respond(event.respond().grpc_respond()); - break; - } - default: - break; - } + respond(event.respond(), last_action); break; } case test::common::upstream::Action::kTriggerIntervalTimer: { - switch (type_) { - case HealthCheckFuzz::Type::HTTP: { - http_fuzz_test_->triggerIntervalTimer(false); - break; - } - case HealthCheckFuzz::Type::TCP: { - tcp_fuzz_test_->triggerIntervalTimer(); - break; - } - case HealthCheckFuzz::Type::GRPC: { - grpc_fuzz_test_->triggerIntervalTimer(false); - break; - } - default: - break; - } + triggerIntervalTimer(false); break; } case test::common::upstream::Action::kTriggerTimeoutTimer: { - switch (type_) { - case HealthCheckFuzz::Type::HTTP: { - http_fuzz_test_->triggerTimeoutTimer(last_action); - break; - } - case HealthCheckFuzz::Type::TCP: { - tcp_fuzz_test_->triggerTimeoutTimer(last_action); - break; - } - case HealthCheckFuzz::Type::GRPC: { - grpc_fuzz_test_->triggerTimeoutTimer(last_action); - break; - } - default: - break; - } + triggerTimeoutTimer(last_action); break; } case test::common::upstream::Action::kRaiseEvent: { - switch (type_) { - case HealthCheckFuzz::Type::HTTP: { - http_fuzz_test_->raiseEvent(getEventTypeFromProto(event.raise_event()), last_action); - break; - } - case HealthCheckFuzz::Type::TCP: { - tcp_fuzz_test_->raiseEvent(getEventTypeFromProto(event.raise_event()), last_action); - break; - } - case HealthCheckFuzz::Type::GRPC: { - grpc_fuzz_test_->raiseEvent(getEventTypeFromProto(event.raise_event()), last_action); - break; - } - default: - break; - } + raiseEvent(getEventTypeFromProto(event.raise_event()), last_action); break; } default: diff --git a/test/common/upstream/health_check_fuzz.h b/test/common/upstream/health_check_fuzz.h index 809af14b219b..ea17615e1270 100644 --- a/test/common/upstream/health_check_fuzz.h +++ b/test/common/upstream/health_check_fuzz.h @@ -9,27 +9,57 @@ namespace Envoy { namespace Upstream { -class HttpHealthCheckFuzz : HttpHealthCheckerImplTestBase { +class HealthCheckFuzz { +public: + HealthCheckFuzz() = default; + // This will delegate to the specific classes + void initializeAndReplay(test::common::upstream::HealthCheckTestCase input); + enum class Type { + HTTP, + TCP, + GRPC, + }; + + // The specific implementations of respond look into the respond proto, which has all three types + // of response + virtual void respond(test::common::upstream::Respond respond, bool last_action) PURE; + + virtual void initialize(test::common::upstream::HealthCheckTestCase input) PURE; + virtual void triggerIntervalTimer(bool expect_client_create) PURE; + virtual void triggerTimeoutTimer(bool last_action) PURE; + virtual void raiseEvent(const Network::ConnectionEvent& event_type, bool last_action) PURE; + + virtual ~HealthCheckFuzz() = default; + +private: + Network::ConnectionEvent getEventTypeFromProto(const test::common::upstream::RaiseEvent& event); + + void replay(const test::common::upstream::HealthCheckTestCase& input); +}; + +class HttpHealthCheckFuzz : public HealthCheckFuzz, HttpHealthCheckerImplTestBase { public: void allocHttpHealthCheckerFromProto(const envoy::config::core::v3::HealthCheck& config); - void initialize(test::common::upstream::HealthCheckTestCase input); - void respond(const test::fuzz::Headers& headers, uint64_t status); - void triggerIntervalTimer(bool expect_client_create); - void triggerTimeoutTimer(bool last_action); - void raiseEvent(const Network::ConnectionEvent& event_type, bool last_action); + void initialize(test::common::upstream::HealthCheckTestCase input) override; + void respond(test::common::upstream::Respond respond, bool last_action) override; + void triggerIntervalTimer(bool expect_client_create) override; + void triggerTimeoutTimer(bool last_action) override; + void raiseEvent(const Network::ConnectionEvent& event_type, bool last_action) override; + ~HttpHealthCheckFuzz() override = default; // Determines whether the client gets reused or not after response bool reuse_connection_ = true; }; -class TcpHealthCheckFuzz : TcpHealthCheckerImplTestBase { +class TcpHealthCheckFuzz : public HealthCheckFuzz, TcpHealthCheckerImplTestBase { public: void allocTcpHealthCheckerFromProto(const envoy::config::core::v3::HealthCheck& config); - void initialize(test::common::upstream::HealthCheckTestCase input); - void respond(std::string data, bool last_action); - void triggerIntervalTimer(); - void triggerTimeoutTimer(bool last_action); - void raiseEvent(const Network::ConnectionEvent& event_type, bool last_action); + void initialize(test::common::upstream::HealthCheckTestCase input) override; + void respond(test::common::upstream::Respond respond, bool last_action) override; + void triggerIntervalTimer(bool expect_client_create) override; + void triggerTimeoutTimer(bool last_action) override; + void raiseEvent(const Network::ConnectionEvent& event_type, bool last_action) override; + ~TcpHealthCheckFuzz() override = default; // Determines whether the client gets reused or not after response bool reuse_connection_ = true; @@ -39,16 +69,17 @@ class TcpHealthCheckFuzz : TcpHealthCheckerImplTestBase { bool empty_response_ = true; }; -class GrpcHealthCheckFuzz : GrpcHealthCheckerImplTestBaseUtils { +class GrpcHealthCheckFuzz : public HealthCheckFuzz, GrpcHealthCheckerImplTestBaseUtils { public: void allocGrpcHealthCheckerFromProto(const envoy::config::core::v3::HealthCheck& config); - void initialize(test::common::upstream::HealthCheckTestCase input); + void initialize(test::common::upstream::HealthCheckTestCase input) override; // This has three components, headers, raw bytes, and trailers - void respond(test::common::upstream::GrpcRespond grpc_respond); - void triggerIntervalTimer(bool expect_client_create); - void triggerTimeoutTimer(bool last_action); - void raiseEvent(const Network::ConnectionEvent& event_type, bool last_action); + void respond(test::common::upstream::Respond respond, bool last_action) override; + void triggerIntervalTimer(bool expect_client_create) override; + void triggerTimeoutTimer(bool last_action) override; + void raiseEvent(const Network::ConnectionEvent& event_type, bool last_action) override; void raiseGoAway(bool no_error); + ~GrpcHealthCheckFuzz() override = default; // Determines whether the client gets reused or not after response bool reuse_connection_ = true; @@ -58,27 +89,5 @@ class GrpcHealthCheckFuzz : GrpcHealthCheckerImplTestBaseUtils { bool received_no_error_goaway_ = false; }; -class HealthCheckFuzz { -public: - HealthCheckFuzz() = default; - // This will delegate to the specific classes - void initializeAndReplay(test::common::upstream::HealthCheckTestCase input); - enum class Type { - HTTP, - TCP, - GRPC, - }; - -private: - Network::ConnectionEvent getEventTypeFromProto(const test::common::upstream::RaiseEvent& event); - - void replay(const test::common::upstream::HealthCheckTestCase& input); - - Type type_; - std::unique_ptr http_fuzz_test_; - std::unique_ptr tcp_fuzz_test_; - std::unique_ptr grpc_fuzz_test_; -}; - } // namespace Upstream } // namespace Envoy diff --git a/test/common/upstream/health_check_fuzz_test.cc b/test/common/upstream/health_check_fuzz_test.cc index 83b7023ea8c4..d5834144e293 100644 --- a/test/common/upstream/health_check_fuzz_test.cc +++ b/test/common/upstream/health_check_fuzz_test.cc @@ -15,9 +15,27 @@ DEFINE_PROTO_FUZZER(const test::common::upstream::HealthCheckTestCase input) { return; } - HealthCheckFuzz health_check_fuzz; + std::unique_ptr health_check_fuzz; - health_check_fuzz.initializeAndReplay(input); + switch (input.health_check_config().health_checker_case()) { + case envoy::config::core::v3::HealthCheck::kHttpHealthCheck: { + health_check_fuzz = std::make_unique(); + break; + } + case envoy::config::core::v3::HealthCheck::kTcpHealthCheck: { + health_check_fuzz = std::make_unique(); + break; + } + case envoy::config::core::v3::HealthCheck::kGrpcHealthCheck: { + health_check_fuzz = std::make_unique(); + break; + } + default: // Handles custom health checker + ENVOY_LOG_MISC(trace, "Custom Health Checker currently unsupported, skipping"); + return; + } + + health_check_fuzz->initializeAndReplay(input); } } // namespace Upstream From 221965d7c81e70b9cb651a237239cd8fb501b7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Wed, 7 Oct 2020 11:37:01 -0400 Subject: [PATCH 15/27] Fix missing case of -Wrange-loop-analysis (#13415) Leftover from #13364. Signed-off-by: Raul Gutierrez Segales --- .../retry/priority/previous_priorities/previous_priorities.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/extensions/retry/priority/previous_priorities/previous_priorities.cc b/source/extensions/retry/priority/previous_priorities/previous_priorities.cc index 96dc7c540b25..e01028e7fc3b 100644 --- a/source/extensions/retry/priority/previous_priorities/previous_priorities.cc +++ b/source/extensions/retry/priority/previous_priorities/previous_priorities.cc @@ -51,7 +51,7 @@ bool PreviousPrioritiesRetryPriority::adjustForAttemptedPriorities( // This allows us to fall back to the unmodified priority load when we run out of priorities // instead of failing to route requests. if (total_availability == 0) { - for (auto&& excluded_priority : excluded_priorities_) { + for (auto excluded_priority : excluded_priorities_) { excluded_priority = false; } attempted_hosts_.clear(); From a74bd4b9fa3747ab87c794d2d304d46d7b5ac0f1 Mon Sep 17 00:00:00 2001 From: Dhi Aurrahman Date: Wed, 7 Oct 2020 22:38:17 +0700 Subject: [PATCH 16/27] docs: Rearrange release notes entries related to ext_authz (#13419) Signed-off-by: Dhi Aurrahman --- docs/root/version_history/current.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index a3b4de11bbf8..6cbea7ef4ee5 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -25,7 +25,6 @@ Minor Behavior Changes * ext_authz filter: request timeout will now count from the time the check request is created, instead of when it becomes active. This makes sure that the timeout is enforced even if the ext_authz cluster's circuit breaker is engaged. This behavior can be reverted by setting runtime feature ``envoy.reloadable_features.ext_authz_measure_timeout_on_check_created`` to false. When enabled, a new `ext_authz.timeout` stat is counted when timeout occurs. See :ref:`stats // `. -* ext_authz_filter: added :ref:`disable_request_body_buffering ` to disable request data buffering per-route. * grpc reverse bridge: upstream headers will no longer be propagated when the response is missing or contains an unexpected content-type. * http: added :ref:`contains ` a new string matcher type which matches if the value of the string has the substring mentioned in contains matcher. * http: added :ref:`contains ` a new header matcher type which matches if the value of the header has the substring mentioned in contains matcher. @@ -102,9 +101,10 @@ New Features * ext_authz filter: added support for emitting dynamic metadata for both :ref:`HTTP ` and :ref:`network ` filters. The emitted dynamic metadata is set by :ref:`dynamic metadata ` field in a returned :ref:`CheckResponse `. * ext_authz filter: added :ref:`stat_prefix ` as an optional additional prefix for the statistics emitted from `ext_authz` HTTP filter. +* ext_authz filter: added support for enabling the filter based on :ref:`dynamic metadata `. * ext_authz filter: added support for letting the authorization server instruct Envoy to remove headers from the original request by setting the new field :ref:`headers_to_remove ` before forwarding it to the upstream. * ext_authz filter: added support for sending :ref:`raw bytes as request body ` of a gRPC check request by setting :ref:`pack_as_bytes ` to true. -* ext_authz filter: added support for enabling the filter based on :ref:`dynamic metadata `. +* ext_authz_filter: added :ref:`disable_request_body_buffering ` to disable request data buffering per-route. * grpc-json: support specifying `response_body` field in for `google.api.HttpBody` message. * hds: added :ref:`cluster_endpoints_health ` to HDS responses, keeping endpoints in the same groupings as they were configured in the HDS specifier by cluster and locality instead of as a flat list. * hds: added :ref:`transport_socket_matches ` to HDS cluster health check specifier, so the existing match filter :ref:`transport_socket_match_criteria ` in the repeated field :ref:`health_checks ` has context to match against. This unblocks support for health checks over HTTPS and HTTP/2. From 4c0d2d2a36996f2ec7d00b519ea90068629a2c55 Mon Sep 17 00:00:00 2001 From: Dmitry Rozhkov Date: Wed, 7 Oct 2020 20:32:22 +0300 Subject: [PATCH 17/27] memory: switch to the latest tcmalloc for x86_64 builds (#13251) Signed-off-by: Dmitry Rozhkov --- bazel/BUILD | 31 ++++++++++++ bazel/README.md | 6 ++- bazel/envoy_internal.bzl | 17 +++++-- bazel/envoy_library.bzl | 6 +++ bazel/repositories.bzl | 11 +++++ bazel/repository_locations.bzl | 12 +++++ ci/do_ci.sh | 4 +- docs/root/version_history/current.rst | 1 + source/common/memory/stats.cc | 49 ++++++++++++++++++- source/common/memory/utils.cc | 19 +++++-- source/common/profiler/profiler.h | 4 +- .../quiche/platform/quic_mem_slice_impl.cc | 2 +- test/common/memory/heap_shrinker_test.cc | 2 +- test/common/stats/stat_test_utility.cc | 2 +- test/common/stats/symbol_table_impl_test.cc | 6 +-- test/common/stats/thread_local_store_test.cc | 2 +- test/exe/main_common_test.cc | 19 ++++++- test/integration/stats_integration_test.cc | 3 +- 18 files changed, 170 insertions(+), 26 deletions(-) diff --git a/bazel/BUILD b/bazel/BUILD index 0cdca5a06d7f..5bdd7b7520cc 100644 --- a/bazel/BUILD +++ b/bazel/BUILD @@ -155,6 +155,37 @@ config_setting( values = {"define": "tcmalloc=debug"}, ) +config_setting( + name = "gperftools_tcmalloc", + values = {"define": "tcmalloc=gperftools"}, +) + +# As select() can't be nested we need these specialized settings to avoid ambiguity when choosing +# tcmalloc's flavor for x86_64 builds. +config_setting( + name = "disable_tcmalloc_on_linux_x86_64", + values = { + "define": "tcmalloc=disabled", + "cpu": "k8", + }, +) + +config_setting( + name = "gperftools_tcmalloc_on_linux_x86_64", + values = { + "define": "tcmalloc=gperftools", + "cpu": "k8", + }, +) + +config_setting( + name = "debug_tcmalloc_on_linux_x86_64", + values = { + "define": "tcmalloc=debug", + "cpu": "k8", + }, +) + config_setting( name = "disable_signal_trace", values = {"define": "signal_trace=disabled"}, diff --git a/bazel/README.md b/bazel/README.md index aae81ce988cc..36e8f7ebc324 100644 --- a/bazel/README.md +++ b/bazel/README.md @@ -607,7 +607,8 @@ The following optional features can be disabled on the Bazel build command-line: * Google C++ gRPC client with `--define google_grpc=disabled` * Backtracing on signals with `--define signal_trace=disabled` * Active stream state dump on signals with `--define signal_trace=disabled` or `--define disable_object_dump_on_signal_trace=disabled` -* tcmalloc with `--define tcmalloc=disabled` +* tcmalloc with `--define tcmalloc=disabled`. Also you can choose Gperftools' implementation of + tcmalloc with `--define tcmalloc=gperftools` which is the default for non-x86 builds. * deprecated features with `--define deprecated_features=disabled` @@ -626,7 +627,8 @@ The following optional features can be enabled on the Bazel build command-line: `--define log_debug_assert_in_release=enabled`. The default behavior is to compile debug assertions out of release builds so that the condition is not evaluated. This option has no effect in debug builds. * memory-debugging (scribbling over memory after allocation and before freeing) with - `--define tcmalloc=debug`. Note this option cannot be used with FIPS-compliant mode BoringSSL. + `--define tcmalloc=debug`. Note this option cannot be used with FIPS-compliant mode BoringSSL and + tcmalloc is built from the sources of Gperftools. * Default [path normalization](https://github.com/envoyproxy/envoy/issues/6435) with `--define path_normalization_by_default=true`. Note this still could be disable by explicit xDS config. * Manual stamping via VersionInfo with `--define manual_stamp=manual_stamp`. diff --git a/bazel/envoy_internal.bzl b/bazel/envoy_internal.bzl index f15af170819c..5ad86609a00d 100644 --- a/bazel/envoy_internal.bzl +++ b/bazel/envoy_internal.bzl @@ -60,10 +60,13 @@ def envoy_copts(repository, test = False): "//conditions:default": [], }) + select({ repository + "//bazel:disable_tcmalloc": ["-DABSL_MALLOC_HOOK_MMAP_DISABLE"], - "//conditions:default": ["-DTCMALLOC"], - }) + select({ - repository + "//bazel:debug_tcmalloc": ["-DENVOY_MEMORY_DEBUG_ENABLED=1"], - "//conditions:default": [], + repository + "//bazel:disable_tcmalloc_on_linux_x86_64": ["-DABSL_MALLOC_HOOK_MMAP_DISABLE"], + repository + "//bazel:gperftools_tcmalloc": ["-DGPERFTOOLS_TCMALLOC"], + repository + "//bazel:gperftools_tcmalloc_on_linux_x86_64": ["-DGPERFTOOLS_TCMALLOC"], + repository + "//bazel:debug_tcmalloc": ["-DENVOY_MEMORY_DEBUG_ENABLED=1", "-DGPERFTOOLS_TCMALLOC"], + repository + "//bazel:debug_tcmalloc_on_linux_x86_64": ["-DENVOY_MEMORY_DEBUG_ENABLED=1", "-DGPERFTOOLS_TCMALLOC"], + repository + "//bazel:linux_x86_64": ["-DTCMALLOC"], + "//conditions:default": ["-DGPERFTOOLS_TCMALLOC"], }) + select({ repository + "//bazel:disable_signal_trace": [], "//conditions:default": ["-DENVOY_HANDLE_SIGNALS"], @@ -118,6 +121,12 @@ def envoy_stdlib_deps(): def tcmalloc_external_dep(repository): return select({ repository + "//bazel:disable_tcmalloc": None, + repository + "//bazel:disable_tcmalloc_on_linux_x86_64": None, + repository + "//bazel:debug_tcmalloc": envoy_external_dep_path("gperftools"), + repository + "//bazel:debug_tcmalloc_on_linux_x86_64": envoy_external_dep_path("gperftools"), + repository + "//bazel:gperftools_tcmalloc": envoy_external_dep_path("gperftools"), + repository + "//bazel:gperftools_tcmalloc_on_linux_x86_64": envoy_external_dep_path("gperftools"), + repository + "//bazel:linux_x86_64": envoy_external_dep_path("tcmalloc"), "//conditions:default": envoy_external_dep_path("gperftools"), }) diff --git a/bazel/envoy_library.bzl b/bazel/envoy_library.bzl index bf0c89773fcb..5eb90df500c0 100644 --- a/bazel/envoy_library.bzl +++ b/bazel/envoy_library.bzl @@ -20,6 +20,12 @@ load( def tcmalloc_external_deps(repository): return select({ repository + "//bazel:disable_tcmalloc": [], + repository + "//bazel:disable_tcmalloc_on_linux_x86_64": [], + repository + "//bazel:debug_tcmalloc": [envoy_external_dep_path("gperftools")], + repository + "//bazel:debug_tcmalloc_on_linux_x86_64": [envoy_external_dep_path("gperftools")], + repository + "//bazel:gperftools_tcmalloc": [envoy_external_dep_path("gperftools")], + repository + "//bazel:gperftools_tcmalloc_on_linux_x86_64": [envoy_external_dep_path("gperftools")], + repository + "//bazel:linux_x86_64": [envoy_external_dep_path("tcmalloc")], "//conditions:default": [envoy_external_dep_path("gperftools")], }) diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 359897306793..64d61ea49940 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -197,6 +197,7 @@ def envoy_dependencies(skip_targets = []): _com_github_google_benchmark() _com_github_google_jwt_verify() _com_github_google_libprotobuf_mutator() + _com_github_google_tcmalloc() _com_github_gperftools_gperftools() _com_github_grpc_grpc() _com_github_jbeder_yaml_cpp() @@ -875,6 +876,16 @@ def _com_github_moonjit_moonjit(): actual = "@envoy//bazel/foreign_cc:moonjit", ) +def _com_github_google_tcmalloc(): + _repository_impl( + name = "com_github_google_tcmalloc", + ) + + native.bind( + name = "tcmalloc", + actual = "@com_github_google_tcmalloc//tcmalloc", + ) + def _com_github_gperftools_gperftools(): location = _get_location("com_github_gperftools_gperftools") http_archive( diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index f5dc9ccc5527..6eba5a821d1d 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -241,6 +241,18 @@ DEPENDENCY_REPOSITORIES_SPEC = dict( last_updated = "2020-08-18", use_category = ["test_only"], ), + com_github_google_tcmalloc = dict( + project_name = "tcmalloc", + project_desc = "Fast, multi-threaded malloc implementation", + project_url = "https://github.com/google/tcmalloc", + version = "d1311bf409db47c3441d3de6ea07d768c6551dec", + sha256 = "e22444b6544edd81f11c987dd5e482a2e00bbff717badb388779ca57525dad50", + strip_prefix = "tcmalloc-{version}", + urls = ["https://github.com/google/tcmalloc/archive/{version}.tar.gz"], + use_category = ["dataplane_core", "controlplane"], + last_updated = "2020-09-16", + cpe = "N/A", + ), com_github_gperftools_gperftools = dict( project_name = "gperftools", project_desc = "tcmalloc and profiling libraries", diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 4d8324d08284..f470f60c19e9 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -282,6 +282,7 @@ elif [[ "$CI_TARGET" == "bazel.compile_time_options" ]]; then "--define" "path_normalization_by_default=true" "--define" "deprecated_features=disabled" "--define" "use_new_codecs_in_integration_tests=true" + "--define" "tcmalloc=gperftools" "--define" "zlib=ng") ENVOY_STDLIB="${ENVOY_STDLIB:-libstdc++}" @@ -330,7 +331,8 @@ elif [[ "$CI_TARGET" == "bazel.coverage" || "$CI_TARGET" == "bazel.fuzz_coverage [[ "$CI_TARGET" == "bazel.fuzz_coverage" ]] && export FUZZ_COVERAGE=true - BAZEL_BUILD_OPTIONS="${BAZEL_BUILD_OPTIONS[*]}" test/run_envoy_bazel_coverage.sh "${COVERAGE_TEST_TARGETS[@]}" + # We use custom BAZEL_BUILD_OPTIONS here to cover profiler's code. + BAZEL_BUILD_OPTIONS="${BAZEL_BUILD_OPTIONS[*]} --define tcmalloc=gperftools" test/run_envoy_bazel_coverage.sh "${COVERAGE_TEST_TARGETS[@]}" collect_build_profile coverage exit 0 elif [[ "$CI_TARGET" == "bazel.clang_tidy" ]]; then diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 6cbea7ef4ee5..c869e0294565 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -46,6 +46,7 @@ Minor Behavior Changes * logging: nghttp2 log messages no longer appear at trace level unless `ENVOY_NGHTTP2_TRACE` is set in the environment. * lua: changed the response body returned by `httpCall()` API to raw data. Previously, the returned data was string. +* memory: switched to the `new tcmalloc `_ for linux_x86_64 builds. The `old tcmalloc `_ can still be enabled with the `--define tcmalloc=gperftools` option. * postgres: changed log format to tokenize fields of Postgres messages. * router: added transport failure reason to response body when upstream reset happens. After this change, the response body will be of the form `upstream connect error or disconnect/reset before headers. reset reason:{}, transport failure reason:{}`.This behavior may be reverted by setting runtime feature `envoy.reloadable_features.http_transport_failure_reason_in_body` to false. * router: now consumes all retry related headers to prevent them from being propagated to the upstream. This behavior may be reverted by setting runtime feature `envoy.reloadable_features.consume_all_retry_headers` to false. diff --git a/source/common/memory/stats.cc b/source/common/memory/stats.cc index 657f9b6b9b08..5b4e97261a4c 100644 --- a/source/common/memory/stats.cc +++ b/source/common/memory/stats.cc @@ -4,7 +4,52 @@ #include "common/common/logger.h" -#ifdef TCMALLOC +#if defined(TCMALLOC) + +#include "tcmalloc/malloc_extension.h" + +namespace Envoy { +namespace Memory { + +uint64_t Stats::totalCurrentlyAllocated() { + return tcmalloc::MallocExtension::GetNumericProperty("generic.current_allocated_bytes") + .value_or(0); +} + +uint64_t Stats::totalCurrentlyReserved() { + // In Google's tcmalloc the semantics of generic.heap_size has + // changed: it doesn't include unmapped bytes. + return tcmalloc::MallocExtension::GetNumericProperty("generic.heap_size").value_or(0) + + tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_unmapped_bytes") + .value_or(0); +} + +uint64_t Stats::totalThreadCacheBytes() { + return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.current_total_thread_cache_bytes") + .value_or(0); +} + +uint64_t Stats::totalPageHeapFree() { + return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_free_bytes").value_or(0); +} + +uint64_t Stats::totalPageHeapUnmapped() { + return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_unmapped_bytes") + .value_or(0); +} + +uint64_t Stats::totalPhysicalBytes() { + return tcmalloc::MallocExtension::GetProperties()["generic.physical_memory_used"].value; +} + +void Stats::dumpStatsToLog() { + ENVOY_LOG_MISC(debug, "TCMalloc stats:\n{}", tcmalloc::MallocExtension::GetStats()); +} + +} // namespace Memory +} // namespace Envoy + +#elif defined(GPERFTOOLS_TCMALLOC) #include "gperftools/malloc_extension.h" @@ -74,4 +119,4 @@ void Stats::dumpStatsToLog() {} } // namespace Memory } // namespace Envoy -#endif // #ifdef TCMALLOC +#endif // #if defined(TCMALLOC) diff --git a/source/common/memory/utils.cc b/source/common/memory/utils.cc index 2fa957572217..c6ac4f6c5fe8 100644 --- a/source/common/memory/utils.cc +++ b/source/common/memory/utils.cc @@ -3,15 +3,26 @@ #include "common/common/assert.h" #include "common/memory/stats.h" -#ifdef TCMALLOC +#if defined(TCMALLOC) +#include "tcmalloc/malloc_extension.h" +#elif defined(GPERFTOOLS_TCMALLOC) #include "gperftools/malloc_extension.h" #endif namespace Envoy { namespace Memory { +namespace { +#if defined(TCMALLOC) || defined(GPERFTOOLS_TCMALLOC) +// TODO(zyfjeff): Make max unfreed memory byte configurable +constexpr uint64_t MAX_UNFREED_MEMORY_BYTE = 100 * 1024 * 1024; +#endif +} // namespace + void Utils::releaseFreeMemory() { -#ifdef TCMALLOC +#if defined(TCMALLOC) + tcmalloc::MallocExtension::ReleaseMemoryToSystem(MAX_UNFREED_MEMORY_BYTE); +#elif defined(GPERFTOOLS_TCMALLOC) MallocExtension::instance()->ReleaseFreeMemory(); #endif } @@ -23,9 +34,7 @@ void Utils::releaseFreeMemory() { Ref: https://github.com/envoyproxy/envoy/pull/9471#discussion_r363825985 */ void Utils::tryShrinkHeap() { -#ifdef TCMALLOC - // TODO(zyfjeff): Make max unfreed memory byte configurable - static const uint64_t MAX_UNFREED_MEMORY_BYTE = 100 * 1024 * 1024; +#if defined(TCMALLOC) || defined(GPERFTOOLS_TCMALLOC) auto total_physical_bytes = Stats::totalPhysicalBytes(); auto allocated_size_by_app = Stats::totalCurrentlyAllocated(); diff --git a/source/common/profiler/profiler.h b/source/common/profiler/profiler.h index fdf4b20ee8f9..057ffda6f271 100644 --- a/source/common/profiler/profiler.h +++ b/source/common/profiler/profiler.h @@ -2,10 +2,10 @@ #include -// Profiling support is provided in the release tcmalloc, but not in the library +// Profiling support is provided in the release tcmalloc of `gperftools`, but not in the library // that supplies the debug tcmalloc. So all the profiling code must be ifdef'd // on PROFILER_AVAILABLE which is dependent on those two settings. -#if defined(TCMALLOC) && !defined(ENVOY_MEMORY_DEBUG_ENABLED) +#if defined(GPERFTOOLS_TCMALLOC) && !defined(ENVOY_MEMORY_DEBUG_ENABLED) #define PROFILER_AVAILABLE #endif diff --git a/source/extensions/quic_listeners/quiche/platform/quic_mem_slice_impl.cc b/source/extensions/quic_listeners/quiche/platform/quic_mem_slice_impl.cc index c75f99c0bafb..903ee1332d04 100644 --- a/source/extensions/quic_listeners/quiche/platform/quic_mem_slice_impl.cc +++ b/source/extensions/quic_listeners/quiche/platform/quic_mem_slice_impl.cc @@ -16,7 +16,7 @@ QuicMemSliceImpl::QuicMemSliceImpl(QuicUniqueBufferPtr buffer, size_t length) : fragment_(std::make_unique( buffer.release(), length, [](const void* p, size_t, const Envoy::Buffer::BufferFragmentImpl*) { - delete static_cast(p); + delete[] static_cast(p); })) { single_slice_buffer_.addBufferFragment(*fragment_); ASSERT(this->length() == length); diff --git a/test/common/memory/heap_shrinker_test.cc b/test/common/memory/heap_shrinker_test.cc index 9b921847f671..346a74899ff9 100644 --- a/test/common/memory/heap_shrinker_test.cc +++ b/test/common/memory/heap_shrinker_test.cc @@ -67,7 +67,7 @@ TEST_F(HeapShrinkerTest, ShrinkWhenTriggered) { const uint64_t physical_mem_after_shrink = Stats::totalCurrentlyReserved() - Stats::totalPageHeapUnmapped(); -#ifdef TCMALLOC +#if defined(TCMALLOC) || defined(GPERFTOOLS_TCMALLOC) EXPECT_GE(physical_mem_before_shrink, physical_mem_after_shrink); #else EXPECT_EQ(physical_mem_before_shrink, physical_mem_after_shrink); diff --git a/test/common/stats/stat_test_utility.cc b/test/common/stats/stat_test_utility.cc index 7cdbc08ab4dc..a47c84d346f1 100644 --- a/test/common/stats/stat_test_utility.cc +++ b/test/common/stats/stat_test_utility.cc @@ -101,7 +101,7 @@ void forEachSampleStat(int num_clusters, std::function } MemoryTest::Mode MemoryTest::mode() { -#if !defined(TCMALLOC) || defined(ENVOY_MEMORY_DEBUG_ENABLED) +#if !(defined(TCMALLOC) || defined(GPERFTOOLS_TCMALLOC)) || defined(ENVOY_MEMORY_DEBUG_ENABLED) // We can only test absolute memory usage if the malloc library is a known // quantity. This decision is centralized here. As the preferred malloc // library for Envoy is TCMALLOC that's what we test for here. If we switch diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index d8d8db14d999..5ac09db06a4b 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -732,11 +732,9 @@ TEST(SymbolTableTest, Memory) { } } - // Make sure we don't regress. Data as of 2019/05/29: - // - // string_mem_used: 6710912 (libc++), 7759488 (libstdc++). + // Make sure we don't regress. + // Data as of 2019/05/29: // symbol_table_mem_used: 1726056 (3.9x) -- does not seem to depend on STL sizes. - EXPECT_MEMORY_LE(string_mem_used, 7759488); EXPECT_MEMORY_LE(symbol_table_mem_used, string_mem_used / 3); EXPECT_MEMORY_EQ(symbol_table_mem_used, 1726056); } diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 18004e4ce5ec..395a84cf32e6 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -1180,7 +1180,7 @@ TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithTlsRealSymbolTable) { TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( 100, [this](absl::string_view name) { store_.counterFromString(std::string(name)); }); - EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 827632); // July 20, 2020 + EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 827616); // Sep 25, 2020 EXPECT_MEMORY_LE(memory_test.consumedBytes(), 0.9 * million_); } diff --git a/test/exe/main_common_test.cc b/test/exe/main_common_test.cc index 39d0486683d7..000142b7ff2e 100644 --- a/test/exe/main_common_test.cc +++ b/test/exe/main_common_test.cc @@ -31,6 +31,23 @@ using testing::Return; namespace Envoy { +namespace { + +#if !(defined(__clang_analyzer__) || \ + (defined(__has_feature) && \ + (__has_feature(thread_sanitizer) || __has_feature(address_sanitizer) || \ + __has_feature(memory_sanitizer)))) +const std::string& outOfMemoryPattern() { +#if defined(TCMALLOC) + CONSTRUCT_ON_FIRST_USE(std::string, ".*Unable to allocate.*"); +#else + CONSTRUCT_ON_FIRST_USE(std::string, ".*panic: out of memory.*"); +#endif +} +#endif + +} // namespace + /** * Captures common functions needed for invoking MainCommon.Maintains * an argv array that is terminated with nullptr. Identifies the config @@ -177,7 +194,7 @@ TEST_P(MainCommonDeathTest, OutOfMemoryHandler) { ENVOY_LOG_MISC(debug, "p={}", reinterpret_cast(p)); } }(), - ".*panic: out of memory.*"); + outOfMemoryPattern()); #endif } diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 08e8019111a4..c264096681f3 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -267,6 +267,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSize) { // 2020/08/10 12275 37061 38000 Re-organize tls histogram maps to improve continuity. // 2020/08/11 12202 37061 38500 router: add new retry back-off strategy // 2020/09/11 12973 38993 upstream: predictive prefetch + // 2020/10/02 13251 39326 switch to google tcmalloc // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -287,7 +288,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSize) { // https://github.com/envoyproxy/envoy/issues/12209 // EXPECT_MEMORY_EQ(m_per_cluster, 37061); } - EXPECT_MEMORY_LE(m_per_cluster, 39000); // Round up to allow platform variations. + EXPECT_MEMORY_LE(m_per_cluster, 39350); // Round up to allow platform variations. } TEST_P(ClusterMemoryTestRunner, MemoryLargeHostSizeWithStats) { From efd7cc52bffbbfba211829001738b25a5d818fa6 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 7 Oct 2020 15:32:35 -0400 Subject: [PATCH 18/27] governance: clean up release instructions (#13428) Removing a deprecated step. Risk Level: n/a Testing: n/a Docs Changes: yes Release Notes: no Signed-off-by: Alyssa Wilk --- GOVERNANCE.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 572d6eb9fcaf..8c4c2ec7dbd4 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -137,10 +137,7 @@ Deprecated ---------- ``` * Run the deprecate_versions.py script (e.g. `sh tools/deprecate_version/deprecate_version.sh`) - to file tracking issues for code which can be removed. -* Run the deprecate_features.py script (e.g. `sh tools/deprecate_features/deprecate_features.sh`) - to make the last release's deprecated features fatal-by-default. Submit the resultant PR and send - an email to envoy-announce. + to file tracking issues for runtime guarded code which can be removed. * Check source/common/runtime/runtime_features.cc and see if any runtime guards in disabled_runtime_features should be reassessed, and ping on the relevant issues. From ede6604de73b237dab35d38188d3fc293f866abe Mon Sep 17 00:00:00 2001 From: fpliu233 <62083774+fpliu233@users.noreply.github.com> Date: Wed, 7 Oct 2020 12:59:57 -0700 Subject: [PATCH 19/27] ext_authz: Add Google gRPC client cache (#13265) This patch adds Google gRPC client cache for ext_authz filter to avoid channel creation for each request, which greatly increased the latency. Risk Level: Low Testing: Added Docs Changes: N/A Release Notes: N/A Signed-off-by: Fangpeng Liu --- include/envoy/grpc/async_client.h | 1 + .../common/grpc/google_async_client_impl.cc | 1 + source/common/grpc/stat_names.cc | 3 +- source/common/grpc/stat_names.h | 2 + source/common/grpc/typed_async_client.h | 3 +- .../extensions/filters/common/ext_authz/BUILD | 1 + .../common/ext_authz/ext_authz_grpc_impl.cc | 21 +++++- .../common/ext_authz/ext_authz_grpc_impl.h | 36 +++++++++- .../filters/http/ext_authz/config.cc | 31 ++++++++- .../filters/http/ext_authz/config.h | 4 ++ .../extensions/filters/common/ext_authz/BUILD | 1 + .../ext_authz/ext_authz_grpc_impl_test.cc | 60 ++++++++++++++++ .../filters/http/ext_authz/config_test.cc | 2 + .../ext_authz/ext_authz_integration_test.cc | 69 +++++++++++++++++-- 14 files changed, 223 insertions(+), 12 deletions(-) diff --git a/include/envoy/grpc/async_client.h b/include/envoy/grpc/async_client.h index b2005723fab2..bba3d106df00 100644 --- a/include/envoy/grpc/async_client.h +++ b/include/envoy/grpc/async_client.h @@ -187,6 +187,7 @@ class RawAsyncClient { }; using RawAsyncClientPtr = std::unique_ptr; +using RawAsyncClientSharedPtr = std::shared_ptr; } // namespace Grpc } // namespace Envoy diff --git a/source/common/grpc/google_async_client_impl.cc b/source/common/grpc/google_async_client_impl.cc index e4b329d3e67e..319ee5be4693 100644 --- a/source/common/grpc/google_async_client_impl.cc +++ b/source/common/grpc/google_async_client_impl.cc @@ -88,6 +88,7 @@ GoogleAsyncClientImpl::GoogleAsyncClientImpl(Event::Dispatcher& dispatcher, // new connection implied. std::shared_ptr channel = GoogleGrpcUtils::createChannel(config, api); stub_ = stub_factory.createStub(channel); + scope_->counterFromStatName(stat_names.google_grpc_client_creation_).inc(); // Initialize client stats. // TODO(jmarantz): Capture these names in async_client_manager_impl.cc and // pass in a struct of StatName objects so we don't have to take locks here. diff --git a/source/common/grpc/stat_names.cc b/source/common/grpc/stat_names.cc index 101ddeec84db..3366c45c99cf 100644 --- a/source/common/grpc/stat_names.cc +++ b/source/common/grpc/stat_names.cc @@ -4,7 +4,8 @@ namespace Envoy { namespace Grpc { StatNames::StatNames(Stats::SymbolTable& symbol_table) - : pool_(symbol_table), streams_total_(pool_.add("streams_total")) { + : pool_(symbol_table), streams_total_(pool_.add("streams_total")), + google_grpc_client_creation_(pool_.add("google_grpc_client_creation")) { for (uint32_t i = 0; i <= Status::WellKnownGrpcStatus::MaximumKnown; ++i) { std::string status_str = absl::StrCat(i); streams_closed_[i] = pool_.add(absl::StrCat("streams_closed_", status_str)); diff --git a/source/common/grpc/stat_names.h b/source/common/grpc/stat_names.h index c0dfe03b683b..daa2f7c9aeb9 100644 --- a/source/common/grpc/stat_names.h +++ b/source/common/grpc/stat_names.h @@ -21,6 +21,8 @@ struct StatNames { Stats::StatName streams_total_; std::array streams_closed_; absl::flat_hash_map status_names_; + // Stat name tracking the creation of the Google grpc client. + Stats::StatName google_grpc_client_creation_; }; } // namespace Grpc diff --git a/source/common/grpc/typed_async_client.h b/source/common/grpc/typed_async_client.h index 241926ee4ed7..2905db8f345a 100644 --- a/source/common/grpc/typed_async_client.h +++ b/source/common/grpc/typed_async_client.h @@ -160,6 +160,7 @@ template class AsyncClient /* : public Raw public: AsyncClient() = default; AsyncClient(RawAsyncClientPtr&& client) : client_(std::move(client)) {} + AsyncClient(RawAsyncClientSharedPtr client) : client_(client) {} virtual ~AsyncClient() = default; virtual AsyncRequest* send(const Protobuf::MethodDescriptor& service_method, @@ -192,7 +193,7 @@ template class AsyncClient /* : public Raw void reset() { client_.reset(); } private: - RawAsyncClientPtr client_{}; + RawAsyncClientSharedPtr client_{}; }; } // namespace Grpc diff --git a/source/extensions/filters/common/ext_authz/BUILD b/source/extensions/filters/common/ext_authz/BUILD index fd01520b7b5c..da799d784237 100644 --- a/source/extensions/filters/common/ext_authz/BUILD +++ b/source/extensions/filters/common/ext_authz/BUILD @@ -45,6 +45,7 @@ envoy_cc_library( "//source/common/protobuf", "//source/common/tracing:http_tracer_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg_cc_proto", "@envoy_api//envoy/service/auth/v2alpha:pkg_cc_proto", "@envoy_api//envoy/service/auth/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.cc b/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.cc index 3efbfac5d26d..f4b133e2b01a 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.cc +++ b/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.cc @@ -17,11 +17,11 @@ namespace Filters { namespace Common { namespace ExtAuthz { -GrpcClientImpl::GrpcClientImpl(Grpc::RawAsyncClientPtr&& async_client, +GrpcClientImpl::GrpcClientImpl(Grpc::RawAsyncClientSharedPtr async_client, const absl::optional& timeout, envoy::config::core::v3::ApiVersion transport_api_version, bool use_alpha) - : async_client_(std::move(async_client)), timeout_(timeout), + : async_client_(async_client), timeout_(timeout), service_method_(Grpc::VersionedMethods("envoy.service.auth.v3.Authorization.Check", "envoy.service.auth.v2.Authorization.Check", "envoy.service.auth.v2alpha.Authorization.Check") @@ -143,6 +143,23 @@ void GrpcClientImpl::toAuthzResponseHeader( } } +const Grpc::RawAsyncClientSharedPtr AsyncClientCache::getOrCreateAsyncClient( + const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& proto_config) { + // The cache stores Google gRPC client, so channel is not created for each request. + ASSERT(proto_config.has_grpc_service() && proto_config.grpc_service().has_google_grpc()); + auto& cache = tls_slot_->getTyped(); + const std::size_t cache_key = MessageUtil::hash(proto_config.grpc_service().google_grpc()); + const auto it = cache.async_clients_.find(cache_key); + if (it != cache.async_clients_.end()) { + return it->second; + } + const Grpc::AsyncClientFactoryPtr factory = + async_client_manager_.factoryForGrpcService(proto_config.grpc_service(), scope_, true); + const Grpc::RawAsyncClientSharedPtr async_client = factory->create(); + cache.async_clients_.emplace(cache_key, async_client); + return async_client; +} + } // namespace ExtAuthz } // namespace Common } // namespace Filters diff --git a/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.h b/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.h index 626a17eb2bb7..5fb3cf46a1bb 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.h +++ b/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.h @@ -7,6 +7,7 @@ #include #include "envoy/config/core/v3/base.pb.h" +#include "envoy/extensions/filters/http/ext_authz/v3/ext_authz.pb.h" #include "envoy/grpc/async_client.h" #include "envoy/grpc/async_client_manager.h" #include "envoy/http/filter.h" @@ -44,7 +45,7 @@ class GrpcClientImpl : public Client, public Logger::Loggable { public: // TODO(gsagula): remove `use_alpha` param when V2Alpha gets deprecated. - GrpcClientImpl(Grpc::RawAsyncClientPtr&& async_client, + GrpcClientImpl(Grpc::RawAsyncClientSharedPtr async_client, const absl::optional& timeout, envoy::config::core::v3::ApiVersion transport_api_version, bool use_alpha); ~GrpcClientImpl() override; @@ -81,6 +82,39 @@ class GrpcClientImpl : public Client, using GrpcClientImplPtr = std::unique_ptr; +// The client cache for RawAsyncClient for Google grpc so channel is not created for each request. +// TODO(fpliu233): The cache will cause resource leak that a new channel is created every time a new +// config is pushed. Improve gRPC channel cache with better solution. +class AsyncClientCache : public Singleton::Instance { +public: + AsyncClientCache(Grpc::AsyncClientManager& async_client_manager, Stats::Scope& scope, + ThreadLocal::SlotAllocator& tls) + : async_client_manager_(async_client_manager), scope_(scope), tls_slot_(tls.allocateSlot()) { + tls_slot_->set([](Event::Dispatcher&) { return std::make_shared(); }); + } + + const Grpc::RawAsyncClientSharedPtr getOrCreateAsyncClient( + const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& proto_config); + +private: + /** + * Per-thread cache. + */ + struct ThreadLocalCache : public ThreadLocal::ThreadLocalObject { + ThreadLocalCache() = default; + // The client cache stored with key as hash of + // envoy::config::core::v3::GrpcService::GoogleGrpc config. + // TODO(fpliu233): Remove when the cleaner and generic solution for gRPC is live. + absl::flat_hash_map async_clients_; + }; + + Grpc::AsyncClientManager& async_client_manager_; + Stats::Scope& scope_; + ThreadLocal::SlotPtr tls_slot_; +}; + +using AsyncClientCacheSharedPtr = std::shared_ptr; + } // namespace ExtAuthz } // namespace Common } // namespace Filters diff --git a/source/extensions/filters/http/ext_authz/config.cc b/source/extensions/filters/http/ext_authz/config.cc index 0888c1dda022..2d258f2da60b 100644 --- a/source/extensions/filters/http/ext_authz/config.cc +++ b/source/extensions/filters/http/ext_authz/config.cc @@ -41,8 +41,23 @@ Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoTyped( callbacks.addStreamDecoderFilter(Http::StreamDecoderFilterSharedPtr{ std::make_shared(filter_config, std::move(client))}); }; + } else if (proto_config.grpc_service().has_google_grpc()) { + // Google gRPC client. + const uint32_t timeout_ms = + PROTOBUF_GET_MS_OR_DEFAULT(proto_config.grpc_service(), timeout, DefaultTimeout); + auto async_client_cache = getAsyncClientCacheSingleton(context); + callback = [async_client_cache, filter_config, timeout_ms, proto_config, + transport_api_version = proto_config.transport_api_version(), + use_alpha = proto_config.hidden_envoy_deprecated_use_alpha()]( + Http::FilterChainFactoryCallbacks& callbacks) { + auto client = std::make_unique( + async_client_cache->getOrCreateAsyncClient(proto_config), + std::chrono::milliseconds(timeout_ms), transport_api_version, use_alpha); + callbacks.addStreamDecoderFilter(Http::StreamDecoderFilterSharedPtr{ + std::make_shared(filter_config, std::move(client))}); + }; } else { - // gRPC client. + // Envoy gRPC client. const uint32_t timeout_ms = PROTOBUF_GET_MS_OR_DEFAULT(proto_config.grpc_service(), timeout, DefaultTimeout); callback = [grpc_service = proto_config.grpc_service(), &context, filter_config, timeout_ms, @@ -61,7 +76,7 @@ Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoTyped( } return callback; -}; +} Router::RouteSpecificFilterConfigConstSharedPtr ExtAuthzFilterConfig::createRouteSpecificFilterConfigTyped( @@ -76,6 +91,18 @@ ExtAuthzFilterConfig::createRouteSpecificFilterConfigTyped( REGISTER_FACTORY(ExtAuthzFilterConfig, Server::Configuration::NamedHttpFilterConfigFactory){"envoy.ext_authz"}; +SINGLETON_MANAGER_REGISTRATION(google_grpc_async_client_cache); + +Filters::Common::ExtAuthz::AsyncClientCacheSharedPtr +getAsyncClientCacheSingleton(Server::Configuration::FactoryContext& context) { + return context.singletonManager().getTyped( + SINGLETON_MANAGER_REGISTERED_NAME(google_grpc_async_client_cache), [&context] { + return std::make_shared( + context.clusterManager().grpcAsyncClientManager(), context.scope(), + context.threadLocal()); + }); +} + } // namespace ExtAuthz } // namespace HttpFilters } // namespace Extensions diff --git a/source/extensions/filters/http/ext_authz/config.h b/source/extensions/filters/http/ext_authz/config.h index cf6dc6ddf914..5ca66d423805 100644 --- a/source/extensions/filters/http/ext_authz/config.h +++ b/source/extensions/filters/http/ext_authz/config.h @@ -3,6 +3,7 @@ #include "envoy/extensions/filters/http/ext_authz/v3/ext_authz.pb.h" #include "envoy/extensions/filters/http/ext_authz/v3/ext_authz.pb.validate.h" +#include "extensions/filters/common/ext_authz/ext_authz_grpc_impl.h" #include "extensions/filters/http/common/factory_base.h" #include "extensions/filters/http/well_known_names.h" @@ -33,6 +34,9 @@ class ExtAuthzFilterConfig ProtobufMessage::ValidationVisitor& validator) override; }; +Filters::Common::ExtAuthz::AsyncClientCacheSharedPtr +getAsyncClientCacheSingleton(Server::Configuration::FactoryContext& context); + } // namespace ExtAuthz } // namespace HttpFilters } // namespace Extensions diff --git a/test/extensions/filters/common/ext_authz/BUILD b/test/extensions/filters/common/ext_authz/BUILD index ca23f69a5d3a..cec0bdbe9bf7 100644 --- a/test/extensions/filters/common/ext_authz/BUILD +++ b/test/extensions/filters/common/ext_authz/BUILD @@ -32,6 +32,7 @@ envoy_cc_test( deps = [ "//source/extensions/filters/common/ext_authz:ext_authz_grpc_lib", "//test/extensions/filters/common/ext_authz:ext_authz_test_common", + "//test/mocks/thread_local:thread_local_mocks", "//test/mocks/tracing:tracing_mocks", "//test/test_common:test_runtime_lib", "@envoy_api//envoy/service/auth/v2alpha:pkg_cc_proto", diff --git a/test/extensions/filters/common/ext_authz/ext_authz_grpc_impl_test.cc b/test/extensions/filters/common/ext_authz/ext_authz_grpc_impl_test.cc index 1e289f6df716..0d8cabfa1b60 100644 --- a/test/extensions/filters/common/ext_authz/ext_authz_grpc_impl_test.cc +++ b/test/extensions/filters/common/ext_authz/ext_authz_grpc_impl_test.cc @@ -12,6 +12,7 @@ #include "test/extensions/filters/common/ext_authz/test_common.h" #include "test/mocks/grpc/mocks.h" #include "test/mocks/stream_info/mocks.h" +#include "test/mocks/thread_local/mocks.h" #include "test/mocks/tracing/mocks.h" #include "test/test_common/test_runtime.h" @@ -373,6 +374,65 @@ TEST_P(ExtAuthzGrpcClientTest, AuthorizationOkWithDynamicMetadata) { client_->onSuccess(std::move(check_response), span_); } +class AsyncClientCacheTest : public testing::Test { +public: + AsyncClientCacheTest() { + client_cache_ = std::make_unique(async_client_manager_, scope_, tls_); + } + + void expectClientCreation() { + factory_ = new Grpc::MockAsyncClientFactory; + async_client_ = new Grpc::MockAsyncClient; + EXPECT_CALL(async_client_manager_, factoryForGrpcService(_, _, true)) + .WillOnce(Invoke([this](const envoy::config::core::v3::GrpcService&, Stats::Scope&, bool) { + EXPECT_CALL(*factory_, create()).WillOnce(Invoke([this] { + return Grpc::RawAsyncClientPtr{async_client_}; + })); + return Grpc::AsyncClientFactoryPtr{factory_}; + })); + } + + NiceMock tls_; + Grpc::MockAsyncClientManager async_client_manager_; + Grpc::MockAsyncClient* async_client_ = nullptr; + Grpc::MockAsyncClientFactory* factory_ = nullptr; + std::unique_ptr client_cache_; + NiceMock scope_; +}; + +TEST_F(AsyncClientCacheTest, Deduplication) { + Stats::IsolatedStoreImpl scope; + testing::InSequence s; + + envoy::extensions::filters::http::ext_authz::v3::ExtAuthz config; + config.mutable_grpc_service()->mutable_google_grpc()->set_target_uri("dns://test01"); + config.mutable_grpc_service()->mutable_google_grpc()->set_credentials_factory_name( + "test_credential01"); + + expectClientCreation(); + Grpc::RawAsyncClientSharedPtr test_client_01 = client_cache_->getOrCreateAsyncClient(config); + // Fetches the existing client. + EXPECT_EQ(test_client_01, client_cache_->getOrCreateAsyncClient(config)); + + config.mutable_grpc_service()->mutable_google_grpc()->set_credentials_factory_name( + "test_credential02"); + expectClientCreation(); + // Different credentials use different clients. + EXPECT_NE(test_client_01, client_cache_->getOrCreateAsyncClient(config)); + Grpc::RawAsyncClientSharedPtr test_client_02 = client_cache_->getOrCreateAsyncClient(config); + + config.mutable_grpc_service()->mutable_google_grpc()->set_credentials_factory_name( + "test_credential02"); + // No creation, fetching the existing one. + EXPECT_EQ(test_client_02, client_cache_->getOrCreateAsyncClient(config)); + + // Different targets use different clients. + config.mutable_grpc_service()->mutable_google_grpc()->set_target_uri("dns://test02"); + expectClientCreation(); + EXPECT_NE(test_client_01, client_cache_->getOrCreateAsyncClient(config)); + EXPECT_NE(test_client_02, client_cache_->getOrCreateAsyncClient(config)); +} + } // namespace ExtAuthz } // namespace Common } // namespace Filters diff --git a/test/extensions/filters/http/ext_authz/config_test.cc b/test/extensions/filters/http/ext_authz/config_test.cc index 3974cb24233b..c198dc5152ef 100644 --- a/test/extensions/filters/http/ext_authz/config_test.cc +++ b/test/extensions/filters/http/ext_authz/config_test.cc @@ -36,6 +36,8 @@ void expectCorrectProtoGrpc(envoy::config::core::v3::ApiVersion api_version) { fmt::format(yaml, TestUtility::getVersionStringFromApiVersion(api_version)), *proto_config); testing::StrictMock context; + EXPECT_CALL(context, singletonManager()).Times(1); + EXPECT_CALL(context, threadLocal()).Times(1); EXPECT_CALL(context, messageValidationVisitor()).Times(1); EXPECT_CALL(context, clusterManager()).Times(1); EXPECT_CALL(context, runtime()).Times(1); diff --git a/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc index 9828b56e5b22..ae82ccfbe6b5 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc @@ -311,11 +311,7 @@ class ExtAuthzGrpcIntegrationTest : public Grpc::VersionedGrpcClientIntegrationP void cleanup() { if (fake_ext_authz_connection_ != nullptr) { - if (clientType() != Grpc::ClientType::GoogleGrpc) { - AssertionResult result = fake_ext_authz_connection_->close(); - RELEASE_ASSERT(result, result.message()); - } - AssertionResult result = fake_ext_authz_connection_->waitForDisconnect(); + AssertionResult result = fake_ext_authz_connection_->close(); RELEASE_ASSERT(result, result.message()); } cleanupUpstreamAndDownstream(); @@ -827,4 +823,67 @@ TEST_P(ExtAuthzLocalReplyIntegrationTest, DeniedHeaderTest) { cleanup(); } +TEST_P(ExtAuthzGrpcIntegrationTest, GoogleAsyncClientCreation) { + initializeConfig(); + setDownstreamProtocol(Http::CodecClient::Type::HTTP2); + HttpIntegrationTest::initialize(); + initiateClientConnection(4, Headers{}, Headers{}); + + waitForExtAuthzRequest(expectedCheckRequest(Http::CodecClient::Type::HTTP2)); + if (clientType() == Grpc::ClientType::GoogleGrpc) { + // Make sure one Google grpc client is created. + EXPECT_EQ(1, test_server_->counter("grpc.ext_authz.google_grpc_client_creation")->value()); + } + sendExtAuthzResponse(Headers{}, Headers{}, Headers{}, Http::TestRequestHeaderMapImpl{}, + Http::TestRequestHeaderMapImpl{}); + + waitForSuccessfulUpstreamResponse("200"); + + Http::TestRequestHeaderMapImpl headers{ + {":method", "POST"}, {":path", "/test"}, {":scheme", "http"}, {":authority", "host"}}; + TestUtility::feedBufferWithRandomCharacters(request_body_, 4); + response_ = codec_client_->makeRequestWithBody(headers, request_body_.toString()); + + auto result = fake_ext_authz_connection_->waitForNewStream(*dispatcher_, ext_authz_request_); + RELEASE_ASSERT(result, result.message()); + + envoy::service::auth::v3::CheckRequest check_request; + result = ext_authz_request_->waitForGrpcMessage(*dispatcher_, check_request); + RELEASE_ASSERT(result, result.message()); + + EXPECT_EQ("POST", ext_authz_request_->headers().getMethodValue()); + EXPECT_EQ(TestUtility::getVersionedMethodPath("envoy.service.auth.{}.Authorization", "Check", + apiVersion()), + ext_authz_request_->headers().getPathValue()); + EXPECT_EQ("application/grpc", ext_authz_request_->headers().getContentTypeValue()); + result = ext_authz_request_->waitForEndStream(*dispatcher_); + RELEASE_ASSERT(result, result.message()); + + if (clientType() == Grpc::ClientType::GoogleGrpc) { + // Make sure one Google grpc client is created. + EXPECT_EQ(1, test_server_->counter("grpc.ext_authz.google_grpc_client_creation")->value()); + } + sendExtAuthzResponse(Headers{}, Headers{}, Headers{}, Http::TestRequestHeaderMapImpl{}, + Http::TestRequestHeaderMapImpl{}); + + result = fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_); + RELEASE_ASSERT(result, result.message()); + result = upstream_request_->waitForEndStream(*dispatcher_); + RELEASE_ASSERT(result, result.message()); + + upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, false); + upstream_request_->encodeData(response_size_, true); + + response_->waitForEndStream(); + + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(request_body_.length(), upstream_request_->bodyLength()); + + EXPECT_TRUE(response_->complete()); + EXPECT_EQ("200", response_->headers().getStatusValue()); + EXPECT_EQ(response_size_, response_->body().size()); + + cleanup(); +} + } // namespace Envoy From d62a3882e584c591cddb2d7a5d0752312bd07344 Mon Sep 17 00:00:00 2001 From: Florin Coras Date: Wed, 7 Oct 2020 16:08:31 -0700 Subject: [PATCH 20/27] network: pass buffer to io handle for writing (#13282) Signed-off-by: Florin Coras --- include/envoy/buffer/BUILD | 1 - include/envoy/buffer/buffer.h | 10 -- include/envoy/network/io_handle.h | 9 ++ source/common/buffer/buffer_impl.cc | 10 -- source/common/buffer/buffer_impl.h | 2 - source/common/buffer/watermark_buffer.cc | 6 - source/common/buffer/watermark_buffer.h | 1 - .../common/network/io_socket_handle_impl.cc | 10 ++ source/common/network/io_socket_handle_impl.h | 2 + source/common/network/raw_buffer_socket.cc | 2 +- .../quiche/quic_io_handle_wrapper.h | 7 + .../proxy_protocol/proxy_protocol.cc | 2 +- test/common/buffer/buffer_fuzz.cc | 11 +- test/common/buffer/owned_impl_test.cc | 14 +- test/common/buffer/watermark_buffer_test.cc | 2 +- test/common/network/BUILD | 2 + test/common/network/connection_impl_test.cc | 55 ++++--- .../proxy_protocol/proxy_protocol_test.cc | 144 +++++++++++------- test/integration/integration_tcp_client.cc | 12 +- .../integration/tcp_proxy_integration_test.cc | 4 +- test/mocks/api/mocks.cc | 12 ++ test/mocks/buffer/mocks.h | 24 +-- test/mocks/network/io_handle.h | 1 + 23 files changed, 179 insertions(+), 164 deletions(-) diff --git a/include/envoy/buffer/BUILD b/include/envoy/buffer/BUILD index d16a2fe36505..499ec8605a2e 100644 --- a/include/envoy/buffer/BUILD +++ b/include/envoy/buffer/BUILD @@ -16,7 +16,6 @@ envoy_cc_library( ], deps = [ "//include/envoy/api:os_sys_calls_interface", - "//include/envoy/network:io_handle_interface", "//source/common/common:byte_order_lib", "//source/common/common:utility_lib", ], diff --git a/include/envoy/buffer/buffer.h b/include/envoy/buffer/buffer.h index ab810e374e07..3ab150504ccd 100644 --- a/include/envoy/buffer/buffer.h +++ b/include/envoy/buffer/buffer.h @@ -9,7 +9,6 @@ #include "envoy/common/exception.h" #include "envoy/common/platform.h" #include "envoy/common/pure.h" -#include "envoy/network/io_handle.h" #include "common/common/byte_order.h" #include "common/common/utility.h" @@ -237,15 +236,6 @@ class Instance { */ virtual std::string toString() const PURE; - /** - * Write the buffer out to a file descriptor. - * @param io_handle supplies the io_handle to write to. - * @return a IoCallUint64Result with err_ = nullptr and rc_ = the number of bytes - * written if successful, or err_ = some IoError for failure. If call failed, rc_ shouldn't be - * used. - */ - virtual Api::IoCallUint64Result write(Network::IoHandle& io_handle) PURE; - /** * Copy an integer out of the buffer. * @param start supplies the buffer index to start copying from. diff --git a/include/envoy/network/io_handle.h b/include/envoy/network/io_handle.h index 0bd769092be6..f11855014a3e 100644 --- a/include/envoy/network/io_handle.h +++ b/include/envoy/network/io_handle.h @@ -85,6 +85,15 @@ class IoHandle { */ virtual Api::IoCallUint64Result writev(const Buffer::RawSlice* slices, uint64_t num_slice) PURE; + /** + * Write the buffer out to a file descriptor. + * @param buffer supplies the buffer to write to. + * @return a IoCallUint64Result with err_ = nullptr and rc_ = the number of bytes + * written if successful, or err_ = some IoError for failure. If call failed, rc_ shouldn't be + * used. + */ + virtual Api::IoCallUint64Result write(Buffer::Instance& buffer) PURE; + /** * Send a message to the address. * @param slices points to the location of data to be sent. diff --git a/source/common/buffer/buffer_impl.cc b/source/common/buffer/buffer_impl.cc index 801f0f4621a6..556faf73d638 100644 --- a/source/common/buffer/buffer_impl.cc +++ b/source/common/buffer/buffer_impl.cc @@ -498,16 +498,6 @@ bool OwnedImpl::startsWith(absl::string_view data) const { NOT_REACHED_GCOVR_EXCL_LINE; } -Api::IoCallUint64Result OwnedImpl::write(Network::IoHandle& io_handle) { - constexpr uint64_t MaxSlices = 16; - RawSliceVector slices = getRawSlices(MaxSlices); - Api::IoCallUint64Result result = io_handle.writev(slices.begin(), slices.size()); - if (result.ok() && result.rc_ > 0) { - drain(static_cast(result.rc_)); - } - return result; -} - OwnedImpl::OwnedImpl() = default; OwnedImpl::OwnedImpl(absl::string_view data) : OwnedImpl() { add(data); } diff --git a/source/common/buffer/buffer_impl.h b/source/common/buffer/buffer_impl.h index 9d2a9736befa..cfbaadd82323 100644 --- a/source/common/buffer/buffer_impl.h +++ b/source/common/buffer/buffer_impl.h @@ -6,7 +6,6 @@ #include #include "envoy/buffer/buffer.h" -#include "envoy/network/io_handle.h" #include "common/common/assert.h" #include "common/common/non_copyable.h" @@ -573,7 +572,6 @@ class OwnedImpl : public LibEventInstance { uint64_t reserve(uint64_t length, RawSlice* iovecs, uint64_t num_iovecs) override; ssize_t search(const void* data, uint64_t size, size_t start, size_t length) const override; bool startsWith(absl::string_view data) const override; - Api::IoCallUint64Result write(Network::IoHandle& io_handle) override; std::string toString() const override; // LibEventInstance diff --git a/source/common/buffer/watermark_buffer.cc b/source/common/buffer/watermark_buffer.cc index c8a005ae4071..0503266085b7 100644 --- a/source/common/buffer/watermark_buffer.cc +++ b/source/common/buffer/watermark_buffer.cc @@ -63,12 +63,6 @@ uint64_t WatermarkBuffer::reserve(uint64_t length, RawSlice* iovecs, uint64_t nu return bytes_reserved; } -Api::IoCallUint64Result WatermarkBuffer::write(Network::IoHandle& io_handle) { - Api::IoCallUint64Result result = OwnedImpl::write(io_handle); - checkLowWatermark(); - return result; -} - void WatermarkBuffer::appendSliceForTest(const void* data, uint64_t size) { OwnedImpl::appendSliceForTest(data, size); checkHighAndOverflowWatermarks(); diff --git a/source/common/buffer/watermark_buffer.h b/source/common/buffer/watermark_buffer.h index a41bb8ffa790..23c7c32854d2 100644 --- a/source/common/buffer/watermark_buffer.h +++ b/source/common/buffer/watermark_buffer.h @@ -36,7 +36,6 @@ class WatermarkBuffer : public OwnedImpl { void move(Instance& rhs, uint64_t length) override; SliceDataPtr extractMutableFrontSlice() override; uint64_t reserve(uint64_t length, RawSlice* iovecs, uint64_t num_iovecs) override; - Api::IoCallUint64Result write(Network::IoHandle& io_handle) override; void postProcess() override { checkLowWatermark(); } void appendSliceForTest(const void* data, uint64_t size) override; void appendSliceForTest(absl::string_view data) override; diff --git a/source/common/network/io_socket_handle_impl.cc b/source/common/network/io_socket_handle_impl.cc index df1732fc1627..52a6ae6e5ddb 100644 --- a/source/common/network/io_socket_handle_impl.cc +++ b/source/common/network/io_socket_handle_impl.cc @@ -120,6 +120,16 @@ Api::IoCallUint64Result IoSocketHandleImpl::writev(const Buffer::RawSlice* slice Api::OsSysCallsSingleton::get().writev(fd_, iov.begin(), num_slices_to_write)); } +Api::IoCallUint64Result IoSocketHandleImpl::write(Buffer::Instance& buffer) { + constexpr uint64_t MaxSlices = 16; + Buffer::RawSliceVector slices = buffer.getRawSlices(MaxSlices); + Api::IoCallUint64Result result = writev(slices.begin(), slices.size()); + if (result.ok() && result.rc_ > 0) { + buffer.drain(static_cast(result.rc_)); + } + return result; +} + Api::IoCallUint64Result IoSocketHandleImpl::sendmsg(const Buffer::RawSlice* slices, uint64_t num_slice, int flags, const Address::Ip* self_ip, diff --git a/source/common/network/io_socket_handle_impl.h b/source/common/network/io_socket_handle_impl.h index 914f85b37739..6b811bfe505f 100644 --- a/source/common/network/io_socket_handle_impl.h +++ b/source/common/network/io_socket_handle_impl.h @@ -37,6 +37,8 @@ class IoSocketHandleImpl : public IoHandle, protected Logger::LoggableioHandle()); + Api::IoCallUint64Result result = callbacks_->ioHandle().write(buffer); if (result.ok()) { ENVOY_CONN_LOG(trace, "write returns: {}", callbacks_->connection(), result.rc_); diff --git a/source/extensions/quic_listeners/quiche/quic_io_handle_wrapper.h b/source/extensions/quic_listeners/quiche/quic_io_handle_wrapper.h index 96159db116d0..023b0ec5b43f 100644 --- a/source/extensions/quic_listeners/quiche/quic_io_handle_wrapper.h +++ b/source/extensions/quic_listeners/quiche/quic_io_handle_wrapper.h @@ -43,6 +43,13 @@ class QuicIoHandleWrapper : public Network::IoHandle { } return io_handle_.writev(slices, num_slice); } + Api::IoCallUint64Result write(Buffer::Instance& buffer) override { + if (closed_) { + return Api::IoCallUint64Result(0, Api::IoErrorPtr(new Network::IoSocketError(EBADF), + Network::IoSocketError::deleteIoError)); + } + return io_handle_.write(buffer); + } Api::IoCallUint64Result sendmsg(const Buffer::RawSlice* slices, uint64_t num_slice, int flags, const Envoy::Network::Address::Ip* self_ip, const Network::Address::Instance& peer_address) override { diff --git a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc index a32b5c7bdd70..3d4f716421e7 100644 --- a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc +++ b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc @@ -82,7 +82,7 @@ Network::IoResult UpstreamProxyProtocolSocket::writeHeader() { break; } - Api::IoCallUint64Result result = header_buffer_.write(callbacks_->ioHandle()); + Api::IoCallUint64Result result = callbacks_->ioHandle().write(header_buffer_); if (result.ok()) { ENVOY_CONN_LOG(trace, "write returns: {}", callbacks_->connection(), result.rc_); diff --git a/test/common/buffer/buffer_fuzz.cc b/test/common/buffer/buffer_fuzz.cc index 793481f0a75f..4128ceea866d 100644 --- a/test/common/buffer/buffer_fuzz.cc +++ b/test/common/buffer/buffer_fuzz.cc @@ -163,15 +163,6 @@ class StringBuffer : public Buffer::Instance { std::string toString() const override { return std::string(data_.data() + start_, size_); } - Api::IoCallUint64Result write(Network::IoHandle& io_handle) override { - const Buffer::RawSlice slice{const_cast(start()), size_}; - Api::IoCallUint64Result result = io_handle.writev(&slice, 1); - FUZZ_ASSERT(result.ok()); - start_ += result.rc_; - size_ -= result.rc_; - return result; - } - absl::string_view asStringView() const { return {start(), size_}; } char* mutableStart() { return data_.data() + start_; } @@ -361,7 +352,7 @@ uint32_t bufferAction(Context& ctxt, char insert_value, uint32_t max_alloc, Buff do { const bool empty = target_buffer.length() == 0; const std::string previous_data = target_buffer.toString(); - const auto result = target_buffer.write(io_handle); + const auto result = io_handle.write(target_buffer); FUZZ_ASSERT(result.ok()); rc = result.rc_; ENVOY_LOG_MISC(trace, "Write rc: {} errno: {}", rc, diff --git a/test/common/buffer/owned_impl_test.cc b/test/common/buffer/owned_impl_test.cc index 1bcd9cf473a9..dc15d80b4b5d 100644 --- a/test/common/buffer/owned_impl_test.cc +++ b/test/common/buffer/owned_impl_test.cc @@ -269,45 +269,45 @@ TEST_F(OwnedImplTest, Write) { Network::IoSocketHandleImpl io_handle; buffer.add("example"); EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{7, 0})); - Api::IoCallUint64Result result = buffer.write(io_handle); + Api::IoCallUint64Result result = io_handle.write(buffer); EXPECT_TRUE(result.ok()); EXPECT_EQ(7, result.rc_); EXPECT_EQ(0, buffer.length()); buffer.add("example"); EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{6, 0})); - result = buffer.write(io_handle); + result = io_handle.write(buffer); EXPECT_TRUE(result.ok()); EXPECT_EQ(6, result.rc_); EXPECT_EQ(1, buffer.length()); EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{0, 0})); - result = buffer.write(io_handle); + result = io_handle.write(buffer); EXPECT_TRUE(result.ok()); EXPECT_EQ(0, result.rc_); EXPECT_EQ(1, buffer.length()); EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{-1, 0})); - result = buffer.write(io_handle); + result = io_handle.write(buffer); EXPECT_EQ(Api::IoError::IoErrorCode::UnknownError, result.err_->getErrorCode()); EXPECT_EQ(0, result.rc_); EXPECT_EQ(1, buffer.length()); EXPECT_CALL(os_sys_calls, writev(_, _, _)) .WillOnce(Return(Api::SysCallSizeResult{-1, SOCKET_ERROR_AGAIN})); - result = buffer.write(io_handle); + result = io_handle.write(buffer); EXPECT_EQ(Api::IoError::IoErrorCode::Again, result.err_->getErrorCode()); EXPECT_EQ(0, result.rc_); EXPECT_EQ(1, buffer.length()); EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{1, 0})); - result = buffer.write(io_handle); + result = io_handle.write(buffer); EXPECT_TRUE(result.ok()); EXPECT_EQ(1, result.rc_); EXPECT_EQ(0, buffer.length()); EXPECT_CALL(os_sys_calls, writev(_, _, _)).Times(0); - result = buffer.write(io_handle); + result = io_handle.write(buffer); EXPECT_EQ(0, result.rc_); EXPECT_EQ(0, buffer.length()); } diff --git a/test/common/buffer/watermark_buffer_test.cc b/test/common/buffer/watermark_buffer_test.cc index b1a565561246..1ce8149f3dac 100644 --- a/test/common/buffer/watermark_buffer_test.cc +++ b/test/common/buffer/watermark_buffer_test.cc @@ -253,7 +253,7 @@ TEST_F(WatermarkBufferTest, WatermarkFdFunctions) { int bytes_written_total = 0; Network::IoSocketHandleImpl io_handle1(pipe_fds[1]); while (bytes_written_total < 20) { - Api::IoCallUint64Result result = buffer_.write(io_handle1); + Api::IoCallUint64Result result = io_handle1.write(buffer_); if (!result.ok()) { ASSERT_EQ(Api::IoError::IoErrorCode::Again, result.err_->getErrorCode()); } else { diff --git a/test/common/network/BUILD b/test/common/network/BUILD index d173d8195e7b..ef62c131dba5 100644 --- a/test/common/network/BUILD +++ b/test/common/network/BUILD @@ -83,6 +83,7 @@ envoy_cc_test( "//source/common/network:listen_socket_lib", "//source/common/network:utility_lib", "//source/common/stats:stats_lib", + "//test/mocks/api:api_mocks", "//test/mocks/buffer:buffer_mocks", "//test/mocks/event:event_mocks", "//test/mocks/network:network_mocks", @@ -91,6 +92,7 @@ envoy_cc_test( "//test/test_common:network_utility_lib", "//test/test_common:simulated_time_system_lib", "//test/test_common:test_time_lib", + "//test/test_common:threadsafe_singleton_injector_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) diff --git a/test/common/network/connection_impl_test.cc b/test/common/network/connection_impl_test.cc index d62524e262d4..47ba64c2bbb1 100644 --- a/test/common/network/connection_impl_test.cc +++ b/test/common/network/connection_impl_test.cc @@ -5,6 +5,7 @@ #include "envoy/common/platform.h" #include "envoy/config/core/v3/base.pb.h" +#include "common/api/os_sys_calls_impl.h" #include "common/buffer/buffer_impl.h" #include "common/common/empty_string.h" #include "common/common/fmt.h" @@ -16,6 +17,7 @@ #include "common/network/utility.h" #include "common/runtime/runtime_impl.h" +#include "test/mocks/api/mocks.h" #include "test/mocks/buffer/mocks.h" #include "test/mocks/event/mocks.h" #include "test/mocks/network/mocks.h" @@ -24,6 +26,7 @@ #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/threadsafe_singleton_injector.h" #include "test/test_common/utility.h" #include "gmock/gmock.h" @@ -169,14 +172,14 @@ class ConnectionImplTest : public testing::TestWithParam { if (client_write_buffer_) { EXPECT_CALL(*client_write_buffer_, drain(_)) .Times(AnyNumber()) - .WillOnce(Invoke([&](uint64_t size) -> void { client_write_buffer_->baseDrain(size); })); + .WillRepeatedly( + Invoke([&](uint64_t size) -> void { client_write_buffer_->baseDrain(size); })); } EXPECT_CALL(client_callbacks_, onEvent(ConnectionEvent::LocalClose)); client_connection_->close(ConnectionCloseType::NoFlush); if (wait_for_remote_close) { EXPECT_CALL(server_callbacks_, onEvent(ConnectionEvent::RemoteClose)) .WillOnce(Invoke([&](Network::ConnectionEvent) -> void { dispatcher_->exit(); })); - dispatcher_->run(Event::Dispatcher::RunType::Block); } else { dispatcher_->run(Event::Dispatcher::RunType::NonBlock); @@ -779,8 +782,6 @@ TEST_P(ConnectionImplTest, WriteWatermarks) { // Stick 5 bytes in the connection buffer. std::unique_ptr buffer(new Buffer::OwnedImpl("hello")); int buffer_len = buffer->length(); - EXPECT_CALL(*client_write_buffer_, write(_)) - .WillOnce(Invoke(client_write_buffer_, &MockWatermarkBuffer::failWrite)); EXPECT_CALL(*client_write_buffer_, move(_)); client_write_buffer_->move(*buffer); @@ -950,8 +951,6 @@ TEST_P(ConnectionImplTest, BasicWrite) { EXPECT_CALL(*client_write_buffer_, move(_)) .WillRepeatedly(DoAll(AddBufferToStringWithoutDraining(&data_written), Invoke(client_write_buffer_, &MockWatermarkBuffer::baseMove))); - EXPECT_CALL(*client_write_buffer_, write(_)) - .WillOnce(Invoke(client_write_buffer_, &MockWatermarkBuffer::trackWrites)); EXPECT_CALL(*client_write_buffer_, drain(_)) .WillOnce(Invoke(client_write_buffer_, &MockWatermarkBuffer::trackDrains)); client_connection_->write(buffer_to_write, false); @@ -977,8 +976,6 @@ TEST_P(ConnectionImplTest, WriteWithWatermarks) { EXPECT_CALL(*client_write_buffer_, move(_)) .WillRepeatedly(DoAll(AddBufferToStringWithoutDraining(&data_written), Invoke(client_write_buffer_, &MockWatermarkBuffer::baseMove))); - EXPECT_CALL(*client_write_buffer_, write(_)) - .WillOnce(Invoke(client_write_buffer_, &MockWatermarkBuffer::trackWrites)); EXPECT_CALL(*client_write_buffer_, drain(_)) .WillOnce(Invoke(client_write_buffer_, &MockWatermarkBuffer::trackDrains)); // The write() call on the connection will buffer enough data to bring the connection above the @@ -987,21 +984,26 @@ TEST_P(ConnectionImplTest, WriteWithWatermarks) { // connection_impl, and try an immediate drain inside of write() to avoid thrashing here. EXPECT_CALL(client_callbacks_, onAboveWriteBufferHighWatermark()); EXPECT_CALL(client_callbacks_, onBelowWriteBufferLowWatermark()); + client_connection_->write(first_buffer_to_write, false); dispatcher_->run(Event::Dispatcher::RunType::NonBlock); EXPECT_EQ(data_to_write, data_written); - // Now do the write again, but this time configure buffer_ to reject the write - // with errno set to EAGAIN via failWrite(). This should result in going above the high + // Now do the write again, but this time configure os_sys_calls to reject the write + // with errno set to EAGAIN. This should result in going above the high // watermark and not returning. Buffer::OwnedImpl second_buffer_to_write(data_to_write); EXPECT_CALL(*client_write_buffer_, move(_)) .WillRepeatedly(DoAll(AddBufferToStringWithoutDraining(&data_written), Invoke(client_write_buffer_, &MockWatermarkBuffer::baseMove))); - EXPECT_CALL(*client_write_buffer_, write(_)) - .WillOnce(Invoke([&](IoHandle& io_handle) -> Api::IoCallUint64Result { + NiceMock os_sys_calls; + TestThreadsafeSingletonInjector os_calls(&os_sys_calls); + EXPECT_CALL(os_sys_calls, writev(_, _, _)) + .WillOnce(Invoke([&](os_fd_t, const iovec*, int) -> Api::SysCallSizeResult { dispatcher_->exit(); - return client_write_buffer_->failWrite(io_handle); + // Return to default os_sys_calls implementation + os_calls.~TestThreadsafeSingletonInjector(); + return {-1, SOCKET_ERROR_AGAIN}; })); // The write() call on the connection will buffer enough data to bring the connection above the // high watermark and as the data will not flush it should not return below the watermark. @@ -1012,8 +1014,6 @@ TEST_P(ConnectionImplTest, WriteWithWatermarks) { // Clean up the connection. The close() (called via disconnect) will attempt to flush. The // call to write() will succeed, bringing the connection back under the low watermark. - EXPECT_CALL(*client_write_buffer_, write(_)) - .WillOnce(Invoke(client_write_buffer_, &MockWatermarkBuffer::trackWrites)); EXPECT_CALL(client_callbacks_, onBelowWriteBufferLowWatermark()).Times(1); disconnect(true); @@ -1034,8 +1034,12 @@ TEST_P(ConnectionImplTest, WatermarkFuzzing) { bool is_below = true; bool is_above = false; - ON_CALL(*client_write_buffer_, write(_)) - .WillByDefault(testing::Invoke(client_write_buffer_, &MockWatermarkBuffer::failWrite)); + NiceMock os_sys_calls; + TestThreadsafeSingletonInjector os_calls(&os_sys_calls); + ON_CALL(os_sys_calls, writev(_, _, _)) + .WillByDefault(Invoke([&](os_fd_t, const iovec*, int) -> Api::SysCallSizeResult { + return {-1, SOCKET_ERROR_AGAIN}; + })); ON_CALL(*client_write_buffer_, drain(_)) .WillByDefault(testing::Invoke(client_write_buffer_, &MockWatermarkBuffer::baseDrain)); EXPECT_CALL(*client_write_buffer_, drain(_)).Times(AnyNumber()); @@ -1078,15 +1082,18 @@ TEST_P(ConnectionImplTest, WatermarkFuzzing) { } // Do the actual work. Write |buffer_to_write| bytes to the connection and - // drain |bytes_to_flush| before having the buffer failWrite() + // drain |bytes_to_flush| before having writev syscall fail with EAGAIN EXPECT_CALL(*client_write_buffer_, move(_)) .WillOnce(Invoke(client_write_buffer_, &MockWatermarkBuffer::baseMove)); - EXPECT_CALL(*client_write_buffer_, write(_)) - .WillOnce( - DoAll(Invoke([&](IoHandle&) -> void { client_write_buffer_->drain(bytes_to_flush); }), - Return(testing::ByMove(Api::IoCallUint64Result( - bytes_to_flush, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})))))) - .WillRepeatedly(testing::Invoke(client_write_buffer_, &MockWatermarkBuffer::failWrite)); + EXPECT_CALL(os_sys_calls, writev(_, _, _)) + .WillOnce(Invoke([&](os_fd_t, const iovec*, int) -> Api::SysCallSizeResult { + client_write_buffer_->drain(bytes_to_flush); + return {-1, SOCKET_ERROR_AGAIN}; + })) + .WillRepeatedly(Invoke([&](os_fd_t, const iovec*, int) -> Api::SysCallSizeResult { + return {-1, SOCKET_ERROR_AGAIN}; + })); + client_connection_->write(buffer_to_write, false); dispatcher_->run(Event::Dispatcher::RunType::NonBlock); } diff --git a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc index 141bba9703a2..953e5999a5fb 100644 --- a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc @@ -33,8 +33,6 @@ namespace TransportSockets { namespace ProxyProtocol { namespace { -constexpr uint64_t MaxSlices = 16; - class ProxyProtocolTest : public testing::Test { public: void initialize(ProxyProtocolConfig_Version version, @@ -63,14 +61,17 @@ TEST_F(ProxyProtocolTest, InjectesHeaderOnlyOnce) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, nullptr); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); auto msg2 = Buffer::OwnedImpl("more data"); + { InSequence s; EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); @@ -90,12 +91,14 @@ TEST_F(ProxyProtocolTest, BytesProcessedIncludesProxyProtocolHeader) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, nullptr); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); auto msg2 = Buffer::OwnedImpl("more data"); { @@ -121,19 +124,23 @@ TEST_F(ProxyProtocolTest, ReturnsKeepOpenWhenWriteErrorIsAgain) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, nullptr); auto msg = Buffer::OwnedImpl("some data"); { InSequence s; - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - 0, Api::IoErrorPtr(Network::IoSocketError::getIoSocketEagainInstance(), - Network::IoSocketError::deleteIoError))))); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance&) -> Api::IoCallUint64Result { + return Api::IoCallUint64Result( + 0, Api::IoErrorPtr(Network::IoSocketError::getIoSocketEagainInstance(), + Network::IoSocketError::deleteIoError)); + })); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)) .WillOnce(Return(Network::IoResult{Network::PostIoAction::KeepOpen, msg.length(), false})); } @@ -153,16 +160,17 @@ TEST_F(ProxyProtocolTest, ReturnsCloseWhenWriteErrorIsNotAgain) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, nullptr); auto msg = Buffer::OwnedImpl("some data"); { InSequence s; - EXPECT_CALL(io_handle_, writev(_, _)) - .WillOnce(Return(testing::ByMove( - Api::IoCallUint64Result(0, Api::IoErrorPtr(new Network::IoSocketError(EADDRNOTAVAIL), - [](Api::IoError* err) { delete err; }))))); + EXPECT_CALL(io_handle_, write(_)) + .WillOnce(Invoke([&](Buffer::Instance&) -> Api::IoCallUint64Result { + return Api::IoCallUint64Result(0, + Api::IoErrorPtr(new Network::IoSocketError(EADDRNOTAVAIL), + Network::IoSocketError::deleteIoError)); + })); } auto resp = proxy_protocol_socket_->doWrite(msg, false); @@ -178,12 +186,14 @@ TEST_F(ProxyProtocolTest, V1IPV4LocalAddressWhenTransportOptionsAreNull) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, nullptr); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); @@ -199,13 +209,15 @@ TEST_F(ProxyProtocolTest, V1IPV4LocalAddressesWhenHeaderOptionsAreNull) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, std::make_shared()); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), 1)) - .WillOnce(Return(testing::ByMove( - Api::IoCallUint64Result(43, Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = 43; + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); @@ -221,13 +233,15 @@ TEST_F(ProxyProtocolTest, V1IPV6LocalAddressesWhenHeaderOptionsAreNull) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("a:b:c:d::", "e:b:c:f::", 50000, 8080, Network::Address::IpVersion::v6, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, std::make_shared()); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); @@ -252,12 +266,14 @@ TEST_F(ProxyProtocolTest, V1IPV4DownstreamAddresses) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("202.168.0.13", "174.2.2.222", 52000, 80, Network::Address::IpVersion::v4, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, socket_options); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); @@ -282,12 +298,14 @@ TEST_F(ProxyProtocolTest, V1IPV6DownstreamAddresses) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("1::2:3", "a:b:c:d::", 52000, 80, Network::Address::IpVersion::v6, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, socket_options); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); @@ -302,12 +320,14 @@ TEST_F(ProxyProtocolTest, V2IPV4LocalCommandWhenTransportOptionsAreNull) { Network::Utility::resolveUrl("tcp://0.1.1.2:513"); Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV2LocalHeader(expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, nullptr); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); @@ -322,13 +342,15 @@ TEST_F(ProxyProtocolTest, V2IPV4LocalCommandWhenHeaderOptionsAreNull) { Network::Utility::resolveUrl("tcp://0.1.1.2:513"); Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV2LocalHeader(expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, std::make_shared()); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); @@ -353,12 +375,14 @@ TEST_F(ProxyProtocolTest, V2IPV4DownstreamAddresses) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV2Header("1.2.3.4", "0.1.1.2", 773, 513, Network::Address::IpVersion::v4, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, socket_options); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); @@ -383,12 +407,14 @@ TEST_F(ProxyProtocolTest, V2IPV6DownstreamAddresses) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV2Header("1:2:3::4", "1:100:200:3::", 8, 2, Network::Address::IpVersion::v6, expected_buff); - auto expected_slices = expected_buff.getRawSlices(MaxSlices); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, socket_options); - EXPECT_CALL(io_handle_, writev(RawSliceVectorEqual(expected_slices), expected_slices.size())) - .WillOnce(Return(testing::ByMove(Api::IoCallUint64Result( - expected_buff.length(), Api::IoErrorPtr(nullptr, [](Api::IoError*) {}))))); + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); auto msg = Buffer::OwnedImpl("some data"); EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); diff --git a/test/integration/integration_tcp_client.cc b/test/integration/integration_tcp_client.cc index 203a0c85a8ba..590b5c92fcf4 100644 --- a/test/integration/integration_tcp_client.cc +++ b/test/integration/integration_tcp_client.cc @@ -54,7 +54,7 @@ IntegrationTcpClient::IntegrationTcpClient( Network::Address::InstanceConstSharedPtr(), Network::Test::createRawBufferSocket(), options); ON_CALL(*client_write_buffer_, drain(_)) - .WillByDefault(testing::Invoke(client_write_buffer_, &MockWatermarkBuffer::baseDrain)); + .WillByDefault(Invoke(client_write_buffer_, &MockWatermarkBuffer::trackDrains)); EXPECT_CALL(*client_write_buffer_, drain(_)).Times(AnyNumber()); connection_->enableHalfClose(enable_half_close); @@ -116,26 +116,26 @@ AssertionResult IntegrationTcpClient::write(const std::string& data, bool end_st if (verify) { EXPECT_CALL(*client_write_buffer_, move(_)); if (!data.empty()) { - EXPECT_CALL(*client_write_buffer_, write(_)).Times(AtLeast(1)); + EXPECT_CALL(*client_write_buffer_, drain(_)).Times(AtLeast(1)); } } - int bytes_expected = client_write_buffer_->bytes_written() + data.size(); + uint64_t bytes_expected = client_write_buffer_->bytesDrained() + data.size(); connection_->write(buffer, end_stream); do { connection_->dispatcher().run(Event::Dispatcher::RunType::NonBlock); - if (client_write_buffer_->bytes_written() == bytes_expected || disconnected_) { + if (client_write_buffer_->bytesDrained() == bytes_expected || disconnected_) { break; } } while (bound.withinBound()); if (!bound.withinBound()) { return AssertionFailure() << "Timed out completing write"; - } else if (verify && (disconnected_ || client_write_buffer_->bytes_written() != bytes_expected)) { + } else if (verify && (disconnected_ || client_write_buffer_->bytesDrained() != bytes_expected)) { return AssertionFailure() << "Failed to complete write or unexpected disconnect. disconnected_: " << disconnected_ - << " bytes_written: " << client_write_buffer_->bytes_written() + << " bytes_drained: " << client_write_buffer_->bytesDrained() << " bytes_expected: " << bytes_expected; } diff --git a/test/integration/tcp_proxy_integration_test.cc b/test/integration/tcp_proxy_integration_test.cc index 00259a146ecb..37ff51d8b976 100644 --- a/test/integration/tcp_proxy_integration_test.cc +++ b/test/integration/tcp_proxy_integration_test.cc @@ -1102,7 +1102,7 @@ void TcpProxySslIntegrationTest::sendAndReceiveTlsData(const std::string& data_t // Ship some data upstream. Buffer::OwnedImpl buffer(data_to_send_upstream); ssl_client_->write(buffer, false); - while (client_write_buffer_->bytes_drained() != data_to_send_upstream.size()) { + while (client_write_buffer_->bytesDrained() != data_to_send_upstream.size()) { dispatcher_->run(Event::Dispatcher::RunType::NonBlock); } @@ -1169,7 +1169,7 @@ TEST_P(TcpProxySslIntegrationTest, UpstreamHalfClose) { const std::string& val("data"); Buffer::OwnedImpl buffer(val); ssl_client_->write(buffer, false); - while (client_write_buffer_->bytes_drained() != val.size()) { + while (client_write_buffer_->bytesDrained() != val.size()) { dispatcher_->run(Event::Dispatcher::RunType::NonBlock); } ASSERT_TRUE(fake_upstream_connection_->waitForData(val.size())); diff --git a/test/mocks/api/mocks.cc b/test/mocks/api/mocks.cc index f824e15ac479..6678bf4b15ba 100644 --- a/test/mocks/api/mocks.cc +++ b/test/mocks/api/mocks.cc @@ -31,8 +31,20 @@ Event::DispatcherPtr MockApi::allocateDispatcher(const std::string& name, MockOsSysCalls::MockOsSysCalls() { ON_CALL(*this, close(_)).WillByDefault(Invoke([](os_fd_t fd) { +#ifdef WIN32 + int rc = ::closesocket(fd); + int last_error = ::GetLastError(); + // It might be the case that the fd is not actually a socket. In that case Winsock api is + // failing with error `WSAENOTSOCK`. In that case we fall back to a regular close. + if (last_error == WSAENOTSOCK) { + rc = ::close(fd); + last_error = ::GetLastError(); + } + return SysCallIntResult{rc, last_error}; +#else const int rc = ::close(fd); return SysCallIntResult{rc, errno}; +#endif })); } diff --git a/test/mocks/buffer/mocks.h b/test/mocks/buffer/mocks.h index 6918729c7b39..87d9c11f2942 100644 --- a/test/mocks/buffer/mocks.h +++ b/test/mocks/buffer/mocks.h @@ -20,7 +20,6 @@ template class MockBufferBase : public BaseClass { MockBufferBase(std::function below_low, std::function above_high, std::function above_overflow); - MOCK_METHOD(Api::IoCallUint64Result, write, (Network::IoHandle & io_handle)); MOCK_METHOD(void, move, (Buffer::Instance & rhs)); MOCK_METHOD(void, move, (Buffer::Instance & rhs, uint64_t length)); MOCK_METHOD(void, drain, (uint64_t size)); @@ -28,31 +27,14 @@ template class MockBufferBase : public BaseClass { void baseMove(Buffer::Instance& rhs) { BaseClass::move(rhs); } void baseDrain(uint64_t size) { BaseClass::drain(size); } - Api::IoCallUint64Result trackWrites(Network::IoHandle& io_handle) { - Api::IoCallUint64Result result = BaseClass::write(io_handle); - if (result.ok() && result.rc_ > 0) { - bytes_written_ += result.rc_; - } - return result; - } - void trackDrains(uint64_t size) { bytes_drained_ += size; BaseClass::drain(size); } - // A convenience function to invoke on write() which fails the write with EAGAIN. - Api::IoCallUint64Result failWrite(Network::IoHandle&) { - return Api::IoCallUint64Result( - /*rc=*/0, - Api::IoErrorPtr(Network::IoSocketError::getIoSocketEagainInstance(), [](Api::IoError*) {})); - } - - int bytes_written() const { return bytes_written_; } - uint64_t bytes_drained() const { return bytes_drained_; } + uint64_t bytesDrained() const { return bytes_drained_; } private: - int bytes_written_{0}; uint64_t bytes_drained_{0}; }; @@ -71,8 +53,6 @@ template <> MockBufferBase::MockBufferBase(); class MockBuffer : public MockBufferBase { public: MockBuffer() { - ON_CALL(*this, write(testing::_)) - .WillByDefault(testing::Invoke(this, &MockBuffer::trackWrites)); ON_CALL(*this, move(testing::_)).WillByDefault(testing::Invoke(this, &MockBuffer::baseMove)); } }; @@ -84,8 +64,6 @@ class MockWatermarkBuffer : public MockBufferBase { MockWatermarkBuffer(std::function below_low, std::function above_high, std::function above_overflow) : BaseClass(below_low, above_high, above_overflow) { - ON_CALL(*this, write(testing::_)) - .WillByDefault(testing::Invoke(this, &MockWatermarkBuffer::trackWrites)); ON_CALL(*this, move(testing::_)) .WillByDefault(testing::Invoke(this, &MockWatermarkBuffer::baseMove)); } diff --git a/test/mocks/network/io_handle.h b/test/mocks/network/io_handle.h index b6488eb750d3..df94028210eb 100644 --- a/test/mocks/network/io_handle.h +++ b/test/mocks/network/io_handle.h @@ -27,6 +27,7 @@ class MockIoHandle : public IoHandle { MOCK_METHOD(Api::IoCallUint64Result, read, (Buffer::Instance & buffer, uint64_t max_length)); MOCK_METHOD(Api::IoCallUint64Result, writev, (const Buffer::RawSlice* slices, uint64_t num_slice)); + MOCK_METHOD(Api::IoCallUint64Result, write, (Buffer::Instance & buffer)); MOCK_METHOD(Api::IoCallUint64Result, sendmsg, (const Buffer::RawSlice* slices, uint64_t num_slice, int flags, const Address::Ip* self_ip, const Address::Instance& peer_address)); From f288c809572403021b313c85badeb132b5ab51c0 Mon Sep 17 00:00:00 2001 From: Elisha Ziskind Date: Wed, 7 Oct 2020 19:11:11 -0400 Subject: [PATCH 21/27] overload: add scaling support to the disable-keepalive and reject-request overload actions (#13120) Signed-off-by: Elisha Ziskind --- include/envoy/common/random_generator.h | 12 ++++++++++++ source/common/http/conn_manager_impl.cc | 6 ++++-- test/common/common/random_generator_test.cc | 18 ++++++++++++++++++ test/common/http/conn_manager_impl_test_2.cc | 10 ++++++++-- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/include/envoy/common/random_generator.h b/include/envoy/common/random_generator.h index 90fb1b7c1543..f778d909a49b 100644 --- a/include/envoy/common/random_generator.h +++ b/include/envoy/common/random_generator.h @@ -46,6 +46,18 @@ class RandomGenerator { * for example, 7c25513b-0466-4558-a64c-12c6704f37ed */ virtual std::string uuid() PURE; + + /** + * @return a random boolean value, with probability `p` equaling true. + */ + bool bernoulli(float p) { + if (p <= 0) { + return false; + } else if (p >= 1) { + return true; + } + return random() < static_cast(p * static_cast(max())); + } }; using RandomGeneratorPtr = std::unique_ptr; diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index f5608014aec2..cc6e667867a9 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -842,7 +842,8 @@ void ConnectionManagerImpl::ActiveStream::decodeHeaders(RequestHeaderMapPtr&& he filter_manager_.maybeEndDecode(end_stream); // Drop new requests when overloaded as soon as we have decoded the headers. - if (connection_manager_.overload_stop_accepting_requests_ref_.isSaturated()) { + if (connection_manager_.random_generator_.bernoulli( + connection_manager_.overload_stop_accepting_requests_ref_.value())) { // In this one special case, do not create the filter chain. If there is a risk of memory // overload it is more important to avoid unnecessary allocation than to create the filters. filter_manager_.skipFilterChainCreation(); @@ -1338,7 +1339,8 @@ void ConnectionManagerImpl::ActiveStream::encodeHeaders(ResponseHeaderMap& heade bool drain_connection_due_to_overload = false; if (connection_manager_.drain_state_ == DrainState::NotDraining && - connection_manager_.overload_disable_keepalive_ref_.isSaturated()) { + connection_manager_.random_generator_.bernoulli( + connection_manager_.overload_disable_keepalive_ref_.value())) { ENVOY_STREAM_LOG(debug, "disabling keepalive due to envoy overload", *this); if (connection_manager_.codec_->protocol() < Protocol::Http2 || Runtime::runtimeFeatureEnabled( diff --git a/test/common/common/random_generator_test.cc b/test/common/common/random_generator_test.cc index b2098f987b62..71bf25624740 100644 --- a/test/common/common/random_generator_test.cc +++ b/test/common/common/random_generator_test.cc @@ -67,6 +67,24 @@ TEST(UUID, SanityCheckOfUniqueness) { EXPECT_EQ(num_of_uuids, uuids.size()); } +TEST(Random, Bernoilli) { + Random::RandomGeneratorImpl random; + + EXPECT_FALSE(random.bernoulli(0)); + EXPECT_FALSE(random.bernoulli(-1)); + EXPECT_TRUE(random.bernoulli(1)); + EXPECT_TRUE(random.bernoulli(2)); + + int true_count = 0; + static const auto num_rolls = 100000; + for (size_t i = 0; i < num_rolls; ++i) { + if (random.bernoulli(0.4)) { + ++true_count; + } + } + EXPECT_NEAR(static_cast(true_count) / num_rolls, 0.4, 0.01); +} + } // namespace } // namespace Random } // namespace Envoy diff --git a/test/common/http/conn_manager_impl_test_2.cc b/test/common/http/conn_manager_impl_test_2.cc index 7d601824544f..64d3d7d72f6e 100644 --- a/test/common/http/conn_manager_impl_test_2.cc +++ b/test/common/http/conn_manager_impl_test_2.cc @@ -2164,13 +2164,16 @@ TEST(HttpConnectionManagerTracingStatsTest, verifyTracingStats) { } TEST_F(HttpConnectionManagerImplTest, NoNewStreamWhenOverloaded) { - Server::OverloadActionState stop_accepting_requests = Server::OverloadActionState::saturated(); + Server::OverloadActionState stop_accepting_requests = Server::OverloadActionState(0.8); ON_CALL(overload_manager_.overload_state_, getState(Server::OverloadActionNames::get().StopAcceptingRequests)) .WillByDefault(ReturnRef(stop_accepting_requests)); setup(false, ""); + EXPECT_CALL(random_, random()) + .WillRepeatedly(Return(static_cast(Random::RandomGenerator::max()) * 0.5)); + // 503 direct response when overloaded. EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { @@ -2186,7 +2189,7 @@ TEST_F(HttpConnectionManagerImplTest, NoNewStreamWhenOverloaded) { } TEST_F(HttpConnectionManagerImplTest, DisableHttp1KeepAliveWhenOverloaded) { - Server::OverloadActionState disable_http_keep_alive = Server::OverloadActionState::saturated(); + Server::OverloadActionState disable_http_keep_alive = Server::OverloadActionState(0.8); ON_CALL(overload_manager_.overload_state_, getState(Server::OverloadActionNames::get().DisableHttpKeepAlive)) .WillByDefault(ReturnRef(disable_http_keep_alive)); @@ -2194,6 +2197,9 @@ TEST_F(HttpConnectionManagerImplTest, DisableHttp1KeepAliveWhenOverloaded) { codec_->protocol_ = Protocol::Http11; setup(false, ""); + EXPECT_CALL(random_, random()) + .WillRepeatedly(Return(static_cast(Random::RandomGenerator::max()) * 0.5)); + std::shared_ptr filter(new NiceMock()); EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillOnce(Invoke([&](FilterChainFactoryCallbacks& callbacks) -> void { From 8888ee7e24999354c320bd5062fe500077fa142e Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 7 Oct 2020 19:11:27 -0400 Subject: [PATCH 22/27] perf: avoiding string view copies (#13432) Signed-off-by: Alyssa Wilk --- .../quic_listeners/quiche/envoy_quic_proof_verifier.cc | 2 +- test/extensions/filters/http/cdn_loop/parser_fuzz_test.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.cc b/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.cc index b7040d1279d7..08ce684a428c 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.cc +++ b/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.cc @@ -35,7 +35,7 @@ quic::QuicAsyncStatus EnvoyQuicProofVerifier::VerifyCertChain( std::unique_ptr cert_view = quic::CertificateView::ParseSingleCertificate(certs[0]); ASSERT(cert_view != nullptr); - for (const absl::string_view config_san : cert_view->subject_alt_name_domains()) { + for (const absl::string_view& config_san : cert_view->subject_alt_name_domains()) { if (Extensions::TransportSockets::Tls::ContextImpl::dnsNameMatch(hostname, config_san)) { return quic::QUIC_SUCCESS; } diff --git a/test/extensions/filters/http/cdn_loop/parser_fuzz_test.cc b/test/extensions/filters/http/cdn_loop/parser_fuzz_test.cc index eb1708c3a9ae..8fa6c16085b6 100644 --- a/test/extensions/filters/http/cdn_loop/parser_fuzz_test.cc +++ b/test/extensions/filters/http/cdn_loop/parser_fuzz_test.cc @@ -21,7 +21,7 @@ DEFINE_FUZZER(const uint8_t* buf, size_t len) { // If we successfully parse input, we should make sure that cdn_ids we find appear in the input // string in order. size_t start = 0; - for (const absl::string_view cdn_id : list->cdnIds()) { + for (const absl::string_view& cdn_id : list->cdnIds()) { size_t pos = input.find(cdn_id, start); FUZZ_ASSERT(pos != absl::string_view::npos); FUZZ_ASSERT(pos >= start); From 7604cf405ce99a0258f809ec85bdff1a96e41c06 Mon Sep 17 00:00:00 2001 From: moderation Date: Thu, 8 Oct 2020 06:03:11 -0700 Subject: [PATCH 23/27] Dependencies: adding missing hashes for deprecate_versions script (#13433) Commit Message: Fixes missing pip hashes for tools/deprecate_version/deprecate_version.sh Risk Level: Low Testing: sh tools/deprecate_version/deprecate_version.sh Docs Changes: N/A Release Notes: N/A Signed-off-by: Michael Payne --- tools/deprecate_version/requirements.txt | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tools/deprecate_version/requirements.txt b/tools/deprecate_version/requirements.txt index c63789b0daf5..e37631303df1 100644 --- a/tools/deprecate_version/requirements.txt +++ b/tools/deprecate_version/requirements.txt @@ -1,6 +1,35 @@ +certifi==2020.6.20 \ + --hash=sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3 \ + --hash=sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41 +chardet==3.0.4 \ + --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \ + --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 +Deprecated==1.2.10 \ + --hash=sha256:525ba66fb5f90b07169fdd48b6373c18f1ee12728ca277ca44567a367d9d7f74 \ + --hash=sha256:a766c1dccb30c5f6eb2b203f87edd1d8588847709c78589e1521d769addc8218 +gitdb==4.0.5 \ + --hash=sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac \ + --hash=sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9 GitPython==3.1.8 \ --hash=sha256:080bf8e2cf1a2b907634761c2eaefbe83b69930c94c66ad11b65a8252959f912 \ --hash=sha256:1858f4fd089abe92ae465f01d5aaaf55e937eca565fb2c1fce35a51b5f85c910 +idna==2.10 \ + --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \ + --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 PyGithub==1.53 \ --hash=sha256:776befaddab9d8fddd525d52a6ca1ac228cf62b5b1e271836d766f4925e1452e \ --hash=sha256:8ad656bf79958e775ec59f7f5a3dbcbadac12147ae3dc42708b951064096af15 +PyJWT==1.7.1 \ + --hash=sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e \ + --hash=sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96 +requests==2.24.0 \ + --hash=sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b \ + --hash=sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898 +smmap==3.0.4 \ + --hash=sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4 \ + --hash=sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24 +urllib3==1.25.10 \ + --hash=sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a \ + --hash=sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461 +wrapt==1.12.1 \ + --hash=sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7 From 5d12b675fe13747bc716859275c2d3c21004e99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Thu, 8 Oct 2020 11:10:57 -0400 Subject: [PATCH 24/27] Add HTTP local rate limiter (#13395) Signed-off-by: Raul Gutierrez Segales --- CODEOWNERS | 3 + api/BUILD | 1 + .../filters/http/local_ratelimit/v3/BUILD | 13 ++ .../local_ratelimit/v3/local_rate_limit.proto | 70 ++++++++ api/versioning/BUILD | 1 + .../http/http_filters/http_filters.rst | 1 + .../http_filters/local_rate_limit_filter.rst | 134 ++++++++++++++ .../other_features/local_rate_limiting.rst | 7 +- docs/root/version_history/current.rst | 1 + .../filters/http/local_ratelimit/v3/BUILD | 13 ++ .../local_ratelimit/v3/local_rate_limit.proto | 70 ++++++++ source/extensions/extensions_build_config.bzl | 1 + .../filters/common/local_ratelimit/BUILD | 20 +++ .../local_ratelimit/local_ratelimit_impl.cc | 78 ++++++++ .../local_ratelimit/local_ratelimit_impl.h | 41 +++++ .../filters/http/local_ratelimit/BUILD | 46 +++++ .../filters/http/local_ratelimit/config.cc | 43 +++++ .../filters/http/local_ratelimit/config.h | 34 ++++ .../http/local_ratelimit/local_ratelimit.cc | 110 ++++++++++++ .../http/local_ratelimit/local_ratelimit.h | 107 +++++++++++ .../filters/network/local_ratelimit/BUILD | 2 +- .../local_ratelimit/local_ratelimit.cc | 59 +----- .../network/local_ratelimit/local_ratelimit.h | 12 +- .../filters/common/local_ratelimit/BUILD | 18 ++ .../local_ratelimit/local_ratelimit_test.cc | 170 ++++++++++++++++++ .../filters/http/local_ratelimit/BUILD | 34 ++++ .../http/local_ratelimit/config_test.cc | 131 ++++++++++++++ .../http/local_ratelimit/filter_test.cc | 146 +++++++++++++++ .../local_ratelimit/local_ratelimit_test.cc | 165 +---------------- 29 files changed, 1305 insertions(+), 226 deletions(-) create mode 100644 api/envoy/extensions/filters/http/local_ratelimit/v3/BUILD create mode 100644 api/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto create mode 100644 docs/root/configuration/http/http_filters/local_rate_limit_filter.rst create mode 100644 generated_api_shadow/envoy/extensions/filters/http/local_ratelimit/v3/BUILD create mode 100644 generated_api_shadow/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto create mode 100644 source/extensions/filters/common/local_ratelimit/BUILD create mode 100644 source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.cc create mode 100644 source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.h create mode 100644 source/extensions/filters/http/local_ratelimit/BUILD create mode 100644 source/extensions/filters/http/local_ratelimit/config.cc create mode 100644 source/extensions/filters/http/local_ratelimit/config.h create mode 100644 source/extensions/filters/http/local_ratelimit/local_ratelimit.cc create mode 100644 source/extensions/filters/http/local_ratelimit/local_ratelimit.h create mode 100644 test/extensions/filters/common/local_ratelimit/BUILD create mode 100644 test/extensions/filters/common/local_ratelimit/local_ratelimit_test.cc create mode 100644 test/extensions/filters/http/local_ratelimit/BUILD create mode 100644 test/extensions/filters/http/local_ratelimit/config_test.cc create mode 100644 test/extensions/filters/http/local_ratelimit/filter_test.cc diff --git a/CODEOWNERS b/CODEOWNERS index cc797579a8c5..a8546ebd99de 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -140,3 +140,6 @@ extensions/upstreams/http/tcp @alyssawilk @mattklein123 extensions/upstreams/http/default @alyssawilk @snowp @mattklein123 # OAuth2 extensions/filters/http/oauth2 @rgs1 @derekargueta @snowp +# HTTP Local Rate Limit +/*/extensions/filters/http/local_ratelimit @rgs1 @mattklein123 +/*/extensions/filters/common/local_ratelimit @mattklein123 @rgs1 diff --git a/api/BUILD b/api/BUILD index 79b2194fd92e..b13a579dd18c 100644 --- a/api/BUILD +++ b/api/BUILD @@ -190,6 +190,7 @@ proto_library( "//envoy/extensions/filters/http/health_check/v3:pkg", "//envoy/extensions/filters/http/ip_tagging/v3:pkg", "//envoy/extensions/filters/http/jwt_authn/v3:pkg", + "//envoy/extensions/filters/http/local_ratelimit/v3:pkg", "//envoy/extensions/filters/http/lua/v3:pkg", "//envoy/extensions/filters/http/oauth2/v3alpha:pkg", "//envoy/extensions/filters/http/on_demand/v3:pkg", diff --git a/api/envoy/extensions/filters/http/local_ratelimit/v3/BUILD b/api/envoy/extensions/filters/http/local_ratelimit/v3/BUILD new file mode 100644 index 000000000000..ad2fc9a9a84f --- /dev/null +++ b/api/envoy/extensions/filters/http/local_ratelimit/v3/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/core/v3:pkg", + "//envoy/type/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto b/api/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto new file mode 100644 index 000000000000..94f21edd3eed --- /dev/null +++ b/api/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto @@ -0,0 +1,70 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.local_ratelimit.v3; + +import "envoy/config/core/v3/base.proto"; +import "envoy/type/v3/http_status.proto"; +import "envoy/type/v3/token_bucket.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.http.local_ratelimit.v3"; +option java_outer_classname = "LocalRateLimitProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Local Rate limit] +// Local Rate limit :ref:`configuration overview `. +// [#extension: envoy.filters.http.local_ratelimit] + +// [#next-free-field: 7] +message LocalRateLimit { + // The human readable prefix to use when emitting stats. + string stat_prefix = 1 [(validate.rules).string = {min_len: 1}]; + + // This field allows for a custom HTTP response status code to the downstream client when + // the request has been rate limited. + // Defaults to 429 (TooManyRequests). + // + // .. note:: + // If this is set to < 400, 429 will be used instead. + type.v3.HttpStatus status = 2; + + // The token bucket configuration to use for rate limiting requests that are processed by this + // filter. Each request processed by the filter consumes a single token. If the token is available, + // the request will be allowed. If no tokens are available, the request will receive the configured + // rate limit status. + // + // .. note:: + // It's fine for the token bucket to be unset for the global configuration since the rate limit + // can be applied at a the virtual host or route level. Thus, the token bucket must be set + // for the per route configuration otherwise the config will be rejected. + // + // .. note:: + // When using per route configuration, the bucket becomes unique to that route. + // + // .. note:: + // In the current implementation the token bucket's :ref:`fill_interval + // ` must be >= 50ms to avoid too aggressive + // refills. + type.v3.TokenBucket token_bucket = 3; + + // If set, this will enable -- but not necessarily enforce -- the rate limit for the given + // fraction of requests. + // Defaults to 0% of requests for safety. + config.core.v3.RuntimeFractionalPercent filter_enabled = 4; + + // If set, this will enforce the rate limit decisions for the given fraction of requests. + // + // Note: this only applies to the fraction of enabled requests. + // + // Defaults to 0% of requests for safety. + config.core.v3.RuntimeFractionalPercent filter_enforced = 5; + + // Specifies a list of HTTP headers that should be added to each response for requests that + // have been rate limited. + repeated config.core.v3.HeaderValueOption response_headers_to_add = 6 + [(validate.rules).repeated = {max_items: 10}]; +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index 9abb9f09c4e8..d5a154363088 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -73,6 +73,7 @@ proto_library( "//envoy/extensions/filters/http/health_check/v3:pkg", "//envoy/extensions/filters/http/ip_tagging/v3:pkg", "//envoy/extensions/filters/http/jwt_authn/v3:pkg", + "//envoy/extensions/filters/http/local_ratelimit/v3:pkg", "//envoy/extensions/filters/http/lua/v3:pkg", "//envoy/extensions/filters/http/oauth2/v3alpha:pkg", "//envoy/extensions/filters/http/on_demand/v3:pkg", diff --git a/docs/root/configuration/http/http_filters/http_filters.rst b/docs/root/configuration/http/http_filters/http_filters.rst index 7288aed5d584..e2f5838f2c78 100644 --- a/docs/root/configuration/http/http_filters/http_filters.rst +++ b/docs/root/configuration/http/http_filters/http_filters.rst @@ -30,6 +30,7 @@ HTTP filters header_to_metadata_filter ip_tagging_filter jwt_authn_filter + local_rate_limit_filter lua_filter oauth2_filter on_demand_updates_filter diff --git a/docs/root/configuration/http/http_filters/local_rate_limit_filter.rst b/docs/root/configuration/http/http_filters/local_rate_limit_filter.rst new file mode 100644 index 000000000000..dd5465b06dba --- /dev/null +++ b/docs/root/configuration/http/http_filters/local_rate_limit_filter.rst @@ -0,0 +1,134 @@ +.. _config_http_filters_local_rate_limit: + +Local rate limit +================ + +* Local rate limiting :ref:`architecture overview ` +* :ref:`v3 API reference ` +* This filter should be configured with the name *envoy.filters.http.local_ratelimit*. + +The HTTP local rate limit filter applies a :ref:`token bucket +` rate +limit when the request's route or virtual host has a per filter +:ref:`local rate limit configuration `. + +If the local rate limit token bucket is checked, and there are no token availables, a 429 response is returned +(the response is configurable). The local rate limit filter also sets the +:ref:`x-envoy-ratelimited` header. Additional response +headers may be configured. + +Example configuration +--------------------- + +Example filter configuration for a globally set rate limiter (e.g.: all vhosts/routes share the same token bucket): + +.. code-block:: yaml + + name: envoy.filters.http.local_ratelimit + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit + stat_prefix: http_local_rate_limiter + token_bucket: + max_tokens: 10000 + tokens_per_fill: 1000 + fill_interval: 1s + filter_enabled: + runtime_key: local_rate_limit_enabled + default_value: + numerator: 100 + denominator: HUNDRED + filter_enforced: + runtime_key: local_rate_limit_enforced + default_value: + numerator: 100 + denominator: HUNDRED + response_headers_to_add: + - append: false + header: + key: x-local-rate-limit + value: 'true' + + +Example filter configuration for a globally disabled rate limiter but enabled for a specific route: + +.. code-block:: yaml + + name: envoy.filters.http.local_ratelimit + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit + stat_prefix: http_local_rate_limiter + + +The route specific configuration: + +.. code-block:: yaml + + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: ["*"] + routes: + - match: { prefix: "/path/with/rate/limit" } + route: { cluster: service_protected_by_rate_limit } + typed_per_filter_config: + envoy.filters.http.local_ratelimit: + "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit + token_bucket: + max_tokens: 10000 + tokens_per_fill: 1000 + fill_interval: 1s + filter_enabled: + runtime_key: local_rate_limit_enabled + default_value: + numerator: 100 + denominator: HUNDRED + filter_enforced: + runtime_key: local_rate_limit_enforced + default_value: + numerator: 100 + denominator: HUNDRED + response_headers_to_add: + - append: false + header: + key: x-local-rate-limit + value: 'true' + - match: { prefix: "/" } + route: { cluster: default_service } + + +Note that if this filter is configured as globally disabled and there are no virtual host or route level +token buckets, no rate limiting will be applied. + +Statistics +---------- + +The local rate limit filter outputs statistics in the *.http_local_rate_limit.* namespace. +429 responses -- or the configured status code -- are emitted to the normal cluster :ref:`dynamic HTTP statistics +`. + +.. csv-table:: + :header: Name, Type, Description + :widths: 1, 1, 2 + + enabled, Counter, Total number of requests for which the rate limiter was consulted + ok, Counter, Total under limit responses from the token bucket + rate_limited, Counter, Total responses without an available token (but not necessarily enforced) + enforced, Counter, Total number of requests for which rate limiting was applied (e.g.: 429 returned) + +.. _config_http_filters_local_rate_limit_runtime: + +Runtime +------- + +The HTTP rate limit filter supports the following runtime fractional settings: + +http_filter_enabled + % of requests that will check the local rate limit decision, but not enforce, for a given *route_key* specified + in the :ref:`local rate limit configuration `. + Defaults to 0. + +http_filter_enforcing + % of requests that will enforce the local rate limit decision for a given *route_key* specified in the + :ref:`local rate limit configuration `. + Defaults to 0. This can be used to test what would happen before fully enforcing the outcome. diff --git a/docs/root/intro/arch_overview/other_features/local_rate_limiting.rst b/docs/root/intro/arch_overview/other_features/local_rate_limiting.rst index 0fc3369db24f..0e2927254c67 100644 --- a/docs/root/intro/arch_overview/other_features/local_rate_limiting.rst +++ b/docs/root/intro/arch_overview/other_features/local_rate_limiting.rst @@ -6,6 +6,11 @@ Local rate limiting Envoy supports local (non-distributed) rate limiting of L4 connections via the :ref:`local rate limit filter `. -Note that Envoy also supports :ref:`global rate limiting `. Local +Envoy additionally supports local rate limiting of HTTP requests via the +:ref:`HTTP local rate limit filter `. This can +be activated globally at the listener level or at a more specific level (e.g.: the virtual +host or route level). + +Finally, Envoy also supports :ref:`global rate limiting `. Local rate limiting can be used in conjunction with global rate limiting to reduce load on the global rate limit service. diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index c869e0294565..7f07bae28159 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -121,6 +121,7 @@ New Features * load balancer: added a :ref:`configuration` option to specify the active request bias used by the least request load balancer. * load balancer: added an :ref:`option ` to optimize subset load balancing when there is only one host per subset. * load balancer: added support for bounded load per host for consistent hash load balancers via :ref:`hash_balance_factor `. +* local_ratelimit: added new :ref:`HTTP local ratelimit filter `. * local_reply config: added :ref:`content_type` field to set content-type. * lua: added Lua APIs to access :ref:`SSL connection info ` object. * lua: added Lua API for :ref:`base64 escaping a string `. diff --git a/generated_api_shadow/envoy/extensions/filters/http/local_ratelimit/v3/BUILD b/generated_api_shadow/envoy/extensions/filters/http/local_ratelimit/v3/BUILD new file mode 100644 index 000000000000..ad2fc9a9a84f --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/http/local_ratelimit/v3/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/core/v3:pkg", + "//envoy/type/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto b/generated_api_shadow/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto new file mode 100644 index 000000000000..94f21edd3eed --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto @@ -0,0 +1,70 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.local_ratelimit.v3; + +import "envoy/config/core/v3/base.proto"; +import "envoy/type/v3/http_status.proto"; +import "envoy/type/v3/token_bucket.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.http.local_ratelimit.v3"; +option java_outer_classname = "LocalRateLimitProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Local Rate limit] +// Local Rate limit :ref:`configuration overview `. +// [#extension: envoy.filters.http.local_ratelimit] + +// [#next-free-field: 7] +message LocalRateLimit { + // The human readable prefix to use when emitting stats. + string stat_prefix = 1 [(validate.rules).string = {min_len: 1}]; + + // This field allows for a custom HTTP response status code to the downstream client when + // the request has been rate limited. + // Defaults to 429 (TooManyRequests). + // + // .. note:: + // If this is set to < 400, 429 will be used instead. + type.v3.HttpStatus status = 2; + + // The token bucket configuration to use for rate limiting requests that are processed by this + // filter. Each request processed by the filter consumes a single token. If the token is available, + // the request will be allowed. If no tokens are available, the request will receive the configured + // rate limit status. + // + // .. note:: + // It's fine for the token bucket to be unset for the global configuration since the rate limit + // can be applied at a the virtual host or route level. Thus, the token bucket must be set + // for the per route configuration otherwise the config will be rejected. + // + // .. note:: + // When using per route configuration, the bucket becomes unique to that route. + // + // .. note:: + // In the current implementation the token bucket's :ref:`fill_interval + // ` must be >= 50ms to avoid too aggressive + // refills. + type.v3.TokenBucket token_bucket = 3; + + // If set, this will enable -- but not necessarily enforce -- the rate limit for the given + // fraction of requests. + // Defaults to 0% of requests for safety. + config.core.v3.RuntimeFractionalPercent filter_enabled = 4; + + // If set, this will enforce the rate limit decisions for the given fraction of requests. + // + // Note: this only applies to the fraction of enabled requests. + // + // Defaults to 0% of requests for safety. + config.core.v3.RuntimeFractionalPercent filter_enforced = 5; + + // Specifies a list of HTTP headers that should be added to each response for requests that + // have been rate limited. + repeated config.core.v3.HeaderValueOption response_headers_to_add = 6 + [(validate.rules).repeated = {max_items: 10}]; +} diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 54d6cb5a4a71..111292995a01 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -65,6 +65,7 @@ EXTENSIONS = { "envoy.filters.http.health_check": "//source/extensions/filters/http/health_check:config", "envoy.filters.http.ip_tagging": "//source/extensions/filters/http/ip_tagging:config", "envoy.filters.http.jwt_authn": "//source/extensions/filters/http/jwt_authn:config", + "envoy.filters.http.local_ratelimit": "//source/extensions/filters/http/local_ratelimit:config", "envoy.filters.http.lua": "//source/extensions/filters/http/lua:config", "envoy.filters.http.oauth2": "//source/extensions/filters/http/oauth2:config", "envoy.filters.http.on_demand": "//source/extensions/filters/http/on_demand:config", diff --git a/source/extensions/filters/common/local_ratelimit/BUILD b/source/extensions/filters/common/local_ratelimit/BUILD new file mode 100644 index 000000000000..1a201025ca3f --- /dev/null +++ b/source/extensions/filters/common/local_ratelimit/BUILD @@ -0,0 +1,20 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "local_ratelimit_lib", + srcs = ["local_ratelimit_impl.cc"], + hdrs = ["local_ratelimit_impl.h"], + deps = [ + "//include/envoy/event:dispatcher_interface", + "//include/envoy/event:timer_interface", + "//source/common/common:thread_synchronizer_lib", + ], +) diff --git a/source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.cc b/source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.cc new file mode 100644 index 000000000000..2adee384673e --- /dev/null +++ b/source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.cc @@ -0,0 +1,78 @@ +#include "extensions/filters/common/local_ratelimit/local_ratelimit_impl.h" + +namespace Envoy { +namespace Extensions { +namespace Filters { +namespace Common { +namespace LocalRateLimit { + +LocalRateLimiterImpl::LocalRateLimiterImpl(const std::chrono::milliseconds fill_interval, + const uint32_t max_tokens, + const uint32_t tokens_per_fill, + Event::Dispatcher& dispatcher) + : fill_interval_(fill_interval), max_tokens_(max_tokens), tokens_per_fill_(tokens_per_fill), + fill_timer_(fill_interval_ > std::chrono::milliseconds(0) + ? dispatcher.createTimer([this] { onFillTimer(); }) + : nullptr) { + if (fill_timer_ && fill_interval_ < std::chrono::milliseconds(50)) { + throw EnvoyException("local rate limit token bucket fill timer must be >= 50ms"); + } + + tokens_ = max_tokens; + + if (fill_timer_) { + fill_timer_->enableTimer(fill_interval_); + } +} + +LocalRateLimiterImpl::~LocalRateLimiterImpl() { + if (fill_timer_ != nullptr) { + fill_timer_->disableTimer(); + } +} + +void LocalRateLimiterImpl::onFillTimer() { + // Relaxed consistency is used for all operations because we don't care about ordering, just the + // final atomic correctness. + uint32_t expected_tokens = tokens_.load(std::memory_order_relaxed); + uint32_t new_tokens_value; + do { + // expected_tokens is either initialized above or reloaded during the CAS failure below. + new_tokens_value = std::min(max_tokens_, expected_tokens + tokens_per_fill_); + + // Testing hook. + synchronizer_.syncPoint("on_fill_timer_pre_cas"); + + // Loop while the weak CAS fails trying to update the tokens value. + } while ( + !tokens_.compare_exchange_weak(expected_tokens, new_tokens_value, std::memory_order_relaxed)); + + fill_timer_->enableTimer(fill_interval_); +} + +bool LocalRateLimiterImpl::requestAllowed() const { + // Relaxed consistency is used for all operations because we don't care about ordering, just the + // final atomic correctness. + uint32_t expected_tokens = tokens_.load(std::memory_order_relaxed); + do { + // expected_tokens is either initialized above or reloaded during the CAS failure below. + if (expected_tokens == 0) { + return false; + } + + // Testing hook. + synchronizer_.syncPoint("allowed_pre_cas"); + + // Loop while the weak CAS fails trying to subtract 1 from expected. + } while (!tokens_.compare_exchange_weak(expected_tokens, expected_tokens - 1, + std::memory_order_relaxed)); + + // We successfully decremented the counter by 1. + return true; +} + +} // namespace LocalRateLimit +} // namespace Common +} // namespace Filters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.h b/source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.h new file mode 100644 index 000000000000..2e35dc5b0ef4 --- /dev/null +++ b/source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include "envoy/event/dispatcher.h" +#include "envoy/event/timer.h" + +#include "common/common/thread_synchronizer.h" + +namespace Envoy { +namespace Extensions { +namespace Filters { +namespace Common { +namespace LocalRateLimit { + +class LocalRateLimiterImpl { +public: + LocalRateLimiterImpl(const std::chrono::milliseconds fill_interval, const uint32_t max_tokens, + const uint32_t tokens_per_fill, Event::Dispatcher& dispatcher); + ~LocalRateLimiterImpl(); + + bool requestAllowed() const; + +private: + void onFillTimer(); + + const std::chrono::milliseconds fill_interval_; + const uint32_t max_tokens_; + const uint32_t tokens_per_fill_; + const Event::TimerPtr fill_timer_; + mutable std::atomic tokens_; + mutable Thread::ThreadSynchronizer synchronizer_; // Used for testing only. + + friend class LocalRateLimiterImplTest; +}; + +} // namespace LocalRateLimit +} // namespace Common +} // namespace Filters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/local_ratelimit/BUILD b/source/extensions/filters/http/local_ratelimit/BUILD new file mode 100644 index 000000000000..048d7d4ed4e0 --- /dev/null +++ b/source/extensions/filters/http/local_ratelimit/BUILD @@ -0,0 +1,46 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +# Local Ratelimit L7 HTTP filter +# Public docs: docs/root/configuration/http_filters/local_rate_limit_filter.rst + +envoy_extension_package() + +envoy_cc_library( + name = "local_ratelimit_lib", + srcs = ["local_ratelimit.cc"], + hdrs = ["local_ratelimit.h"], + deps = [ + "//include/envoy/http:codes_interface", + "//include/envoy/server:filter_config_interface", + "//include/envoy/stats:stats_macros", + "//source/common/common:utility_lib", + "//source/common/http:header_utility_lib", + "//source/common/http:headers_lib", + "//source/common/router:header_parser_lib", + "//source/common/runtime:runtime_lib", + "//source/extensions/filters/common/local_ratelimit:local_ratelimit_lib", + "//source/extensions/filters/http/common:pass_through_filter_lib", + "@envoy_api//envoy/extensions/filters/http/local_ratelimit/v3:pkg_cc_proto", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + security_posture = "unknown", + deps = [ + ":local_ratelimit_lib", + "//include/envoy/http:filter_interface", + "//source/common/protobuf:utility_lib", + "//source/extensions/filters/http/common:factory_base_lib", + "@envoy_api//envoy/extensions/filters/http/local_ratelimit/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/filters/http/local_ratelimit/config.cc b/source/extensions/filters/http/local_ratelimit/config.cc new file mode 100644 index 000000000000..529fd0dd2977 --- /dev/null +++ b/source/extensions/filters/http/local_ratelimit/config.cc @@ -0,0 +1,43 @@ +#include "extensions/filters/http/local_ratelimit/config.h" + +#include + +#include "envoy/registry/registry.h" + +#include "common/protobuf/utility.h" + +#include "extensions/filters/http/local_ratelimit/local_ratelimit.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace LocalRateLimitFilter { + +Http::FilterFactoryCb LocalRateLimitFilterConfig::createFilterFactoryFromProtoTyped( + const envoy::extensions::filters::http::local_ratelimit::v3::LocalRateLimit& proto_config, + const std::string&, Server::Configuration::FactoryContext& context) { + FilterConfigSharedPtr filter_config = std::make_shared( + proto_config, context.dispatcher(), context.scope(), context.runtime()); + return [filter_config](Http::FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addStreamFilter(std::make_shared(filter_config)); + }; +} + +Router::RouteSpecificFilterConfigConstSharedPtr +LocalRateLimitFilterConfig::createRouteSpecificFilterConfigTyped( + const envoy::extensions::filters::http::local_ratelimit::v3::LocalRateLimit& proto_config, + Server::Configuration::ServerFactoryContext& context, ProtobufMessage::ValidationVisitor&) { + return std::make_shared(proto_config, context.dispatcher(), context.scope(), + context.runtime(), true); +} + +/** + * Static registration for the rate limit filter. @see RegisterFactory. + */ +REGISTER_FACTORY(LocalRateLimitFilterConfig, + Server::Configuration::NamedHttpFilterConfigFactory){"envoy.local_rate_limit"}; + +} // namespace LocalRateLimitFilter +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/local_ratelimit/config.h b/source/extensions/filters/http/local_ratelimit/config.h new file mode 100644 index 000000000000..4ee849e368be --- /dev/null +++ b/source/extensions/filters/http/local_ratelimit/config.h @@ -0,0 +1,34 @@ +#pragma once + +#include "envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.pb.h" +#include "envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.pb.validate.h" + +#include "extensions/filters/http/common/factory_base.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace LocalRateLimitFilter { + +/** + * Config registration for the local rate limit filter. @see NamedHttpFilterConfigFactory. + */ +class LocalRateLimitFilterConfig + : public Common::FactoryBase< + envoy::extensions::filters::http::local_ratelimit::v3::LocalRateLimit> { +public: + LocalRateLimitFilterConfig() : FactoryBase("envoy.filters.http.local_ratelimit") {} + +private: + Http::FilterFactoryCb createFilterFactoryFromProtoTyped( + const envoy::extensions::filters::http::local_ratelimit::v3::LocalRateLimit& proto_config, + const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; + Router::RouteSpecificFilterConfigConstSharedPtr createRouteSpecificFilterConfigTyped( + const envoy::extensions::filters::http::local_ratelimit::v3::LocalRateLimit& proto_config, + Server::Configuration::ServerFactoryContext&, ProtobufMessage::ValidationVisitor&) override; +}; + +} // namespace LocalRateLimitFilter +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/local_ratelimit/local_ratelimit.cc b/source/extensions/filters/http/local_ratelimit/local_ratelimit.cc new file mode 100644 index 000000000000..3b13bfa374ac --- /dev/null +++ b/source/extensions/filters/http/local_ratelimit/local_ratelimit.cc @@ -0,0 +1,110 @@ +#include "extensions/filters/http/local_ratelimit/local_ratelimit.h" + +#include +#include + +#include "envoy/http/codes.h" + +#include "common/http/utility.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace LocalRateLimitFilter { + +FilterConfig::FilterConfig( + const envoy::extensions::filters::http::local_ratelimit::v3::LocalRateLimit& config, + Event::Dispatcher& dispatcher, Stats::Scope& scope, Runtime::Loader& runtime, + const bool per_route) + : status_(toErrorCode(config.status().code())), + stats_(generateStats(config.stat_prefix(), scope)), + rate_limiter_(Filters::Common::LocalRateLimit::LocalRateLimiterImpl( + std::chrono::milliseconds( + PROTOBUF_GET_MS_OR_DEFAULT(config.token_bucket(), fill_interval, 0)), + config.token_bucket().max_tokens(), + PROTOBUF_GET_WRAPPED_OR_DEFAULT(config.token_bucket(), tokens_per_fill, 1), dispatcher)), + runtime_(runtime), + filter_enabled_( + config.has_filter_enabled() + ? absl::optional( + Envoy::Runtime::FractionalPercent(config.filter_enabled(), runtime_)) + : absl::nullopt), + filter_enforced_( + config.has_filter_enabled() + ? absl::optional( + Envoy::Runtime::FractionalPercent(config.filter_enforced(), runtime_)) + : absl::nullopt), + response_headers_parser_( + Envoy::Router::HeaderParser::configure(config.response_headers_to_add())) { + // Note: no token bucket is fine for the global config, which would be the case for enabling + // the filter globally but disabled and then applying limits at the virtual host or + // route level. At the virtual or route level, it makes no sense to have an no token + // bucket so we throw an error. If there's no token bucket configured globally or + // at the vhost/route level, no rate limiting is applied. + if (per_route && !config.has_token_bucket()) { + throw EnvoyException("local rate limit token bucket must be set for per filter configs"); + } +} + +bool FilterConfig::requestAllowed() const { return rate_limiter_.requestAllowed(); } + +LocalRateLimitStats FilterConfig::generateStats(const std::string& prefix, Stats::Scope& scope) { + const std::string final_prefix = prefix + ".http_local_rate_limit"; + return {ALL_LOCAL_RATE_LIMIT_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; +} + +bool FilterConfig::enabled() const { + return filter_enabled_.has_value() ? filter_enabled_->enabled() : false; +} + +bool FilterConfig::enforced() const { + return filter_enforced_.has_value() ? filter_enforced_->enabled() : false; +} + +Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap&, bool) { + const auto* config = getConfig(); + + if (!config->enabled()) { + return Http::FilterHeadersStatus::Continue; + } + + config->stats().enabled_.inc(); + + if (config->requestAllowed()) { + config->stats().ok_.inc(); + return Http::FilterHeadersStatus::Continue; + } + + config->stats().rate_limited_.inc(); + + if (!config->enforced()) { + return Http::FilterHeadersStatus::Continue; + } + + config->stats().enforced_.inc(); + + decoder_callbacks_->sendLocalReply( + config->status(), "local_rate_limited", + [this, config](Http::HeaderMap& headers) { + config->responseHeadersParser().evaluateHeaders(headers, decoder_callbacks_->streamInfo()); + }, + absl::nullopt, "local_rate_limited"); + decoder_callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::RateLimited); + + return Http::FilterHeadersStatus::StopIteration; +} + +const FilterConfig* Filter::getConfig() const { + const auto* config = Http::Utility::resolveMostSpecificPerFilterConfig( + "envoy.filters.http.local_ratelimit", decoder_callbacks_->route()); + if (config) { + return config; + } + + return config_.get(); +} + +} // namespace LocalRateLimitFilter +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/local_ratelimit/local_ratelimit.h b/source/extensions/filters/http/local_ratelimit/local_ratelimit.h new file mode 100644 index 000000000000..6549094d07c3 --- /dev/null +++ b/source/extensions/filters/http/local_ratelimit/local_ratelimit.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include + +#include "envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.pb.h" +#include "envoy/http/filter.h" +#include "envoy/runtime/runtime.h" +#include "envoy/stats/scope.h" +#include "envoy/stats/stats_macros.h" + +#include "common/common/assert.h" +#include "common/http/header_map_impl.h" +#include "common/router/header_parser.h" +#include "common/runtime/runtime_protos.h" + +#include "extensions/filters/common/local_ratelimit/local_ratelimit_impl.h" +#include "extensions/filters/http/common/pass_through_filter.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace LocalRateLimitFilter { + +/** + * All local rate limit stats. @see stats_macros.h + */ +#define ALL_LOCAL_RATE_LIMIT_STATS(COUNTER) \ + COUNTER(enabled) \ + COUNTER(enforced) \ + COUNTER(rate_limited) \ + COUNTER(ok) + +/** + * Struct definition for all local rate limit stats. @see stats_macros.h + */ +struct LocalRateLimitStats { + ALL_LOCAL_RATE_LIMIT_STATS(GENERATE_COUNTER_STRUCT) +}; + +/** + * Global configuration for the HTTP local rate limit filter. + */ +class FilterConfig : public ::Envoy::Router::RouteSpecificFilterConfig { +public: + FilterConfig(const envoy::extensions::filters::http::local_ratelimit::v3::LocalRateLimit& config, + Event::Dispatcher& dispatcher, Stats::Scope& scope, Runtime::Loader& runtime, + bool per_route = false); + ~FilterConfig() override = default; + Runtime::Loader& runtime() { return runtime_; } + bool requestAllowed() const; + bool enabled() const; + bool enforced() const; + LocalRateLimitStats& stats() const { return stats_; } + const Router::HeaderParser& responseHeadersParser() const { return *response_headers_parser_; } + Http::Code status() const { return status_; } + +private: + friend class FilterTest; + + static LocalRateLimitStats generateStats(const std::string& prefix, Stats::Scope& scope); + + static Http::Code toErrorCode(uint64_t status) { + const auto code = static_cast(status); + if (code >= Http::Code::BadRequest) { + return code; + } + return Http::Code::TooManyRequests; + } + + const Http::Code status_; + mutable LocalRateLimitStats stats_; + Filters::Common::LocalRateLimit::LocalRateLimiterImpl rate_limiter_; + Runtime::Loader& runtime_; + const absl::optional filter_enabled_; + const absl::optional filter_enforced_; + Router::HeaderParserPtr response_headers_parser_; +}; + +using FilterConfigSharedPtr = std::shared_ptr; + +/** + * HTTP local rate limit filter. Depending on the route configuration, this filter calls consults + * with local token bucket before allowing further filter iteration. + */ +class Filter : public Http::PassThroughFilter { +public: + Filter(FilterConfigSharedPtr config) : config_(config) {} + + // Http::StreamDecoderFilter + Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers, + bool end_stream) override; + +private: + friend class FilterTest; + + const FilterConfig* getConfig() const; + + FilterConfigSharedPtr config_; +}; + +} // namespace LocalRateLimitFilter +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/network/local_ratelimit/BUILD b/source/extensions/filters/network/local_ratelimit/BUILD index 13389742fa56..ad61ff36235e 100644 --- a/source/extensions/filters/network/local_ratelimit/BUILD +++ b/source/extensions/filters/network/local_ratelimit/BUILD @@ -22,9 +22,9 @@ envoy_cc_library( "//include/envoy/network:filter_interface", "//include/envoy/runtime:runtime_interface", "//include/envoy/stats:stats_macros", - "//source/common/common:thread_synchronizer_lib", "//source/common/protobuf:utility_lib", "//source/common/runtime:runtime_lib", + "//source/extensions/filters/common/local_ratelimit:local_ratelimit_lib", "@envoy_api//envoy/extensions/filters/network/local_ratelimit/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/network/local_ratelimit/local_ratelimit.cc b/source/extensions/filters/network/local_ratelimit/local_ratelimit.cc index 6c574188fed5..773daf175139 100644 --- a/source/extensions/filters/network/local_ratelimit/local_ratelimit.cc +++ b/source/extensions/filters/network/local_ratelimit/local_ratelimit.cc @@ -13,64 +13,21 @@ namespace LocalRateLimitFilter { Config::Config( const envoy::extensions::filters::network::local_ratelimit::v3::LocalRateLimit& proto_config, Event::Dispatcher& dispatcher, Stats::Scope& scope, Runtime::Loader& runtime) - : fill_timer_(dispatcher.createTimer([this] { onFillTimer(); })), - max_tokens_(proto_config.token_bucket().max_tokens()), - tokens_per_fill_( - PROTOBUF_GET_WRAPPED_OR_DEFAULT(proto_config.token_bucket(), tokens_per_fill, 1)), - fill_interval_(PROTOBUF_GET_MS_REQUIRED(proto_config.token_bucket(), fill_interval)), + : rate_limiter_(Filters::Common::LocalRateLimit::LocalRateLimiterImpl( + std::chrono::milliseconds( + PROTOBUF_GET_MS_REQUIRED(proto_config.token_bucket(), fill_interval)), + proto_config.token_bucket().max_tokens(), + PROTOBUF_GET_WRAPPED_OR_DEFAULT(proto_config.token_bucket(), tokens_per_fill, 1), + dispatcher)), enabled_(proto_config.runtime_enabled(), runtime), - stats_(generateStats(proto_config.stat_prefix(), scope)), tokens_(max_tokens_) { - if (fill_interval_ < std::chrono::milliseconds(50)) { - throw EnvoyException("local rate limit token bucket fill timer must be >= 50ms"); - } - fill_timer_->enableTimer(fill_interval_); -} + stats_(generateStats(proto_config.stat_prefix(), scope)) {} LocalRateLimitStats Config::generateStats(const std::string& prefix, Stats::Scope& scope) { const std::string final_prefix = "local_rate_limit." + prefix; return {ALL_LOCAL_RATE_LIMIT_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; } -void Config::onFillTimer() { - // Relaxed consistency is used for all operations because we don't care about ordering, just the - // final atomic correctness. - uint32_t expected_tokens = tokens_.load(std::memory_order_relaxed); - uint32_t new_tokens_value; - do { - // expected_tokens is either initialized above or reloaded during the CAS failure below. - new_tokens_value = std::min(max_tokens_, expected_tokens + tokens_per_fill_); - - // Testing hook. - synchronizer_.syncPoint("on_fill_timer_pre_cas"); - - // Loop while the weak CAS fails trying to update the tokens value. - } while ( - !tokens_.compare_exchange_weak(expected_tokens, new_tokens_value, std::memory_order_relaxed)); - - ENVOY_LOG(trace, "local_rate_limit: fill tokens={}", new_tokens_value); - fill_timer_->enableTimer(fill_interval_); -} - -bool Config::canCreateConnection() { - // Relaxed consistency is used for all operations because we don't care about ordering, just the - // final atomic correctness. - uint32_t expected_tokens = tokens_.load(std::memory_order_relaxed); - do { - // expected_tokens is either initialized above or reloaded during the CAS failure below. - if (expected_tokens == 0) { - return false; - } - - // Testing hook. - synchronizer_.syncPoint("can_create_connection_pre_cas"); - - // Loop while the weak CAS fails trying to subtract 1 from expected. - } while (!tokens_.compare_exchange_weak(expected_tokens, expected_tokens - 1, - std::memory_order_relaxed)); - - // We successfully decremented the counter by 1. - return true; -} +bool Config::canCreateConnection() { return rate_limiter_.requestAllowed(); } Network::FilterStatus Filter::onNewConnection() { if (!config_->enabled()) { diff --git a/source/extensions/filters/network/local_ratelimit/local_ratelimit.h b/source/extensions/filters/network/local_ratelimit/local_ratelimit.h index 30a48f876d97..e1cd52ac1bee 100644 --- a/source/extensions/filters/network/local_ratelimit/local_ratelimit.h +++ b/source/extensions/filters/network/local_ratelimit/local_ratelimit.h @@ -9,6 +9,8 @@ #include "common/common/thread_synchronizer.h" #include "common/runtime/runtime_protos.h" +#include "extensions/filters/common/local_ratelimit/local_ratelimit_impl.h" + namespace Envoy { namespace Extensions { namespace NetworkFilters { @@ -43,17 +45,9 @@ class Config : Logger::Loggable { static LocalRateLimitStats generateStats(const std::string& prefix, Stats::Scope& scope); void onFillTimer(); - // TODO(mattklein123): Determine if/how to merge this with token_bucket_impl.h/cc. This - // implementation is geared towards multi-threading as well assumes a high call rate (which is - // why a fixed periodic refresh timer is used). - const Event::TimerPtr fill_timer_; - const uint32_t max_tokens_; - const uint32_t tokens_per_fill_; - const std::chrono::milliseconds fill_interval_; + Filters::Common::LocalRateLimit::LocalRateLimiterImpl rate_limiter_; Runtime::FeatureFlag enabled_; LocalRateLimitStats stats_; - std::atomic tokens_; - Thread::ThreadSynchronizer synchronizer_; // Used for testing only. friend class LocalRateLimitTestBase; }; diff --git a/test/extensions/filters/common/local_ratelimit/BUILD b/test/extensions/filters/common/local_ratelimit/BUILD new file mode 100644 index 000000000000..96bd5d38a495 --- /dev/null +++ b/test/extensions/filters/common/local_ratelimit/BUILD @@ -0,0 +1,18 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_test( + name = "local_ratelimit_test", + srcs = ["local_ratelimit_test.cc"], + deps = [ + "//source/extensions/filters/common/local_ratelimit:local_ratelimit_lib", + "//test/mocks/event:event_mocks", + ], +) diff --git a/test/extensions/filters/common/local_ratelimit/local_ratelimit_test.cc b/test/extensions/filters/common/local_ratelimit/local_ratelimit_test.cc new file mode 100644 index 000000000000..a6142dfb16aa --- /dev/null +++ b/test/extensions/filters/common/local_ratelimit/local_ratelimit_test.cc @@ -0,0 +1,170 @@ +#include "extensions/filters/common/local_ratelimit/local_ratelimit_impl.h" + +#include "test/mocks/event/mocks.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::_; +using testing::NiceMock; + +namespace Envoy { +namespace Extensions { +namespace Filters { +namespace Common { +namespace LocalRateLimit { + +class LocalRateLimiterImplTest : public testing::Test { +public: + void initialize(const std::chrono::milliseconds fill_interval, const uint32_t max_tokens, + const uint32_t tokens_per_fill) { + + fill_timer_ = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*fill_timer_, enableTimer(_, nullptr)); + EXPECT_CALL(*fill_timer_, disableTimer()); + + rate_limiter_ = std::make_shared(fill_interval, max_tokens, + tokens_per_fill, dispatcher_); + } + + Thread::ThreadSynchronizer& synchronizer() { return rate_limiter_->synchronizer_; } + + NiceMock dispatcher_; + Event::MockTimer* fill_timer_{}; + std::shared_ptr rate_limiter_; +}; + +// Make sure we fail with a fill rate this is too fast. +TEST_F(LocalRateLimiterImplTest, TooFastFillRate) { + EXPECT_THROW_WITH_MESSAGE( + LocalRateLimiterImpl(std::chrono::milliseconds(49), 100, 1, dispatcher_), EnvoyException, + "local rate limit token bucket fill timer must be >= 50ms"); +} + +// Verify various token bucket CAS edge cases. +TEST_F(LocalRateLimiterImplTest, CasEdgeCases) { + // This tests the case in which an allowed check races with the fill timer. + { + initialize(std::chrono::milliseconds(50), 1, 1); + + synchronizer().enable(); + + // Start a thread and start the fill callback. This will wait pre-CAS. + synchronizer().waitOn("on_fill_timer_pre_cas"); + std::thread t1([&] { + EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(50), nullptr)); + fill_timer_->invokeCallback(); + }); + // Wait until the thread is actually waiting. + synchronizer().barrierOn("on_fill_timer_pre_cas"); + + // This should succeed. + EXPECT_TRUE(rate_limiter_->requestAllowed()); + + // Now signal the thread to continue which should cause a CAS failure and the loop to repeat. + synchronizer().signal("on_fill_timer_pre_cas"); + t1.join(); + + // 1 -> 0 tokens + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_FALSE(rate_limiter_->requestAllowed()); + } + + // This tests the case in which two allowed checks race. + { + initialize(std::chrono::milliseconds(200), 1, 1); + + synchronizer().enable(); + + // Start a thread and see if we are under limit. This will wait pre-CAS. + synchronizer().waitOn("allowed_pre_cas"); + std::thread t1([&] { EXPECT_FALSE(rate_limiter_->requestAllowed()); }); + // Wait until the thread is actually waiting. + synchronizer().barrierOn("allowed_pre_cas"); + + // Consume a token on this thread, which should cause the CAS to fail on the other thread. + EXPECT_TRUE(rate_limiter_->requestAllowed()); + synchronizer().signal("allowed_pre_cas"); + t1.join(); + } +} + +// Verify token bucket functionality with a single token. +TEST_F(LocalRateLimiterImplTest, TokenBucket) { + initialize(std::chrono::milliseconds(200), 1, 1); + + // 1 -> 0 tokens + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_FALSE(rate_limiter_->requestAllowed()); + EXPECT_FALSE(rate_limiter_->requestAllowed()); + + // 0 -> 1 tokens + EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); + fill_timer_->invokeCallback(); + + // 1 -> 0 tokens + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_FALSE(rate_limiter_->requestAllowed()); + + // 0 -> 1 tokens + EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); + fill_timer_->invokeCallback(); + + // 1 -> 1 tokens + EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); + fill_timer_->invokeCallback(); + + // 1 -> 0 tokens + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_FALSE(rate_limiter_->requestAllowed()); +} + +// Verify token bucket functionality with max tokens and tokens per fill > 1. +TEST_F(LocalRateLimiterImplTest, TokenBucketMultipleTokensPerFill) { + initialize(std::chrono::milliseconds(200), 2, 2); + + // 2 -> 0 tokens + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_FALSE(rate_limiter_->requestAllowed()); + + // 0 -> 2 tokens + EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); + fill_timer_->invokeCallback(); + + // 2 -> 1 tokens + EXPECT_TRUE(rate_limiter_->requestAllowed()); + + // 1 -> 2 tokens + EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); + fill_timer_->invokeCallback(); + + // 2 -> 0 tokens + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_FALSE(rate_limiter_->requestAllowed()); +} + +// Verify token bucket functionality with max tokens > tokens per fill. +TEST_F(LocalRateLimiterImplTest, TokenBucketMaxTokensGreaterThanTokensPerFill) { + initialize(std::chrono::milliseconds(200), 2, 1); + + // 2 -> 0 tokens + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_FALSE(rate_limiter_->requestAllowed()); + + // 0 -> 1 tokens + EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); + fill_timer_->invokeCallback(); + + // 1 -> 0 tokens + EXPECT_TRUE(rate_limiter_->requestAllowed()); + EXPECT_FALSE(rate_limiter_->requestAllowed()); +} + +} // Namespace LocalRateLimit +} // namespace Common +} // namespace Filters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/http/local_ratelimit/BUILD b/test/extensions/filters/http/local_ratelimit/BUILD new file mode 100644 index 000000000000..38cd85098ee7 --- /dev/null +++ b/test/extensions/filters/http/local_ratelimit/BUILD @@ -0,0 +1,34 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_package", +) +load( + "//test/extensions:extensions_build_system.bzl", + "envoy_extension_cc_test", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_extension_cc_test( + name = "filter_test", + srcs = ["filter_test.cc"], + extension_name = "envoy.filters.http.local_ratelimit", + deps = [ + "//source/extensions/filters/http/local_ratelimit:local_ratelimit_lib", + "//test/common/stream_info:test_util", + "//test/mocks/http:http_mocks", + "@envoy_api//envoy/extensions/filters/http/local_ratelimit/v3:pkg_cc_proto", + ], +) + +envoy_extension_cc_test( + name = "config_test", + srcs = ["config_test.cc"], + extension_name = "envoy.filters.http.local_ratelimit", + deps = [ + "//source/extensions/filters/http/local_ratelimit:config", + "//test/mocks/server:server_mocks", + ], +) diff --git a/test/extensions/filters/http/local_ratelimit/config_test.cc b/test/extensions/filters/http/local_ratelimit/config_test.cc new file mode 100644 index 000000000000..3bbf10ad1ee1 --- /dev/null +++ b/test/extensions/filters/http/local_ratelimit/config_test.cc @@ -0,0 +1,131 @@ +#include "extensions/filters/http/local_ratelimit/config.h" +#include "extensions/filters/http/local_ratelimit/local_ratelimit.h" + +#include "test/mocks/server/mocks.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace LocalRateLimitFilter { + +TEST(Factory, GlobalEmptyConfig) { + const std::string yaml = R"( +stat_prefix: test + )"; + + LocalRateLimitFilterConfig factory; + ProtobufTypes::MessagePtr proto_config = factory.createEmptyRouteConfigProto(); + TestUtility::loadFromYaml(yaml, *proto_config); + + NiceMock context; + + EXPECT_CALL(context.dispatcher_, createTimer_(_)).Times(0); + auto callback = factory.createFilterFactoryFromProto(*proto_config, "stats", context); + Http::MockFilterChainFactoryCallbacks filter_callback; + EXPECT_CALL(filter_callback, addStreamFilter(_)); + callback(filter_callback); +} + +TEST(Factory, RouteSpecificFilterConfig) { + const std::string config_yaml = R"( +stat_prefix: test +token_bucket: + max_tokens: 1 + tokens_per_fill: 1 + fill_interval: 1000s +filter_enabled: + runtime_key: test_enabled + default_value: + numerator: 100 + denominator: HUNDRED +filter_enforced: + runtime_key: test_enforced + default_value: + numerator: 100 + denominator: HUNDRED +response_headers_to_add: + - append: false + header: + key: x-test-rate-limit + value: 'true' + )"; + + LocalRateLimitFilterConfig factory; + ProtobufTypes::MessagePtr proto_config = factory.createEmptyRouteConfigProto(); + TestUtility::loadFromYaml(config_yaml, *proto_config); + + NiceMock context; + + EXPECT_CALL(context.dispatcher_, createTimer_(_)).Times(1); + const auto route_config = factory.createRouteSpecificFilterConfig( + *proto_config, context, ProtobufMessage::getNullValidationVisitor()); + const auto* config = dynamic_cast(route_config.get()); + EXPECT_TRUE(config->requestAllowed()); +} + +TEST(Factory, EnabledEnforcedDisabledByDefault) { + const std::string config_yaml = R"( +stat_prefix: test +token_bucket: + max_tokens: 1 + tokens_per_fill: 1 + fill_interval: 1000s + )"; + + LocalRateLimitFilterConfig factory; + ProtobufTypes::MessagePtr proto_config = factory.createEmptyRouteConfigProto(); + TestUtility::loadFromYaml(config_yaml, *proto_config); + + NiceMock context; + + EXPECT_CALL(context.dispatcher_, createTimer_(_)).Times(1); + const auto route_config = factory.createRouteSpecificFilterConfig( + *proto_config, context, ProtobufMessage::getNullValidationVisitor()); + const auto* config = dynamic_cast(route_config.get()); + EXPECT_FALSE(config->enabled()); + EXPECT_FALSE(config->enforced()); +} + +TEST(Factory, PerRouteConfigNoTokenBucket) { + const std::string config_yaml = R"( +stat_prefix: test + )"; + + LocalRateLimitFilterConfig factory; + ProtobufTypes::MessagePtr proto_config = factory.createEmptyRouteConfigProto(); + TestUtility::loadFromYaml(config_yaml, *proto_config); + + NiceMock context; + EXPECT_THROW(factory.createRouteSpecificFilterConfig(*proto_config, context, + ProtobufMessage::getNullValidationVisitor()), + EnvoyException); +} + +TEST(Factory, FillTimerTooLow) { + const std::string config_yaml = R"( +stat_prefix: test +token_bucket: + max_tokens: 1 + tokens_per_fill: 1 + fill_interval: 0.040s + )"; + + LocalRateLimitFilterConfig factory; + ProtobufTypes::MessagePtr proto_config = factory.createEmptyRouteConfigProto(); + TestUtility::loadFromYaml(config_yaml, *proto_config); + + NiceMock context; + + EXPECT_CALL(context.dispatcher_, createTimer_(_)).Times(1); + EXPECT_THROW(factory.createRouteSpecificFilterConfig(*proto_config, context, + ProtobufMessage::getNullValidationVisitor()), + EnvoyException); +} + +} // namespace LocalRateLimitFilter +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/http/local_ratelimit/filter_test.cc b/test/extensions/filters/http/local_ratelimit/filter_test.cc new file mode 100644 index 000000000000..e3d3cd538ff4 --- /dev/null +++ b/test/extensions/filters/http/local_ratelimit/filter_test.cc @@ -0,0 +1,146 @@ +#include "envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.pb.h" + +#include "extensions/filters/http/local_ratelimit/local_ratelimit.h" + +#include "test/mocks/http/mocks.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace LocalRateLimitFilter { + +static const std::string config_yaml = R"( +stat_prefix: test +token_bucket: + max_tokens: {} + tokens_per_fill: 1 + fill_interval: 1000s +filter_enabled: + runtime_key: test_enabled + default_value: + numerator: 100 + denominator: HUNDRED +filter_enforced: + runtime_key: test_enforced + default_value: + numerator: 100 + denominator: HUNDRED +response_headers_to_add: + - append: false + header: + key: x-test-rate-limit + value: 'true' + )"; + +class FilterTest : public testing::Test { +public: + FilterTest() = default; + + void setup(const std::string& yaml, const bool enabled = true, const bool enforced = true) { + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled(absl::string_view("test_enabled"), + testing::Matcher(Percent(100)))) + .WillRepeatedly(testing::Return(enabled)); + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled(absl::string_view("test_enforced"), + testing::Matcher(Percent(100)))) + .WillRepeatedly(testing::Return(enforced)); + + envoy::extensions::filters::http::local_ratelimit::v3::LocalRateLimit config; + TestUtility::loadFromYaml(yaml, config); + config_ = std::make_shared(config, dispatcher_, stats_, runtime_); + filter_ = std::make_shared(config_); + filter_->setDecoderFilterCallbacks(decoder_callbacks_); + } + + uint64_t findCounter(const std::string& name) { + const auto counter = TestUtility::findCounter(stats_, name); + return counter != nullptr ? counter->value() : 0; + } + + Http::Code toErrorCode(const uint64_t code) { return config_->toErrorCode(code); } + + Stats::IsolatedStoreImpl stats_; + testing::NiceMock decoder_callbacks_; + NiceMock dispatcher_; + NiceMock runtime_; + std::shared_ptr config_; + std::shared_ptr filter_; +}; + +TEST_F(FilterTest, Runtime) { + setup(fmt::format(config_yaml, "1"), false, false); + EXPECT_EQ(&runtime_, &(config_->runtime())); +} + +TEST_F(FilterTest, ToErrorCode) { + setup(fmt::format(config_yaml, "1"), false, false); + EXPECT_EQ(Http::Code::BadRequest, toErrorCode(400)); +} + +TEST_F(FilterTest, Disabled) { + setup(fmt::format(config_yaml, "1"), false, false); + auto headers = Http::TestRequestHeaderMapImpl(); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers, false)); + EXPECT_EQ(0U, findCounter("test.http_local_rate_limit.enabled")); + EXPECT_EQ(0U, findCounter("test.http_local_rate_limit.enforced")); +} + +TEST_F(FilterTest, RequestOk) { + setup(fmt::format(config_yaml, "1")); + auto headers = Http::TestRequestHeaderMapImpl(); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers, false)); + EXPECT_EQ(1U, findCounter("test.http_local_rate_limit.enabled")); + EXPECT_EQ(0U, findCounter("test.http_local_rate_limit.enforced")); + EXPECT_EQ(1U, findCounter("test.http_local_rate_limit.ok")); +} + +TEST_F(FilterTest, RequestRateLimited) { + setup(fmt::format(config_yaml, "0")); + + EXPECT_CALL(decoder_callbacks_, sendLocalReply(Http::Code::TooManyRequests, _, _, _, _)) + .WillOnce(Invoke([](Http::Code code, absl::string_view body, + std::function modify_headers, + const absl::optional grpc_status, + absl::string_view details) { + EXPECT_EQ(Http::Code::TooManyRequests, code); + EXPECT_EQ("local_rate_limited", body); + + Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; + modify_headers(response_headers); + EXPECT_EQ("true", response_headers.get(Http::LowerCaseString("x-test-rate-limit")) + ->value() + .getStringView()); + + EXPECT_EQ(grpc_status, absl::nullopt); + EXPECT_EQ(details, "local_rate_limited"); + })); + + auto headers = Http::TestRequestHeaderMapImpl(); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->decodeHeaders(headers, false)); + EXPECT_EQ(1U, findCounter("test.http_local_rate_limit.enabled")); + EXPECT_EQ(1U, findCounter("test.http_local_rate_limit.enforced")); + EXPECT_EQ(1U, findCounter("test.http_local_rate_limit.rate_limited")); +} + +TEST_F(FilterTest, RequestRateLimitedButNotEnforced) { + setup(fmt::format(config_yaml, "0"), true, false); + + EXPECT_CALL(decoder_callbacks_, sendLocalReply(Http::Code::TooManyRequests, _, _, _, _)).Times(0); + + auto headers = Http::TestRequestHeaderMapImpl(); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers, false)); + EXPECT_EQ(1U, findCounter("test.http_local_rate_limit.enabled")); + EXPECT_EQ(0U, findCounter("test.http_local_rate_limit.enforced")); + EXPECT_EQ(1U, findCounter("test.http_local_rate_limit.rate_limited")); +} + +} // namespace LocalRateLimitFilter +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/network/local_ratelimit/local_ratelimit_test.cc b/test/extensions/filters/network/local_ratelimit/local_ratelimit_test.cc index 395f4c0a891c..d764a1bab8ec 100644 --- a/test/extensions/filters/network/local_ratelimit/local_ratelimit_test.cc +++ b/test/extensions/filters/network/local_ratelimit/local_ratelimit_test.cc @@ -30,12 +30,11 @@ class LocalRateLimitTestBase : public testing::Test { fill_timer_ = new Event::MockTimer(&dispatcher_); if (expect_timer_create) { EXPECT_CALL(*fill_timer_, enableTimer(_, nullptr)); + EXPECT_CALL(*fill_timer_, disableTimer()); } config_ = std::make_shared(proto_config, dispatcher_, stats_store_, runtime_); } - Thread::ThreadSynchronizer& synchronizer() { return config_->synchronizer_; } - NiceMock dispatcher_; Stats::IsolatedStoreImpl stats_store_; NiceMock runtime_; @@ -43,168 +42,6 @@ class LocalRateLimitTestBase : public testing::Test { ConfigSharedPtr config_; }; -// Make sure we fail with a fill rate this is too fast. -TEST_F(LocalRateLimitTestBase, TooFastFillRate) { - EXPECT_THROW_WITH_MESSAGE(initialize(R"EOF( -stat_prefix: local_rate_limit_stats -token_bucket: - max_tokens: 1 - fill_interval: 0.049s -)EOF", - false), - EnvoyException, - "local rate limit token bucket fill timer must be >= 50ms"); -} - -// Verify various token bucket CAS edge cases. -TEST_F(LocalRateLimitTestBase, CasEdgeCases) { - // This tests the case in which a connection creation races with the fill timer. - { - initialize(R"EOF( - stat_prefix: local_rate_limit_stats - token_bucket: - max_tokens: 1 - fill_interval: 0.05s - )EOF"); - - synchronizer().enable(); - - // Start a thread and start the fill callback. This will wait pre-CAS. - synchronizer().waitOn("on_fill_timer_pre_cas"); - std::thread t1([&] { - EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(50), nullptr)); - fill_timer_->invokeCallback(); - }); - // Wait until the thread is actually waiting. - synchronizer().barrierOn("on_fill_timer_pre_cas"); - - // Create a connection. This should succeed. - EXPECT_TRUE(config_->canCreateConnection()); - - // Now signal the thread to continue which should cause a CAS failure and the loop to repeat. - synchronizer().signal("on_fill_timer_pre_cas"); - t1.join(); - - // 1 -> 0 tokens - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_FALSE(config_->canCreateConnection()); - } - - // This tests the case in which two connection creations race. - { - initialize(R"EOF( - stat_prefix: local_rate_limit_stats - token_bucket: - max_tokens: 1 - fill_interval: 0.2s - )EOF"); - - synchronizer().enable(); - - // Start a thread and see if we can create a connection. This will wait pre-CAS. - synchronizer().waitOn("can_create_connection_pre_cas"); - std::thread t1([&] { EXPECT_FALSE(config_->canCreateConnection()); }); - // Wait until the thread is actually waiting. - synchronizer().barrierOn("can_create_connection_pre_cas"); - - // Create the connection on this thread, which should cause the CAS to fail on the other thread. - EXPECT_TRUE(config_->canCreateConnection()); - synchronizer().signal("can_create_connection_pre_cas"); - t1.join(); - } -} - -// Verify token bucket functionality with a single token. -TEST_F(LocalRateLimitTestBase, TokenBucket) { - initialize(R"EOF( -stat_prefix: local_rate_limit_stats -token_bucket: - max_tokens: 1 - fill_interval: 0.2s -)EOF"); - - // 1 -> 0 tokens - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_FALSE(config_->canCreateConnection()); - EXPECT_FALSE(config_->canCreateConnection()); - - // 0 -> 1 tokens - EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); - fill_timer_->invokeCallback(); - - // 1 -> 0 tokens - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_FALSE(config_->canCreateConnection()); - - // 0 -> 1 tokens - EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); - fill_timer_->invokeCallback(); - - // 1 -> 1 tokens - EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); - fill_timer_->invokeCallback(); - - // 1 -> 0 tokens - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_FALSE(config_->canCreateConnection()); -} - -// Verify token bucket functionality with max tokens and tokens per fill > 1. -TEST_F(LocalRateLimitTestBase, TokenBucketMultipleTokensPerFill) { - initialize(R"EOF( -stat_prefix: local_rate_limit_stats -token_bucket: - max_tokens: 2 - tokens_per_fill: 2 - fill_interval: 0.2s -)EOF"); - - // 2 -> 0 tokens - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_FALSE(config_->canCreateConnection()); - - // 0 -> 2 tokens - EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); - fill_timer_->invokeCallback(); - - // 2 -> 1 tokens - EXPECT_TRUE(config_->canCreateConnection()); - - // 1 -> 2 tokens - EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); - fill_timer_->invokeCallback(); - - // 2 -> 0 tokens - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_FALSE(config_->canCreateConnection()); -} - -// Verify token bucket functionality with max tokens > tokens per fill. -TEST_F(LocalRateLimitTestBase, TokenBucketMaxTokensGreaterThanTokensPerFill) { - initialize(R"EOF( -stat_prefix: local_rate_limit_stats -token_bucket: - max_tokens: 2 - tokens_per_fill: 1 - fill_interval: 0.2s -)EOF"); - - // 2 -> 0 tokens - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_FALSE(config_->canCreateConnection()); - - // 0 -> 1 tokens - EXPECT_CALL(*fill_timer_, enableTimer(std::chrono::milliseconds(200), nullptr)); - fill_timer_->invokeCallback(); - - // 1 -> 0 tokens - EXPECT_TRUE(config_->canCreateConnection()); - EXPECT_FALSE(config_->canCreateConnection()); -} - class LocalRateLimitFilterTest : public LocalRateLimitTestBase { public: struct ActiveFilter { From 850a0f22d3acdd78a44b944111c5f50cd93bb65e Mon Sep 17 00:00:00 2001 From: "Adi (Suissa) Peleg" Date: Thu, 8 Oct 2020 11:40:37 -0400 Subject: [PATCH 25/27] fuzz: Adding lazy map threshold conf to headermap fuzzer (#13402) Adding lazy map threshold config to headermap fuzzer Signed-off-by: Adi Suissa-Peleg --- test/common/http/BUILD | 1 + .../header_map_impl_corpus/example_lazymap | 236 ++++++++++++++++++ test/common/http/header_map_impl_fuzz.proto | 6 + test/common/http/header_map_impl_fuzz_test.cc | 9 + 4 files changed, 252 insertions(+) create mode 100644 test/common/http/header_map_impl_corpus/example_lazymap diff --git a/test/common/http/BUILD b/test/common/http/BUILD index c5bd17d29fd8..7b19f269bbdd 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -308,6 +308,7 @@ envoy_cc_fuzz_test( ":header_map_impl_fuzz_proto_cc_proto", "//source/common/http:header_map_lib", "//test/fuzz:utility_lib", + "//test/test_common:test_runtime_lib", ], ) diff --git a/test/common/http/header_map_impl_corpus/example_lazymap b/test/common/http/header_map_impl_corpus/example_lazymap new file mode 100644 index 000000000000..4f3bcda6fc4c --- /dev/null +++ b/test/common/http/header_map_impl_corpus/example_lazymap @@ -0,0 +1,236 @@ +actions { + add_reference { + key: "foo" + value: "bar" + } +} +actions { + add_reference { + key: "foo" + value: "baz" + } +} +actions { + add_reference_key { + key: "foo_string_key" + string_value: "barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr" + } +} +actions { + add_reference_key { + key: "foo_string_key" + string_value: "baz" + } +} +actions { + add_reference_key { + key: "foo_uint64_key" + uint64_value: 42 + } +} +actions { + add_reference_key { + key: "foo_uint64_key" + uint64_value: 37 + } +} +actions { + add_copy { + key: "foo_string_key" + string_value: "barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr" + } +} +actions { + add_copy { + key: "foo_string_key" + string_value: "baz" + } +} +actions { + add_copy { + key: "foo_uint64_key" + uint64_value: 42 + } +} +actions { + add_copy { + key: "foo_uint64_key" + uint64_value: 37 + } +} +actions { + set_reference { + key: "foo" + value: "bar" + } +} +actions { + set_reference { + key: "foo" + value: "baz" + } +} +actions { + set_reference_key { + key: "foo" + value: "bar" + } +} +actions { + set_reference_key { + key: "foo" + value: "baz" + } +} + +actions { + add_reference { + key: ":method" + value: "bar" + } +} +actions { + add_reference { + key: ":method" + value: "baz" + } +} +actions { + add_reference_key { + key: ":method" + string_value: "bar" + } +} +actions { + add_reference_key { + key: ":method" + string_value: "baz" + } +} +actions { + add_reference_key { + key: ":method" + uint64_value: 42 + } +} +actions { + add_reference_key { + key: ":method" + uint64_value: 37 + } +} +actions { + add_copy { + key: ":method" + string_value: "bar" + } +} +actions { + add_copy { + key: ":method" + string_value: "baz" + } +} +actions { + add_copy { + key: ":method" + uint64_value: 42 + } +} +actions { + add_copy { + key: ":method" + uint64_value: 37 + } +} +actions { + set_reference { + key: ":method" + value: "bar" + } +} +actions { + set_reference { + key: ":method" + value: "baz" + } +} +actions { + set_reference_key { + key: ":method" + value: "bar" + } +} +actions { + set_reference_key { + key: ":method" + value: "baz" + } +} + +actions { + get_and_mutate { + key: ":method" + append: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" + } +} +actions { + get_and_mutate { + key: ":method" + append: "aa" + } +} +actions { + get_and_mutate { + key: ":method" + clear: {} + } +} +actions { + get_and_mutate { + key: ":method" + find: "a" + } +} +actions { + get_and_mutate { + key: ":method" + set_copy: "a" + } +} +actions { + get_and_mutate { + key: ":method" + set_integer: 0 + } +} +actions { + get_and_mutate { + key: ":method" + set_reference: "a" + } +} +actions { + copy: {} +} +actions { + lookup: ":method" +} +actions { + lookup: "foo" +} +actions { + remove: "f" +} +actions { + remove_prefix: "foo" +} +actions { + remove: ":m" +} +actions { + remove_prefix: ":m" +} +config { + lazy_map_min_size: 0 +} diff --git a/test/common/http/header_map_impl_fuzz.proto b/test/common/http/header_map_impl_fuzz.proto index 69e4ae244a0a..ea06c5c1b593 100644 --- a/test/common/http/header_map_impl_fuzz.proto +++ b/test/common/http/header_map_impl_fuzz.proto @@ -88,6 +88,12 @@ message Action { } } +message Config { + uint32 lazy_map_min_size = 1; +} + message HeaderMapImplFuzzTestCase { repeated Action actions = 1; + // Optional threshold value configuration for the lazy header-map. + Config config = 2; } diff --git a/test/common/http/header_map_impl_fuzz_test.cc b/test/common/http/header_map_impl_fuzz_test.cc index 97e327ce57f1..5600af1c3160 100644 --- a/test/common/http/header_map_impl_fuzz_test.cc +++ b/test/common/http/header_map_impl_fuzz_test.cc @@ -7,6 +7,7 @@ #include "test/common/http/header_map_impl_fuzz.pb.h" #include "test/fuzz/fuzz_runner.h" #include "test/fuzz/utility.h" +#include "test/test_common/test_runtime.h" #include "absl/strings/ascii.h" @@ -16,6 +17,14 @@ namespace Envoy { // Fuzz the header map implementation. DEFINE_PROTO_FUZZER(const test::common::http::HeaderMapImplFuzzTestCase& input) { + TestScopedRuntime runtime; + // Set the lazy header-map threshold if found. + if (input.has_config()) { + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.http.headermap.lazy_map_min_size", + absl::StrCat(input.config().lazy_map_min_size())}}); + } + auto header_map = Http::RequestHeaderMapImpl::create(); std::vector> lower_case_strings; std::vector> strings; From 8fb3cb86082b17144a80402f5367ae65f06083bd Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 8 Oct 2020 11:56:25 -0400 Subject: [PATCH 26/27] release: 1.16.0 (#13438) Signed-off-by: Alyssa Wilk --- GOVERNANCE.md | 1 + RELEASES.md | 2 +- VERSION | 2 +- docs/root/install/building.rst | 4 ++- docs/root/version_history/current.rst | 44 +++++++++++++-------------- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 8c4c2ec7dbd4..767605eda220 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -87,6 +87,7 @@ or you can subscribe to the iCal feed [here](webcal://kubernetes.app.opsgenie.co * Remove the "Pending" tags and add dates to the top of the [release notes for this version](docs/root/version_history/current.rst). * Switch the [VERSION](VERSION) from a "dev" variant to a final variant. E.g., "1.6.0-dev" to "1.6.0". + * Update the [RELEASES](RELEASES.md) doc with the relevant dates. * Get a review and merge. * Wait for tests to pass on [master](https://dev.azure.com/cncf/envoy/_build). * Create a [tagged release](https://github.com/envoyproxy/envoy/releases). The release should diff --git a/RELEASES.md b/RELEASES.md index 3ca3f28c376c..0a58aa22c4c2 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -64,7 +64,7 @@ deadline of 3 weeks. | 1.13.0 | 2019/12/31 | 2020/01/20 | +20 days | 2021/01/20 | | 1.14.0 | 2020/03/31 | 2020/04/08 | +8 days | 2021/04/08 | | 1.15.0 | 2020/06/30 | 2020/07/07 | +7 days | 2021/07/07 | -| 1.16.0 | 2020/09/30 | | | | +| 1.16.0 | 2020/09/30 | 2020/10/08 | +8 days | 2021/10/08 | | 1.17.0 | 2020/12/31 | | | | diff --git a/VERSION b/VERSION index 1f0d2f335194..15b989e398fc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.16.0-dev +1.16.0 diff --git a/docs/root/install/building.rst b/docs/root/install/building.rst index 77dc3ba74c10..71b2f8787901 100644 --- a/docs/root/install/building.rst +++ b/docs/root/install/building.rst @@ -20,11 +20,13 @@ recent Linux including Ubuntu 18.04 LTS. Building Envoy has the following requirements: -* GCC 7+ or Clang/LLVM 7+ (for C++14 support). +* GCC 7+ or Clang/LLVM 7+ (for C++14 support). Clang/LLVM 9+ preferred where Clang is used (see below). * These :repo:`Bazel native ` dependencies. Please see the linked :repo:`CI ` and :repo:`Bazel ` documentation for more information on performing manual builds. +Please note that for Clang/LLVM 8 and lower, Envoy may need to be built with `--define tcmalloc=gperftools` +as the new tcmalloc code is not guaranteed to compile with lower versions of Clang. .. _install_binaries: diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 7f07bae28159..ef773abe13bb 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -1,15 +1,16 @@ -1.16.0 (Pending) -================ +1.16.0 (October 8, 2020) +======================== Incompatible Behavior Changes ----------------------------- *Changes that are expected to cause an incompatibility if applicable; deployment changes are likely required* -* build: added visibility rules for upstream. If these cause visibility related breakage, see notes in //BUILD. +* build: added visibility rules for upstream. If these cause visibility related breakage, see notes in :repo:`BUILD `. +* build: tcmalloc changes require Clang 9. This requirement change can be avoided by building with `--define tcmalloc=gperftools` to use the older tcmalloc code. * config: additional warnings have been added for the use of v2 APIs. These appear as log messages and are also captured in the :ref:`deprecated_feature_use ` counter after server initialization. -* dns: ``envoy.restart_features.use_apple_api_for_dns_lookups`` is on by default. This flag only affects Apple platforms (macOS, iOS). It is incompatible to have the runtime flag set to true at the same time as specifying the ``use_tcp_for_dns_lookups`` option or custom dns resolvers. Doing so will cause failure. +* dns: `envoy.restart_features.use_apple_api_for_dns_lookups` is on by default. This flag only affects Apple platforms (macOS, iOS). It is incompatible to have the runtime flag set to true at the same time as specifying the ``use_tcp_for_dns_lookups`` option or custom dns resolvers. Doing so will cause failure. * watchdog: added two guarddogs, breaking the aggregated stats for the single guarddog system. The aggregated stats for the guarddogs will have the following prefixes: `main_thread` and `workers`. Concretely, anything monitoring `server.watchdog_miss` and `server.watchdog_mega_miss` will need to be updated. Minor Behavior Changes @@ -23,25 +24,24 @@ Minor Behavior Changes * compressor: always insert `Vary` headers for compressible resources even if it's decided not to compress a response due to incompatible `Accept-Encoding` value. The `Vary` header needs to be inserted to let a caching proxy in front of Envoy know that the requested resource still can be served with compression applied. * decompressor: headers-only requests were incorrectly not advertising accept-encoding when configured to do so. This is now fixed. * ext_authz filter: request timeout will now count from the time the check request is created, instead of when it becomes active. This makes sure that the timeout is enforced even if the ext_authz cluster's circuit breaker is engaged. - This behavior can be reverted by setting runtime feature ``envoy.reloadable_features.ext_authz_measure_timeout_on_check_created`` to false. When enabled, a new `ext_authz.timeout` stat is counted when timeout occurs. See :ref:`stats - // `. + This behavior can be reverted by setting runtime feature `envoy.reloadable_features.ext_authz_measure_timeout_on_check_created` to false. When enabled, a new `ext_authz.timeout` stat is counted when timeout occurs. See :ref:`stats `. * grpc reverse bridge: upstream headers will no longer be propagated when the response is missing or contains an unexpected content-type. -* http: added :ref:`contains ` a new string matcher type which matches if the value of the string has the substring mentioned in contains matcher. -* http: added :ref:`contains ` a new header matcher type which matches if the value of the header has the substring mentioned in contains matcher. +* http: added :ref:`contains `, a new string matcher type which matches if the value of the string has the substring mentioned in contains matcher. +* http: added :ref:`contains `, a new header matcher type which matches if the value of the header has the substring mentioned in contains matcher. * http: added :ref:`headers_to_add ` to :ref:`local reply mapper ` to allow its users to add/append/override response HTTP headers to local replies. * http: added HCM level configuration of :ref:`error handling on invalid messaging ` which substantially changes Envoy's behavior when encountering invalid HTTP/1.1 defaulting to closing the connection instead of allowing reuse. This can temporarily be reverted by setting `envoy.reloadable_features.hcm_stream_error_on_invalid_message` to false, or permanently reverted by setting the HTTP/1 configuration :ref:`override_stream_error_on_invalid_http_message ` to true to restore prior HTTP/1.1 behavior (i.e. connection isn't terminated) and to retain prior HTTP/2 behavior (i.e. connection is terminated). * http: added HCM level configuration of :ref:`error handling on invalid messaging ` which substantially changes Envoy's behavior when encountering invalid HTTP/1.1 defaulting to closing the connection instead of allowing reuse. This can temporarily be reverted by setting `envoy.reloadable_features.hcm_stream_error_on_invalid_message` to false, or permanently reverted by setting the :ref:`HCM option ` to true to restore prior HTTP/1.1 beavior and setting the *new* HTTP/2 configuration :ref:`override_stream_error_on_invalid_http_message ` to false to retain prior HTTP/2 behavior. * http: applying route level header modifications to local replies sent on that route. This behavior may be temporarily reverted by setting `envoy.reloadable_features.always_apply_route_header_rules` to false. * http: changed Envoy to send GOAWAY to HTTP2 downstreams when the :ref:`disable_keepalive ` overload action is active. This behavior may be temporarily reverted by setting `envoy.reloadable_features.overload_manager_disable_keepalive_drain_http2` to false. * http: changed Envoy to send error headers and body when possible. This behavior may be temporarily reverted by setting `envoy.reloadable_features.allow_response_for_timeout` to false. -* http: changed empty trailers encoding behavior by sending empty data with ``end_stream`` true (instead of sending empty trailers) for HTTP/2. This behavior can be reverted temporarily by setting runtime feature ``envoy.reloadable_features.http2_skip_encoding_empty_trailers`` to false. +* http: changed empty trailers encoding behavior by sending empty data with ``end_stream`` true (instead of sending empty trailers) for HTTP/2. This behavior can be reverted temporarily by setting runtime feature `envoy.reloadable_features.http2_skip_encoding_empty_trailers` to false. * http: changed how local replies are processed for requests which transform from grpc to not-grpc, or not-grpc to grpc. Previously the initial generated reply depended on which filter sent the reply, but now the reply is consistently generated the way the downstream expects. This behavior can be temporarily reverted by setting `envoy.reloadable_features.unify_grpc_handling` to false. * http: clarified and enforced 1xx handling. Multiple 100-continue headers are coalesced when proxying. 1xx headers other than {100, 101} are dropped. * http: fixed a bug in access logs where early stream termination could be incorrectly tagged as a downstream disconnect, and disconnects after partial response were not flagged. * http: fixed the 100-continue response path to properly handle upstream failure by sending 5xx responses. This behavior can be temporarily reverted by setting `envoy.reloadable_features.allow_500_after_100` to false. * http: the per-stream FilterState maintained by the HTTP connection manager will now provide read/write access to the downstream connection FilterState. As such, code that relies on interacting with this might see a change in behavior. -* logging: added fine-grain logging for file level log control with logger management at administration interface. It can be enabled by option `--enable-fine-grain-logging`. +* logging: added fine-grain logging for file level log control with logger management at administration interface. It can be enabled by option :option:`--enable-fine-grain-logging`. * logging: changed default log format to `"[%Y-%m-%d %T.%e][%t][%l][%n] [%g:%#] %v"` and default value of :option:`--log-format-prefix-with-location` to `0`. * logging: nghttp2 log messages no longer appear at trace level unless `ENVOY_NGHTTP2_TRACE` is set in the environment. @@ -50,9 +50,9 @@ Minor Behavior Changes * postgres: changed log format to tokenize fields of Postgres messages. * router: added transport failure reason to response body when upstream reset happens. After this change, the response body will be of the form `upstream connect error or disconnect/reset before headers. reset reason:{}, transport failure reason:{}`.This behavior may be reverted by setting runtime feature `envoy.reloadable_features.http_transport_failure_reason_in_body` to false. * router: now consumes all retry related headers to prevent them from being propagated to the upstream. This behavior may be reverted by setting runtime feature `envoy.reloadable_features.consume_all_retry_headers` to false. -* stats: the fake symbol table implemention has been removed from the binary, and the option "--use-fake-symbol-table" is now a no-op with a warning. +* stats: the fake symbol table implemention has been removed from the binary, and the option `--use-fake-symbol-table` is now a no-op with a warning. * thrift_proxy: special characters {'\0', '\r', '\n'} will be stripped from thrift headers. -* watchdog: replaced single watchdog with separate watchdog configuration for worker threads and for the main thread :ref:`Watchdogs`. It works with :ref:`watchdog` by having the worker thread and main thread watchdogs have same config. +* watchdog: replaced single watchdog with separate watchdog configuration for worker threads and for the main thread configured via :ref:`Watchdogs`. It works with :ref:`watchdog` by having the worker thread and main thread watchdogs have same config. Bug Fixes --------- @@ -68,7 +68,7 @@ Bug Fixes * http: made the HeaderValues::prefix() method const. * jwt_authn: supports jwt payload without "iss" field. * listener: fixed crash at listener inplace update when connetion load balancer is set. -* rocketmq_proxy network-level filter: fixed an issue involving incorrect header lengths. In debug mode it causes crash and in release mode it causes underflow. +* rocketmq_proxy: fixed an issue involving incorrect header lengths. In debug mode it causes crash and in release mode it causes underflow. * thrift_proxy: fixed crashing bug on request overflow. * udp_proxy: fixed a crash due to UDP packets being processed after listener removal. @@ -96,7 +96,7 @@ New Features * build: enable building envoy :ref:`arm64 images ` by buildx tool in x86 CI platform. * cluster: added new :ref:`connection_pool_per_downstream_connection ` flag, which enable creation of a new connection pool for each downstream connection. * decompressor filter: reports compressed and uncompressed bytes in trailers. -* dns: added support for doing DNS resolution using Apple's DnsService APIs in Apple platforms (macOS, iOS). This feature is ON by default, and is only configurable via the ``envoy.restart_features.use_apple_api_for_dns_lookups`` runtime key. Note that this value is latched during server startup and changing the runtime key is a no-op during the lifetime of the process. +* dns: added support for doing DNS resolution using Apple's DnsService APIs in Apple platforms (macOS, iOS). This feature is ON by default, and is only configurable via the `envoy.restart_features.use_apple_api_for_dns_lookups` runtime key. Note that this value is latched during server startup and changing the runtime key is a no-op during the lifetime of the process. * dns_filter: added support for answering :ref:`service record` queries. * dynamic_forward_proxy: added :ref:`use_tcp_for_dns_lookups` option to use TCP for DNS lookups in order to match the DNS options for :ref:`Clusters`. * ext_authz filter: added support for emitting dynamic metadata for both :ref:`HTTP ` and :ref:`network ` filters. @@ -115,7 +115,7 @@ New Features * http: added :ref:`allow_chunked_length ` configuration option for HTTP/1 codec to allow processing requests/responses with both Content-Length and Transfer-Encoding: chunked headers. If such message is served and option is enabled - per RFC Content-Length is ignored and removed. * http: added :ref:`CDN Loop filter ` and :ref:`documentation `. * http: added :ref:`MaxStreamDuration proto ` for configuring per-route downstream duration timeouts. -* http: introduced new HTTP/1 and HTTP/2 codec implementations that will remove the use of exceptions for control flow due to high risk factors and instead use error statuses. The old behavior is used by default, but the new codecs can be enabled for testing by setting the runtime feature `envoy.reloadable_features.new_codec_behavior` to true. The new codecs will be in development for one month, and then enabled by default while the old codecs are deprecated. +* http: introduced new HTTP/1 and HTTP/2 codec implementations that will remove the use of exceptions for control flow due to high risk factors and instead use error statuses. The old behavior is used by default for HTTP/1.1 and HTTP/2 server connections. The new codecs can be enabled for testing by setting the runtime feature `envoy.reloadable_features.new_codec_behavior` to true. The new codecs will be in development for one month, and then enabled by default while the old codecs are deprecated. * http: modified the HTTP header-map data-structure to use an underlying dictionary and a list (no change to the header-map API). To conform with previous versions, the use of a dictionary is currently disabled. It can be enabled by setting the `envoy.http.headermap.lazy_map_min_size` runtime feature to a non-negative number which defines the minimal number of headers in a request/response/trailers required for using a dictionary in addition to the list. Our current benchmarks suggest that the value 3 is a good threshold for most workloads. * load balancer: added :ref:`RingHashLbConfig` to configure the table size of Maglev consistent hash. * load balancer: added a :ref:`configuration` option to specify the active request bias used by the least request load balancer. @@ -149,7 +149,6 @@ New Features that track headers and body sizes of requests and responses. * stats: allow configuring histogram buckets for stats sinks and admin endpoints that support it. * tap: added :ref:`generic body matcher` to scan http requests and responses for text or hex patterns. -* tcp: switched the TCP connection pool to the new "shared" connection pool, sharing a common code base with HTTP and HTTP/2. Any unexpected behavioral changes can be temporarily reverted by setting `envoy.reloadable_features.new_tcp_connection_pool` to false. * tcp_proxy: added :ref:`max_downstream_connection_duration` for downstream connection. When max duration is reached the connection will be closed. * tcp_proxy: allow earlier network filters to set metadataMatchCriteria on the connection StreamInfo to influence load balancing. * tls: added OCSP stapling support through the :ref:`ocsp_staple ` and :ref:`ocsp_staple_policy ` configuration options. See :ref:`OCSP Stapling ` for usage and runtime flags. @@ -159,9 +158,9 @@ New Features * udp_proxy: added :ref:`hash_policies ` to support hash based routing. * udp_proxy: added :ref:`use_original_src_ip ` option to replicate the downstream remote address of the packets on the upstream side of Envoy. It is similar to :ref:`original source filter `. * watchdog: support randomizing the watchdog's kill timeout to prevent synchronized kills via a maximium jitter parameter :ref:`max_kill_timeout_jitter`. -* watchdog: supports an extension point where actions can be registered to fire on watchdog events such as miss, megamiss, kill and multikill. See ref:`watchdog actions`. -* watchdog: watchdog action extension that does cpu profiling. See ref:`Profile Action `. -* watchdog: watchdog action extension that sends SIGABRT to the stuck thread to terminate the process. See ref:`Abort Action `. +* watchdog: supports an extension point where actions can be registered to fire on watchdog events such as miss, megamiss, kill and multikill. See :ref:`watchdog actions`. +* watchdog: watchdog action extension that does cpu profiling. See :ref:`Profile Action `. +* watchdog: watchdog action extension that sends SIGABRT to the stuck thread to terminate the process. See :ref:`Abort Action `. * xds: added :ref:`extension config discovery` support for HTTP filters. * xds: added support for mixed v2/v3 discovery response, which enable type url downgrade and upgrade. This feature is disabled by default and is controlled by runtime guard `envoy.reloadable_features.enable_type_url_downgrade_and_upgrade`. * zlib: added option to use `zlib-ng `_ as zlib library. @@ -169,9 +168,10 @@ New Features Deprecated ---------- -* build: Alpine based debug image is deprecated in favor of :ref:`Ubuntu based debug image `. -* The :ref:`track_timeout_budgets ` +* build: alpine based debug image is deprecated in favor of :ref:`Ubuntu based debug image `. +* cluster: the :ref:`track_timeout_budgets ` field has been deprecated in favor of `timeout_budgets` part of an :ref:`Optional Configuration `. +* ext_authz: the :ref:`dynamic metadata ` field in :ref:`OkHttpResponse ` has been deprecated in favor of :ref:`dynamic metadata ` field in :ref:`CheckResponse `. * hds: the :ref:`endpoints_health ` field has been deprecated in favor of :ref:`cluster_endpoints_health ` to maintain grouping by cluster and locality. @@ -180,7 +180,5 @@ Deprecated * router: the :ref:`grpc_timeout_offset ` field has been deprecated in favor of :ref:`grpc_timeout_header_offset `. * tap: the :ref:`match_config ` field has been deprecated in favor of :ref:`match ` field. -* ext_authz: the :ref:`dynamic metadata ` field in :ref:`OkHttpResponse ` - has been deprecated in favor of :ref:`dynamic metadata ` field in :ref:`CheckResponse `. * router_check_tool: `request_header_fields`, `response_header_fields` config deprecated in favor of `request_header_matches`, `response_header_matches`. * watchdog: :ref:`watchdog ` deprecated in favor of :ref:`watchdogs `. From 5b550325270c8477e3ef96660ebc02d216f0fc7a Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Thu, 8 Oct 2020 12:36:33 -0400 Subject: [PATCH 27/27] http2: Proactively disconnect connections flooded with GOAWAY frames (#13406) Proactively disconnect connections flooded with GOAWAY frames Signed-off-by: Yan Avlasov --- source/common/http/http2/codec_impl.cc | 1 + source/common/http/http2/codec_impl_legacy.cc | 1 + test/common/http/http2/codec_impl_test.cc | 40 ++++++ test/integration/http2_integration_test.cc | 123 ++++++++++++++++++ test/integration/http2_integration_test.h | 1 + 5 files changed, 166 insertions(+) diff --git a/source/common/http/http2/codec_impl.cc b/source/common/http/http2/codec_impl.cc index 68adc0128603..f8d8b1b2ffbf 100644 --- a/source/common/http/http2/codec_impl.cc +++ b/source/common/http/http2/codec_impl.cc @@ -704,6 +704,7 @@ void ConnectionImpl::shutdownNotice() { auto status = sendPendingFrames(); // See comment in the `encodeHeadersBase()` method about this RELEASE_ASSERT. RELEASE_ASSERT(status.ok(), "sendPendingFrames() failure in non dispatching context"); + checkProtocolConstraintViolation(); } Status ConnectionImpl::onBeforeFrameReceived(const nghttp2_frame_hd* hd) { diff --git a/source/common/http/http2/codec_impl_legacy.cc b/source/common/http/http2/codec_impl_legacy.cc index 354fb689752c..2ba5c23f4d22 100644 --- a/source/common/http/http2/codec_impl_legacy.cc +++ b/source/common/http/http2/codec_impl_legacy.cc @@ -671,6 +671,7 @@ void ConnectionImpl::shutdownNotice() { ASSERT(rc == 0); sendPendingFrames(); + checkProtocolConstraintViolation(); } int ConnectionImpl::onBeforeFrameReceived(const nghttp2_frame_hd* hd) { diff --git a/test/common/http/http2/codec_impl_test.cc b/test/common/http/http2/codec_impl_test.cc index 3e348a12393b..9cbb761d329e 100644 --- a/test/common/http/http2/codec_impl_test.cc +++ b/test/common/http/http2/codec_impl_test.cc @@ -2260,6 +2260,46 @@ TEST_P(Http2CodecImplTest, EmptyDataFloodOverride) { EXPECT_TRUE(status.ok()); } +// Verify that codec detects flood of outbound frames caused by shutdownNotice() method +TEST_P(Http2CodecImplTest, ShudowNoticeCausesOutboundFlood) { + initialize(); + + TestRequestHeaderMapImpl request_headers; + HttpTestUtility::addDefaultHeaders(request_headers); + EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); + request_encoder_->encodeHeaders(request_headers, false); + + int frame_count = 0; + Buffer::OwnedImpl buffer; + ON_CALL(server_connection_, write(_, _)) + .WillByDefault(Invoke([&buffer, &frame_count](Buffer::Instance& frame, bool) { + ++frame_count; + buffer.move(frame); + })); + + auto* violation_callback = + new NiceMock(&server_connection_.dispatcher_); + + TestResponseHeaderMapImpl response_headers{{":status", "200"}}; + response_encoder_->encodeHeaders(response_headers, false); + // Account for the single HEADERS frame above + for (uint32_t i = 0; i < CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES - 1; ++i) { + Buffer::OwnedImpl data("0"); + EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); + } + + EXPECT_FALSE(violation_callback->enabled_); + + server_->shutdownNotice(); + + EXPECT_TRUE(violation_callback->enabled_); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + violation_callback->invokeCallback(); + + EXPECT_EQ(frame_count, CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1); + EXPECT_EQ(1, server_stats_store_.counter("http2.outbound_flood").value()); +} + // CONNECT without upgrade type gets tagged with "bytestream" TEST_P(Http2CodecImplTest, ConnectTest) { client_http2_options_.set_allow_connect(true); diff --git a/test/integration/http2_integration_test.cc b/test/integration/http2_integration_test.cc index d08a282e66a4..d9f1ed21e899 100644 --- a/test/integration/http2_integration_test.cc +++ b/test/integration/http2_integration_test.cc @@ -1753,6 +1753,15 @@ void Http2FloodMitigationTest::prefillOutboundDownstreamQueue(uint32_t data_fram EXPECT_EQ(0, test_server_->counter("http2.outbound_flood")->value()); } +void Http2FloodMitigationTest::triggerListenerDrain() { + absl::Notification drain_sequence_started; + test_server_->server().dispatcher().post([this, &drain_sequence_started]() { + test_server_->drainManager().startDrainSequence([] {}); + drain_sequence_started.Notify(); + }); + drain_sequence_started.WaitForNotification(); +} + INSTANTIATE_TEST_SUITE_P(IpVersions, Http2FloodMitigationTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), TestUtility::ipTestParamsToString); @@ -1992,6 +2001,120 @@ TEST_P(Http2FloodMitigationTest, RST_STREAM) { test_server_->counter("http.config_test.downstream_cx_delayed_close_timeout")->value()); } +// Verify detection of overflowing outbound frame queue with the GOAWAY frames sent after the +// downstream idle connection timeout disconnects the connection. +// The test verifies protocol constraint violation handling in the +// Http2::ConnectionImpl::shutdownNotice() method. +TEST_P(Http2FloodMitigationTest, DownstreamIdleTimeoutTriggersFloodProtection) { + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + auto* http_protocol_options = hcm.mutable_common_http_protocol_options(); + auto* idle_time_out = http_protocol_options->mutable_idle_timeout(); + std::chrono::milliseconds timeout(1000); + auto seconds = std::chrono::duration_cast(timeout); + idle_time_out->set_seconds(seconds.count()); + }); + + prefillOutboundDownstreamQueue(AllFrameFloodLimit - 1); + tcp_client_->waitForDisconnect(); + + EXPECT_EQ(1, test_server_->counter("http2.outbound_flood")->value()); + EXPECT_EQ(1, test_server_->counter("http.config_test.downstream_cx_idle_timeout")->value()); +} + +// Verify detection of overflowing outbound frame queue with the GOAWAY frames sent after the +// downstream connection duration timeout disconnects the connection. +// The test verifies protocol constraint violation handling in the +// Http2::ConnectionImpl::shutdownNotice() method. +TEST_P(Http2FloodMitigationTest, DownstreamConnectionDurationTimeoutTriggersFloodProtection) { + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + auto* http_protocol_options = hcm.mutable_common_http_protocol_options(); + auto* max_connection_duration = http_protocol_options->mutable_max_connection_duration(); + std::chrono::milliseconds timeout(1000); + auto seconds = std::chrono::duration_cast(timeout); + max_connection_duration->set_seconds(seconds.count()); + }); + prefillOutboundDownstreamQueue(AllFrameFloodLimit - 1); + tcp_client_->waitForDisconnect(); + + EXPECT_EQ(1, test_server_->counter("http2.outbound_flood")->value()); + EXPECT_EQ(1, + test_server_->counter("http.config_test.downstream_cx_max_duration_reached")->value()); +} + +// Verify detection of frame flood when sending GOAWAY frame during processing of response headers +// on a draining listener. +TEST_P(Http2FloodMitigationTest, GoawayOverflowDuringResponseWhenDraining) { + // pre-fill one away from overflow + prefillOutboundDownstreamQueue(AllFrameFloodLimit - 1); + + triggerListenerDrain(); + + // Send second request which should trigger Envoy to send GOAWAY (since it is in the draining + // state) when processing response headers. Verify that connection was disconnected and + // appropriate counters were set. + auto request2 = + Http2Frame::makeRequest(Http2Frame::makeClientStreamId(1), "host", "/test/long/url"); + sendFrame(request2); + + // Wait for connection to be flooded with outbound GOAWAY frame and disconnected. + tcp_client_->waitForDisconnect(); + + // Verify that the upstream connection is still alive. + ASSERT_EQ(1, test_server_->gauge("cluster.cluster_0.upstream_cx_active")->value()); + ASSERT_EQ(0, test_server_->counter("cluster.cluster_0.upstream_cx_destroy")->value()); + // Verify that the flood check was triggered + EXPECT_EQ(1, test_server_->counter("http2.outbound_flood")->value()); + EXPECT_EQ(1, test_server_->counter("http.config_test.downstream_cx_drain_close")->value()); +} + +// Verify detection of frame flood when sending GOAWAY frame during call to sendLocalReply() +// from decoder filter on a draining listener. +TEST_P(Http2FloodMitigationTest, GoawayOverflowFromDecoderFilterSendLocalReplyWhenDraining) { + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { + const std::string yaml_string = R"EOF( +name: send_local_reply_filter +typed_config: + "@type": type.googleapis.com/test.integration.filters.SetResponseCodeFilterConfig + prefix: "/call_send_local_reply" + code: 404 + )EOF"; + TestUtility::loadFromYaml(yaml_string, *hcm.add_http_filters()); + // keep router the last + auto size = hcm.http_filters_size(); + hcm.mutable_http_filters()->SwapElements(size - 2, size - 1); + }); + + // pre-fill one away from overflow + prefillOutboundDownstreamQueue(AllFrameFloodLimit - 1); + + triggerListenerDrain(); + + // At this point the outbound downstream frame queue should be 1 away from overflowing. + // Make the SetResponseCodeFilterConfig decoder filter call sendLocalReply without body which + // should trigger Envoy to send GOAWAY (since it is in the draining state) when processing + // sendLocalReply() headers. Verify that connection was disconnected and appropriate counters were + // set. + auto request2 = + Http2Frame::makeRequest(Http2Frame::makeClientStreamId(1), "host", "/call_send_local_reply"); + sendFrame(request2); + + // Wait for connection to be flooded with outbound GOAWAY frame and disconnected. + tcp_client_->waitForDisconnect(); + + // Verify that the upstream connection is still alive. + ASSERT_EQ(1, test_server_->gauge("cluster.cluster_0.upstream_cx_active")->value()); + ASSERT_EQ(0, test_server_->counter("cluster.cluster_0.upstream_cx_destroy")->value()); + // Verify that the flood check was triggered + EXPECT_EQ(1, test_server_->counter("http2.outbound_flood")->value()); + EXPECT_EQ(1, test_server_->counter("http.config_test.downstream_cx_drain_close")->value()); +} + // Verify that the server stop reading downstream connection on protocol error. TEST_P(Http2FloodMitigationTest, TooManyStreams) { config_helper_.addConfigModifier( diff --git a/test/integration/http2_integration_test.h b/test/integration/http2_integration_test.h index c6b56d6037ca..daa4838756f1 100644 --- a/test/integration/http2_integration_test.h +++ b/test/integration/http2_integration_test.h @@ -139,5 +139,6 @@ class Http2FloodMitigationTest : public SocketInterfaceSwap, public Http2FrameIn void setNetworkConnectionBufferSize(); void beginSession() override; void prefillOutboundDownstreamQueue(uint32_t data_frame_count); + void triggerListenerDrain(); }; } // namespace Envoy