From e079438923ce0a57d2257732b79c5bfabbb44145 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Mon, 11 Mar 2024 17:44:24 -0400 Subject: [PATCH 001/124] mobile: Enable setting post DNS refresh drain options for Cronvoy (#32827) Signed-off-by: Ali Beyad --- .../net/impl/NativeCronvoyEngineBuilderImpl.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java b/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java index 3ee404f7a5ff..5236472c7622 100644 --- a/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java +++ b/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java @@ -45,7 +45,7 @@ public class NativeCronvoyEngineBuilderImpl extends CronvoyEngineBuilderImpl { private final List mDnsFallbackNameservers = Collections.emptyList(); private final boolean mEnableDnsFilterUnroutableFamilies = true; private final boolean mDnsUseSystemResolver = true; - private final boolean mEnableDrainPostDnsRefresh = false; + private boolean mEnableDrainPostDnsRefresh = false; private final boolean mEnableGzipDecompression = true; private final boolean mEnableSocketTag = true; private final boolean mEnableInterfaceBinding = false; @@ -71,6 +71,17 @@ public class NativeCronvoyEngineBuilderImpl extends CronvoyEngineBuilderImpl { */ public NativeCronvoyEngineBuilderImpl(Context context) { super(context); } + /** + * Enable draining of the connections after a DNS refresh changes the host address mapping. + * The default behavior is to not enable draining post DNS refresh. + * + * @param enable If true, enable drain post DNS refresh; otherwise, don't. + */ + public NativeCronvoyEngineBuilderImpl setEnableDrainPostDnsRefresh(boolean enable) { + mEnableDrainPostDnsRefresh = enable; + return this; + } + /** * Indicates to skip the TLS certificate verification. * From ec6843953ecca86877b2c4acd3a794282695fee8 Mon Sep 17 00:00:00 2001 From: Ben Plotnick Date: Mon, 11 Mar 2024 18:04:57 -0400 Subject: [PATCH 002/124] oauth2: on refresh, replace instead of append cookies (#32781) Signed-off-by: Ben Plotnick --- changelogs/current.yaml | 4 ++ .../extensions/filters/http/oauth2/filter.cc | 2 +- .../filters/http/oauth2/filter_test.cc | 59 ++++++++++++++++--- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index e77a0bfda18e..e9f1ae697e5f 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -148,6 +148,10 @@ bug_fixes: - area: tracing change: | Dynatrace resource detector: Only log warning message when no enrichment attributes are found. +- area: oauth + change: | + When performing a token refresh and forwarding tokens upstream, replace existing token cookies rather than appending as + another Cookie header. removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` diff --git a/source/extensions/filters/http/oauth2/filter.cc b/source/extensions/filters/http/oauth2/filter.cc index 3eefed0e15a0..5bd8aba7030a 100644 --- a/source/extensions/filters/http/oauth2/filter.cc +++ b/source/extensions/filters/http/oauth2/filter.cc @@ -608,7 +608,7 @@ void OAuth2Filter::finishRefreshAccessTokenFlow() { } std::string new_cookies(absl::StrJoin(cookies, "; ", absl::PairFormatter("="))); - request_headers_->addReferenceKey(Http::Headers::get().Cookie, new_cookies); + request_headers_->setReferenceKey(Http::Headers::get().Cookie, new_cookies); if (config_->forwardBearerToken() && !access_token_.empty()) { setBearerToken(*request_headers_, access_token_); } diff --git a/test/extensions/filters/http/oauth2/filter_test.cc b/test/extensions/filters/http/oauth2/filter_test.cc index d216c156cb08..bf0822bdbcf9 100644 --- a/test/extensions/filters/http/oauth2/filter_test.cc +++ b/test/extensions/filters/http/oauth2/filter_test.cc @@ -2217,12 +2217,17 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessToken) { init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */)); + const auto expires_at_s = DateUtil::nowToSeconds(test_time_.timeSystem()) - 10; + // the third request to the oauth filter with URI parameters. Http::TestRequestHeaderMapImpl request_headers{ {Http::Headers::get().Path.get(), "/test?name=admin&level=trace"}, {Http::Headers::get().Host.get(), "traffic.example.com"}, {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Post}, {Http::Headers::get().Scheme.get(), "https"}, + {Http::Headers::get().Cookie.get(), fmt::format("OauthExpires={}", expires_at_s)}, + {Http::Headers::get().Cookie.get(), "BearerToken=xyztoken"}, + {Http::Headers::get().Cookie.get(), "OauthHMAC=dCu0otMcLoaGF73jrT+R8rGA0pnWyMgNf4+GivGrHEI="}, }; std::string legit_refresh_token{"legit_refresh_token"}; @@ -2241,6 +2246,11 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessToken) { EXPECT_CALL(decoder_callbacks_, continueDecoding()); + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(0))); + const std::chrono::seconds expiredTime(10); + filter_->updateTokens("accessToken", "idToken", "refreshToken", expiredTime); + filter_->finishRefreshAccessTokenFlow(); Http::TestResponseHeaderMapImpl response_headers{}; @@ -2249,13 +2259,24 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessToken) { Http::TestResponseHeaderMapImpl expected_response_headers{ {Http::Headers::get().SetCookie.get(), "OauthHMAC=" - "fV62OgLipChTQQC3UFgDp+l5sCiSb3zt7nCoJiVivWw=;" - "path=/;Max-Age=;secure;HttpOnly"}, - {Http::Headers::get().SetCookie.get(), "OauthExpires=;path=/;Max-Age=;secure;HttpOnly"}, - {Http::Headers::get().SetCookie.get(), "BearerToken=;path=/;Max-Age=;secure;HttpOnly"}, + "OYnODPsSGabEpZ2LAiPxyjAFgN/7/5Xg24G7jUoUbyI=;" + "path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), "OauthExpires=10;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=accessToken;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), "IdToken=idToken;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=refreshToken;path=/;Max-Age=10;secure;HttpOnly"}, }; EXPECT_THAT(response_headers, HeaderMapEqualRef(&expected_response_headers)); + + auto cookies = Http::Utility::parseCookies(request_headers); + EXPECT_EQ(cookies.at("OauthHMAC"), "OYnODPsSGabEpZ2LAiPxyjAFgN/7/5Xg24G7jUoUbyI="); + EXPECT_EQ(cookies.at("OauthExpires"), "10"); + EXPECT_EQ(cookies.at("BearerToken"), "accessToken"); + EXPECT_EQ(cookies.at("IdToken"), "idToken"); + EXPECT_EQ(cookies.at("RefreshToken"), "refreshToken"); } TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessTokenWithBasicAuth) { @@ -2265,11 +2286,17 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessTokenWithBasicAuth) { OAuth2Config_AuthType_BASIC_AUTH /* authType */)); + const auto expires_at_s = DateUtil::nowToSeconds(test_time_.timeSystem()) - 10; + Http::TestRequestHeaderMapImpl request_headers{ {Http::Headers::get().Path.get(), "/test?name=admin&level=trace"}, {Http::Headers::get().Host.get(), "traffic.example.com"}, {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Post}, {Http::Headers::get().Scheme.get(), "https"}, + {Http::Headers::get().Cookie.get(), fmt::format("OauthExpires={}", expires_at_s)}, + {Http::Headers::get().Cookie.get(), "BearerToken=xyztoken"}, + {Http::Headers::get().Cookie.get(), "OauthHMAC=dCu0otMcLoaGF73jrT+R8rGA0pnWyMgNf4+GivGrHEI="}, + {Http::Headers::get().Cookie.get(), "RefreshToken=legit_refresh_token"}, }; std::string legit_refresh_token{"legit_refresh_token"}; @@ -2288,6 +2315,11 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessTokenWithBasicAuth) { EXPECT_CALL(decoder_callbacks_, continueDecoding()); + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(0))); + const std::chrono::seconds expiredTime(10); + filter_->updateTokens("accessToken", "idToken", "refreshToken", expiredTime); + filter_->finishRefreshAccessTokenFlow(); Http::TestResponseHeaderMapImpl response_headers{}; @@ -2296,13 +2328,24 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessTokenWithBasicAuth) { Http::TestResponseHeaderMapImpl expected_response_headers{ {Http::Headers::get().SetCookie.get(), "OauthHMAC=" - "fV62OgLipChTQQC3UFgDp+l5sCiSb3zt7nCoJiVivWw=;" - "path=/;Max-Age=;secure;HttpOnly"}, - {Http::Headers::get().SetCookie.get(), "OauthExpires=;path=/;Max-Age=;secure;HttpOnly"}, - {Http::Headers::get().SetCookie.get(), "BearerToken=;path=/;Max-Age=;secure;HttpOnly"}, + "OYnODPsSGabEpZ2LAiPxyjAFgN/7/5Xg24G7jUoUbyI=;" + "path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), "OauthExpires=10;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=accessToken;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), "IdToken=idToken;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=refreshToken;path=/;Max-Age=10;secure;HttpOnly"}, }; EXPECT_THAT(response_headers, HeaderMapEqualRef(&expected_response_headers)); + + auto cookies = Http::Utility::parseCookies(request_headers); + EXPECT_EQ(cookies.at("OauthHMAC"), "OYnODPsSGabEpZ2LAiPxyjAFgN/7/5Xg24G7jUoUbyI="); + EXPECT_EQ(cookies.at("OauthExpires"), "10"); + EXPECT_EQ(cookies.at("BearerToken"), "accessToken"); + EXPECT_EQ(cookies.at("IdToken"), "idToken"); + EXPECT_EQ(cookies.at("RefreshToken"), "refreshToken"); } } // namespace Oauth2 From 708fa7b4d8269372fdac39b11caf2a3bf7b18d53 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Mon, 11 Mar 2024 18:55:23 -0400 Subject: [PATCH 003/124] runtime: removing most of the exceptions (#32708) Signed-off-by: Alyssa Wilk --- envoy/runtime/runtime.h | 4 +- source/common/runtime/runtime_impl.cc | 119 +++++++++++------- source/common/runtime/runtime_impl.h | 27 ++-- source/server/admin/runtime_handler.cc | 2 +- source/server/server.cc | 7 +- test/common/runtime/runtime_impl_test.cc | 83 ++++++------ test/common/stats/thread_local_store_test.cc | 8 +- .../http_connection_manager/config_test.cc | 4 +- test/mocks/runtime/mocks.h | 2 +- test/test_common/test_runtime.h | 16 ++- tools/code_format/config.yaml | 1 + 11 files changed, 167 insertions(+), 106 deletions(-) diff --git a/envoy/runtime/runtime.h b/envoy/runtime/runtime.h index 28952a52e97f..a3c1f8bd1353 100644 --- a/envoy/runtime/runtime.h +++ b/envoy/runtime/runtime.h @@ -247,8 +247,10 @@ class Loader { * Merge the given map of key-value pairs into the runtime's state. To remove a previous merge for * a key, use an empty string as the value. * @param values the values to merge + * @return a status indicating success or failure. */ - virtual void mergeValues(const absl::node_hash_map& values) PURE; + virtual absl::Status + mergeValues(const absl::node_hash_map& values) PURE; /** * Initiate all RTDS subscriptions. The `on_done` callback is invoked when all RTDS requests diff --git a/source/common/runtime/runtime_impl.cc b/source/common/runtime/runtime_impl.cc index 17511407813a..ab2843df4888 100644 --- a/source/common/runtime/runtime_impl.cc +++ b/source/common/runtime/runtime_impl.cc @@ -399,7 +399,7 @@ SnapshotImpl::Entry SnapshotImpl::createEntry(const ProtobufWkt::Value& value, return entry; } -void AdminLayer::mergeValues(const absl::node_hash_map& values) { +absl::Status AdminLayer::mergeValues(const absl::node_hash_map& values) { #ifdef ENVOY_ENABLE_YAML for (const auto& kv : values) { values_.erase(kv.first); @@ -408,37 +408,38 @@ void AdminLayer::mergeValues(const absl::node_hash_map } } stats_.admin_overrides_active_.set(values_.empty() ? 0 : 1); + return absl::OkStatus(); #else - IS_ENVOY_BUG("Runtime admin reload requires YAML support"); UNREFERENCED_PARAMETER(values); - return; + return absl::InvalidArgumentError("Runtime admin reload requires YAML support"); #endif } -DiskLayer::DiskLayer(absl::string_view name, const std::string& path, Api::Api& api) +DiskLayer::DiskLayer(absl::string_view name, const std::string& path, Api::Api& api, + absl::Status& creation_status) : OverrideLayerImpl{name} { - walkDirectory(path, "", 1, api); + creation_status = walkDirectory(path, "", 1, api); } -void DiskLayer::walkDirectory(const std::string& path, const std::string& prefix, uint32_t depth, - Api::Api& api) { +absl::Status DiskLayer::walkDirectory(const std::string& path, const std::string& prefix, + uint32_t depth, Api::Api& api) { // Maximum recursion depth for walkDirectory(). static constexpr uint32_t MaxWalkDepth = 16; ENVOY_LOG(debug, "walking directory: {}", path); if (depth > MaxWalkDepth) { - throwEnvoyExceptionOrPanic(absl::StrCat("Walk recursion depth exceeded ", MaxWalkDepth)); + return absl::InvalidArgumentError(absl::StrCat("Walk recursion depth exceeded ", MaxWalkDepth)); } // Check if this is an obviously bad path. if (api.fileSystem().illegalPath(path)) { - throwEnvoyExceptionOrPanic(absl::StrCat("Invalid path: ", path)); + return absl::InvalidArgumentError(absl::StrCat("Invalid path: ", path)); } Filesystem::Directory directory(path); Filesystem::DirectoryIteratorImpl it = directory.begin(); - THROW_IF_NOT_OK_REF(it.status()); + RETURN_IF_STATUS_NOT_OK(it); for (; it != directory.end(); ++it) { - THROW_IF_NOT_OK_REF(it.status()); + RETURN_IF_STATUS_NOT_OK(it); Filesystem::DirectoryEntry entry = *it; std::string full_path = path + "/" + entry.name_; std::string full_prefix; @@ -450,7 +451,8 @@ void DiskLayer::walkDirectory(const std::string& path, const std::string& prefix if (entry.type_ == Filesystem::FileType::Directory && entry.name_ != "." && entry.name_ != "..") { - walkDirectory(full_path, full_prefix, depth + 1, api); + absl::Status status = walkDirectory(full_path, full_prefix, depth + 1, api); + RETURN_IF_NOT_OK(status); } else if (entry.type_ == Filesystem::FileType::Regular) { // Suck the file into a string. This is not very efficient but it should be good enough // for small files. Also, as noted elsewhere, none of this is non-blocking which could @@ -461,7 +463,7 @@ void DiskLayer::walkDirectory(const std::string& path, const std::string& prefix // Read the file and remove any comments. A comment is a line starting with a '#' character. // Comments are useful for placeholder files with no value. auto file_or_error = api.fileSystem().fileReadToEnd(full_path); - THROW_IF_STATUS_NOT_OK(file_or_error, throw); + RETURN_IF_STATUS_NOT_OK(file_or_error); const std::string text_file{file_or_error.value()}; const auto lines = StringUtil::splitToken(text_file, "\n"); @@ -484,26 +486,32 @@ void DiskLayer::walkDirectory(const std::string& path, const std::string& prefix #else IS_ENVOY_BUG("Runtime admin reload requires YAML support"); UNREFERENCED_PARAMETER(value); - return; + return absl::OkStatus(); #endif } } - THROW_IF_NOT_OK_REF(it.status()); + RETURN_IF_STATUS_NOT_OK(it); + return absl::OkStatus(); } -ProtoLayer::ProtoLayer(absl::string_view name, const ProtobufWkt::Struct& proto) +ProtoLayer::ProtoLayer(absl::string_view name, const ProtobufWkt::Struct& proto, + absl::Status& creation_status) : OverrideLayerImpl{name} { + creation_status = absl::OkStatus(); for (const auto& f : proto.fields()) { - walkProtoValue(f.second, f.first); + creation_status = walkProtoValue(f.second, f.first); + if (!creation_status.ok()) { + return; + } } } -void ProtoLayer::walkProtoValue(const ProtobufWkt::Value& v, const std::string& prefix) { +absl::Status ProtoLayer::walkProtoValue(const ProtobufWkt::Value& v, const std::string& prefix) { switch (v.kind_case()) { case ProtobufWkt::Value::KIND_NOT_SET: case ProtobufWkt::Value::kListValue: case ProtobufWkt::Value::kNullValue: - throwEnvoyExceptionOrPanic(absl::StrCat("Invalid runtime entry value for ", prefix)); + return absl::InvalidArgumentError(absl::StrCat("Invalid runtime entry value for ", prefix)); break; case ProtobufWkt::Value::kStringValue: SnapshotImpl::addEntry(values_, prefix, v, ""); @@ -525,26 +533,32 @@ void ProtoLayer::walkProtoValue(const ProtobufWkt::Value& v, const std::string& break; } for (const auto& f : s.fields()) { - walkProtoValue(f.second, prefix + "." + f.first); + absl::Status status = walkProtoValue(f.second, prefix + "." + f.first); + RETURN_IF_NOT_OK(status); } break; } } + return absl::OkStatus(); } LoaderImpl::LoaderImpl(Event::Dispatcher& dispatcher, ThreadLocal::SlotAllocator& tls, const envoy::config::bootstrap::v3::LayeredRuntime& config, const LocalInfo::LocalInfo& local_info, Stats::Store& store, Random::RandomGenerator& generator, - ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api) + ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api, + absl::Status& creation_status) : generator_(generator), stats_(generateStats(store)), tls_(tls.allocateSlot()), config_(config), service_cluster_(local_info.clusterName()), api_(api), init_watcher_("RTDS", [this]() { onRtdsReady(); }), store_(store) { + creation_status = absl::OkStatus(); absl::node_hash_set layer_names; for (const auto& layer : config_.layers()) { auto ret = layer_names.insert(layer.name()); if (!ret.second) { - throwEnvoyExceptionOrPanic(absl::StrCat("Duplicate layer name: ", layer.name())); + creation_status = + absl::InvalidArgumentError(absl::StrCat("Duplicate layer name: ", layer.name())); + return; } switch (layer.layer_specifier_case()) { case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kStaticLayer: @@ -552,8 +566,9 @@ LoaderImpl::LoaderImpl(Event::Dispatcher& dispatcher, ThreadLocal::SlotAllocator break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kAdminLayer: if (admin_layer_ != nullptr) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Too many admin layers specified in LayeredRuntime, at most one may be specified"); + return; } admin_layer_ = std::make_unique(layer.name(), stats_); break; @@ -562,7 +577,7 @@ LoaderImpl::LoaderImpl(Event::Dispatcher& dispatcher, ThreadLocal::SlotAllocator watcher_ = dispatcher.createFilesystemWatcher(); } watcher_->addWatch(layer.disk_layer().symlink_root(), Filesystem::Watcher::Events::MovedTo, - [this](uint32_t) -> void { loadNewSnapshot(); }); + [this](uint32_t) -> void { THROW_IF_NOT_OK(loadNewSnapshot()); }); break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kRtdsLayer: subscriptions_.emplace_back( @@ -570,11 +585,12 @@ LoaderImpl::LoaderImpl(Event::Dispatcher& dispatcher, ThreadLocal::SlotAllocator init_manager_.add(subscriptions_.back()->init_target_); break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::LAYER_SPECIFIER_NOT_SET: - throwEnvoyExceptionOrPanic("layer specifier not set"); + creation_status = absl::InvalidArgumentError("layer specifier not set"); + return; } } - loadNewSnapshot(); + creation_status = loadNewSnapshot(); } void LoaderImpl::initialize(Upstream::ClusterManager& cm) { @@ -626,7 +642,7 @@ RtdsSubscription::onConfigUpdate(const std::vector& } ENVOY_LOG(debug, "Reloading RTDS snapshot for onConfigUpdate"); proto_.CopyFrom(runtime.layer()); - parent_.loadNewSnapshot(); + RETURN_IF_NOT_OK(parent_.loadNewSnapshot()); init_target_.ready(); return absl::OkStatus(); } @@ -680,13 +696,15 @@ absl::Status RtdsSubscription::onConfigRemoved( } ENVOY_LOG(debug, "Clear RTDS snapshot for onConfigUpdate"); proto_.Clear(); - parent_.loadNewSnapshot(); + RETURN_IF_NOT_OK(parent_.loadNewSnapshot()); init_target_.ready(); return absl::OkStatus(); } -void LoaderImpl::loadNewSnapshot() { - std::shared_ptr ptr = createNewSnapshot(); +absl::Status LoaderImpl::loadNewSnapshot() { + auto snapshot_or_error = createNewSnapshot(); + RETURN_IF_STATUS_NOT_OK(snapshot_or_error); + std::shared_ptr ptr = std::move(snapshot_or_error.value()); tls_->set([ptr](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { return std::static_pointer_cast(ptr); }); @@ -697,6 +715,7 @@ void LoaderImpl::loadNewSnapshot() { absl::MutexLock lock(&snapshot_mutex_); thread_safe_snapshot_ = ptr; } + return absl::OkStatus(); } const Snapshot& LoaderImpl::snapshot() { @@ -716,12 +735,12 @@ SnapshotConstSharedPtr LoaderImpl::threadsafeSnapshot() { } } -void LoaderImpl::mergeValues(const absl::node_hash_map& values) { +absl::Status LoaderImpl::mergeValues(const absl::node_hash_map& values) { if (admin_layer_ == nullptr) { - throwEnvoyExceptionOrPanic("No admin layer specified"); + return absl::InvalidArgumentError("No admin layer specified"); } - admin_layer_->mergeValues(values); - loadNewSnapshot(); + RETURN_IF_NOT_OK(admin_layer_->mergeValues(values)); + return loadNewSnapshot(); } Stats::Scope& LoaderImpl::getRootScope() { return *store_.rootScope(); } @@ -735,15 +754,18 @@ RuntimeStats LoaderImpl::generateStats(Stats::Store& store) { return stats; } -SnapshotImplPtr LoaderImpl::createNewSnapshot() { +absl::StatusOr LoaderImpl::createNewSnapshot() { std::vector layers; uint32_t disk_layers = 0; uint32_t error_layers = 0; uint32_t rtds_layer = 0; + absl::Status creation_status; for (const auto& layer : config_.layers()) { switch (layer.layer_specifier_case()) { case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kStaticLayer: - layers.emplace_back(std::make_unique(layer.name(), layer.static_layer())); + layers.emplace_back( + std::make_unique(layer.name(), layer.static_layer(), creation_status)); + RETURN_IF_NOT_OK(creation_status); break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kDiskLayer: { std::string path = @@ -752,18 +774,27 @@ SnapshotImplPtr LoaderImpl::createNewSnapshot() { absl::StrAppend(&path, "/", service_cluster_); } if (api_.fileSystem().directoryExists(path)) { + std::unique_ptr disk_layer; + std::string error; TRY_ASSERT_MAIN_THREAD { - layers.emplace_back(std::make_unique(layer.name(), path, api_)); - ++disk_layers; + absl::Status creation_status; + disk_layer = std::make_unique(layer.name(), path, api_, creation_status); + if (!creation_status.ok()) { + error = creation_status.message(); + } + END_TRY } - END_TRY - CATCH(EnvoyException & e, { + CATCH(EnvoyException & e, { error = e.what(); }); + if (error.empty()) { + layers.emplace_back(std::move(disk_layer)); + ++disk_layers; + } else { // TODO(htuch): Consider latching here, rather than ignoring the // layer. This would be consistent with filesystem RTDS. ++error_layers; ENVOY_LOG(debug, "error loading runtime values for layer {} from disk: {}", - layer.DebugString(), e.what()); - }); + layer.DebugString(), error); + } } break; } @@ -772,7 +803,9 @@ SnapshotImplPtr LoaderImpl::createNewSnapshot() { break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kRtdsLayer: { auto* subscription = subscriptions_[rtds_layer++].get(); - layers.emplace_back(std::make_unique(layer.name(), subscription->proto_)); + layers.emplace_back( + std::make_unique(layer.name(), subscription->proto_, creation_status)); + RETURN_IF_NOT_OK(creation_status); break; } case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::LAYER_SPECIFIER_NOT_SET: diff --git a/source/common/runtime/runtime_impl.h b/source/common/runtime/runtime_impl.h index e2ebde9e0c8b..68f27f09bc08 100644 --- a/source/common/runtime/runtime_impl.h +++ b/source/common/runtime/runtime_impl.h @@ -117,8 +117,8 @@ class OverrideLayerImpl : public Snapshot::OverrideLayer { /** * Extension of OverrideLayerImpl that maintains an in-memory set of values. These values can be - * modified programmatically via mergeValues(). AdminLayer is so named because it can be accessed - * and manipulated by Envoy's admin interface. + * modified programmatically via mergeValues(). AdminLayer is so named because it + * can be accessed and manipulated by Envoy's admin interface. */ class AdminLayer : public OverrideLayerImpl { public: @@ -135,7 +135,7 @@ class AdminLayer : public OverrideLayerImpl { * Merge the provided values into our entry map. An empty value indicates that a key should be * removed from our map. */ - void mergeValues(const absl::node_hash_map& values); + absl::Status mergeValues(const absl::node_hash_map& values); private: RuntimeStats& stats_; @@ -148,11 +148,12 @@ using AdminLayerPtr = std::unique_ptr; */ class DiskLayer : public OverrideLayerImpl, Logger::Loggable { public: - DiskLayer(absl::string_view name, const std::string& path, Api::Api& api); + DiskLayer(absl::string_view name, const std::string& path, Api::Api& api, + absl::Status& creation_status); private: - void walkDirectory(const std::string& path, const std::string& prefix, uint32_t depth, - Api::Api& api); + absl::Status walkDirectory(const std::string& path, const std::string& prefix, uint32_t depth, + Api::Api& api); const std::string path_; const Filesystem::WatcherPtr watcher_; @@ -163,10 +164,11 @@ class DiskLayer : public OverrideLayerImpl, Logger::Loggable { public: - ProtoLayer(absl::string_view name, const ProtobufWkt::Struct& proto); + ProtoLayer(absl::string_view name, const ProtobufWkt::Struct& proto, + absl::Status& creation_status); private: - void walkProtoValue(const ProtobufWkt::Value& v, const std::string& prefix); + absl::Status walkProtoValue(const ProtobufWkt::Value& v, const std::string& prefix); }; class LoaderImpl; @@ -216,13 +218,14 @@ class LoaderImpl : public Loader, Logger::Loggable { const envoy::config::bootstrap::v3::LayeredRuntime& config, const LocalInfo::LocalInfo& local_info, Stats::Store& store, Random::RandomGenerator& generator, - ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api); + ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api, + absl::Status& creation_status); // Runtime::Loader void initialize(Upstream::ClusterManager& cm) override; const Snapshot& snapshot() override; SnapshotConstSharedPtr threadsafeSnapshot() override; - void mergeValues(const absl::node_hash_map& values) override; + absl::Status mergeValues(const absl::node_hash_map& values) override; void startRtdsSubscriptions(ReadyCallback on_done) override; Stats::Scope& getRootScope() override; void countDeprecatedFeatureUse() const override; @@ -231,9 +234,9 @@ class LoaderImpl : public Loader, Logger::Loggable { friend RtdsSubscription; // Create a new Snapshot - SnapshotImplPtr createNewSnapshot(); + absl::StatusOr createNewSnapshot(); // Load a new Snapshot into TLS - void loadNewSnapshot(); + absl::Status loadNewSnapshot(); RuntimeStats generateStats(Stats::Store& store); void onRtdsReady(); diff --git a/source/server/admin/runtime_handler.cc b/source/server/admin/runtime_handler.cc index 7cb9c2531e5a..456146cb8e9c 100644 --- a/source/server/admin/runtime_handler.cc +++ b/source/server/admin/runtime_handler.cc @@ -90,7 +90,7 @@ Http::Code RuntimeHandler::handlerRuntimeModify(Http::ResponseHeaderMap&, for (const auto& it : params.data()) { overrides.insert({it.first, it.second[0]}); } - TRY_ASSERT_MAIN_THREAD { server_.runtime().mergeValues(overrides); } + TRY_ASSERT_MAIN_THREAD { THROW_IF_NOT_OK(server_.runtime().mergeValues(overrides)); } END_TRY catch (const EnvoyException& e) { ENVOY_LOG_MISC(error, "{}", e.what()); diff --git a/source/server/server.cc b/source/server/server.cc index 0b9863428a95..1bd625cd09dc 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -879,10 +879,13 @@ Runtime::LoaderPtr InstanceUtil::createRuntime(Instance& server, #ifdef ENVOY_ENABLE_YAML ENVOY_LOG(info, "runtime: {}", MessageUtil::getYamlStringFromMessage(config.runtime())); #endif - return std::make_unique( + absl::Status creation_status; + auto loader = std::make_unique( server.dispatcher(), server.threadLocal(), config.runtime(), server.localInfo(), server.stats(), server.api().randomGenerator(), - server.messageValidationContext().dynamicValidationVisitor(), server.api()); + server.messageValidationContext().dynamicValidationVisitor(), server.api(), creation_status); + THROW_IF_NOT_OK(creation_status); + return loader; } void InstanceBase::loadServerFlags(const absl::optional& flags_path) { diff --git a/test/common/runtime/runtime_impl_test.cc b/test/common/runtime/runtime_impl_test.cc index e8913f451a0a..88989f71947e 100644 --- a/test/common/runtime/runtime_impl_test.cc +++ b/test/common/runtime/runtime_impl_test.cc @@ -94,8 +94,9 @@ class DiskLoaderImplTest : public LoaderImplTest { envoy::config::bootstrap::v3::LayeredRuntime layered_runtime; Config::translateRuntime(runtime, layered_runtime); + absl::Status creation_status; loader_ = std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, store_, - generator_, validation_visitor_, *api_); + generator_, validation_visitor_, *api_, creation_status); } void write(const std::string& path, const std::string& value) { @@ -287,7 +288,7 @@ TEST_F(DiskLoaderImplTest, GetLayers) { EXPECT_NE(nullptr, dynamic_cast(layers.back().get())); EXPECT_TRUE(layers[3]->values().empty()); - loader_->mergeValues({{"foo", "bar"}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", "bar"}}).ok()); // The old snapshot and its layers should have been invalidated. Refetch. const auto& new_layers = loader_->snapshot().getLayers(); EXPECT_EQ("bar", new_layers[3]->values().find("foo")->second.raw_string_value_); @@ -336,7 +337,7 @@ TEST_F(DiskLoaderImplTest, PercentHandling) { // Smoke test integer value of 0, should be interpreted as 0% { - loader_->mergeValues({{"foo", "0"}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", "0"}}).ok()); EXPECT_FALSE(loader_->snapshot().featureEnabled("foo", default_value, 0)); EXPECT_FALSE(loader_->snapshot().featureEnabled("foo", default_value, 5)); @@ -344,7 +345,7 @@ TEST_F(DiskLoaderImplTest, PercentHandling) { // Smoke test integer value of 5, should be interpreted as 5% { - loader_->mergeValues({{"foo", "5"}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", "5"}}).ok()); EXPECT_TRUE(loader_->snapshot().featureEnabled("foo", default_value, 0)); EXPECT_TRUE(loader_->snapshot().featureEnabled("foo", default_value, 4)); EXPECT_FALSE(loader_->snapshot().featureEnabled("foo", default_value, 5)); @@ -361,7 +362,7 @@ TEST_F(DiskLoaderImplTest, PercentHandling) { // not the uint32 conversion is handled properly. uint64_t high_value = 1ULL << 60; std::string high_value_str = std::to_string(high_value); - loader_->mergeValues({{"foo", high_value_str}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", high_value_str}}).ok()); EXPECT_TRUE(loader_->snapshot().featureEnabled("foo", default_value, 0)); EXPECT_TRUE(loader_->snapshot().featureEnabled("foo", default_value, 50)); EXPECT_TRUE(loader_->snapshot().featureEnabled("foo", default_value, 100)); @@ -375,32 +376,32 @@ void testNewOverrides(Loader& loader, Stats::TestUtil::TestStore& store) { store.gauge("runtime.admin_overrides_active", Stats::Gauge::ImportMode::NeverImport); // New string. - loader.mergeValues({{"foo", "bar"}}); + ASSERT_TRUE(loader.mergeValues({{"foo", "bar"}}).ok()); EXPECT_EQ("bar", loader.snapshot().get("foo").value().get()); EXPECT_EQ(1, admin_overrides_active.value()); // Remove new string. - loader.mergeValues({{"foo", ""}}); + ASSERT_TRUE(loader.mergeValues({{"foo", ""}}).ok()); EXPECT_FALSE(loader.snapshot().get("foo").has_value()); EXPECT_EQ(0, admin_overrides_active.value()); // New integer. - loader.mergeValues({{"baz", "42"}}); + ASSERT_TRUE(loader.mergeValues({{"baz", "42"}}).ok()); EXPECT_EQ(42, loader.snapshot().getInteger("baz", 0)); EXPECT_EQ(1, admin_overrides_active.value()); // Remove new integer. - loader.mergeValues({{"baz", ""}}); + ASSERT_TRUE(loader.mergeValues({{"baz", ""}}).ok()); EXPECT_EQ(0, loader.snapshot().getInteger("baz", 0)); EXPECT_EQ(0, admin_overrides_active.value()); // New double. - loader.mergeValues({{"beep", "42.1"}}); + ASSERT_TRUE(loader.mergeValues({{"beep", "42.1"}}).ok()); EXPECT_EQ(42.1, loader.snapshot().getDouble("beep", 1.2)); EXPECT_EQ(1, admin_overrides_active.value()); // Remove new double. - loader.mergeValues({{"beep", ""}}); + ASSERT_TRUE(loader.mergeValues({{"beep", ""}}).ok()); EXPECT_EQ(1.2, loader.snapshot().getDouble("beep", 1.2)); EXPECT_EQ(0, admin_overrides_active.value()); } @@ -413,42 +414,42 @@ TEST_F(DiskLoaderImplTest, MergeValues) { store_.gauge("runtime.admin_overrides_active", Stats::Gauge::ImportMode::NeverImport); // Override string - loader_->mergeValues({{"file2", "new world"}}); + ASSERT_TRUE(loader_->mergeValues({{"file2", "new world"}}).ok()); EXPECT_EQ("new world", loader_->snapshot().get("file2").value().get()); EXPECT_EQ(1, admin_overrides_active.value()); // Remove overridden string - loader_->mergeValues({{"file2", ""}}); + ASSERT_TRUE(loader_->mergeValues({{"file2", ""}}).ok()); EXPECT_EQ("world", loader_->snapshot().get("file2").value().get()); EXPECT_EQ(0, admin_overrides_active.value()); // Override integer - loader_->mergeValues({{"file3", "42"}}); + ASSERT_TRUE(loader_->mergeValues({{"file3", "42"}}).ok()); EXPECT_EQ(42, loader_->snapshot().getInteger("file3", 1)); EXPECT_EQ(1, admin_overrides_active.value()); // Remove overridden integer - loader_->mergeValues({{"file3", ""}}); + ASSERT_TRUE(loader_->mergeValues({{"file3", ""}}).ok()); EXPECT_EQ(2, loader_->snapshot().getInteger("file3", 1)); EXPECT_EQ(0, admin_overrides_active.value()); // Override double - loader_->mergeValues({{"file_with_double", "42.1"}}); + ASSERT_TRUE(loader_->mergeValues({{"file_with_double", "42.1"}}).ok()); EXPECT_EQ(42.1, loader_->snapshot().getDouble("file_with_double", 1.1)); EXPECT_EQ(1, admin_overrides_active.value()); // Remove overridden double - loader_->mergeValues({{"file_with_double", ""}}); + ASSERT_TRUE(loader_->mergeValues({{"file_with_double", ""}}).ok()); EXPECT_EQ(23.2, loader_->snapshot().getDouble("file_with_double", 1.1)); EXPECT_EQ(0, admin_overrides_active.value()); // Override override string - loader_->mergeValues({{"file1", "hello overridden override"}}); + ASSERT_TRUE(loader_->mergeValues({{"file1", "hello overridden override"}}).ok()); EXPECT_EQ("hello overridden override", loader_->snapshot().get("file1").value().get()); EXPECT_EQ(1, admin_overrides_active.value()); // Remove overridden override string - loader_->mergeValues({{"file1", ""}}); + ASSERT_TRUE(loader_->mergeValues({{"file1", ""}}).ok()); EXPECT_EQ("hello override", loader_->snapshot().get("file1").value().get()); EXPECT_EQ(0, admin_overrides_active.value()); EXPECT_EQ(0, store_.gauge("runtime.admin_overrides_active", Stats::Gauge::ImportMode::NeverImport) @@ -472,14 +473,14 @@ TEST_F(DiskLoaderImplTest, LayersOverride) { EXPECT_EQ("thing", loader_->snapshot().get("some").value().get()); EXPECT_EQ("thang", loader_->snapshot().get("other").value().get()); // Admin overrides disk and bootstrap. - loader_->mergeValues({{"file2", "pluto"}, {"some", "day soon"}}); + ASSERT_TRUE(loader_->mergeValues({{"file2", "pluto"}, {"some", "day soon"}}).ok()); EXPECT_EQ("pluto", loader_->snapshot().get("file2").value().get()); EXPECT_EQ("day soon", loader_->snapshot().get("some").value().get()); EXPECT_EQ("thang", loader_->snapshot().get("other").value().get()); // Admin overrides stick over filesystem updates. EXPECT_EQ("Layer cake", loader_->snapshot().get("file14").value().get()); EXPECT_EQ("Cheese cake", loader_->snapshot().get("file15").value().get()); - loader_->mergeValues({{"file14", "Mega layer cake"}}); + ASSERT_TRUE(loader_->mergeValues({{"file14", "Mega layer cake"}}).ok()); EXPECT_EQ("Mega layer cake", loader_->snapshot().get("file14").value().get()); EXPECT_EQ("Cheese cake", loader_->snapshot().get("file15").value().get()); write("test/common/runtime/test_data/current/envoy/file14", "Sad cake"); @@ -503,11 +504,12 @@ TEST_F(DiskLoaderImplTest, MultipleAdminLayersFail) { layer->set_name("admin_1"); layer->mutable_admin_layer(); } - EXPECT_THROW_WITH_MESSAGE( + absl::Status creation_status; + auto loader = std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, store_, - generator_, validation_visitor_, *api_), - EnvoyException, - "Too many admin layers specified in LayeredRuntime, at most one may be specified"); + generator_, validation_visitor_, *api_, creation_status); + EXPECT_EQ(creation_status.message(), + "Too many admin layers specified in LayeredRuntime, at most one may be specified"); } class StaticLoaderImplTest : public LoaderImplTest { @@ -525,8 +527,10 @@ class StaticLoaderImplTest : public LoaderImplTest { layer->set_name("admin"); layer->mutable_admin_layer(); } + absl::Status creation_status; loader_ = std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, store_, - generator_, validation_visitor_, *api_); + generator_, validation_visitor_, *api_, creation_status); + THROW_IF_NOT_OK(creation_status); } ProtobufWkt::Struct base_; @@ -807,7 +811,7 @@ TEST_F(StaticLoaderImplTest, RuntimeFromNonWorkerThreads) { setup(); // Set up foo -> bar - loader_->mergeValues({{"foo", "bar"}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", "bar"}}).ok()); EXPECT_EQ("bar", loader_->threadsafeSnapshot()->get("foo").value().get()); const Snapshot* original_snapshot_pointer = loader_->threadsafeSnapshot().get(); @@ -844,7 +848,7 @@ TEST_F(StaticLoaderImplTest, RuntimeFromNonWorkerThreads) { if (!read_bar) { foo_read.wait(mutex); } - loader_->mergeValues({{"foo", "eep"}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", "eep"}}).ok()); updated_eep = true; } @@ -875,21 +879,23 @@ class DiskLayerTest : public testing::Test { }; TEST_F(DiskLayerTest, IllegalPath) { + absl::Status creation_status; #ifdef WIN32 - EXPECT_THROW_WITH_MESSAGE(DiskLayer("test", R"EOF(\\.\)EOF", *api_), EnvoyException, - R"EOF(Invalid path: \\.\)EOF"); + DiskLayer layer("test", R"EOF(\\.\)EOF", *api_, creation_status); + EXPECT_EQ(creation_status.message(), R"EOF(Invalid path: \\.\)EOF"); #else - EXPECT_THROW_WITH_MESSAGE(DiskLayer("test", "/dev", *api_), EnvoyException, "Invalid path: /dev"); + DiskLayer layer("test", "/dev", *api_, creation_status); + EXPECT_EQ(creation_status.message(), "Invalid path: /dev"); #endif } // Validate that we catch recursion that goes too deep in the runtime filesystem // walk. TEST_F(DiskLayerTest, Loop) { - EXPECT_THROW_WITH_MESSAGE( - DiskLayer("test", TestEnvironment::temporaryPath("test/common/runtime/test_data/loop"), - *api_), - EnvoyException, "Walk recursion depth exceeded 16"); + absl::Status creation_status; + DiskLayer layer("test", TestEnvironment::temporaryPath("test/common/runtime/test_data/loop"), + *api_, creation_status); + EXPECT_EQ(creation_status.message(), "Walk recursion depth exceeded 16"); } TEST(NoRuntime, FeatureEnabled) { @@ -936,8 +942,10 @@ class RtdsLoaderImplTest : public LoaderImplTest { rtds_callbacks_.push_back(&callbacks); return ret; })); + absl::Status creation_status; loader_ = std::make_unique(dispatcher_, tls_, config, local_info_, store_, - generator_, validation_visitor_, *api_); + generator_, validation_visitor_, *api_, creation_status); + THROW_IF_NOT_OK(creation_status); loader_->initialize(cm_); for (auto* sub : rtds_subscriptions_) { EXPECT_CALL(*sub, start(_)); @@ -1271,8 +1279,9 @@ TEST_F(RtdsLoaderImplTest, BadConfigSource) { rtds_layer->mutable_rtds_config(); EXPECT_CALL(cm_, subscriptionFactory()); + absl::Status creation_status; LoaderImpl loader(dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, - *api_); + *api_, creation_status); EXPECT_THROW_WITH_MESSAGE(loader.initialize(cm_), EnvoyException, "bad config"); } diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 36c0ea4bf253..23a3c599f0f3 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -2161,9 +2161,11 @@ class HistogramParameterisedTest : public HistogramTest, layer->set_name("admin"); layer->mutable_admin_layer(); } - loader_ = - std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, - *store_, generator_, validation_visitor_, *api_); + absl::Status creation_status; + loader_ = std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, + *store_, generator_, validation_visitor_, *api_, + creation_status); + THROW_IF_NOT_OK(creation_status); } Event::MockDispatcher dispatcher_; diff --git a/test/extensions/filters/network/http_connection_manager/config_test.cc b/test/extensions/filters/network/http_connection_manager/config_test.cc index 1b13b76a00bc..f47c2f1a1525 100644 --- a/test/extensions/filters/network/http_connection_manager/config_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_test.cc @@ -658,8 +658,10 @@ TEST_F(HttpConnectionManagerConfigTest, OverallSampling) { envoy::config::bootstrap::v3::LayeredRuntime runtime_config; NiceMock local_info; NiceMock validation_visitor; + absl::Status creation_status; Runtime::LoaderImpl runtime(dispatcher, tls, runtime_config, local_info, store, generator, - validation_visitor, *api); + validation_visitor, *api, creation_status); + ASSERT_TRUE(creation_status.ok()); int sampled_count = 0; NiceMock route; diff --git a/test/mocks/runtime/mocks.h b/test/mocks/runtime/mocks.h index c1ebf09315aa..0329bf8154ca 100644 --- a/test/mocks/runtime/mocks.h +++ b/test/mocks/runtime/mocks.h @@ -64,7 +64,7 @@ class MockLoader : public Loader { MOCK_METHOD(void, initialize, (Upstream::ClusterManager & cm)); MOCK_METHOD(const Snapshot&, snapshot, ()); MOCK_METHOD(SnapshotConstSharedPtr, threadsafeSnapshot, ()); - MOCK_METHOD(void, mergeValues, ((const absl::node_hash_map&))); + MOCK_METHOD(absl::Status, mergeValues, ((const absl::node_hash_map&))); MOCK_METHOD(void, startRtdsSubscriptions, (ReadyCallback)); MOCK_METHOD(Stats::Scope&, getRootScope, ()); MOCK_METHOD(void, countDeprecatedFeatureUse, (), (const)); diff --git a/test/test_common/test_runtime.h b/test/test_common/test_runtime.h index f270948f3f13..318f7ead190e 100644 --- a/test/test_common/test_runtime.h +++ b/test/test_common/test_runtime.h @@ -43,8 +43,11 @@ class TestScopedRuntime { // The existence of an admin layer is required for mergeValues() to work. config.add_layers()->mutable_admin_layer(); + absl::Status creation_status; Runtime::LoaderPtr runtime_ptr = std::make_unique( - dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_); + dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_, + creation_status); + THROW_IF_NOT_OK(creation_status); // This will ignore values set in test, but just use flag defaults! runtime_ = std::move(runtime_ptr); } @@ -52,7 +55,7 @@ class TestScopedRuntime { Runtime::Loader& loader() { return *runtime_; } void mergeValues(const absl::node_hash_map& values) { - loader().mergeValues(values); + THROW_IF_NOT_OK(loader().mergeValues(values)); } protected: @@ -85,8 +88,11 @@ class TestScopedStaticReloadableFeaturesRuntime { } runtime->mutable_static_layer()->MergeFrom(envoy_layer); + absl::Status creation_status; Runtime::LoaderPtr runtime_ptr = std::make_unique( - dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_); + dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_, + creation_status); + THROW_IF_NOT_OK(creation_status); // This will ignore values set in test, but just use flag defaults! runtime_ = std::move(runtime_ptr); } @@ -107,10 +113,10 @@ class TestDeprecatedV2Api : public TestScopedRuntime { public: TestDeprecatedV2Api() { allowDeprecatedV2(); } void allowDeprecatedV2() { - loader().mergeValues({ + THROW_IF_NOT_OK(loader().mergeValues({ {"envoy.test_only.broken_in_production.enable_deprecated_v2_api", "true"}, {"envoy.features.enable_all_deprecated_features", "true"}, - }); + })); } }; diff --git a/tools/code_format/config.yaml b/tools/code_format/config.yaml index 7ba8ba23cbd0..01d341738e95 100644 --- a/tools/code_format/config.yaml +++ b/tools/code_format/config.yaml @@ -152,6 +152,7 @@ paths: - source/server/overload_manager_impl.cc - source/server/config_validation/server.cc - source/server/admin/html/active_stats.js + - source/server/admin/runtime_handler.cc - source/server/server.cc - source/server/hot_restarting_base.cc - source/server/hot_restart_impl.cc From 795ca0e1a30695d660f2648e6039957a4aa962f8 Mon Sep 17 00:00:00 2001 From: "dependency-envoy[bot]" <148525496+dependency-envoy[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 04:59:32 +0000 Subject: [PATCH 004/124] deps: Bump build images -> `f94a38f` (#32824) * deps: Bump build images -> `f94a38f` * deps: Bump `envoy_build_tools` -> 32570f7 Signed-off-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> Signed-off-by: Ryan Northey --- .bazelrc | 4 ++-- .devcontainer/Dockerfile | 2 +- .github/config.yml | 8 ++++---- bazel/repository_locations.bzl | 6 +++--- examples/shared/build/Dockerfile | 2 +- mobile/third_party/rbe_configs/config/BUILD | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.bazelrc b/.bazelrc index 36a2766787ef..51490a2493fb 100644 --- a/.bazelrc +++ b/.bazelrc @@ -359,7 +359,7 @@ build:compile-time-options --@envoy//source/extensions/filters/http/kill_request # Docker sandbox # NOTE: Update this from https://github.com/envoyproxy/envoy-build-tools/blob/main/toolchains/rbe_toolchains_config.bzl#L8 -build:docker-sandbox --experimental_docker_image=envoyproxy/envoy-build-ubuntu:0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:d736c58f06f36848e7966752cc7e01519cc1b5101a178d5c6634807e8ac3deab +build:docker-sandbox --experimental_docker_image=envoyproxy/envoy-build-ubuntu:f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:2dd96b6f43c08ccabd5f4747fce5854f5f96af509b32e5cf6493f136e9833649 build:docker-sandbox --spawn_strategy=docker build:docker-sandbox --strategy=Javac=docker build:docker-sandbox --strategy=Closure=docker @@ -523,7 +523,7 @@ build:bes-envoy-engflow --bes_upload_mode=fully_async build:rbe-envoy-engflow --config=cache-envoy-engflow build:rbe-envoy-engflow --config=bes-envoy-engflow build:rbe-envoy-engflow --remote_executor=grpcs://morganite.cluster.engflow.com -build:rbe-envoy-engflow --remote_default_exec_properties=container-image=docker://docker.io/envoyproxy/envoy-build-ubuntu:0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:d736c58f06f36848e7966752cc7e01519cc1b5101a178d5c6634807e8ac3deab +build:rbe-envoy-engflow --remote_default_exec_properties=container-image=docker://docker.io/envoyproxy/envoy-build-ubuntu:f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:2dd96b6f43c08ccabd5f4747fce5854f5f96af509b32e5cf6493f136e9833649 ############################################################################# # debug: Various Bazel debugging flags diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f5687d40bd6d..e62b0b56fd5f 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/envoy-ci/envoy-build:0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:0434d2f6a65895a00f8936fba83b7ff14d21e149dc15c9ac25a31a6fa018234f +FROM gcr.io/envoy-ci/envoy-build:f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:7adc40c09508f957624c4d2e0f5aeecb73a59207ee6ded53b107eac828c091b2 ARG USERNAME=vscode ARG USER_UID=501 diff --git a/.github/config.yml b/.github/config.yml index b3bdcd3985c7..316b204fe4a0 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -2,11 +2,11 @@ agent-ubuntu: ubuntu-22.04 build-image: # Authoritative configuration for build image/s repo: envoyproxy/envoy-build-ubuntu - sha: d736c58f06f36848e7966752cc7e01519cc1b5101a178d5c6634807e8ac3deab - mobile-sha: 13674edd989ada8b94257f410e38e5e8e4c6a0a9c2fdf8bda9261047a7f843e4 + sha: 2dd96b6f43c08ccabd5f4747fce5854f5f96af509b32e5cf6493f136e9833649 + mobile-sha: c9b6001d0c24170ae77b0ad39e1b0fcda49580c72a8578755dc65aec9be85be3 # this is authoritative, but is not currently used in github ci - gcr-sha: 0434d2f6a65895a00f8936fba83b7ff14d21e149dc15c9ac25a31a6fa018234f - tag: 0ca52447572ee105a4730da5e76fe47c9c5a7c64 + gcr-sha: 7adc40c09508f957624c4d2e0f5aeecb73a59207ee6ded53b107eac828c091b2 + tag: f94a38f62220a2b017878b790b6ea98a0f6c5f9c config: envoy: diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index e4483c6063c1..c7eb93471255 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -102,11 +102,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "envoy-build-tools", project_desc = "Common build tools shared by the Envoy/UDPA ecosystem", project_url = "https://github.com/envoyproxy/envoy-build-tools", - version = "7cd964feb564419532ec8a825031e4e1b7b6974f", - sha256 = "74182edc4c40a4d3b775598cdcf7f88000a96cc8753ffbd6d94083fb403f5e78", + version = "32570f74e1e36d2711f5f0a156b9623cdfa4b384", + sha256 = "962e398567306e3230eb2457c5eabec26616667379bfba566894a7bd334416d4", strip_prefix = "envoy-build-tools-{version}", urls = ["https://github.com/envoyproxy/envoy-build-tools/archive/{version}.tar.gz"], - release_date = "2024-01-08", + release_date = "2024-03-11", use_category = ["build"], license = "Apache-2.0", license_url = "https://github.com/envoyproxy/envoy-build-tools/blob/{version}/LICENSE", diff --git a/examples/shared/build/Dockerfile b/examples/shared/build/Dockerfile index 6cfa7fdd567a..92ea0ad5f146 100644 --- a/examples/shared/build/Dockerfile +++ b/examples/shared/build/Dockerfile @@ -1,4 +1,4 @@ -FROM envoyproxy/envoy-build-ubuntu:0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:d736c58f06f36848e7966752cc7e01519cc1b5101a178d5c6634807e8ac3deab +FROM envoyproxy/envoy-build-ubuntu:f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:2dd96b6f43c08ccabd5f4747fce5854f5f96af509b32e5cf6493f136e9833649 ENV DEBIAN_FRONTEND=noninteractive RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ diff --git a/mobile/third_party/rbe_configs/config/BUILD b/mobile/third_party/rbe_configs/config/BUILD index add523ffc4d8..84a24d8f450a 100644 --- a/mobile/third_party/rbe_configs/config/BUILD +++ b/mobile/third_party/rbe_configs/config/BUILD @@ -42,7 +42,7 @@ platform( "@bazel_tools//tools/cpp:clang", ], exec_properties = { - "container-image": "docker://envoyproxy/envoy-build-ubuntu:mobile-0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:13674edd989ada8b94257f410e38e5e8e4c6a0a9c2fdf8bda9261047a7f843e4", + "container-image": "docker://envoyproxy/envoy-build-ubuntu:mobile-f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:c9b6001d0c24170ae77b0ad39e1b0fcda49580c72a8578755dc65aec9be85be3", "OSFamily": "Linux", "Pool": "linux", }, @@ -57,7 +57,7 @@ platform( "@bazel_tools//tools/cpp:clang", ], exec_properties = { - "container-image": "docker://envoyproxy/envoy-build-ubuntu:mobile-0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:13674edd989ada8b94257f410e38e5e8e4c6a0a9c2fdf8bda9261047a7f843e4", + "container-image": "docker://envoyproxy/envoy-build-ubuntu:mobile-f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:c9b6001d0c24170ae77b0ad39e1b0fcda49580c72a8578755dc65aec9be85be3", "OSFamily": "Linux", "Pool": "linux", # Necessary to workaround https://github.com/google/sanitizers/issues/916, otherwise, dangling threads in the From 08231e383fc3fb1c3bb207774d8295995759552a Mon Sep 17 00:00:00 2001 From: Matt Jones Date: Mon, 11 Mar 2024 22:20:37 -0700 Subject: [PATCH 005/124] jwt_authn: Add subject constraints for JwtProviders (#32374) Adds new `subjects` config field to restrict subjects accepted from a `JwtProvider` partially implementing #31455 Risk Level: Low Testing: Unit testing Docs Changes: Added `subjects` description inline in proto. Release Notes: Attached Optional [API Considerations](https://github.com/envoyproxy/envoy/blob/main/api/review_checklist.md): Feature is opt in, without specifying the config, there's no behavior change. Signed-off-by: Matthew Jones --- .../filters/http/jwt_authn/v3/BUILD | 1 + .../filters/http/jwt_authn/v3/config.proto | 17 ++++++- changelogs/current.yaml | 5 ++ .../filters/http/jwt_authn/authenticator.cc | 7 +++ .../filters/http/jwt_authn/jwks_cache.cc | 17 +++++++ .../filters/http/jwt_authn/jwks_cache.h | 4 ++ .../http/jwt_authn/authenticator_test.cc | 29 +++++++++++- .../filters/http/jwt_authn/jwks_cache_test.cc | 40 ++++++++++++++++ test/extensions/filters/http/jwt_authn/mock.h | 3 ++ .../filters/http/jwt_authn/test_common.h | 46 +++++++++++++++++++ 10 files changed, 167 insertions(+), 2 deletions(-) diff --git a/api/envoy/extensions/filters/http/jwt_authn/v3/BUILD b/api/envoy/extensions/filters/http/jwt_authn/v3/BUILD index cea648f6d0ec..fd0f6d5f15c4 100644 --- a/api/envoy/extensions/filters/http/jwt_authn/v3/BUILD +++ b/api/envoy/extensions/filters/http/jwt_authn/v3/BUILD @@ -8,6 +8,7 @@ api_proto_package( deps = [ "//envoy/config/core/v3:pkg", "//envoy/config/route/v3:pkg", + "//envoy/type/matcher/v3:pkg", "@com_github_cncf_xds//udpa/annotations:pkg", ], ) diff --git a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto index d8bfd7d16e7e..1256a40bdc2f 100644 --- a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto +++ b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto @@ -5,6 +5,7 @@ package envoy.extensions.filters.http.jwt_authn.v3; import "envoy/config/core/v3/base.proto"; import "envoy/config/core/v3/http_uri.proto"; import "envoy/config/route/v3/route_components.proto"; +import "envoy/type/matcher/v3/string.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; @@ -53,7 +54,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // cache_duration: // seconds: 300 // -// [#next-free-field: 19] +// [#next-free-field: 20] message JwtProvider { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.jwt_authn.v2alpha.JwtProvider"; @@ -104,6 +105,20 @@ message JwtProvider { // repeated string audiences = 2; + // Restrict the `subjects `_ + // that the JwtProvider can assert. For instance, this could implement JWT-SVID + // `subject restrictions `_. + // If not specified, will not check subjects in the token. + // + // Example: + // + // .. code-block:: yaml + // + // subjects: + // prefix: spiffe://spiffe.example.com/ + // + type.matcher.v3.StringMatcher subjects = 19; + // `JSON Web Key Set (JWKS) `_ is needed to // validate signature of a JWT. This field specifies where to fetch JWKS. oneof jwks_source_specifier { diff --git a/changelogs/current.yaml b/changelogs/current.yaml index e9f1ae697e5f..7e8961cfaf5e 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -297,6 +297,11 @@ new_features: change: | :ref:`deny_redirect_matcher ` to support disabling authorization redirects for specific requests, e.g. AJAX requests. +- area: jwt_authn + change: | + Added + :ref:`subjects ` to allow restrictions + of subjects a ``JwtProvider`` can assert. - area: aws_request_signing change: | Update ``aws_request_signing`` filter to support optionally sending the aws signature in query parameters rather than headers, diff --git a/source/extensions/filters/http/jwt_authn/authenticator.cc b/source/extensions/filters/http/jwt_authn/authenticator.cc index 3e04ca79bd16..56f72f426837 100644 --- a/source/extensions/filters/http/jwt_authn/authenticator.cc +++ b/source/extensions/filters/http/jwt_authn/authenticator.cc @@ -241,6 +241,13 @@ void AuthenticatorImpl::startVerify() { return; } + const bool sub_allowed = jwks_data_->isSubjectAllowed(jwt_->sub_); + + if (!sub_allowed) { + doneWithStatus(Status::JwtVerificationFail); + return; + } + if (use_jwt_cache) { handleGoodJwt(/*cache_hit=*/true); return; diff --git a/source/extensions/filters/http/jwt_authn/jwks_cache.cc b/source/extensions/filters/http/jwt_authn/jwks_cache.cc index 01c1daa11493..053d662e36d9 100644 --- a/source/extensions/filters/http/jwt_authn/jwks_cache.cc +++ b/source/extensions/filters/http/jwt_authn/jwks_cache.cc @@ -8,10 +8,13 @@ #include "envoy/thread_local/thread_local.h" #include "source/common/common/logger.h" +#include "source/common/common/matchers.h" #include "source/common/config/datasource.h" #include "source/common/http/utility.h" #include "absl/container/node_hash_map.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "jwt_verify_lib/check_audience.h" using envoy::extensions::filters::http::jwt_authn::v3::JwtAuthentication; @@ -57,6 +60,11 @@ class JwksDataImpl : public JwksCache::JwksData, public Logger::Loggable(audiences); + + if (jwt_provider_.has_subjects()) { + sub_matcher_.emplace(jwt_provider_.subjects()); + } + bool enable_jwt_cache = jwt_provider_.has_jwt_cache_config(); const auto& config = jwt_provider_.jwt_cache_config(); tls_.set([enable_jwt_cache, config](Envoy::Event::Dispatcher& dispatcher) { @@ -92,6 +100,14 @@ class JwksDataImpl : public JwksCache::JwksData, public Logger::LoggableareAudiencesAllowed(jwt_audiences); } + bool isSubjectAllowed(const absl::string_view jwt_subject) const override { + if (!sub_matcher_.has_value()) { + return true; + } + + return sub_matcher_->match(jwt_subject); + } + const Jwks* getJwksObj() const override { return tls_->jwks_.get(); } bool isExpired() const override { return time_source_.monotonicTime() >= tls_->expire_; } @@ -141,6 +157,7 @@ class JwksDataImpl : public JwksCache::JwksData, public Logger::Loggable tls_; // async fetcher JwksAsyncFetcherPtr async_fetcher_; + absl::optional> sub_matcher_; }; using JwksDataImplPtr = std::unique_ptr; diff --git a/source/extensions/filters/http/jwt_authn/jwks_cache.h b/source/extensions/filters/http/jwt_authn/jwks_cache.h index 981fc0ecd59d..b9a149f9a64c 100644 --- a/source/extensions/filters/http/jwt_authn/jwks_cache.h +++ b/source/extensions/filters/http/jwt_authn/jwks_cache.h @@ -12,6 +12,7 @@ #include "source/extensions/filters/http/jwt_authn/jwt_cache.h" #include "source/extensions/filters/http/jwt_authn/stats.h" +#include "absl/strings/string_view.h" #include "jwt_verify_lib/jwks.h" namespace Envoy { @@ -55,6 +56,9 @@ class JwksCache { // Check if a list of audiences are allowed. virtual bool areAudiencesAllowed(const std::vector& audiences) const PURE; + // Check if a subject is allowed. + virtual bool isSubjectAllowed(absl::string_view sub) const PURE; + // Get the cached config: JWT rule. virtual const envoy::extensions::filters::http::jwt_authn::v3::JwtProvider& getJwtProvider() const PURE; diff --git a/test/extensions/filters/http/jwt_authn/authenticator_test.cc b/test/extensions/filters/http/jwt_authn/authenticator_test.cc index 9a58370bb3a4..902455fc4331 100644 --- a/test/extensions/filters/http/jwt_authn/authenticator_test.cc +++ b/test/extensions/filters/http/jwt_authn/authenticator_test.cc @@ -61,7 +61,8 @@ class AuthenticatorTest : public testing::Test { void expectVerifyStatus(Status expected_status, Http::RequestHeaderMap& headers, bool expect_clear_route = false) { std::function on_complete_cb = [&expected_status](const Status& status) { - ASSERT_EQ(status, expected_status); + ASSERT_STREQ(google::jwt_verify::getStatusString(status).c_str(), + google::jwt_verify::getStatusString(expected_status).c_str()); }; auto set_extracted_jwt_data_cb = [this](const std::string& name, const ProtobufWkt::Struct& extracted_data) { @@ -101,6 +102,32 @@ class AuthenticatorTest : public testing::Test { NiceMock parent_span_; }; +// Test authenticator constraints for subjects +TEST_F(AuthenticatorTest, TestSubject) { + ScopedInjectableLoader engine(std::make_unique()); + TestUtility::loadFromYaml(SubjectConfig, proto_config_); + + { + // Test that GoodToken works. sub is test@example.com and the example_provider + // is constrained to *@example.com + createAuthenticator(); + EXPECT_CALL(*raw_fetcher_, fetch(_, _)) + .WillOnce(Invoke([this](Tracing::Span&, JwksFetcher::JwksReceiver& receiver) { + receiver.onJwksSuccess(std::move(jwks_)); + })); + Http::TestRequestHeaderMapImpl headers{{"Authorization", "Bearer " + std::string(GoodToken)}}; + expectVerifyStatus(Status::Ok, headers); + } + + { + // spiffe_provider is constrained to prefixes spiffe://spiffe.example.com/ so test@eample.com + // should fail. + createAuthenticator(nullptr, "spiffe_provider"); + Http::TestRequestHeaderMapImpl headers{{"Authorization", "Bearer " + std::string(GoodToken)}}; + expectVerifyStatus(Status::JwtVerificationFail, headers); + } +} + // This test validates a good JWT authentication with a remote Jwks. // It also verifies Jwks cache with 10 JWT authentications, but only one Jwks fetch. TEST_F(AuthenticatorTest, TestOkJWTandCache) { diff --git a/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc b/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc index bd27f7417f70..2a9e9b866ef8 100644 --- a/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc +++ b/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc @@ -181,6 +181,46 @@ TEST_F(JwksCacheTest, TestAudiences) { EXPECT_FALSE(jwks->areAudiencesAllowed({"wrong-audience1", "wrong-audience2"})); } +// Test subject constraints for JwtProvider +TEST_F(JwksCacheTest, TestSubjects) { + ScopedInjectableLoader engine(std::make_unique()); + setupCache(SubjectConfig); + + { + auto jwks = cache_->findByIssuer("https://example.com"); + + // example.com has a suffix constraint of "@example.com" + EXPECT_TRUE(jwks->isSubjectAllowed("test@example.com")); + // Negative test for other subjects + EXPECT_FALSE(jwks->isSubjectAllowed("othersubject")); + } + + { + auto jwks = cache_->findByIssuer("https://spiffe.example.com"); + + // Provider has a prefix constraint of spiffe://spiffe.example.com + EXPECT_TRUE(jwks->isSubjectAllowed("spiffe://spiffe.example.com/service")); + // Negative test for other subjects + EXPECT_FALSE(jwks->isSubjectAllowed("spiffe://spiffe.baz.com/service")); + } + + { + auto jwks = cache_->findByIssuer("https://nosub.com"); + + // Provider no constraints, so test any subject should be allowed + EXPECT_TRUE(jwks->isSubjectAllowed("any_subject")); + } + + { + auto jwks = cache_->findByIssuer("https://regexsub.com"); + + // Provider allows spiffe://*.example.com/ + EXPECT_TRUE(jwks->isSubjectAllowed("spiffe://test1.example.com/service")); + EXPECT_TRUE(jwks->isSubjectAllowed("spiffe://test2.example.com/service")); + EXPECT_FALSE(jwks->isSubjectAllowed("spiffe://test1.baz.com/service")); + } +} + } // namespace } // namespace JwtAuthn } // namespace HttpFilters diff --git a/test/extensions/filters/http/jwt_authn/mock.h b/test/extensions/filters/http/jwt_authn/mock.h index e3a792c765ba..533990cdb04d 100644 --- a/test/extensions/filters/http/jwt_authn/mock.h +++ b/test/extensions/filters/http/jwt_authn/mock.h @@ -8,6 +8,7 @@ #include "test/mocks/upstream/cluster_manager.h" +#include "absl/strings/string_view.h" #include "gmock/gmock.h" using ::google::jwt_verify::Status; @@ -76,9 +77,11 @@ class MockJwksData : public JwksCache::JwksData { ON_CALL(*this, getJwtProvider()).WillByDefault(::testing::ReturnRef(jwt_provider_)); ON_CALL(*this, isExpired()).WillByDefault(::testing::Return(false)); ON_CALL(*this, getJwtCache()).WillByDefault(::testing::ReturnRef(jwt_cache_)); + ON_CALL(*this, isSubjectAllowed(_)).WillByDefault(::testing::Return(true)); } MOCK_METHOD(bool, areAudiencesAllowed, (const std::vector&), (const)); + MOCK_METHOD(bool, isSubjectAllowed, (const absl::string_view), (const)); MOCK_METHOD(const envoy::extensions::filters::http::jwt_authn::v3::JwtProvider&, getJwtProvider, (), (const)); MOCK_METHOD(const ::google::jwt_verify::Jwks*, getJwksObj, (), (const)); diff --git a/test/extensions/filters/http/jwt_authn/test_common.h b/test/extensions/filters/http/jwt_authn/test_common.h index 2c583604d20b..cd679510e0f7 100644 --- a/test/extensions/filters/http/jwt_authn/test_common.h +++ b/test/extensions/filters/http/jwt_authn/test_common.h @@ -66,6 +66,52 @@ const char PublicKey[] = R"( } )"; +// Provider config with various subject constraints +const char SubjectConfig[] = R"( +providers: + example_provider: + issuer: https://example.com + subjects: + suffix: "@example.com" + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + spiffe_provider: + issuer: https://spiffe.example.com + subjects: + prefix: spiffe://spiffe.example.com/ + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + no_subj_provider: + issuer: https://nosub.com + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + regex_provider: + issuer: https://regexsub.com + subjects: + safe_regex: + safe_regex: + regex: "spiffe://.*\\.example\\.com/.*" + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + +)"; + // A good config. const char ExampleConfig[] = R"( providers: From 9617778d94adbf12a670e3ac4aa98f1cf4bedb6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 09:53:42 +0000 Subject: [PATCH 006/124] build(deps): bump distroless/base-nossl-debian12 from `0e777c6` to `099c134` in /ci (#32843) build(deps): bump distroless/base-nossl-debian12 in /ci Bumps distroless/base-nossl-debian12 from `0e777c6` to `099c134`. --- updated-dependencies: - dependency-name: distroless/base-nossl-debian12 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ci/Dockerfile-envoy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/Dockerfile-envoy b/ci/Dockerfile-envoy index 07affa3502e3..00e2c7ca472d 100644 --- a/ci/Dockerfile-envoy +++ b/ci/Dockerfile-envoy @@ -58,7 +58,7 @@ COPY --chown=0:0 --chmod=755 \ # STAGE: envoy-distroless -FROM gcr.io/distroless/base-nossl-debian12:nonroot@sha256:0e777c69ba810353b9f3f2033280bbe7d029d81fa55760f6eec817ef595aa19c AS envoy-distroless +FROM gcr.io/distroless/base-nossl-debian12:nonroot@sha256:099c13463fdd2f52d31af8b61f5a991ed8e97bdac529f10b22c4f4ebf0c21c0d AS envoy-distroless EXPOSE 10000 ENTRYPOINT ["/usr/local/bin/envoy"] CMD ["-c", "/etc/envoy/envoy.yaml"] From 88ddcc3452bba14100b36fa0e1dfe8d3263e61c6 Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 12 Mar 2024 12:49:36 +0000 Subject: [PATCH 007/124] examples: Fix zipkin html test (#32846) Signed-off-by: Ryan Northey --- examples/zipkin/verify.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/zipkin/verify.sh b/examples/zipkin/verify.sh index 412e04082df0..0744d0a41be8 100755 --- a/examples/zipkin/verify.sh +++ b/examples/zipkin/verify.sh @@ -19,5 +19,5 @@ responds_with \ run_log "View the traces in Zipkin UI" responds_with \ - "" \ + "" \ "http://localhost:${PORT_UI}/zipkin/" From 85b0f29e73f1c7bd3cba53401fee15acc3772d80 Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 12 Mar 2024 12:55:45 +0000 Subject: [PATCH 008/124] Revert "coverage: lower coverage for source/common/io/ (#32150)" (#32845) This reverts commit 9f92f4c1d49d186488bf5b0f7b0ebbe68ee43394. Signed-off-by: Ryan Northey --- test/per_file_coverage.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index cd8f977a61fb..7a5ac225f950 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -3,7 +3,7 @@ # directory:coverage_percent # for existing directories with low coverage. declare -a KNOWN_LOW_COVERAGE=( -"source/common:95.9" # TODO(#32149): increase this once io_uring is tested. +"source/common:96.2" "source/common/api:84.5" # flaky due to posix: be careful adjusting "source/common/api/posix:83.8" # flaky (accept failover non-deterministic): be careful adjusting "source/common/common/posix:88.8" # No easy way to test pthread_create failure. @@ -12,7 +12,6 @@ declare -a KNOWN_LOW_COVERAGE=( "source/common/event:95.0" # Emulated edge events guards don't report LCOV "source/common/filesystem/posix:96.2" # FileReadToEndNotReadable fails in some env; createPath can't test all failure branches. "source/common/http/http2:95.2" -"source/common/io:57.1" # TODO(#32149): CI has stopped executing this code. "source/common/json:94.6" "source/common/matcher:94.6" "source/common/network:94.4" # Flaky, `activateFileEvents`, `startSecureTransport` and `ioctl`, listener_socket do not always report LCOV From 70ff9a728ac4060b8c57b31e515526f76af9ab17 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Tue, 12 Mar 2024 09:55:05 -0500 Subject: [PATCH 009/124] mobile: Rename tests to make them unique (#32825) Signed-off-by: Fredy Wijaya --- .../proxying/ProxyInfoIntentPerformHTTPRequestUsingProxyTest.kt | 2 +- .../ProxyInfoIntentPerformHTTPSRequestBadHostnameTest.kt | 2 +- .../ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt | 2 +- .../ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt | 2 +- .../proxying/ProxyPollPerformHTTPRequestUsingProxyTest.kt | 2 +- .../ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPRequestUsingProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPRequestUsingProxyTest.kt index 05dcb60e665f..b771f075245e 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPRequestUsingProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPRequestUsingProxyTest.kt @@ -33,7 +33,7 @@ import org.robolectric.RobolectricTestRunner // │ │ // └──────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPRequestUsingProxy { +class ProxyInfoIntentPerformHTTPRequestUsingProxyTest { init { JniLibrary.loadTestLibrary() JniLibrary.load() diff --git a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestBadHostnameTest.kt b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestBadHostnameTest.kt index 7182356cebe6..e0baa24faa05 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestBadHostnameTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestBadHostnameTest.kt @@ -33,7 +33,7 @@ import org.robolectric.RobolectricTestRunner // │ │ // └──────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPSRequestBadHostname { +class ProxyInfoIntentPerformHTTPSRequestBadHostnameTest { init { JniLibrary.loadTestLibrary() JniLibrary.load() diff --git a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt index 672879b70d54..c2b1815800d9 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt @@ -33,7 +33,7 @@ import org.robolectric.RobolectricTestRunner // │ │ // └──────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPSRequestUsingAsyncProxyTest { +class ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest { init { JniLibrary.loadTestLibrary() JniLibrary.load() diff --git a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt index 626a4ee49864..14632160bc31 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt @@ -33,7 +33,7 @@ import org.robolectric.RobolectricTestRunner // │ │ // └──────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPSRequestUsingProxy { +class ProxyInfoIntentPerformHTTPSRequestUsingProxyTest { init { JniLibrary.loadTestLibrary() JniLibrary.load() diff --git a/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestUsingProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestUsingProxyTest.kt index 08504ef1d13f..cb23afc6c3dc 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestUsingProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestUsingProxyTest.kt @@ -32,7 +32,7 @@ import org.robolectric.RobolectricTestRunner // │ │ // └──────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPRequestUsingProxy { +class ProxyPollPerformHTTPRequestUsingProxyTest { init { AndroidJniLibrary.loadTestLibrary() JniLibrary.loadTestLibrary() diff --git a/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt index efeb9e30f11f..21a08ad18722 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt @@ -34,7 +34,7 @@ import org.robolectric.RobolectricTestRunner // │ │ // └─────────────────────────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPRequestUsingProxy { +class ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest { init { JniLibrary.loadTestLibrary() JniLibrary.load() From 54bfb489b40ee2b7f14dc86b9be21e2a34ee25ab Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 12 Mar 2024 12:03:02 -0400 Subject: [PATCH 010/124] tools: hopefully fix repo notifier (#32852) Signed-off-by: Alyssa Wilk --- tools/repo/notify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/repo/notify.py b/tools/repo/notify.py index 689e8dde6e7c..54c1f370835c 100644 --- a/tools/repo/notify.py +++ b/tools/repo/notify.py @@ -287,7 +287,7 @@ async def post_to_oncall(self): await self.send_message(channel='#envoy-maintainer-oncall', text=(f"{oncall}")) await self.send_message(channel='#general', text=(f"{oncall}")) await self.send_message( - channel='#envoy-maintainer-oncall', text=(f"Oncall now @", oncall_handle)) + channel='#envoy-maintainer-oncall', text=(f"Oncall now @{oncall_handle}")) await self.send_message( channel='#envoy-maintainer-oncall', text=(f"*'Unassigned' PRs* (PRs with no maintainer assigned)\n{unassigned}")) From 7bedf619ebf09c8f4c8116d80347c4cec9fbfa23 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 12 Mar 2024 12:42:58 -0400 Subject: [PATCH 011/124] test: bumping coverage (#32822) Signed-off-by: Alyssa Wilk --- test/per_file_coverage.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index 7a5ac225f950..5ab396837967 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -7,16 +7,16 @@ declare -a KNOWN_LOW_COVERAGE=( "source/common/api:84.5" # flaky due to posix: be careful adjusting "source/common/api/posix:83.8" # flaky (accept failover non-deterministic): be careful adjusting "source/common/common/posix:88.8" # No easy way to test pthread_create failure. -"source/common/config:95.4" +"source/common/config:95.8" "source/common/crypto:95.5" -"source/common/event:95.0" # Emulated edge events guards don't report LCOV +"source/common/event:95.1" # Emulated edge events guards don't report LCOV "source/common/filesystem/posix:96.2" # FileReadToEndNotReadable fails in some env; createPath can't test all failure branches. -"source/common/http/http2:95.2" +"source/common/http/http2:95.6" "source/common/json:94.6" "source/common/matcher:94.6" "source/common/network:94.4" # Flaky, `activateFileEvents`, `startSecureTransport` and `ioctl`, listener_socket do not always report LCOV "source/common/network/dns_resolver:91.4" # A few lines of MacOS code not tested in linux scripts. Tested in MacOS scripts -"source/common/quic:93.3" +"source/common/quic:93.5" "source/common/secret:95.1" "source/common/signal:87.2" # Death tests don't report LCOV "source/common/thread:0.0" # Death tests don't report LCOV @@ -30,27 +30,27 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/common/wasm/ext:92.0" "source/extensions/filters/common/fault:94.5" "source/extensions/filters/common/rbac:90.5" -"source/extensions/filters/http/cache:94.9" +"source/extensions/filters/http/cache:95.0" "source/extensions/filters/http/grpc_json_transcoder:93.8" # TODO(#28232) "source/extensions/filters/http/ip_tagging:88.0" "source/extensions/filters/http/kill_request:91.7" # Death tests don't report LCOV "source/extensions/filters/http/wasm:1.8" "source/extensions/filters/listener/original_src:92.1" "source/extensions/filters/network/common:96.4" -"source/extensions/filters/network/mongo_proxy:96.0" +"source/extensions/filters/network/mongo_proxy:96.1" "source/extensions/filters/network/sni_cluster:88.9" "source/extensions/filters/network/wasm:76.9" "source/extensions/http/cache/simple_http_cache:95.9" "source/extensions/rate_limit_descriptors:95.0" "source/extensions/rate_limit_descriptors/expr:95.0" -"source/extensions/stat_sinks/graphite_statsd:78.6" # Death tests don't report LCOV -"source/extensions/stat_sinks/statsd:80.8" # Death tests don't report LCOV -"source/extensions/tracers:96.1" +"source/extensions/stat_sinks/graphite_statsd:82.8" # Death tests don't report LCOV +"source/extensions/stat_sinks/statsd:85.2" # Death tests don't report LCOV +"source/extensions/tracers:96.4" "source/extensions/tracers/common:74.8" "source/extensions/tracers/common/ot:72.9" "source/extensions/tracers/opencensus:94.0" "source/extensions/tracers/zipkin:95.8" -"source/extensions/transport_sockets:95.8" +"source/extensions/transport_sockets:97.4" "source/common/tls:94.9" "source/common/tls/cert_validator:94.2" "source/common/tls/private_key:88.9" @@ -66,7 +66,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/health_checkers:96.0" "source/extensions/health_checkers/http:93.9" "source/extensions/health_checkers/grpc:92.0" -"source/extensions/config_subscription/rest:94.3" +"source/extensions/config_subscription/rest:94.7" "source/extensions/matching/input_matchers/cel_matcher:91.3" #Death tests don't report LCOV ) From 55ea0141b58a790c2f84125f615f4919efc833f6 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 12 Mar 2024 13:14:38 -0400 Subject: [PATCH 012/124] tools: fixing typo (#32854) Signed-off-by: Alyssa Wilk --- tools/repo/notify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/repo/notify.py b/tools/repo/notify.py index 54c1f370835c..d62bcb28a02b 100644 --- a/tools/repo/notify.py +++ b/tools/repo/notify.py @@ -44,7 +44,7 @@ 'kuat': 'kuat', 'Lizan': 'Lizan Zhou', 'Matt': 'mklein', - 'Kateryna': 'nexdolik', + 'Kateryna': 'nezdolik', 'phlax': 'phlax', 'Raven': 'ravenblackx', 'Ryan': 'Ryan Hamilton', From 10a10039b3a82d43ff47c319e0ef4faf229f3327 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Tue, 12 Mar 2024 15:54:53 -0400 Subject: [PATCH 013/124] quic: Use GRO for reading packets from the UDP socket in upstream QUIC (#32628) This behavior is guarded by the runtime flag `envoy.restart_features.prefer_udp_gro`. We previously turned off GRO in `EnvoyQuicClientConnection` because we weren't sure about the performance (see https://github.com/envoyproxy/envoy/pull/19088). However, kernel experts have recommended using GRO over recvmmsg as a much more CPU efficient solution for reading multiple UDP packets into a single buffer. This change also enables setting the option of whether `recvmmsg` should be used or not, so we can disable it for client connections, while keeping it for listener sockets. Risk Level: low (can be turned off with the runtime guard) Testing: unit tests Release Notes: included Runtime guard: envoy.reloadable_features.prefer_udp_gro Signed-off-by: Ali Beyad --- changelogs/current.yaml | 11 + source/common/network/udp_listener_impl.cc | 2 +- source/common/network/utility.cc | 52 ++-- source/common/network/utility.h | 33 ++- source/common/quic/BUILD | 2 + .../quic/client_connection_factory_impl.cc | 5 +- .../quic/envoy_quic_client_connection.cc | 20 +- .../quic/envoy_quic_client_connection.h | 8 +- source/common/quic/envoy_quic_utils.cc | 7 +- source/common/quic/envoy_quic_utils.h | 3 +- source/common/runtime/runtime_features.cc | 2 + .../filters/udp/udp_proxy/udp_proxy_filter.cc | 3 +- .../upstreams/http/udp/upstream_request.cc | 2 +- test/common/http/conn_pool_grid_test.cc | 2 +- test/common/network/udp_listener_impl_test.cc | 2 +- test/common/network/utility_test.cc | 9 +- test/common/quic/BUILD | 3 + .../quic/envoy_quic_client_session_test.cc | 279 ++++++++++++++---- .../quic/envoy_quic_client_stream_test.cc | 3 +- test/common/quic/test_utils.h | 2 +- .../udp/udp_proxy/udp_proxy_filter_test.cc | 3 +- .../integration/quic_http_integration_test.cc | 7 +- test/test_common/network_utility.cc | 7 +- 23 files changed, 361 insertions(+), 106 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 7e8961cfaf5e..e13ab82f5fb5 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -68,6 +68,17 @@ minor_behavior_changes: change: | Simplifies integration with the codec by removing translation between nghttp2 callbacks and Http2VisitorInterface events. Guarded by ``envoy.reloadable_features.http2_skip_callback_visitor``. +- area: http3 + change: | + Use GRO (Generic Receive Offload) for reading packets from a client QUIC UDP socket. See + https://www.kernel.org/doc/html/next/networking/segmentation-offloads.html for a description of + GRO. This behavior change can be reverted by setting + ``envoy.reloadable_features.prefer_quic_client_udp_gro`` to ``false``. +- area: http3 + change: | + Disables recvmmsg (multi-message) for reading packets from a client QUIC UDP socket, if GRO + is not set or not supported. recvmsg will be used instead. This behavior change can be + reverted by setting ``envoy.reloadable_features.disallow_quic_client_udp_mmsg`` to ``false``. bug_fixes: # *Changes expected to improve the state of the world and are unlikely to have negative effects* diff --git a/source/common/network/udp_listener_impl.cc b/source/common/network/udp_listener_impl.cc index a184eaae7036..2411b860609a 100644 --- a/source/common/network/udp_listener_impl.cc +++ b/source/common/network/udp_listener_impl.cc @@ -101,7 +101,7 @@ void UdpListenerImpl::handleReadCallback() { cb_.onReadReady(); const Api::IoErrorPtr result = Utility::readPacketsFromSocket( socket_->ioHandle(), *socket_->connectionInfoProvider().localAddress(), *this, time_source_, - config_.prefer_gro_, packets_dropped_); + config_.prefer_gro_, /*allow_mmsg=*/true, packets_dropped_); if (result == nullptr) { // No error. The number of reads was limited by read rate. There are more packets to read. // Register to read more in the next event loop. diff --git a/source/common/network/utility.cc b/source/common/network/utility.cc index eb7bb2a72951..eeacd295e028 100644 --- a/source/common/network/utility.cc +++ b/source/common/network/utility.cc @@ -598,13 +598,13 @@ void passPayloadToProcessor(uint64_t bytes_read, Buffer::InstancePtr buffer, std::move(buffer), receive_time); } -Api::IoCallUint64Result Utility::readFromSocket(IoHandle& handle, - const Address::Instance& local_address, - UdpPacketProcessor& udp_packet_processor, - MonotonicTime receive_time, bool use_gro, - uint32_t* packets_dropped) { - - if (use_gro) { +Api::IoCallUint64Result +Utility::readFromSocket(IoHandle& handle, const Address::Instance& local_address, + UdpPacketProcessor& udp_packet_processor, MonotonicTime receive_time, + UdpRecvMsgMethod recv_msg_method, uint32_t* packets_dropped) { + if (recv_msg_method == UdpRecvMsgMethod::RecvMsgWithGro) { + ASSERT(Api::OsSysCallsSingleton::get().supportsUdpGro(), + "cannot use GRO when the platform doesn't support it."); Buffer::InstancePtr buffer = std::make_unique(); IoHandle::RecvMsgOutput output(1, packets_dropped); @@ -646,7 +646,9 @@ Api::IoCallUint64Result Utility::readFromSocket(IoHandle& handle, return result; } - if (handle.supportsMmsg()) { + if (recv_msg_method == UdpRecvMsgMethod::RecvMmsg) { + ASSERT(Api::OsSysCallsSingleton::get().supportsMmsg(), + "cannot use recvmmsg when the platform doesn't support it."); const auto max_rx_datagram_size = udp_packet_processor.maxDatagramSize(); // Buffer::ReservationSingleSlice is always passed by value, and can only be constructed @@ -720,24 +722,40 @@ Api::IoCallUint64Result Utility::readFromSocket(IoHandle& handle, Api::IoErrorPtr Utility::readPacketsFromSocket(IoHandle& handle, const Address::Instance& local_address, UdpPacketProcessor& udp_packet_processor, - TimeSource& time_source, bool prefer_gro, - uint32_t& packets_dropped) { + TimeSource& time_source, bool allow_gro, + bool allow_mmsg, uint32_t& packets_dropped) { + UdpRecvMsgMethod recv_msg_method = UdpRecvMsgMethod::RecvMsg; + if (allow_gro && handle.supportsUdpGro()) { + recv_msg_method = UdpRecvMsgMethod::RecvMsgWithGro; + } else if (allow_mmsg && handle.supportsMmsg()) { + recv_msg_method = UdpRecvMsgMethod::RecvMmsg; + } + // Read at least one time, and attempt to read numPacketsExpectedPerEventLoop() packets unless // this goes over MAX_NUM_PACKETS_PER_EVENT_LOOP. size_t num_packets_to_read = std::min( MAX_NUM_PACKETS_PER_EVENT_LOOP, udp_packet_processor.numPacketsExpectedPerEventLoop()); - const bool use_gro = prefer_gro && handle.supportsUdpGro(); - size_t num_reads = - use_gro ? (num_packets_to_read / NUM_DATAGRAMS_PER_RECEIVE) - : (handle.supportsMmsg() ? (num_packets_to_read / NUM_DATAGRAMS_PER_RECEIVE) - : num_packets_to_read); + size_t num_reads; + switch (recv_msg_method) { + case UdpRecvMsgMethod::RecvMsgWithGro: + num_reads = (num_packets_to_read / NUM_DATAGRAMS_PER_RECEIVE); + break; + case UdpRecvMsgMethod::RecvMmsg: + num_reads = (num_packets_to_read / NUM_DATAGRAMS_PER_RECEIVE); + break; + case UdpRecvMsgMethod::RecvMsg: + num_reads = num_packets_to_read; + break; + } // Make sure to read at least once. num_reads = std::max(1, num_reads); + do { const uint32_t old_packets_dropped = packets_dropped; const MonotonicTime receive_time = time_source.monotonicTime(); - Api::IoCallUint64Result result = Utility::readFromSocket( - handle, local_address, udp_packet_processor, receive_time, use_gro, &packets_dropped); + Api::IoCallUint64Result result = + Utility::readFromSocket(handle, local_address, udp_packet_processor, receive_time, + recv_msg_method, &packets_dropped); if (!result.ok()) { // No more to read or encountered a system error. diff --git a/source/common/network/utility.h b/source/common/network/utility.h index 1d2352549191..14aa8fe9b28b 100644 --- a/source/common/network/utility.h +++ b/source/common/network/utility.h @@ -85,6 +85,17 @@ struct ResolvedUdpSocketConfig { bool prefer_gro_; }; +// The different options for receiving UDP packet(s) from system calls. +enum class UdpRecvMsgMethod { + // The `recvmsg` system call. + RecvMsg, + // The `recvmsg` system call using GRO (generic receive offload). This is the preferred method, + // if the platform supports it. + RecvMsgWithGro, + // The `recvmmsg` system call. + RecvMmsg, +}; + /** * Common network utility routines. */ @@ -352,15 +363,15 @@ class Utility { * @param udp_packet_processor is the callback to receive the packet. * @param receive_time is the timestamp passed to udp_packet_processor for the * receive time of the packet. - * @param prefer_gro supplies whether to use GRO if the OS supports it. + * @param recv_msg_method the type of system call and socket options combination to use when + * receiving packets from the kernel. * @param packets_dropped is the output parameter for number of packets dropped in kernel. If the * caller is not interested in it, nullptr can be passed in. */ - static Api::IoCallUint64Result readFromSocket(IoHandle& handle, - const Address::Instance& local_address, - UdpPacketProcessor& udp_packet_processor, - MonotonicTime receive_time, bool use_gro, - uint32_t* packets_dropped); + static Api::IoCallUint64Result + readFromSocket(IoHandle& handle, const Address::Instance& local_address, + UdpPacketProcessor& udp_packet_processor, MonotonicTime receive_time, + UdpRecvMsgMethod recv_msg_method, uint32_t* packets_dropped); /** * Read some packets from a given UDP socket and pass the packet to a given @@ -369,7 +380,11 @@ class Utility { * @param local_address is the socket's local address used to populate port. * @param udp_packet_processor is the callback to receive the packets. * @param time_source is the time source used to generate the time stamp of the received packets. - * @param prefer_gro supplies whether to use GRO if the OS supports it. + * @param allow_gro whether to use GRO, iff the platform supports it. This function will check + * the IoHandle to ensure the platform supports GRO before using it. + * @param allow_mmsg whether to use recvmmsg, iff the platform supports it. This function will + * check the IoHandle to ensure the platform supports recvmmsg before using it. If `allow_gro` is + * true and the platform supports GRO, then it will take precedence over using recvmmsg. * @param packets_dropped is the output parameter for number of packets dropped in kernel. * Return the io error encountered or nullptr if no io error but read stopped * because of MAX_NUM_PACKETS_PER_EVENT_LOOP. @@ -384,8 +399,8 @@ class Utility { static Api::IoErrorPtr readPacketsFromSocket(IoHandle& handle, const Address::Instance& local_address, UdpPacketProcessor& udp_packet_processor, - TimeSource& time_source, bool prefer_gro, - uint32_t& packets_dropped); + TimeSource& time_source, bool allow_gro, + bool allow_mmsg, uint32_t& packets_dropped); private: static void throwWithMalformedIp(absl::string_view ip_address); diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index 2b9890ed086f..7aafa7be99f8 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -180,6 +180,7 @@ envoy_cc_library( "//envoy/http:codec_interface", "//envoy/http:persistent_quic_info_interface", "//envoy/registry", + "//source/common/runtime:runtime_lib", "//source/common/tls:ssl_socket_lib", "//source/extensions/quic/crypto_stream:envoy_quic_crypto_client_stream_lib", "@com_github_google_quiche//:quic_core_http_spdy_session_lib", @@ -362,6 +363,7 @@ envoy_cc_library( "//envoy/event:dispatcher_interface", "//source/common/network:socket_option_factory_lib", "//source/common/network:udp_packet_writer_handler_lib", + "//source/common/runtime:runtime_lib", "@com_github_google_quiche//:quic_core_connection_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], diff --git a/source/common/quic/client_connection_factory_impl.cc b/source/common/quic/client_connection_factory_impl.cc index 3317e11463bc..08d7ce0fa05c 100644 --- a/source/common/quic/client_connection_factory_impl.cc +++ b/source/common/quic/client_connection_factory_impl.cc @@ -1,5 +1,7 @@ #include "source/common/quic/client_connection_factory_impl.h" +#include "source/common/runtime/runtime_features.h" + namespace Envoy { namespace Quic { @@ -45,7 +47,8 @@ std::unique_ptr createQuicNetworkConnection( ASSERT(!quic_versions.empty()); auto connection = std::make_unique( quic::QuicUtils::CreateRandomConnectionId(), server_addr, info_impl->conn_helper_, - info_impl->alarm_factory_, quic_versions, local_addr, dispatcher, options, generator); + info_impl->alarm_factory_, quic_versions, local_addr, dispatcher, options, generator, + Runtime::runtimeFeatureEnabled("envoy.reloadable_features.prefer_quic_client_udp_gro")); // TODO (danzh) move this temporary config and initial RTT configuration to h3 pool. quic::QuicConfig config = info_impl->quic_config_; diff --git a/source/common/quic/envoy_quic_client_connection.cc b/source/common/quic/envoy_quic_client_connection.cc index aa17972506f5..633f6822068e 100644 --- a/source/common/quic/envoy_quic_client_connection.cc +++ b/source/common/quic/envoy_quic_client_connection.cc @@ -7,6 +7,7 @@ #include "source/common/network/socket_option_factory.h" #include "source/common/network/udp_packet_writer_handler_impl.h" #include "source/common/quic/envoy_quic_utils.h" +#include "source/common/runtime/runtime_features.h" namespace Envoy { namespace Quic { @@ -30,35 +31,38 @@ EnvoyQuicClientConnection::EnvoyQuicClientConnection( const quic::ParsedQuicVersionVector& supported_versions, Network::Address::InstanceConstSharedPtr local_addr, Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options, - quic::ConnectionIdGeneratorInterface& generator) + quic::ConnectionIdGeneratorInterface& generator, const bool prefer_gro) : EnvoyQuicClientConnection( server_connection_id, helper, alarm_factory, supported_versions, dispatcher, - createConnectionSocket(initial_peer_address, local_addr, options), generator) {} + createConnectionSocket(initial_peer_address, local_addr, options, prefer_gro), generator, + prefer_gro) {} EnvoyQuicClientConnection::EnvoyQuicClientConnection( const quic::QuicConnectionId& server_connection_id, quic::QuicConnectionHelperInterface& helper, quic::QuicAlarmFactory& alarm_factory, const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher, Network::ConnectionSocketPtr&& connection_socket, - quic::ConnectionIdGeneratorInterface& generator) + quic::ConnectionIdGeneratorInterface& generator, const bool prefer_gro) : EnvoyQuicClientConnection( server_connection_id, helper, alarm_factory, new EnvoyQuicPacketWriter( std::make_unique(connection_socket->ioHandle())), /*owns_writer=*/true, supported_versions, dispatcher, std::move(connection_socket), - generator) {} + generator, prefer_gro) {} EnvoyQuicClientConnection::EnvoyQuicClientConnection( const quic::QuicConnectionId& server_connection_id, quic::QuicConnectionHelperInterface& helper, quic::QuicAlarmFactory& alarm_factory, quic::QuicPacketWriter* writer, bool owns_writer, const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher, Network::ConnectionSocketPtr&& connection_socket, - quic::ConnectionIdGeneratorInterface& generator) + quic::ConnectionIdGeneratorInterface& generator, const bool prefer_gro) : quic::QuicConnection(server_connection_id, quic::QuicSocketAddress(), envoyIpAddressToQuicSocketAddress( connection_socket->connectionInfoProvider().remoteAddress()->ip()), &helper, &alarm_factory, writer, owns_writer, quic::Perspective::IS_CLIENT, supported_versions, generator), - QuicNetworkConnection(std::move(connection_socket)), dispatcher_(dispatcher) {} + QuicNetworkConnection(std::move(connection_socket)), dispatcher_(dispatcher), + prefer_gro_(prefer_gro), disallow_mmsg_(Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.disallow_quic_client_udp_mmsg")) {} void EnvoyQuicClientConnection::processPacket( Network::Address::InstanceConstSharedPtr local_address, @@ -175,7 +179,7 @@ void EnvoyQuicClientConnection::probeWithNewPort(const quic::QuicSocketAddress& // The probing socket will have the same host but a different port. auto probing_socket = createConnectionSocket(connectionSocket()->connectionInfoProvider().remoteAddress(), - new_local_address, connectionSocket()->options()); + new_local_address, connectionSocket()->options(), prefer_gro_); setUpConnectionSocket(*probing_socket, delegate_); auto writer = std::make_unique( std::make_unique(probing_socket->ioHandle())); @@ -249,7 +253,7 @@ void EnvoyQuicClientConnection::onFileEvent(uint32_t events, if (connected() && (events & Event::FileReadyType::Read)) { Api::IoErrorPtr err = Network::Utility::readPacketsFromSocket( connection_socket.ioHandle(), *connection_socket.connectionInfoProvider().localAddress(), - *this, dispatcher_.timeSource(), /*prefer_gro=*/false, packets_dropped_); + *this, dispatcher_.timeSource(), prefer_gro_, !disallow_mmsg_, packets_dropped_); if (err == nullptr) { // In the case where the path validation fails, the probing socket will be closed and its IO // events are no longer interesting. diff --git a/source/common/quic/envoy_quic_client_connection.h b/source/common/quic/envoy_quic_client_connection.h index 43cbc0d12901..b2faee057fcb 100644 --- a/source/common/quic/envoy_quic_client_connection.h +++ b/source/common/quic/envoy_quic_client_connection.h @@ -60,7 +60,7 @@ class EnvoyQuicClientConnection : public quic::QuicConnection, Network::Address::InstanceConstSharedPtr local_addr, Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options, - quic::ConnectionIdGeneratorInterface& generator); + quic::ConnectionIdGeneratorInterface& generator, bool prefer_gro); EnvoyQuicClientConnection(const quic::QuicConnectionId& server_connection_id, quic::QuicConnectionHelperInterface& helper, @@ -69,7 +69,7 @@ class EnvoyQuicClientConnection : public quic::QuicConnection, const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher, Network::ConnectionSocketPtr&& connection_socket, - quic::ConnectionIdGeneratorInterface& generator); + quic::ConnectionIdGeneratorInterface& generator, bool prefer_gro); // Network::UdpPacketProcessor void processPacket(Network::Address::InstanceConstSharedPtr local_address, @@ -135,7 +135,7 @@ class EnvoyQuicClientConnection : public quic::QuicConnection, const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher, Network::ConnectionSocketPtr&& connection_socket, - quic::ConnectionIdGeneratorInterface& generator); + quic::ConnectionIdGeneratorInterface& generator, bool prefer_gro); void onFileEvent(uint32_t events, Network::ConnectionSocket& connection_socket); @@ -150,6 +150,8 @@ class EnvoyQuicClientConnection : public quic::QuicConnection, bool migrate_port_on_path_degrading_{false}; uint8_t num_socket_switches_{0}; size_t num_packets_with_unknown_dst_address_{0}; + const bool prefer_gro_; + const bool disallow_mmsg_; }; } // namespace Quic diff --git a/source/common/quic/envoy_quic_utils.cc b/source/common/quic/envoy_quic_utils.cc index 1d4b0bc68aa6..2d111361c45c 100644 --- a/source/common/quic/envoy_quic_utils.cc +++ b/source/common/quic/envoy_quic_utils.cc @@ -5,6 +5,7 @@ #include "envoy/common/platform.h" #include "envoy/config/core/v3/base.pb.h" +#include "source/common/api/os_sys_calls_impl.h" #include "source/common/http/utility.h" #include "source/common/network/socket_option_factory.h" #include "source/common/network/utility.h" @@ -137,7 +138,8 @@ Http::StreamResetReason quicErrorCodeToEnvoyRemoteResetReason(quic::QuicErrorCod Network::ConnectionSocketPtr createConnectionSocket(const Network::Address::InstanceConstSharedPtr& peer_addr, Network::Address::InstanceConstSharedPtr& local_addr, - const Network::ConnectionSocket::OptionsSharedPtr& options) { + const Network::ConnectionSocket::OptionsSharedPtr& options, + const bool prefer_gro) { if (local_addr == nullptr) { local_addr = Network::Utility::getLocalAddress(peer_addr->ip()->version()); } @@ -149,6 +151,9 @@ createConnectionSocket(const Network::Address::InstanceConstSharedPtr& peer_addr } connection_socket->addOptions(Network::SocketOptionFactory::buildIpPacketInfoOptions()); connection_socket->addOptions(Network::SocketOptionFactory::buildRxQueueOverFlowOptions()); + if (prefer_gro && Api::OsSysCallsSingleton::get().supportsUdpGro()) { + connection_socket->addOptions(Network::SocketOptionFactory::buildUdpGroOptions()); + } if (options != nullptr) { connection_socket->addOptions(options); } diff --git a/source/common/quic/envoy_quic_utils.h b/source/common/quic/envoy_quic_utils.h index ae63d9a78297..1dc6991ca3d3 100644 --- a/source/common/quic/envoy_quic_utils.h +++ b/source/common/quic/envoy_quic_utils.h @@ -161,7 +161,8 @@ Http::StreamResetReason quicErrorCodeToEnvoyRemoteResetReason(quic::QuicErrorCod Network::ConnectionSocketPtr createConnectionSocket(const Network::Address::InstanceConstSharedPtr& peer_addr, Network::Address::InstanceConstSharedPtr& local_addr, - const Network::ConnectionSocket::OptionsSharedPtr& options); + const Network::ConnectionSocket::OptionsSharedPtr& options, + bool prefer_gro = false); // Convert a cert in string form to X509 object. // Return nullptr if the bytes passed cannot be passed. diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 0f2f5862ca25..40e526641425 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -38,6 +38,7 @@ RUNTIME_GUARD(envoy_reloadable_features_copy_response_code_to_downstream_stream_ RUNTIME_GUARD(envoy_reloadable_features_defer_processing_backedup_streams); RUNTIME_GUARD(envoy_reloadable_features_detect_and_raise_rst_tcp_connection); RUNTIME_GUARD(envoy_reloadable_features_dfp_mixed_scheme); +RUNTIME_GUARD(envoy_reloadable_features_disallow_quic_client_udp_mmsg); RUNTIME_GUARD(envoy_reloadable_features_dns_cache_set_first_resolve_complete); RUNTIME_GUARD(envoy_reloadable_features_edf_lb_host_scheduler_init_fix); RUNTIME_GUARD(envoy_reloadable_features_edf_lb_locality_scheduler_init_fix); @@ -74,6 +75,7 @@ RUNTIME_GUARD(envoy_reloadable_features_oauth_make_token_cookie_httponly); RUNTIME_GUARD(envoy_reloadable_features_oauth_use_standard_max_age_value); RUNTIME_GUARD(envoy_reloadable_features_oauth_use_url_encoding); RUNTIME_GUARD(envoy_reloadable_features_original_dst_rely_on_idle_timeout); +RUNTIME_GUARD(envoy_reloadable_features_prefer_quic_client_udp_gro); RUNTIME_GUARD(envoy_reloadable_features_proxy_status_mapping_more_core_response_flags); RUNTIME_GUARD(envoy_reloadable_features_proxy_status_upstream_request_timeout); RUNTIME_GUARD(envoy_reloadable_features_quic_fix_filter_manager_uaf); diff --git a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc index 7e8ba256ebc5..2b0e416a9eba 100644 --- a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc +++ b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc @@ -399,7 +399,8 @@ void UdpProxyFilter::UdpActiveSession::onReadReady() { uint32_t packets_dropped = 0; const Api::IoErrorPtr result = Network::Utility::readPacketsFromSocket( udp_socket_->ioHandle(), *addresses_.local_, *this, cluster_.filter_.config_->timeSource(), - cluster_.filter_.config_->upstreamSocketConfig().prefer_gro_, packets_dropped); + cluster_.filter_.config_->upstreamSocketConfig().prefer_gro_, /*allow_mmsg=*/true, + packets_dropped); if (result == nullptr) { udp_socket_->ioHandle().activateFileEvents(Event::FileReadyType::Read); return; diff --git a/source/extensions/upstreams/http/udp/upstream_request.cc b/source/extensions/upstreams/http/udp/upstream_request.cc index 1fd5de949251..fdba4ecc5962 100644 --- a/source/extensions/upstreams/http/udp/upstream_request.cc +++ b/source/extensions/upstreams/http/udp/upstream_request.cc @@ -113,7 +113,7 @@ void UdpUpstream::onSocketReadReady() { uint32_t packets_dropped = 0; const Api::IoErrorPtr result = Network::Utility::readPacketsFromSocket( socket_->ioHandle(), *socket_->connectionInfoProvider().localAddress(), *this, - dispatcher_.timeSource(), /*prefer_gro=*/true, packets_dropped); + dispatcher_.timeSource(), /*allow_gro=*/true, /*allow_mmsg=*/true, packets_dropped); if (result == nullptr) { socket_->ioHandle().activateFileEvents(Event::FileReadyType::Read); return; diff --git a/test/common/http/conn_pool_grid_test.cc b/test/common/http/conn_pool_grid_test.cc index d9b1fff1fdf6..1a44c6f8a5ac 100644 --- a/test/common/http/conn_pool_grid_test.cc +++ b/test/common/http/conn_pool_grid_test.cc @@ -1033,7 +1033,7 @@ TEST_F(ConnectivityGridTest, ConnectionCloseDuringAysnConnect) { ASSERT_EQ(0, Api::OsSysCallsSingleton::get().getifaddrs(interfaces).return_value_); } - Api::MockOsSysCalls os_sys_calls; + NiceMock os_sys_calls; TestThreadsafeSingletonInjector os_calls(&os_sys_calls); EXPECT_CALL(os_sys_calls, supportsGetifaddrs()).WillOnce(Return(supports_getifaddrs)); if (supports_getifaddrs) { diff --git a/test/common/network/udp_listener_impl_test.cc b/test/common/network/udp_listener_impl_test.cc index 6df91c150355..cc4818cfd43a 100644 --- a/test/common/network/udp_listener_impl_test.cc +++ b/test/common/network/udp_listener_impl_test.cc @@ -516,7 +516,7 @@ TEST_P(UdpListenerImplTest, UdpListenerRecvMsgError) { // Inject mocked OsSysCalls implementation to mock a read failure. Api::MockOsSysCalls os_sys_calls; TestThreadsafeSingletonInjector os_calls(&os_sys_calls); - EXPECT_CALL(os_sys_calls, supportsMmsg()).Times((2u)); + EXPECT_CALL(os_sys_calls, supportsMmsg()).Times((1u)); EXPECT_CALL(os_sys_calls, recvmsg(_, _, _)) .WillOnce(Return(Api::SysCallSizeResult{-1, SOCKET_ERROR_NOT_SUP})); diff --git a/test/common/network/utility_test.cc b/test/common/network/utility_test.cc index f4e3e3886d79..765ab7c4eafb 100644 --- a/test/common/network/utility_test.cc +++ b/test/common/network/utility_test.cc @@ -829,7 +829,12 @@ TEST(PacketLoss, LossTest) { NiceMock processor; MonotonicTime time(std::chrono::seconds(0)); uint32_t packets_dropped = 0; - Utility::readFromSocket(handle, *address, processor, time, false, &packets_dropped); + UdpRecvMsgMethod recv_msg_method = UdpRecvMsgMethod::RecvMsg; + if (Api::OsSysCallsSingleton::get().supportsMmsg()) { + recv_msg_method = UdpRecvMsgMethod::RecvMmsg; + } + + Utility::readFromSocket(handle, *address, processor, time, recv_msg_method, &packets_dropped); EXPECT_EQ(1, packets_dropped); // Send another packet. @@ -837,7 +842,7 @@ TEST(PacketLoss, LossTest) { reinterpret_cast(&storage), sizeof(storage))); // Make sure the drop count is now 2. - Utility::readFromSocket(handle, *address, processor, time, false, &packets_dropped); + Utility::readFromSocket(handle, *address, processor, time, recv_msg_method, &packets_dropped); EXPECT_EQ(2, packets_dropped); } #endif diff --git a/test/common/quic/BUILD b/test/common/quic/BUILD index 6c0fff13ff16..b488b190353d 100644 --- a/test/common/quic/BUILD +++ b/test/common/quic/BUILD @@ -190,6 +190,7 @@ envoy_cc_test( deps = [ ":test_utils_lib", "//envoy/stats:stats_macros", + "//source/common/api:os_sys_calls_lib", "//source/common/quic:client_codec_lib", "//source/common/quic:envoy_quic_alarm_factory_lib", "//source/common/quic:envoy_quic_client_connection_lib", @@ -202,6 +203,8 @@ envoy_cc_test( "//test/mocks/stats:stats_mocks", "//test/test_common:logging_lib", "//test/test_common:simulated_time_system_lib", + "//test/test_common:test_runtime_lib", + "//test/test_common:threadsafe_singleton_injector_lib", "@com_github_google_quiche//:quic_test_tools_session_peer_lib", ], ) diff --git a/test/common/quic/envoy_quic_client_session_test.cc b/test/common/quic/envoy_quic_client_session_test.cc index 1a97687d2118..6d1701e7256c 100644 --- a/test/common/quic/envoy_quic_client_session_test.cc +++ b/test/common/quic/envoy_quic_client_session_test.cc @@ -1,5 +1,6 @@ #include "envoy/stats/stats_macros.h" +#include "source/common/api/os_sys_calls_impl.h" #include "source/common/network/transport_socket_options_impl.h" #include "source/common/quic/client_codec_impl.h" #include "source/common/quic/envoy_quic_alarm_factory.h" @@ -18,6 +19,8 @@ #include "test/test_common/logging.h" #include "test/test_common/network_utility.h" #include "test/test_common/simulated_time_system.h" +#include "test/test_common/test_runtime.h" +#include "test/test_common/threadsafe_singleton_injector.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -29,6 +32,7 @@ using testing::_; using testing::Invoke; +using testing::Return; namespace Envoy { namespace Quic { @@ -45,7 +49,7 @@ class TestEnvoyQuicClientConnection : public EnvoyQuicClientConnection { quic::ConnectionIdGeneratorInterface& generator) : EnvoyQuicClientConnection(server_connection_id, helper, alarm_factory, &writer, false, supported_versions, dispatcher, std::move(connection_socket), - generator) { + generator, /*prefer_gro=*/true) { SetEncrypter(quic::ENCRYPTION_FORWARD_SECURE, std::make_unique(quic::ENCRYPTION_FORWARD_SECURE)); InstallDecrypter(quic::ENCRYPTION_FORWARD_SECURE, @@ -73,45 +77,50 @@ class EnvoyQuicClientSessionTest : public testing::TestWithParam( quic::test::crypto_test_utils::ProofVerifierForTesting())), quic_stat_names_(store_.symbolTable()), transport_socket_options_(std::make_shared()), - envoy_quic_session_( - quic_config_, quic_version_, - std::unique_ptr(quic_connection_), - quic::QuicServerId("example.com", 443, false), crypto_config_, *dispatcher_, - /*send_buffer_limit*/ 1024 * 1024, crypto_stream_factory_, quic_stat_names_, {}, - *store_.rootScope(), transport_socket_options_, {}), stats_({ALL_HTTP3_CODEC_STATS(POOL_COUNTER_PREFIX(store_, "http3."), POOL_GAUGE_PREFIX(store_, "http3."))}) { http3_options_.mutable_quic_protocol_options() ->mutable_num_timeouts_to_trigger_port_migration() ->set_value(1); + } + + void SetUp() override { + quic_connection_ = new TestEnvoyQuicClientConnection( + quic::test::TestConnectionId(), connection_helper_, alarm_factory_, writer_, quic_version_, + *dispatcher_, createConnectionSocket(peer_addr_, self_addr_, nullptr, /*prefer_gro=*/true), + connection_id_generator_); + + OptRef cache; + OptRef uts_factory; + envoy_quic_session_ = std::make_unique( + quic_config_, quic_version_, + std::unique_ptr(quic_connection_), + quic::QuicServerId("example.com", 443, false), crypto_config_, *dispatcher_, + /*send_buffer_limit*/ 1024 * 1024, crypto_stream_factory_, quic_stat_names_, cache, + *store_.rootScope(), transport_socket_options_, uts_factory); + http_connection_ = std::make_unique( - envoy_quic_session_, http_connection_callbacks_, stats_, http3_options_, 64 * 1024, 100); - EXPECT_EQ(time_system_.systemTime(), envoy_quic_session_.streamInfo().startTime()); - EXPECT_EQ(EMPTY_STRING, envoy_quic_session_.nextProtocol()); + *envoy_quic_session_, http_connection_callbacks_, stats_, http3_options_, 64 * 1024, 100); + EXPECT_EQ(time_system_.systemTime(), envoy_quic_session_->streamInfo().startTime()); + EXPECT_EQ(EMPTY_STRING, envoy_quic_session_->nextProtocol()); EXPECT_EQ(Http::Protocol::Http3, http_connection_->protocol()); time_system_.advanceTimeWait(std::chrono::milliseconds(1)); ON_CALL(writer_, WritePacket(_, _, _, _, _, _)) .WillByDefault(testing::Return(quic::WriteResult(quic::WRITE_STATUS_OK, 1))); - } - void SetUp() override { - envoy_quic_session_.Initialize(); - setQuicConfigWithDefaultValues(envoy_quic_session_.config()); + envoy_quic_session_->Initialize(); + setQuicConfigWithDefaultValues(envoy_quic_session_->config()); quic::test::QuicConfigPeer::SetReceivedStatelessResetToken( - envoy_quic_session_.config(), + envoy_quic_session_->config(), quic::QuicUtils::GenerateStatelessResetToken(quic::test::TestConnectionId())); - envoy_quic_session_.OnConfigNegotiated(); - envoy_quic_session_.addConnectionCallbacks(network_connection_callbacks_); - envoy_quic_session_.setConnectionStats( + envoy_quic_session_->OnConfigNegotiated(); + envoy_quic_session_->addConnectionCallbacks(network_connection_callbacks_); + envoy_quic_session_->setConnectionStats( {read_total_, read_current_, write_total_, write_current_, nullptr, nullptr}); EXPECT_EQ(&read_total_, &quic_connection_->connectionStats().read_total_); } @@ -121,7 +130,7 @@ class EnvoyQuicClientSessionTest : public testing::TestWithParamclose(Network::ConnectionCloseType::NoFlush); } peer_socket_->close(); } @@ -163,7 +172,7 @@ class EnvoyQuicClientSessionTest : public testing::TestWithParam envoy_quic_session_; Network::MockConnectionCallbacks network_connection_callbacks_; Http::MockServerConnectionCallbacks http_connection_callbacks_; testing::StrictMock read_total_; @@ -199,8 +208,8 @@ TEST_P(EnvoyQuicClientSessionTest, NewStream) { TEST_P(EnvoyQuicClientSessionTest, PacketLimits) { // We always allow for reading packets, even if there's no stream. - EXPECT_EQ(0, envoy_quic_session_.GetNumActiveStreams()); - EXPECT_EQ(16, envoy_quic_session_.numPacketsExpectedPerEventLoop()); + EXPECT_EQ(0, envoy_quic_session_->GetNumActiveStreams()); + EXPECT_EQ(16, envoy_quic_session_->numPacketsExpectedPerEventLoop()); NiceMock response_decoder; NiceMock stream_callbacks; @@ -217,8 +226,8 @@ TEST_P(EnvoyQuicClientSessionTest, PacketLimits) { })); stream.OnStreamHeaderList(/*fin=*/false, headers.uncompressed_header_bytes(), headers); // With one stream, still read 16 packets. - EXPECT_EQ(1, envoy_quic_session_.GetNumActiveStreams()); - EXPECT_EQ(16, envoy_quic_session_.numPacketsExpectedPerEventLoop()); + EXPECT_EQ(1, envoy_quic_session_->GetNumActiveStreams()); + EXPECT_EQ(16, envoy_quic_session_->numPacketsExpectedPerEventLoop()); EnvoyQuicClientStream& stream2 = sendGetRequest(response_decoder, stream_callbacks); EXPECT_CALL(response_decoder, decodeHeaders_(_, /*end_stream=*/false)) @@ -227,13 +236,13 @@ TEST_P(EnvoyQuicClientSessionTest, PacketLimits) { })); stream2.OnStreamHeaderList(/*fin=*/false, headers.uncompressed_header_bytes(), headers); // With 2 streams, read 32 packets. - EXPECT_EQ(2, envoy_quic_session_.GetNumActiveStreams()); - EXPECT_EQ(32, envoy_quic_session_.numPacketsExpectedPerEventLoop()); + EXPECT_EQ(2, envoy_quic_session_->GetNumActiveStreams()); + EXPECT_EQ(32, envoy_quic_session_->numPacketsExpectedPerEventLoop()); EXPECT_CALL(*quic_connection_, SendConnectionClosePacket(quic::QUIC_NO_ERROR, _, "Closed by application")); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)); - envoy_quic_session_.close(Network::ConnectionCloseType::NoFlush); + envoy_quic_session_->close(Network::ConnectionCloseType::NoFlush); } TEST_P(EnvoyQuicClientSessionTest, OnResetFrame) { @@ -246,7 +255,7 @@ TEST_P(EnvoyQuicClientSessionTest, OnResetFrame) { quic::QuicRstStreamFrame rst1(/*control_frame_id=*/1u, stream_id, quic::QUIC_ERROR_PROCESSING_STREAM, /*bytes_written=*/0u); EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::RemoteReset, _)); - envoy_quic_session_.OnRstStream(rst1); + envoy_quic_session_->OnRstStream(rst1); EXPECT_EQ( 1U, TestUtility::findCounter( @@ -263,7 +272,7 @@ TEST_P(EnvoyQuicClientSessionTest, SendResetFrame) { quic::QuicStreamId stream_id = stream.id(); EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::LocalReset, _)); EXPECT_CALL(*quic_connection_, SendControlFrame(_)); - envoy_quic_session_.ResetStream(stream_id, quic::QUIC_ERROR_PROCESSING_STREAM); + envoy_quic_session_->ResetStream(stream_id, quic::QUIC_ERROR_PROCESSING_STREAM); EXPECT_EQ( 1U, TestUtility::findCounter( @@ -276,7 +285,7 @@ TEST_P(EnvoyQuicClientSessionTest, OnGoAwayFrame) { Http::MockStreamCallbacks stream_callbacks; EXPECT_CALL(http_connection_callbacks_, onGoAway(Http::GoAwayErrorCode::NoError)); - envoy_quic_session_.OnHttp3GoAway(4u); + envoy_quic_session_->OnHttp3GoAway(4u); } TEST_P(EnvoyQuicClientSessionTest, ConnectionClose) { @@ -288,8 +297,8 @@ TEST_P(EnvoyQuicClientSessionTest, ConnectionClose) { EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::RemoteClose)); quic_connection_->OnConnectionCloseFrame(frame); EXPECT_EQ(absl::StrCat(quic::QuicErrorCodeToString(error), " with details: ", error_details), - envoy_quic_session_.transportFailureReason()); - EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); + envoy_quic_session_->transportFailureReason()); + EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_->state()); EXPECT_EQ( 1U, TestUtility::findCounter( @@ -305,8 +314,8 @@ TEST_P(EnvoyQuicClientSessionTest, ConnectionCloseWithActiveStream) { SendConnectionClosePacket(quic::QUIC_NO_ERROR, _, "Closed by application")); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)); EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::ConnectionTermination, _)); - envoy_quic_session_.close(Network::ConnectionCloseType::NoFlush); - EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); + envoy_quic_session_->close(Network::ConnectionCloseType::NoFlush); + EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_->state()); EXPECT_TRUE(stream.write_side_closed() && stream.reading_stopped()); EXPECT_EQ(1U, TestUtility::findCounter( store_, "http3.upstream.tx.quic_connection_close_error_code_QUIC_NO_ERROR") @@ -321,8 +330,8 @@ TEST_P(EnvoyQuicClientSessionTest, HandshakeTimesOutWithActiveStream) { SendConnectionClosePacket(quic::QUIC_HANDSHAKE_FAILED, _, "fake handshake time out")); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)); EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::LocalConnectionFailure, _)); - envoy_quic_session_.OnStreamError(quic::QUIC_HANDSHAKE_FAILED, "fake handshake time out"); - EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); + envoy_quic_session_->OnStreamError(quic::QUIC_HANDSHAKE_FAILED, "fake handshake time out"); + EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_->state()); EXPECT_TRUE(stream.write_side_closed() && stream.reading_stopped()); EXPECT_EQ(1U, TestUtility::findCounter( @@ -339,8 +348,8 @@ TEST_P(EnvoyQuicClientSessionTest, ConnectionClosePopulatesQuicVersionStats) { EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::RemoteClose)); quic_connection_->OnConnectionCloseFrame(frame); EXPECT_EQ(absl::StrCat(quic::QuicErrorCodeToString(error), " with details: ", error_details), - envoy_quic_session_.transportFailureReason()); - EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); + envoy_quic_session_->transportFailureReason()); + EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_->state()); std::string quic_version_stat_name; switch (GetParam().transport_version) { case quic::QUIC_VERSION_IETF_DRAFT_29: @@ -360,8 +369,8 @@ TEST_P(EnvoyQuicClientSessionTest, ConnectionClosePopulatesQuicVersionStats) { TEST_P(EnvoyQuicClientSessionTest, IncomingUnidirectionalReadStream) { quic::QuicStreamId stream_id = 1u; quic::QuicStreamFrame stream_frame(stream_id, false, 0, "aaa"); - envoy_quic_session_.OnStreamFrame(stream_frame); - EXPECT_FALSE(quic::test::QuicSessionPeer::IsStreamCreated(&envoy_quic_session_, stream_id)); + envoy_quic_session_->OnStreamFrame(stream_frame); + EXPECT_FALSE(quic::test::QuicSessionPeer::IsStreamCreated(envoy_quic_session_.get(), stream_id)); // IETF stream 3 is server initiated uni-directional stream. stream_id = 3u; auto payload = std::make_unique(8); @@ -371,17 +380,18 @@ TEST_P(EnvoyQuicClientSessionTest, IncomingUnidirectionalReadStream) { EXPECT_CALL(*quic_connection_, SendConnectionClosePacket(quic::QUIC_HTTP_RECEIVE_SERVER_PUSH, _, "Received server push stream")); quic::QuicStreamFrame stream_frame2(stream_id, false, 0, absl::string_view(payload.get(), 1)); - envoy_quic_session_.OnStreamFrame(stream_frame2); + envoy_quic_session_->OnStreamFrame(stream_frame2); } TEST_P(EnvoyQuicClientSessionTest, GetRttAndCwnd) { - EXPECT_GT(envoy_quic_session_.lastRoundTripTime().value(), std::chrono::microseconds(0)); + EXPECT_GT(envoy_quic_session_->lastRoundTripTime().value(), std::chrono::microseconds(0)); // Just make sure the CWND is non-zero. We don't want to make strong assertions on what the value // should be in this test, that is the job the congestion controllers' tests. - EXPECT_GT(envoy_quic_session_.congestionWindowInBytes().value(), 500); + EXPECT_GT(envoy_quic_session_->congestionWindowInBytes().value(), 500); - envoy_quic_session_.configureInitialCongestionWindow(8000000, std::chrono::microseconds(1000000)); - EXPECT_GT(envoy_quic_session_.congestionWindowInBytes().value(), + envoy_quic_session_->configureInitialCongestionWindow(8000000, + std::chrono::microseconds(1000000)); + EXPECT_GT(envoy_quic_session_->congestionWindowInBytes().value(), quic::kInitialCongestionWindow * quic::kDefaultTCPMSS); } @@ -463,7 +473,7 @@ TEST_P(EnvoyQuicClientSessionTest, StatelessResetOnProbingSocket) { // Trigger port migration. quic_connection_->OnPathDegradingDetected(); - EXPECT_TRUE(envoy_quic_session_.HasPendingPathValidation()); + EXPECT_TRUE(envoy_quic_session_->HasPendingPathValidation()); auto* path_validation_context = dynamic_cast( quic_connection_->GetPathValidationContext()); @@ -486,7 +496,7 @@ TEST_P(EnvoyQuicClientSessionTest, StatelessResetOnProbingSocket) { // fail the probing. EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::RemoteClose)) .Times(0); - while (envoy_quic_session_.HasPendingPathValidation()) { + while (envoy_quic_session_->HasPendingPathValidation()) { // Running event loop to receive the STATELESS_RESET and following socket reads shouldn't cause // crash. dispatcher_->run(Event::Dispatcher::RunType::NonBlock); @@ -494,5 +504,172 @@ TEST_P(EnvoyQuicClientSessionTest, StatelessResetOnProbingSocket) { EXPECT_EQ(self_addr_->asString(), quic_connection_->self_address().ToString()); } +class MockOsSysCallsImpl : public Api::OsSysCallsImpl { +public: + MOCK_METHOD(Api::SysCallSizeResult, recvmsg, (os_fd_t socket, msghdr* msg, int flags), + (override)); + MOCK_METHOD(Api::SysCallIntResult, recvmmsg, + (os_fd_t socket, struct mmsghdr* msgvec, unsigned int vlen, int flags, + struct timespec* timeout), + (override)); + MOCK_METHOD(bool, supportsUdpGro, (), (const)); +}; + +// Ensures that the Network::Utility::readFromSocket function uses GRO. +// Only Linux platforms support GRO. +TEST_P(EnvoyQuicClientSessionTest, UsesUdpGro) { + if (!Api::OsSysCallsSingleton::get().supportsUdpGro()) { + GTEST_SKIP() << "Platform doesn't support GRO."; + } + + NiceMock os_sys_calls; + TestThreadsafeSingletonInjector singleton_injector{&os_sys_calls}; + + // Have to connect the QUIC session, so that the socket is set up so we can do I/O on it. + envoy_quic_session_->connect(); + + std::string write_data = "abc"; + Buffer::RawSlice slice; + slice.mem_ = write_data.data(); + slice.len_ = write_data.length(); + + // Make sure the option for GRO is set on the socket. +// Windows doesn't have the GRO socket options. +#if !defined(WIN32) + int sock_opt; + socklen_t sock_len = sizeof(int); + EXPECT_EQ(0, quic_connection_->connectionSocket() + ->getSocketOption(SOL_UDP, UDP_GRO, &sock_opt, &sock_len) + .return_value_); + EXPECT_EQ(1, sock_opt); +#endif + + // GRO uses `recvmsg`, not `recvmmsg`. + EXPECT_CALL(os_sys_calls, supportsUdpGro()).WillRepeatedly(Return(true)); + EXPECT_CALL(os_sys_calls, recvmmsg(_, _, _, _, _)).Times(0); + EXPECT_CALL(os_sys_calls, recvmsg(_, _, _)) + .WillOnce( + Invoke([&](os_fd_t /*socket*/, msghdr* /*msg*/, int /*flags*/) -> Api::SysCallSizeResult { + dispatcher_->exit(); + // Return an error so IoSocketHandleImpl::recvmsg() exits early, instead of trying to + // use the msghdr that would normally have been populated by recvmsg but is not + // populated by this mock. + return {-1, SOCKET_ERROR_AGAIN}; + })); + + peer_socket_->ioHandle().sendmsg(&slice, 1, 0, peer_addr_->ip(), *self_addr_); + + EXPECT_LOG_CONTAINS("trace", "starting gro recvmsg with max", + dispatcher_->run(Event::Dispatcher::RunType::RunUntilExit)); +} + +class EnvoyQuicClientSessionDisallowMmsgTest : public EnvoyQuicClientSessionTest { +public: + void SetUp() override { + EXPECT_CALL(os_sys_calls_, supportsUdpGro()).WillRepeatedly(Return(false)); + EnvoyQuicClientSessionTest::SetUp(); + } + +protected: + NiceMock os_sys_calls_; + +private: + TestThreadsafeSingletonInjector singleton_injector_{&os_sys_calls_}; +}; + +INSTANTIATE_TEST_SUITE_P(EnvoyQuicClientSessionDisallowMmsgTests, + EnvoyQuicClientSessionDisallowMmsgTest, + testing::ValuesIn(quic::CurrentSupportedHttp3Versions())); + +// Ensures that the Network::Utility::readFromSocket function uses `recvmsg` for client QUIC +// connections when GRO is not supported. +TEST_P(EnvoyQuicClientSessionDisallowMmsgTest, UsesRecvMsgWhenNoGro) { + // Have to connect the QUIC session, so that the socket is set up so we can do I/O on it. + envoy_quic_session_->connect(); + + std::string write_data = "abc"; + Buffer::RawSlice slice; + slice.mem_ = write_data.data(); + slice.len_ = write_data.length(); + +// Windows doesn't have the GRO socket options. +#if !defined(WIN32) + // Make sure the option for GRO is *not* set on the socket. + int sock_opt; + socklen_t sock_len = sizeof(int); + EXPECT_EQ(0, quic_connection_->connectionSocket() + ->getSocketOption(SOL_UDP, UDP_GRO, &sock_opt, &sock_len) + .return_value_); + EXPECT_EQ(0, sock_opt); +#endif + + // Uses `recvmsg`, not `recvmmsg`. + EXPECT_CALL(os_sys_calls_, recvmmsg(_, _, _, _, _)).Times(0); + EXPECT_CALL(os_sys_calls_, recvmsg(_, _, _)) + .WillOnce( + Invoke([&](os_fd_t /*socket*/, msghdr* /*msg*/, int /*flags*/) -> Api::SysCallSizeResult { + dispatcher_->exit(); + // Return an error so IoSocketHandleImpl::recvmsg() exits early, instead of trying to + // use the msghdr that would normally have been populated by recvmsg but is not + // populated by this mock. + return {-1, SOCKET_ERROR_AGAIN}; + })); + + peer_socket_->ioHandle().sendmsg(&slice, 1, 0, peer_addr_->ip(), *self_addr_); + + EXPECT_LOG_CONTAINS("trace", "starting recvmsg with max", + dispatcher_->run(Event::Dispatcher::RunType::RunUntilExit)); +} + +class EnvoyQuicClientSessionAllowMmsgTest : public EnvoyQuicClientSessionTest { +public: + void SetUp() override { + EXPECT_CALL(os_sys_calls_, supportsUdpGro()).WillRepeatedly(Return(false)); + + scoped_runtime_.mergeValues( + {{"envoy.reloadable_features.disallow_quic_client_udp_mmsg", "false"}}); + EnvoyQuicClientSessionTest::SetUp(); + } + +protected: + NiceMock os_sys_calls_; + +private: + TestScopedRuntime scoped_runtime_; + TestThreadsafeSingletonInjector singleton_injector_{&os_sys_calls_}; +}; + +INSTANTIATE_TEST_SUITE_P(EnvoyQuicClientSessionAllowMmsgTests, EnvoyQuicClientSessionAllowMmsgTest, + testing::ValuesIn(quic::CurrentSupportedHttp3Versions())); + +TEST_P(EnvoyQuicClientSessionAllowMmsgTest, UsesRecvMmsgWhenNoGroAndMmsgAllowed) { + if (!Api::OsSysCallsSingleton::get().supportsMmsg()) { + GTEST_SKIP() << "Platform doesn't support recvmmsg."; + } + + // Have to connect the QUIC session, so that the socket is set up so we can do I/O on it. + envoy_quic_session_->connect(); + + std::string write_data = "abc"; + Buffer::RawSlice slice; + slice.mem_ = write_data.data(); + slice.len_ = write_data.length(); + + // Make sure recvmmsg is used when GRO isn't supported. + EXPECT_CALL(os_sys_calls_, supportsUdpGro()).WillRepeatedly(Return(false)); + EXPECT_CALL(os_sys_calls_, recvmsg(_, _, _)).Times(0); + EXPECT_CALL(os_sys_calls_, recvmmsg(_, _, _, _, _)) + .WillRepeatedly(Invoke([&](os_fd_t, struct mmsghdr*, unsigned int, int, + struct timespec*) -> Api::SysCallIntResult { + dispatcher_->exit(); + return {0, SOCKET_ERROR_AGAIN}; + })); + + peer_socket_->ioHandle().sendmsg(&slice, 1, 0, peer_addr_->ip(), *self_addr_); + + EXPECT_LOG_CONTAINS("trace", "starting recvmmsg with packets", + dispatcher_->run(Event::Dispatcher::RunType::RunUntilExit)); +} + } // namespace Quic } // namespace Envoy diff --git a/test/common/quic/envoy_quic_client_stream_test.cc b/test/common/quic/envoy_quic_client_stream_test.cc index b579c6962130..4ba077ead61d 100644 --- a/test/common/quic/envoy_quic_client_stream_test.cc +++ b/test/common/quic/envoy_quic_client_stream_test.cc @@ -46,7 +46,8 @@ class EnvoyQuicClientStreamTest : public testing::Test { quic_connection_(new MockEnvoyQuicClientConnection( quic::test::TestConnectionId(), connection_helper_, alarm_factory_, &writer_, /*owns_writer=*/false, {quic_version_}, *dispatcher_, - createConnectionSocket(peer_addr_, self_addr_, nullptr), connection_id_generator_)), + createConnectionSocket(peer_addr_, self_addr_, nullptr, /*prefer_gro=*/true), + connection_id_generator_)), quic_session_(quic_config_, {quic_version_}, std::unique_ptr(quic_connection_), *dispatcher_, quic_config_.GetInitialStreamFlowControlWindowToSend() * 2, diff --git a/test/common/quic/test_utils.h b/test/common/quic/test_utils.h index 0a1a5938eab4..fdf3a1d0e587 100644 --- a/test/common/quic/test_utils.h +++ b/test/common/quic/test_utils.h @@ -75,7 +75,7 @@ class MockEnvoyQuicClientConnection : public EnvoyQuicClientConnection { quic::ConnectionIdGeneratorInterface& generator) : EnvoyQuicClientConnection(server_connection_id, helper, alarm_factory, writer, owns_writer, supported_versions, dispatcher, std::move(connection_socket), - generator) {} + generator, /*prefer_gro=*/true) {} MOCK_METHOD(quic::MessageStatus, SendMessage, (quic::QuicMessageId, absl::Span, bool)); diff --git a/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc b/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc index 02f4dfff74b0..5cff9cebd328 100644 --- a/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc +++ b/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc @@ -148,7 +148,7 @@ class UdpProxyFilterTest : public UdpProxyFilterBase { if (parent_.expect_gro_) { EXPECT_CALL(*socket_->io_handle_, supportsUdpGro()); } - EXPECT_CALL(*socket_->io_handle_, supportsMmsg()).Times(2u); + EXPECT_CALL(*socket_->io_handle_, supportsMmsg()).Times(1u); // Return the datagram. EXPECT_CALL(*socket_->io_handle_, recvmsg(_, 1, _, _)) .WillOnce( @@ -179,7 +179,6 @@ class UdpProxyFilterTest : public UdpProxyFilterBase { } })); // Return an EAGAIN result. - EXPECT_CALL(*socket_->io_handle_, supportsMmsg()); EXPECT_CALL(*socket_->io_handle_, recvmsg(_, 1, _, _)) .WillOnce(Return(ByMove( Api::IoCallUint64Result(0, Network::IoSocketError::getIoSocketEagainError())))); diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index af3420839050..e18042946688 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -73,7 +73,8 @@ class TestEnvoyQuicClientConnection : public EnvoyQuicClientConnection { bool validation_failure_on_path_response, quic::ConnectionIdGeneratorInterface& generator) : EnvoyQuicClientConnection(server_connection_id, initial_peer_address, helper, alarm_factory, - supported_versions, local_addr, dispatcher, options, generator), + supported_versions, local_addr, dispatcher, options, generator, + /*prefer_gro=*/true), dispatcher_(dispatcher), validation_failure_on_path_response_(validation_failure_on_path_response) {} @@ -837,7 +838,7 @@ TEST_P(QuicHttpIntegrationTest, PortMigration) { Network::Address::InstanceConstSharedPtr local_addr = Network::Test::getCanonicalLoopbackAddress(version_); quic_connection_->switchConnectionSocket( - createConnectionSocket(server_addr_, local_addr, nullptr)); + createConnectionSocket(server_addr_, local_addr, nullptr, /*prefer_gro=*/true)); EXPECT_NE(old_port, local_addr->ip()->port()); // Send the rest data. codec_client_->sendData(*request_encoder_, 1024u, true); @@ -866,7 +867,7 @@ TEST_P(QuicHttpIntegrationTest, PortMigration) { auto options = std::make_shared(); options->push_back(option); quic_connection_->switchConnectionSocket( - createConnectionSocket(server_addr_, local_addr, options)); + createConnectionSocket(server_addr_, local_addr, options, /*prefer_gro=*/true)); EXPECT_TRUE(codec_client_->disconnected()); cleanupUpstreamAndDownstream(); } diff --git a/test/test_common/network_utility.cc b/test/test_common/network_utility.cc index c873089811fb..40c0abfa0523 100644 --- a/test/test_common/network_utility.cc +++ b/test/test_common/network_utility.cc @@ -226,8 +226,13 @@ Api::IoCallUint64Result readFromSocket(IoHandle& handle, const Address::Instance std::list& data, uint64_t max_rx_datagram_size) { SyncPacketProcessor processor(data, max_rx_datagram_size); + UdpRecvMsgMethod recv_msg_method = UdpRecvMsgMethod::RecvMsg; + if (Api::OsSysCallsSingleton::get().supportsMmsg()) { + recv_msg_method = UdpRecvMsgMethod::RecvMmsg; + } return Network::Utility::readFromSocket(handle, local_address, processor, - MonotonicTime(std::chrono::seconds(0)), false, nullptr); + MonotonicTime(std::chrono::seconds(0)), recv_msg_method, + nullptr); } UdpSyncPeer::UdpSyncPeer(Network::Address::IpVersion version, uint64_t max_rx_datagram_size) From 3c84a245c056ae5bd74309d766091f33d9190071 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 13 Mar 2024 09:37:42 -0400 Subject: [PATCH 014/124] tooling: hopefully improving mention (#32871) Signed-off-by: Alyssa Wilk --- tools/repo/notify.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tools/repo/notify.py b/tools/repo/notify.py index d62bcb28a02b..275666b358d7 100644 --- a/tools/repo/notify.py +++ b/tools/repo/notify.py @@ -34,24 +34,24 @@ SLACK_EXPORT_URL = "https://api.slack.com/apps/A023NPQQ33K/oauth?" OPSGENIE_TO_SLACK = { - 'Adi': 'Adi Peleg', - 'Alyssa': 'Alyssa Wilk', - 'Greg': 'Greg Greenway', - 'Harvey': 'htuch', - 'Joshua': 'jmarantz', - 'Kevin': 'kbaichoo', - 'Keith': 'Keith Smiley', - 'kuat': 'kuat', - 'Lizan': 'Lizan Zhou', - 'Matt': 'mklein', - 'Kateryna': 'nezdolik', - 'phlax': 'phlax', - 'Raven': 'ravenblackx', - 'Ryan': 'Ryan Hamilton', - 'Hejie': 'soulxu', - 'Baiping': 'wbpcode', - 'Yan': 'Yan Avlasov', - 'Stephan': 'stephan', + 'Adi': 'UT17EMMTP', + 'Alyssa': 'U78RP48V9', + 'Greg': 'U78MBV869', + 'Harvey': 'U78E7055Z', + 'Joshua': 'U80HPLBPG', + 'Kevin': 'U016ZPU8KBK', + 'Keith': 'UGS5P90CF', + 'kuat': 'U7KTRAA8M', + 'Lizan': 'U79E51EQ6', + 'Matt': 'U5CALEVSL', + 'Kateryna': 'UDYUWRL13', + 'phlax': 'U017PLM0GNQ', + 'Raven': 'U02MJHFEX35', + 'Ryan': 'U01SW3JC8GP', + 'Hejie': 'U01GNQ3B8AY', + 'Baiping': 'U017KF5C0Q6', + 'Yan': 'UJHLR5KFS', + 'Stephan': 'U78J72Q82', } MAINTAINERS = { @@ -287,7 +287,7 @@ async def post_to_oncall(self): await self.send_message(channel='#envoy-maintainer-oncall', text=(f"{oncall}")) await self.send_message(channel='#general', text=(f"{oncall}")) await self.send_message( - channel='#envoy-maintainer-oncall', text=(f"Oncall now @{oncall_handle}")) + channel='#envoy-maintainer-oncall', text=(f"Oncall now <@{oncall_handle}>")) await self.send_message( channel='#envoy-maintainer-oncall', text=(f"*'Unassigned' PRs* (PRs with no maintainer assigned)\n{unassigned}")) From 4e9e5bb5b6bb0a2786d34f650907a614c555cc6b Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Wed, 13 Mar 2024 15:01:00 -0700 Subject: [PATCH 015/124] mac: fix PosixThread test (#32857) Signed-off-by: Greg Greenway --- source/common/common/posix/thread_impl.cc | 1 + test/common/common/thread_test.cc | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/common/common/posix/thread_impl.cc b/source/common/common/posix/thread_impl.cc index e89fb16382c3..dc90d1fd2c56 100644 --- a/source/common/common/posix/thread_impl.cc +++ b/source/common/common/posix/thread_impl.cc @@ -99,6 +99,7 @@ void PosixThread::join() { bool PosixThread::joinable() const { return !joined_; } ThreadId PosixThread::pthreadId() const { + ASSERT(!joined_); #if defined(__linux__) return ThreadId(static_cast(thread_handle_->handle())); #elif defined(__APPLE__) diff --git a/test/common/common/thread_test.cc b/test/common/common/thread_test.cc index 8c8a28f7d3d9..7ac215195ed5 100644 --- a/test/common/common/thread_test.cc +++ b/test/common/common/thread_test.cc @@ -257,10 +257,11 @@ TEST(PosixThreadTest, PThreadId) { auto thread = thread_factory->createThread([&]() { thread_id = thread_factory->currentPthreadId(); }, /* options= */ absl::nullopt, /* crash_on_failure= */ false); + auto threadId = thread->pthreadId(); thread->join(); - EXPECT_EQ(thread->pthreadId(), thread_id); - EXPECT_NE(thread->pthreadId(), thread_factory->currentThreadId()); + EXPECT_EQ(threadId, thread_id); + EXPECT_NE(threadId, thread_factory->currentThreadId()); } TEST(PosixThreadTest, Joinable) { From e2f3f5748969d9925eaf1b12406c51e4d645b6dd Mon Sep 17 00:00:00 2001 From: botengyao Date: Wed, 13 Mar 2024 18:13:14 -0400 Subject: [PATCH 016/124] admin: fix the EDS_STATUS_DRAINING map (#32801) Signed-off-by: Boteng Yao --- source/server/admin/clusters_handler.cc | 4 +- source/server/admin/clusters_handler.h | 11 +++ test/server/admin/BUILD | 2 + test/server/admin/clusters_handler_test.cc | 95 ++++++++++++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index 5b05de26a27c..abf3f63430c6 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -100,7 +100,9 @@ void setHealthFlag(Upstream::Host::HealthFlag flag, const Upstream::Host& host, host.healthFlagGet(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT)); break; case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: - health_status.set_eds_health_status(envoy::config::core::v3::DRAINING); + if (host.healthFlagGet(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING)) { + health_status.set_eds_health_status(envoy::config::core::v3::DRAINING); + } break; } } diff --git a/source/server/admin/clusters_handler.h b/source/server/admin/clusters_handler.h index c03623edb36b..14bc570dedb6 100644 --- a/source/server/admin/clusters_handler.h +++ b/source/server/admin/clusters_handler.h @@ -1,5 +1,6 @@ #pragma once +#include "envoy/admin/v3/clusters.pb.h" #include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" #include "envoy/http/header_map.h" @@ -13,6 +14,16 @@ namespace Envoy { namespace Server { +/** + * A utility to set admin health status from a specified host and health flag. + * + * @param healthFlag The specific health status to be checked. + * @param host The target host. + * @param health_status A proto reference representing the admin health status. + */ +void setHealthFlag(Upstream::Host::HealthFlag flag, const Upstream::Host& host, + envoy::admin::v3::HostHealthStatus& health_status); + class ClustersHandler : public HandlerContextBase { public: diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 8c39988f9557..310b5f87b4b8 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -228,6 +228,8 @@ envoy_cc_test( srcs = envoy_select_admin_functionality(["clusters_handler_test.cc"]), deps = [ ":admin_instance_lib", + "//test/common/upstream:utility_lib", + "//test/mocks/event:event_mocks", "@envoy_api//envoy/admin/v3:pkg_cc_proto", ], ) diff --git a/test/server/admin/clusters_handler_test.cc b/test/server/admin/clusters_handler_test.cc index 389e713d1e19..8d5f090ae142 100644 --- a/test/server/admin/clusters_handler_test.cc +++ b/test/server/admin/clusters_handler_test.cc @@ -1,5 +1,7 @@ #include "envoy/admin/v3/clusters.pb.h" +#include "test/common/upstream/utility.h" +#include "test/mocks/event/mocks.h" #include "test/server/admin/admin_instance.h" using testing::Return; @@ -243,5 +245,98 @@ fake_cluster::1.2.3.4:80::local_origin_success_rate::93.2 EXPECT_EQ(expected_text, response2.toString()); } +TEST_P(AdminInstanceTest, TestSetHealthFlag) { + std::shared_ptr cluster{new NiceMock()}; + Event::MockDispatcher dispatcher; + auto host = Upstream::makeTestHost(cluster, "tcp://127.0.0.1:9000", dispatcher.timeSource()); + envoy::admin::v3::HostHealthStatus health_status; + + // FAILED_ACTIVE_HC + host->healthFlagSet(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC, *host, health_status); + EXPECT_TRUE(health_status.failed_active_health_check()); + + host->healthFlagClear(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC, *host, health_status); + EXPECT_FALSE(health_status.failed_active_health_check()); + + // FAILED_OUTLIER_CHECK + host->healthFlagSet(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK); + setHealthFlag(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK, *host, health_status); + EXPECT_TRUE(health_status.failed_outlier_check()); + + host->healthFlagClear(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK); + setHealthFlag(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK, *host, health_status); + EXPECT_FALSE(health_status.failed_outlier_check()); + + // FAILED_EDS_HEALTH + host->healthFlagSet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH); + setHealthFlag(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH, *host, health_status); + EXPECT_EQ(health_status.eds_health_status(), envoy::config::core::v3::UNHEALTHY); + + host->healthFlagClear(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH); + + // DEGRADED_EDS_HEALTH + host->healthFlagSet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH); + setHealthFlag(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH, *host, health_status); + EXPECT_EQ(health_status.eds_health_status(), envoy::config::core::v3::DEGRADED); + + // EDS_STATUS_DRAINING + host->healthFlagSet(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING); + setHealthFlag(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING, *host, health_status); + EXPECT_EQ(health_status.eds_health_status(), envoy::config::core::v3::DRAINING); + + health_status.Clear(); + + host->healthFlagClear(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING); + setHealthFlag(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING, *host, health_status); + EXPECT_FALSE(health_status.eds_health_status()); + + // DEGRADED_ACTIVE_HC + host->healthFlagSet(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC, *host, health_status); + EXPECT_TRUE(health_status.failed_active_degraded_check()); + + host->healthFlagClear(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC, *host, health_status); + EXPECT_FALSE(health_status.failed_active_degraded_check()); + + // PENDING_DYNAMIC_REMOVAL + host->healthFlagSet(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL); + setHealthFlag(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL, *host, health_status); + EXPECT_TRUE(health_status.pending_dynamic_removal()); + + host->healthFlagClear(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL); + setHealthFlag(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL, *host, health_status); + EXPECT_FALSE(health_status.pending_dynamic_removal()); + + // PENDING_ACTIVE_HC + host->healthFlagSet(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC, *host, health_status); + EXPECT_TRUE(health_status.pending_active_hc()); + + host->healthFlagClear(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC, *host, health_status); + EXPECT_FALSE(health_status.pending_active_hc()); + + // EXCLUDED_VIA_IMMEDIATE_HC_FAIL + host->healthFlagSet(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL); + setHealthFlag(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL, *host, health_status); + EXPECT_TRUE(health_status.excluded_via_immediate_hc_fail()); + + host->healthFlagClear(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL); + setHealthFlag(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL, *host, health_status); + EXPECT_FALSE(health_status.excluded_via_immediate_hc_fail()); + + // ACTIVE_HC_TIMEOUT + host->healthFlagSet(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT); + setHealthFlag(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT, *host, health_status); + EXPECT_TRUE(health_status.active_hc_timeout()); + + host->healthFlagClear(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT); + setHealthFlag(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT, *host, health_status); + EXPECT_FALSE(health_status.active_hc_timeout()); +} + } // namespace Server } // namespace Envoy From 5ff5170349fff873797fa0500de89342f9204fd8 Mon Sep 17 00:00:00 2001 From: "dependency-envoy[bot]" <148525496+dependency-envoy[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:05:32 +0000 Subject: [PATCH 017/124] deps: Bump `com_github_axboe_liburing` -> 2.5 (#32878) Signed-off-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> Co-authored-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> --- bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index c7eb93471255..1a1730710911 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -190,12 +190,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "liburing", project_desc = "C helpers to set up and tear down io_uring instances", project_url = "https://github.com/axboe/liburing", - version = "2.3", - sha256 = "60b367dbdc6f2b0418a6e0cd203ee0049d9d629a36706fcf91dfb9428bae23c8", + version = "2.5", + sha256 = "456f5f882165630f0dc7b75e8fd53bd01a955d5d4720729b4323097e6e9f2a98", strip_prefix = "liburing-liburing-{version}", urls = ["https://github.com/axboe/liburing/archive/liburing-{version}.tar.gz"], use_category = ["dataplane_core", "controlplane"], - release_date = "2022-10-26", + release_date = "2023-11-29", cpe = "N/A", ), # This dependency is built only when performance tracing is enabled with the From b1808bbc5c118c676af393e6542869d7a1bc1f91 Mon Sep 17 00:00:00 2001 From: birenroy Date: Wed, 13 Mar 2024 19:47:07 -0400 Subject: [PATCH 018/124] deps: Updates QUICHE from 6f0b1d204 to cc9724b90 (#32874) * Update QUICHE from 6f0b1d204 to cc9724b90 https://github.com/google/quiche/compare/6f0b1d204..cc9724b90 ``` $ git log 6f0b1d204..cc9724b90 --date=short --no-merges --format="%ad %al %s" 2024-03-13 vasilvv Fix standalone QUICHE build. 2024-03-12 wub Add lifetime tracking debug utility to QUICHE. 2024-03-12 danzh Change ClientProofSource::GetCertAndKey() to return shared_ptr instead of a raw pointer of CertAndKey. 2024-03-12 birenroy Adds a way to notify the `Http2Adapter` when the underlying codec decides not to send a frame. 2024-03-12 martinduke SUBSCRIBE_ERROR after OBJECT is a protocol violation. 2024-03-12 awillia Replace deprecated version of absl::HexStringToBytes in quic/core/crypto tests 2024-03-12 martinduke Fix MoqtSessionTests with unitialized peer_role_. 2024-03-11 martinduke Enforce rules about ROLE parameter. 2024-03-11 martinduke Make MoqtForwardingPreference a track property. 2024-03-11 elburrito BlindSignAuth: Refactoring to support future Android BlindSignMessageInterface impl. 2024-03-11 awillia Replace deprecated version of absl::HexStringToBytes in quic/core/http tests 2024-03-11 awillia Replace deprecated version of absl::HexStringToBytes in quic/core/qpack tests 2024-03-08 quiche-dev Retry Poll when system clock is faster than QuicClock 2024-03-08 martinduke New code path does not initialize subscribe_id. 2024-03-08 martinduke Send and handle MoQT datagrams when required. 2024-03-06 birenroy Disables Cookie header field crumbling in OgHttp2Session by default. 2024-03-06 awillia No public description 2024-03-06 vasilvv No public description 2024-03-06 birenroy Adds the ability to skip `Cookie` crumbling in `HpackEncoder`. 2024-03-06 rch Clean up some unused QUICHE flag imports in third_party/http2. 2024-03-06 bnc Remove unused `module` argument from flag implementation. ``` Signed-off-by: Biren Roy --- bazel/repository_locations.bzl | 6 ++-- .../common/quic/platform/quiche_flags_impl.h | 9 +++-- .../quic/platform/quiche_stack_trace_impl.h | 33 +++++++++++++++++++ test/common/runtime/runtime_impl_test.cc | 2 +- 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 1a1730710911..1ecfa2a69763 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -1210,12 +1210,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "QUICHE", project_desc = "QUICHE (QUIC, HTTP/2, Etc) is Google‘s implementation of QUIC and related protocols", project_url = "https://github.com/google/quiche", - version = "6f0b1d204da73155e21c683650dbebe05a36d781", - sha256 = "9023e5fa8c830543c04124dd994f3f8c60d8377e82e116cf1145cd846e686f90", + version = "cc9724b902e0e3392434ea3f985a50c27d99fed4", + sha256 = "adc449ecd70248777565f9a5797f6443728d8edf4d23f6d70f5ea4a10ddb1745", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["controlplane", "dataplane_core"], - release_date = "2024-03-05", + release_date = "2024-03-13", cpe = "N/A", license = "BSD-3-Clause", license_url = "https://github.com/google/quiche/blob/{version}/LICENSE", diff --git a/source/common/quic/platform/quiche_flags_impl.h b/source/common/quic/platform/quiche_flags_impl.h index 77c31aea9432..74d9570f4f4a 100644 --- a/source/common/quic/platform/quiche_flags_impl.h +++ b/source/common/quic/platform/quiche_flags_impl.h @@ -57,15 +57,14 @@ namespace quiche { #define SetQuicheFlagImpl(flag, value) absl::SetFlag(&FLAGS_envoy_##flag, value) -#define GetQuicheReloadableFlagImpl(module, flag) \ - absl::GetFlag(FLAGS_envoy_quic_reloadable_flag_##flag) +#define GetQuicheReloadableFlagImpl(flag) absl::GetFlag(FLAGS_envoy_quic_reloadable_flag_##flag) -#define SetQuicheReloadableFlagImpl(module, flag, value) \ +#define SetQuicheReloadableFlagImpl(flag, value) \ absl::SetFlag(&FLAGS_envoy_quic_reloadable_flag_##flag, value) -#define GetQuicheRestartFlagImpl(module, flag) absl::GetFlag(FLAGS_envoy_quic_restart_flag_##flag) +#define GetQuicheRestartFlagImpl(flag) absl::GetFlag(FLAGS_envoy_quic_restart_flag_##flag) -#define SetQuicheRestartFlagImpl(module, flag, value) \ +#define SetQuicheRestartFlagImpl(flag, value) \ absl::SetFlag(&FLAGS_envoy_quic_restart_flag_##flag, value) } // namespace quiche diff --git a/source/common/quic/platform/quiche_stack_trace_impl.h b/source/common/quic/platform/quiche_stack_trace_impl.h index f7b802127185..e50f52f4471f 100644 --- a/source/common/quic/platform/quiche_stack_trace_impl.h +++ b/source/common/quic/platform/quiche_stack_trace_impl.h @@ -8,11 +8,44 @@ #include #include +#include #include "source/server/backtrace.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/symbolize.h" + namespace quiche { +// NOLINTNEXTLINE(readability-identifier-naming) +inline std::vector CurrentStackTraceImpl() { + constexpr int kMaxStackSize = 64; + std::vector stacktrace(kMaxStackSize, nullptr); + const int depth = absl::GetStackTrace(stacktrace.data(), stacktrace.size(), + /*skip_count=*/0); + if (depth <= 0) { + return {}; + } + stacktrace.resize(depth); + return stacktrace; +} + +// NOLINTNEXTLINE(readability-identifier-naming) +inline std::string SymbolizeStackTraceImpl(absl::Span stacktrace) { + std::ostringstream os; + for (size_t i = 0; i < stacktrace.size(); ++i) { + void* const addr = stacktrace[i]; + char out[1024]; + const bool success = absl::Symbolize(addr, out, sizeof(out)); + if (success) { + os << "#" << i << " " << out << " [" << addr << "]\n"; + } else { + os << "#" << i << " [" << addr << "]\n"; + } + } + return os.str(); +} + // NOLINTNEXTLINE(readability-identifier-naming) inline std::string QuicheStackTraceImpl() { Envoy::BackwardsTrace t; diff --git a/test/common/runtime/runtime_impl_test.cc b/test/common/runtime/runtime_impl_test.cc index 88989f71947e..b3bfb11fa75d 100644 --- a/test/common/runtime/runtime_impl_test.cc +++ b/test/common/runtime/runtime_impl_test.cc @@ -551,7 +551,7 @@ TEST_F(StaticLoaderImplTest, QuicheReloadableFlags) { EXPECT_TRUE(GetQuicReloadableFlag(quic_testonly_default_true)); EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_false)); - SetQuicheReloadableFlag(spdy, quic_testonly_default_true, false); + SetQuicheReloadableFlag(quic_testonly_default_true, false); EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_true)); EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_false)); From 9c379377598f32b51d67d020e2ed5b98dc913ee4 Mon Sep 17 00:00:00 2001 From: RenjieTang Date: Wed, 13 Mar 2024 16:57:04 -0700 Subject: [PATCH 019/124] [mobile] Allow Cronvoy to configure QUIC port migration (#32890) Signed-off-by: Renjie Tang --- .../envoymobile/engine/EnvoyConfiguration.java | 9 ++++++--- .../envoyproxy/envoymobile/engine/JniLibrary.java | 2 +- .../chromium/net/impl/CronvoyEngineBuilderImpl.java | 8 ++++++++ .../net/impl/NativeCronvoyEngineBuilderImpl.java | 11 ++++++----- mobile/library/jni/jni_impl.cc | 13 ++++++++----- .../io/envoyproxy/envoymobile/EngineBuilder.kt | 13 +++++++++++++ .../envoymobile/engine/EnvoyConfigurationTest.kt | 3 +++ 7 files changed, 45 insertions(+), 14 deletions(-) diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java index 207257faa559..84c0d1107909 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java @@ -42,6 +42,7 @@ public enum TrustChainVerification { public final List quicCanonicalSuffixes; public final Boolean enableGzipDecompression; public final Boolean enableBrotliDecompression; + public final Boolean enablePortMigration; public final Boolean enableSocketTagging; public final Boolean enableInterfaceBinding; public final Integer h2ConnectionKeepaliveIdleIntervalMilliseconds; @@ -110,6 +111,7 @@ public enum TrustChainVerification { * decompression. * @param enableBrotliDecompression whether to enable response brotli * decompression. + * @param enablePortMigration whether to enable quic port migration. * @param enableSocketTagging whether to enable socket tagging. * @param enableInterfaceBinding whether to allow interface binding. * @param h2ConnectionKeepaliveIdleIntervalMilliseconds rate in milliseconds seconds to send h2 @@ -157,7 +159,7 @@ public EnvoyConfiguration( boolean enableDrainPostDnsRefresh, boolean enableHttp3, String http3ConnectionOptions, String http3ClientConnectionOptions, Map quicHints, List quicCanonicalSuffixes, boolean enableGzipDecompression, - boolean enableBrotliDecompression, boolean enableSocketTagging, + boolean enableBrotliDecompression, boolean enablePortMigration, boolean enableSocketTagging, boolean enableInterfaceBinding, int h2ConnectionKeepaliveIdleIntervalMilliseconds, int h2ConnectionKeepaliveTimeoutSeconds, int maxConnectionsPerHost, int streamIdleTimeoutSeconds, int perTryIdleTimeoutSeconds, String appVersion, String appId, @@ -192,6 +194,7 @@ public EnvoyConfiguration( this.quicCanonicalSuffixes = quicCanonicalSuffixes; this.enableGzipDecompression = enableGzipDecompression; this.enableBrotliDecompression = enableBrotliDecompression; + this.enablePortMigration = enablePortMigration; this.enableSocketTagging = enableSocketTagging; this.enableInterfaceBinding = enableInterfaceBinding; this.h2ConnectionKeepaliveIdleIntervalMilliseconds = @@ -258,8 +261,8 @@ public long createBootstrap() { dnsFailureRefreshSecondsMax, dnsQueryTimeoutSeconds, dnsMinRefreshSeconds, dnsPreresolve, enableDNSCache, dnsCacheSaveIntervalSeconds, enableDrainPostDnsRefresh, enableHttp3, http3ConnectionOptions, http3ClientConnectionOptions, quicHints, quicSuffixes, - enableGzipDecompression, enableBrotliDecompression, enableSocketTagging, - enableInterfaceBinding, h2ConnectionKeepaliveIdleIntervalMilliseconds, + enableGzipDecompression, enableBrotliDecompression, enablePortMigration, + enableSocketTagging, enableInterfaceBinding, h2ConnectionKeepaliveIdleIntervalMilliseconds, h2ConnectionKeepaliveTimeoutSeconds, maxConnectionsPerHost, streamIdleTimeoutSeconds, perTryIdleTimeoutSeconds, appVersion, appId, enforceTrustChainVerification, filterChain, enablePlatformCertificatesValidation, runtimeGuards, rtdsResourceName, rtdsTimeoutSeconds, diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java index aa2b82c8e4ed..ea8a928591fe 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java @@ -301,7 +301,7 @@ public static native long createBootstrap( boolean enableDrainPostDnsRefresh, boolean enableHttp3, String http3ConnectionOptions, String http3ClientConnectionOptions, byte[][] quicHints, byte[][] quicCanonicalSuffixes, boolean enableGzipDecompression, boolean enableBrotliDecompression, - boolean enableSocketTagging, boolean enableInterfaceBinding, + boolean enablePortMigration, boolean enableSocketTagging, boolean enableInterfaceBinding, long h2ConnectionKeepaliveIdleIntervalMilliseconds, long h2ConnectionKeepaliveTimeoutSeconds, long maxConnectionsPerHost, long streamIdleTimeoutSeconds, long perTryIdleTimeoutSeconds, String appVersion, String appId, boolean trustChainVerification, byte[][] filterChain, diff --git a/mobile/library/java/org/chromium/net/impl/CronvoyEngineBuilderImpl.java b/mobile/library/java/org/chromium/net/impl/CronvoyEngineBuilderImpl.java index 300050049da7..bbd867c55b3e 100644 --- a/mobile/library/java/org/chromium/net/impl/CronvoyEngineBuilderImpl.java +++ b/mobile/library/java/org/chromium/net/impl/CronvoyEngineBuilderImpl.java @@ -62,6 +62,7 @@ final static class Pkp { private String mQuicClientConnectionOptions = ""; private boolean mHttp2Enabled; private boolean mBrotiEnabled; + private boolean mPortMigrationEnabled; private boolean mDisableCache; private int mHttpCacheMode; private long mHttpCacheMaxSize; @@ -239,6 +240,13 @@ public CronvoyEngineBuilderImpl addQuicCanonicalSuffix(String suffix) { List quicCanonicalSuffixes() { return mQuicCanonicalSuffixes; } + public CronvoyEngineBuilderImpl enablePortMigration(boolean enablePortMigration) { + mPortMigrationEnabled = enablePortMigration; + return this; + } + + boolean portMigrationEnabled() { return mPortMigrationEnabled; } + @Override public CronvoyEngineBuilderImpl addPublicKeyPins(String hostName, Set pinsSha256, boolean includeSubdomains, Date expirationDate) { diff --git a/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java b/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java index 5236472c7622..6ba9dcf6e90a 100644 --- a/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java +++ b/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java @@ -136,11 +136,12 @@ private EnvoyConfiguration createEnvoyConfiguration() { mDnsPreresolveHostnames, mEnableDNSCache, mDnsCacheSaveIntervalSeconds, mEnableDrainPostDnsRefresh, quicEnabled(), quicConnectionOptions(), quicClientConnectionOptions(), quicHints(), quicCanonicalSuffixes(), - mEnableGzipDecompression, brotliEnabled(), mEnableSocketTag, mEnableInterfaceBinding, - mH2ConnectionKeepaliveIdleIntervalMilliseconds, mH2ConnectionKeepaliveTimeoutSeconds, - mMaxConnectionsPerHost, mStreamIdleTimeoutSeconds, mPerTryIdleTimeoutSeconds, mAppVersion, - mAppId, mTrustChainVerification, nativeFilterChain, platformFilterChain, stringAccessors, - keyValueStores, runtimeGuards, mEnablePlatformCertificatesValidation, + mEnableGzipDecompression, brotliEnabled(), portMigrationEnabled(), mEnableSocketTag, + mEnableInterfaceBinding, mH2ConnectionKeepaliveIdleIntervalMilliseconds, + mH2ConnectionKeepaliveTimeoutSeconds, mMaxConnectionsPerHost, mStreamIdleTimeoutSeconds, + mPerTryIdleTimeoutSeconds, mAppVersion, mAppId, mTrustChainVerification, nativeFilterChain, + platformFilterChain, stringAccessors, keyValueStores, runtimeGuards, + mEnablePlatformCertificatesValidation, /*rtdsResourceName=*/"", /*rtdsTimeoutSeconds=*/0, /*xdsAddress=*/"", /*xdsPort=*/0, /*xdsGrpcInitialMetadata=*/Collections.emptyMap(), /*xdsSslRootCerts=*/"", mNodeId, mNodeRegion, mNodeZone, mNodeSubZone, diff --git a/mobile/library/jni/jni_impl.cc b/mobile/library/jni/jni_impl.cc index 3146f9271002..78dd725fd581 100644 --- a/mobile/library/jni/jni_impl.cc +++ b/mobile/library/jni/jni_impl.cc @@ -1173,7 +1173,8 @@ void configureBuilder(Envoy::JNI::JniHelper& jni_helper, jlong connect_timeout_s jstring http3_connection_options, jstring http3_client_connection_options, jobjectArray quic_hints, jobjectArray quic_canonical_suffixes, jboolean enable_gzip_decompression, jboolean enable_brotli_decompression, - jboolean enable_socket_tagging, jboolean enable_interface_binding, + jboolean enable_port_migration, jboolean enable_socket_tagging, + jboolean enable_interface_binding, jlong h2_connection_keepalive_idle_interval_milliseconds, jlong h2_connection_keepalive_timeout_seconds, jlong max_connections_per_host, jlong stream_idle_timeout_seconds, jlong per_try_idle_timeout_seconds, @@ -1218,6 +1219,7 @@ void configureBuilder(Envoy::JNI::JniHelper& jni_helper, jlong connect_timeout_s for (const std::string& suffix : suffixes) { builder.addQuicCanonicalSuffix(suffix); } + builder.enablePortMigration(enable_port_migration); #endif builder.enableInterfaceBinding(enable_interface_binding == JNI_TRUE); @@ -1268,8 +1270,9 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr jboolean enable_http3, jstring http3_connection_options, jstring http3_client_connection_options, jobjectArray quic_hints, jobjectArray quic_canonical_suffixes, jboolean enable_gzip_decompression, - jboolean enable_brotli_decompression, jboolean enable_socket_tagging, - jboolean enable_interface_binding, jlong h2_connection_keepalive_idle_interval_milliseconds, + jboolean enable_brotli_decompression, jboolean enable_port_migration, + jboolean enable_socket_tagging, jboolean enable_interface_binding, + jlong h2_connection_keepalive_idle_interval_milliseconds, jlong h2_connection_keepalive_timeout_seconds, jlong max_connections_per_host, jlong stream_idle_timeout_seconds, jlong per_try_idle_timeout_seconds, jstring app_version, jstring app_id, jboolean trust_chain_verification, jobjectArray filter_chain, @@ -1288,8 +1291,8 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr enable_dns_cache, dns_cache_save_interval_seconds, enable_drain_post_dns_refresh, enable_http3, http3_connection_options, http3_client_connection_options, quic_hints, quic_canonical_suffixes, enable_gzip_decompression, - enable_brotli_decompression, enable_socket_tagging, enable_interface_binding, - h2_connection_keepalive_idle_interval_milliseconds, + enable_brotli_decompression, enable_port_migration, enable_socket_tagging, + enable_interface_binding, h2_connection_keepalive_idle_interval_milliseconds, h2_connection_keepalive_timeout_seconds, max_connections_per_host, stream_idle_timeout_seconds, per_try_idle_timeout_seconds, app_version, app_id, trust_chain_verification, filter_chain, enable_platform_certificates_validation, diff --git a/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt b/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt index 06324b588a47..2176ac3b79ca 100644 --- a/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt +++ b/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt @@ -159,6 +159,7 @@ open class EngineBuilder(private val configuration: BaseConfiguration = Standard private var quicCanonicalSuffixes = mutableListOf() private var enableGzipDecompression = true private var enableBrotliDecompression = false + private var enablePortMigration = false private var enableSocketTagging = false private var enableInterfaceBinding = false private var h2ConnectionKeepaliveIdleIntervalMilliseconds = 1 @@ -323,6 +324,17 @@ open class EngineBuilder(private val configuration: BaseConfiguration = Standard return this } + /** + * Specify whether to do quic port migration or not. Defaults to false. + * + * @param enablePortMigration whether or not to allow quic port migration. + * @return This builder. + */ + fun enablePortMigration(enablePortMigration: Boolean): EngineBuilder { + this.enablePortMigration = enablePortMigration + return this + } + /** * Specify whether to support socket tagging or not. Defaults to false. * @@ -655,6 +667,7 @@ open class EngineBuilder(private val configuration: BaseConfiguration = Standard quicCanonicalSuffixes, enableGzipDecompression, enableBrotliDecompression, + enablePortMigration, enableSocketTagging, enableInterfaceBinding, h2ConnectionKeepaliveIdleIntervalMilliseconds, diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt b/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt index f2f2a6c1813f..fe9af5a86d3b 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt @@ -83,6 +83,7 @@ class EnvoyConfigurationTest { quicCanonicalSuffixes: MutableList = mutableListOf(".opq.com", ".xyz.com"), enableGzipDecompression: Boolean = true, enableBrotliDecompression: Boolean = false, + enablePortMigration: Boolean = true, enableSocketTagging: Boolean = false, enableInterfaceBinding: Boolean = false, h2ConnectionKeepaliveIdleIntervalMilliseconds: Int = 222, @@ -131,6 +132,7 @@ class EnvoyConfigurationTest { quicCanonicalSuffixes, enableGzipDecompression, enableBrotliDecompression, + enablePortMigration, enableSocketTagging, enableInterfaceBinding, h2ConnectionKeepaliveIdleIntervalMilliseconds, @@ -205,6 +207,7 @@ class EnvoyConfigurationTest { assertThat(resolvedTemplate).contains(".xyz.com") assertThat(resolvedTemplate).contains("connection_options: 5RTO") assertThat(resolvedTemplate).contains("client_connection_options: MPQC") + assertThat(resolvedTemplate).contains("num_timeouts_to_trigger_port_migration: 4") // Per Host Limits assertThat(resolvedTemplate).contains("max_connections: 543") From b7818b0df716af47ec22982c5a1cbbace5f2ae15 Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Wed, 13 Mar 2024 21:53:19 -0700 Subject: [PATCH 020/124] Begin process of removing singleton use by StringMatcher (#32829) Part of #32792 Signed-off-by: Greg Greenway --- .../network/test/postgres_integration_test.cc | 6 +- envoy/server/factory_context.h | 10 ++ envoy/server/instance.h | 5 + envoy/ssl/context_manager.h | 10 +- .../cert_validator/platform_bridge/config.cc | 2 +- .../cert_validator/platform_bridge/config.h | 5 +- mobile/test/common/integration/BUILD | 2 + mobile/test/common/integration/test_server.cc | 2 +- mobile/test/common/integration/test_server.h | 4 +- .../test/common/integration/xds_test_server.h | 4 +- .../integration/xds_test_server_interface.cc | 4 + source/common/common/matchers.cc | 5 +- source/common/common/matchers.h | 57 ++++++++-- source/common/common/regex.h | 10 ++ .../tls/cert_validator/default_validator.cc | 17 +-- .../tls/cert_validator/default_validator.h | 4 +- source/common/tls/cert_validator/factory.h | 2 +- .../common/tls/cert_validator/san_matcher.cc | 11 +- .../common/tls/cert_validator/san_matcher.h | 10 +- source/common/tls/context_impl.cc | 22 ++-- source/common/tls/context_impl.h | 10 +- source/common/tls/context_manager_impl.cc | 9 +- source/common/tls/context_manager_impl.h | 12 +- source/extensions/string_matcher/lua/match.cc | 13 +-- source/extensions/string_matcher/lua/match.h | 4 +- .../cert_validator/spiffe/spiffe_validator.cc | 14 ++- .../cert_validator/spiffe/spiffe_validator.h | 2 +- .../transport_sockets/tls/config.cc | 5 +- .../extensions/transport_sockets/tls/config.h | 3 +- source/server/config_validation/server.cc | 4 +- source/server/config_validation/server.h | 2 + source/server/server.cc | 2 +- source/server/server.h | 2 + source/server/ssl_context_manager.cc | 7 +- source/server/ssl_context_manager.h | 5 +- .../grpc_client_integration_test_harness.h | 3 +- test/common/quic/BUILD | 2 + .../quic/envoy_quic_proof_source_test.cc | 11 +- .../quic/envoy_quic_proof_verifier_test.cc | 5 +- test/common/tls/cert_validator/BUILD | 2 + .../default_validator_integration_test.cc | 4 +- .../cert_validator/default_validator_test.cc | 72 +++++++----- .../tls/cert_validator/san_matcher_test.cc | 10 +- .../tls/cert_validator/timed_cert_validator.h | 12 +- test/common/tls/context_impl_test.cc | 28 ++--- test/common/tls/handshaker_factory_test.cc | 12 +- .../tls/integration/ssl_integration_test.cc | 4 +- test/common/tls/ssl_socket_test.cc | 103 +++++++++++------- test/common/upstream/hds_test.cc | 3 +- test/common/upstream/test_cluster_manager.h | 3 +- .../tcp_grpc_access_log_integration_test.cc | 4 +- .../tls_inspector_integration_test.cc | 4 +- .../extensions/string_matcher/lua/lua_test.cc | 9 +- .../starttls/starttls_integration_test.cc | 4 +- .../upstream_starttls_integration_test.cc | 4 +- .../tls/cert_validator/spiffe/BUILD | 1 + .../spiffe_validator_integration_test.cc | 4 +- .../spiffe/spiffe_validator_test.cc | 8 +- test/integration/base_integration_test.h | 3 +- .../sds_static_integration_test.cc | 4 +- .../integration/tcp_proxy_integration_test.cc | 4 +- test/integration/utility.cc | 3 +- test/integration/xds_integration_test.cc | 6 +- test/integration/xfcc_integration_test.cc | 4 +- test/mocks/server/instance.cc | 5 +- test/mocks/server/instance.h | 3 +- test/mocks/server/server_factory_context.h | 3 + test/per_file_coverage.sh | 2 +- test/server/BUILD | 1 + .../config_validation/cluster_manager_test.cc | 3 +- test/server/ssl_context_manager_test.cc | 7 +- 71 files changed, 410 insertions(+), 236 deletions(-) diff --git a/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc b/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc index fb47ccde35d0..f027cfec1074 100644 --- a/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc +++ b/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc @@ -295,7 +295,8 @@ class UpstreamSSLBaseIntegrationTest : public PostgresBaseIntegrationTest { // The tls transport socket will be inserted into fake_upstream when // Envoy's upstream starttls transport socket is converted to secure mode. std::unique_ptr tls_context_manager = - std::make_unique(timeSystem()); + std::make_unique( + server_factory_context_); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext downstream_tls_context; @@ -527,7 +528,8 @@ class UpstreamAndDownstreamSSLIntegrationTest : public UpstreamSSLBaseIntegratio // The tls transport socket will be inserted into fake_upstream when // Envoy's upstream starttls transport socket is converted to secure mode. std::unique_ptr tls_context_manager = - std::make_unique(timeSystem()); + std::make_unique( + server_factory_context_); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext upstream_tls_context; diff --git a/envoy/server/factory_context.h b/envoy/server/factory_context.h index 8a139fbcb0c4..f6a34d53cf87 100644 --- a/envoy/server/factory_context.h +++ b/envoy/server/factory_context.h @@ -36,6 +36,11 @@ #include "source/common/protobuf/protobuf.h" namespace Envoy { + +namespace Regex { +class Engine; +} + namespace Server { namespace Configuration { @@ -129,6 +134,11 @@ class CommonFactoryContext { * @return ServerLifecycleNotifier& the lifecycle notifier for the server. */ virtual ServerLifecycleNotifier& lifecycleNotifier() PURE; + + /** + * @return the server regex engine. + */ + virtual Regex::Engine& regexEngine() PURE; }; /** diff --git a/envoy/server/instance.h b/envoy/server/instance.h index 683fef31b60d..a6fe23cd4c75 100644 --- a/envoy/server/instance.h +++ b/envoy/server/instance.h @@ -253,6 +253,11 @@ class Instance { */ virtual Configuration::StatsConfig& statsConfig() PURE; + /** + * @return the server regex engine. + */ + virtual Regex::Engine& regexEngine() PURE; + /** * @return envoy::config::bootstrap::v3::Bootstrap& the servers bootstrap configuration. */ diff --git a/envoy/ssl/context_manager.h b/envoy/ssl/context_manager.h index 8c7fae3707b2..1cd6f4054472 100644 --- a/envoy/ssl/context_manager.h +++ b/envoy/ssl/context_manager.h @@ -10,6 +10,13 @@ #include "envoy/stats/scope.h" namespace Envoy { + +namespace Server { +namespace Configuration { +class CommonFactoryContext; +} // namespace Configuration +} // namespace Server + namespace Ssl { // Opaque type defined and used by the ``ServerContext``. @@ -73,7 +80,8 @@ using ContextManagerPtr = std::unique_ptr; class ContextManagerFactory : public Config::UntypedFactory { public: ~ContextManagerFactory() override = default; - virtual ContextManagerPtr createContextManager(TimeSource& time_source) PURE; + virtual ContextManagerPtr + createContextManager(Server::Configuration::CommonFactoryContext& factory_context) PURE; // There could be only one factory thus the name is static. std::string name() const override { return "ssl_context_manager"; } diff --git a/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc b/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc index 223e6ff8e925..807ec2dcfef7 100644 --- a/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc +++ b/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc @@ -9,7 +9,7 @@ namespace Tls { CertValidatorPtr PlatformBridgeCertValidatorFactory::createCertValidator( const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& /*time_source*/) { + Server::Configuration::CommonFactoryContext& /*context*/) { return std::make_unique(config, stats); } diff --git a/mobile/library/common/extensions/cert_validator/platform_bridge/config.h b/mobile/library/common/extensions/cert_validator/platform_bridge/config.h index bc884fddc3ac..d070c3fc99af 100644 --- a/mobile/library/common/extensions/cert_validator/platform_bridge/config.h +++ b/mobile/library/common/extensions/cert_validator/platform_bridge/config.h @@ -15,8 +15,9 @@ namespace Tls { class PlatformBridgeCertValidatorFactory : public CertValidatorFactory, public Config::TypedFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override; + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override; std::string name() const override { return "envoy_mobile.cert_validator.platform_bridge_cert_validator"; diff --git a/mobile/test/common/integration/BUILD b/mobile/test/common/integration/BUILD index a60cc0281793..e2fd61c2abda 100644 --- a/mobile/test/common/integration/BUILD +++ b/mobile/test/common/integration/BUILD @@ -184,6 +184,7 @@ envoy_cc_test_library( "@envoy//source/exe:process_wide_lib", "@envoy//test/integration:autonomous_upstream_lib", "@envoy//test/integration:utility_lib", + "@envoy//test/mocks/server:server_factory_context_mocks", "@envoy//test/mocks/server:transport_socket_factory_context_mocks", "@envoy//test/test_common:environment_lib", "@envoy_build_config//:extension_registry", @@ -213,6 +214,7 @@ envoy_cc_test_library( "@envoy//source/exe:process_wide_lib", "@envoy//test/integration:autonomous_upstream_lib", "@envoy//test/integration:utility_lib", + "@envoy//test/mocks/server:server_factory_context_mocks", "@envoy//test/mocks/server:transport_socket_factory_context_mocks", "@envoy//test/test_common:environment_lib", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", diff --git a/mobile/test/common/integration/test_server.cc b/mobile/test/common/integration/test_server.cc index 5c478560fa55..6e840f9c8545 100644 --- a/mobile/test/common/integration/test_server.cc +++ b/mobile/test/common/integration/test_server.cc @@ -25,7 +25,7 @@ namespace Envoy { Network::DownstreamTransportSocketFactoryPtr TestServer::createQuicUpstreamTlsContext( testing::NiceMock& factory_context) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager{time_system_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager{server_factory_context_}; tls_context.mutable_common_tls_context()->add_alpn_protocols("h3"); envoy::extensions::transport_sockets::tls::v3::TlsCertificate* certs = tls_context.mutable_common_tls_context()->add_tls_certificates(); diff --git a/mobile/test/common/integration/test_server.h b/mobile/test/common/integration/test_server.h index dc3e7786e87e..6c7c0506b6f0 100644 --- a/mobile/test/common/integration/test_server.h +++ b/mobile/test/common/integration/test_server.h @@ -8,6 +8,7 @@ #include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.h" #include "test/integration/autonomous_upstream.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/integration/server.h" @@ -26,6 +27,7 @@ enum class TestServerType { class TestServer : public ListenerHooks { private: testing::NiceMock factory_context_; + testing::NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; Event::GlobalTimeSystem time_system_; Api::ApiPtr api_; @@ -35,7 +37,7 @@ class TestServer : public ListenerHooks { Thread::SkipAsserts skip_asserts_; ProcessWide process_wide; Thread::MutexBasicLockable lock; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{time_system_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; std::unique_ptr runfiles_; // Either test_server_ will be set for test_server_type is a proxy, otherwise upstream_ will be diff --git a/mobile/test/common/integration/xds_test_server.h b/mobile/test/common/integration/xds_test_server.h index de2fa1252721..f00bc8cb4030 100644 --- a/mobile/test/common/integration/xds_test_server.h +++ b/mobile/test/common/integration/xds_test_server.h @@ -7,6 +7,7 @@ #include "test/integration/fake_upstream.h" #include "test/integration/server.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/test_common/test_time.h" @@ -36,6 +37,7 @@ class XdsTestServer { private: testing::NiceMock factory_context_; + testing::NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; Event::GlobalTimeSystem time_system_; Api::ApiPtr api_; @@ -44,7 +46,7 @@ class XdsTestServer { Event::DispatcherPtr dispatcher_; FakeUpstreamConfig upstream_config_; Thread::MutexBasicLockable lock_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{time_system_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; std::unique_ptr runfiles_; std::unique_ptr xds_upstream_; FakeHttpConnectionPtr xds_connection_; diff --git a/mobile/test/common/integration/xds_test_server_interface.cc b/mobile/test/common/integration/xds_test_server_interface.cc index 12152d5d9071..d4476e768327 100644 --- a/mobile/test/common/integration/xds_test_server_interface.cc +++ b/mobile/test/common/integration/xds_test_server_interface.cc @@ -12,6 +12,10 @@ static std::weak_ptr weak_test_server_; static std::shared_ptr testServer() { return weak_test_server_.lock(); } void initXdsServer() { + // This is called via JNI from kotlin tests, and Envoy doesn't consider it a test thread + // which triggers some failures of `ASSERT_IS_MAIN_OR_TEST_THREAD()`. + Envoy::Thread::SkipAsserts skip; + Envoy::ExtensionRegistry::registerFactories(); strong_test_server_ = std::make_shared(); weak_test_server_ = strong_test_server_; diff --git a/source/common/common/matchers.cc b/source/common/common/matchers.cc index d69fe3c72822..4dded56b26f0 100644 --- a/source/common/common/matchers.cc +++ b/source/common/common/matchers.cc @@ -201,9 +201,10 @@ bool PathMatcher::match(const absl::string_view path) const { return matcher_.match(Http::PathUtil::removeQueryAndFragment(path)); } -StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config) { +StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config, + ThreadLocal::SlotAllocator& tls, Api::Api& api) { auto factory = Config::Utility::getAndCheckFactory(config, false); - return factory->createStringMatcher(config.typed_config()); + return factory->createStringMatcher(config.typed_config(), tls, api); } } // namespace Matchers diff --git a/source/common/common/matchers.h b/source/common/common/matchers.h index ae66633b6660..49b68c705a42 100644 --- a/source/common/common/matchers.h +++ b/source/common/common/matchers.h @@ -86,24 +86,36 @@ class UniversalStringMatcher : public StringMatcher { bool match(absl::string_view) const override { return true; } }; -StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config); +StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config, + ThreadLocal::SlotAllocator& tls, Api::Api& api); template -class StringMatcherImpl : public ValueMatcher, public StringMatcher { +class PrivateStringMatcherImpl : public ValueMatcher, public StringMatcher { public: - explicit StringMatcherImpl(const StringMatcherType& matcher) : matcher_(matcher) { + // TODO(ggreenway): convert all but the first parameter into + // `Server::Configuration::CommonFactoryContext`. + explicit PrivateStringMatcherImpl(const StringMatcherType& matcher, Regex::Engine* regex_engine, + ThreadLocal::SlotAllocator* tls, Api::Api* api) + : matcher_(matcher) { if (matcher.match_pattern_case() == StringMatcherType::MatchPatternCase::kSafeRegex) { if (matcher.ignore_case()) { ExceptionUtil::throwEnvoyException("ignore_case has no effect for safe_regex."); } - regex_ = Regex::Utility::parseRegex(matcher_.safe_regex()); + if (regex_engine != nullptr) { + regex_ = Regex::Utility::parseRegex(matcher_.safe_regex(), *regex_engine); + } else { + // TODO(ggreenway): remove this branch when we always have an engine. This is only + // needed to make tests not complain about dereferencing a null pointer, even though + // the reference isn't actually used. + regex_ = Regex::Utility::parseRegex(matcher_.safe_regex()); + } } else if (matcher.match_pattern_case() == StringMatcherType::MatchPatternCase::kContains) { if (matcher_.ignore_case()) { // Cache the lowercase conversion of the Contains matcher for future use lowercase_contains_match_ = absl::AsciiStrToLower(matcher_.contains()); } } else { - initialize(matcher); + initialize(matcher, tls, api); } } @@ -143,11 +155,13 @@ class StringMatcherImpl : public ValueMatcher, public StringMatcher { // overloading to only handle that case for type `envoy::type::matcher::v3::StringMatcher` to // prevent compilation errors on use of `kCustom`. - void initialize(const xds::type::matcher::v3::StringMatcher&) {} + void initialize(const xds::type::matcher::v3::StringMatcher&, ThreadLocal::SlotAllocator*, + Api::Api*) {} - void initialize(const envoy::type::matcher::v3::StringMatcher& matcher) { + void initialize(const envoy::type::matcher::v3::StringMatcher& matcher, + ThreadLocal::SlotAllocator* tls, Api::Api* api) { if (matcher.has_custom()) { - custom_ = getExtensionStringMatcher(matcher.custom()); + custom_ = getExtensionStringMatcher(matcher.custom(), *tls, *api); } } @@ -193,9 +207,34 @@ class StringMatcherImpl : public ValueMatcher, public StringMatcher { StringMatcherPtr custom_; }; +// Temporarily create two separate types with different constructors, inheriting from the same +// implementation, to make it easier to find and replace all usage of the old one. +// TODO(ggreenway): delete these two extra classes, make `PrivateStringMatcherImpl` back into +// `StringMatcherImpl`. +template +class StringMatcherImplWithContext : public PrivateStringMatcherImpl { +public: + explicit StringMatcherImplWithContext(const StringMatcherType& matcher, + Server::Configuration::CommonFactoryContext& context) + : PrivateStringMatcherImpl(matcher, &context.regexEngine(), + &context.threadLocal(), &context.api()) {} +}; + +template +class StringMatcherImpl : public PrivateStringMatcherImpl { +public: + explicit StringMatcherImpl(const StringMatcherType& matcher) + : PrivateStringMatcherImpl( + matcher, Regex::EngineSingleton::getExisting(), + InjectableSingleton::getExisting(), + InjectableSingleton::getExisting()) {} +}; + class StringMatcherExtensionFactory : public Config::TypedFactory { public: - virtual StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& config) PURE; + // TODO(ggreenway): Convert all but first parameter to `CommonFactoryContext`. + virtual StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& config, + ThreadLocal::SlotAllocator& tls, Api::Api& api) PURE; std::string category() const override { return "envoy.string_matcher"; } }; diff --git a/source/common/common/regex.h b/source/common/common/regex.h index 4189df75fc24..e64fcf90f2ec 100644 --- a/source/common/common/regex.h +++ b/source/common/common/regex.h @@ -79,6 +79,16 @@ class Utility { return EngineSingleton::get().matcher(matcher.regex()); } + + template + static CompiledMatcherPtr parseRegex(const RegexMatcherType& matcher, Engine& engine) { + // Fallback deprecated engine type in regex matcher. + if (matcher.has_google_re2()) { + return std::make_unique(matcher); + } + + return engine.matcher(matcher.regex()); + } }; } // namespace Regex diff --git a/source/common/tls/cert_validator/default_validator.cc b/source/common/tls/cert_validator/default_validator.cc index 8d049f7ba0c9..39319a7312b3 100644 --- a/source/common/tls/cert_validator/default_validator.cc +++ b/source/common/tls/cert_validator/default_validator.cc @@ -44,8 +44,8 @@ namespace Tls { DefaultCertValidator::DefaultCertValidator( const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source) - : config_(config), stats_(stats), time_source_(time_source) { + Server::Configuration::CommonFactoryContext& context) + : config_(config), stats_(stats), context_(context) { if (config_ != nullptr) { allow_untrusted_certificate_ = config_->trustChainVerification() == envoy::extensions::transport_sockets::tls::v3:: @@ -155,7 +155,7 @@ int DefaultCertValidator::initializeSslContexts(std::vector contexts, if (!cert_validation_config->subjectAltNameMatchers().empty()) { for (const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher : cert_validation_config->subjectAltNameMatchers()) { - auto san_matcher = createStringSanMatcher(matcher); + auto san_matcher = createStringSanMatcher(matcher, context_); if (san_matcher == nullptr) { throwEnvoyExceptionOrPanic( absl::StrCat("Failed to create string SAN matcher of type ", matcher.san_type())); @@ -548,18 +548,19 @@ Envoy::Ssl::CertificateDetailsPtr DefaultCertValidator::getCaCertInformation() c if (ca_cert_ == nullptr) { return nullptr; } - return Utility::certificateDetails(ca_cert_.get(), getCaFileName(), time_source_); + return Utility::certificateDetails(ca_cert_.get(), getCaFileName(), context_.timeSource()); } absl::optional DefaultCertValidator::daysUntilFirstCertExpires() const { - return Utility::getDaysUntilExpiration(ca_cert_.get(), time_source_); + return Utility::getDaysUntilExpiration(ca_cert_.get(), context_.timeSource()); } class DefaultCertValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override { - return std::make_unique(config, stats, time_source); + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override { + return std::make_unique(config, stats, context); } std::string name() const override { return "envoy.tls.cert_validator.default"; } diff --git a/source/common/tls/cert_validator/default_validator.h b/source/common/tls/cert_validator/default_validator.h index c3e88bd09ca1..f17a01dd3d5d 100644 --- a/source/common/tls/cert_validator/default_validator.h +++ b/source/common/tls/cert_validator/default_validator.h @@ -35,7 +35,7 @@ namespace Tls { class DefaultCertValidator : public CertValidator, Logger::Loggable { public: DefaultCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source); + SslStats& stats, Server::Configuration::CommonFactoryContext& context); ~DefaultCertValidator() override = default; @@ -110,7 +110,7 @@ class DefaultCertValidator : public CertValidator, Logger::Loggable ca_cert_; diff --git a/source/common/tls/cert_validator/factory.h b/source/common/tls/cert_validator/factory.h index 40f3fc3de92b..8f6aebbd6b4c 100644 --- a/source/common/tls/cert_validator/factory.h +++ b/source/common/tls/cert_validator/factory.h @@ -21,7 +21,7 @@ class CertValidatorFactory : public Config::UntypedFactory { public: virtual CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source) PURE; + Server::Configuration::CommonFactoryContext& context) PURE; std::string category() const override { return "envoy.tls.cert_validator"; } }; diff --git a/source/common/tls/cert_validator/san_matcher.cc b/source/common/tls/cert_validator/san_matcher.cc index 13429c3fcdcc..0229ca1c1273 100644 --- a/source/common/tls/cert_validator/san_matcher.cc +++ b/source/common/tls/cert_validator/san_matcher.cc @@ -28,7 +28,8 @@ bool StringSanMatcher::match(const GENERAL_NAME* general_name) const { } SanMatcherPtr createStringSanMatcher( - envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher const& matcher) { + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher const& matcher, + Server::Configuration::CommonFactoryContext& context) { // Verify that a new san type has not been added. static_assert(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType_MAX == 4); @@ -36,13 +37,13 @@ SanMatcherPtr createStringSanMatcher( switch (matcher.san_type()) { PANIC_ON_PROTO_ENUM_SENTINEL_VALUES; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS: - return SanMatcherPtr{std::make_unique(GEN_DNS, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_DNS, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL: - return SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI: - return SanMatcherPtr{std::make_unique(GEN_URI, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_URI, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS: - return SanMatcherPtr{std::make_unique(GEN_IPADD, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_IPADD, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SAN_TYPE_UNSPECIFIED: PANIC("unhandled value"); } diff --git a/source/common/tls/cert_validator/san_matcher.h b/source/common/tls/cert_validator/san_matcher.h index 260e9cc3075e..b9409555209e 100644 --- a/source/common/tls/cert_validator/san_matcher.h +++ b/source/common/tls/cert_validator/san_matcher.h @@ -34,16 +34,18 @@ class StringSanMatcher : public SanMatcher { public: bool match(const GENERAL_NAME* general_name) const override; ~StringSanMatcher() override = default; - StringSanMatcher(int general_name_type, envoy::type::matcher::v3::StringMatcher matcher) - : general_name_type_(general_name_type), matcher_(matcher) {} + StringSanMatcher(int general_name_type, envoy::type::matcher::v3::StringMatcher matcher, + Server::Configuration::CommonFactoryContext& context) + : general_name_type_(general_name_type), matcher_(matcher, context) {} private: const int general_name_type_; - const Matchers::StringMatcherImpl matcher_; + const Matchers::StringMatcherImplWithContext matcher_; }; SanMatcherPtr createStringSanMatcher( - const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher); + const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher, + Server::Configuration::CommonFactoryContext& context); } // namespace Tls } // namespace TransportSockets diff --git a/source/common/tls/context_impl.cc b/source/common/tls/context_impl.cc index 7d86c94bd5f6..2669fd8559f5 100644 --- a/source/common/tls/context_impl.cc +++ b/source/common/tls/context_impl.cc @@ -80,8 +80,9 @@ int ContextImpl::sslExtendedSocketInfoIndex() { } ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, - TimeSource& time_source, Ssl::ContextAdditionalInitFunc additional_init) - : scope_(scope), stats_(generateSslStats(scope)), time_source_(time_source), + Server::Configuration::CommonFactoryContext& factory_context, + Ssl::ContextAdditionalInitFunc additional_init) + : scope_(scope), stats_(generateSslStats(scope)), factory_context_(factory_context), tls_max_version_(config.maxProtocolVersion()), stat_name_set_(scope.symbolTable().makeSet("TransportSockets::Tls")), unknown_ssl_cipher_(stat_name_set_->add("unknown_ssl_cipher")), @@ -104,7 +105,7 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c } cert_validator_ = cert_validator_factory->createCertValidator( - config.certificateValidationContext(), stats_, time_source_); + config.certificateValidationContext(), stats_, factory_context_); const auto tls_certificates = config.tlsCertificates(); tls_contexts_.resize(std::max(static_cast(1), tls_certificates.size())); @@ -609,7 +610,7 @@ absl::optional ContextImpl::daysUntilFirstCertExpires() const { } for (auto& ctx : tls_contexts_) { const absl::optional tmp = - Utility::getDaysUntilExpiration(ctx.cert_chain_.get(), time_source_); + Utility::getDaysUntilExpiration(ctx.cert_chain_.get(), factory_context_.timeSource()); if (!tmp.has_value()) { return absl::nullopt; } @@ -643,7 +644,7 @@ std::vector ContextImpl::getCertChainInformat } auto detail = Utility::certificateDetails(ctx.cert_chain_.get(), ctx.getCertChainFileName(), - time_source_); + factory_context_.timeSource()); auto ocsp_resp = ctx.ocsp_response_.get(); if (ocsp_resp) { auto* ocsp_details = detail->mutable_ocsp_details(); @@ -659,8 +660,8 @@ std::vector ContextImpl::getCertChainInformat ClientContextImpl::ClientContextImpl(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config, - TimeSource& time_source) - : ContextImpl(scope, config, time_source, nullptr /* additional_init */), + Server::Configuration::CommonFactoryContext& factory_context) + : ContextImpl(scope, config, factory_context, nullptr /* additional_init */), server_name_indication_(config.serverNameIndication()), allow_renegotiation_(config.allowRenegotiation()), enforce_rsa_key_usage_(config.enforceRsaKeyUsage()), @@ -789,9 +790,9 @@ int ClientContextImpl::newSessionKey(SSL_SESSION* session) { ServerContextImpl::ServerContextImpl(Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, const std::vector& server_names, - TimeSource& time_source, + Server::Configuration::CommonFactoryContext& factory_context, Ssl::ContextAdditionalInitFunc additional_init) - : ContextImpl(scope, config, time_source, additional_init), + : ContextImpl(scope, config, factory_context, additional_init), session_ticket_keys_(config.sessionTicketKeys()), ocsp_staple_policy_(config.ocspStaplePolicy()), full_scan_certs_on_sni_mismatch_(config.fullScanCertsOnSNIMismatch()) { @@ -888,7 +889,8 @@ ServerContextImpl::ServerContextImpl(Stats::Scope& scope, throwEnvoyExceptionOrPanic("Required OCSP response is missing from TLS context"); } } else { - auto response = std::make_unique(ocsp_resp_bytes, time_source_); + auto response = std::make_unique(ocsp_resp_bytes, + factory_context_.timeSource()); if (!response->matchesCertificate(*ctx.cert_chain_)) { throwEnvoyExceptionOrPanic("OCSP response does not match its TLS certificate"); } diff --git a/source/common/tls/context_impl.h b/source/common/tls/context_impl.h index 9bffa93a74a4..71f04229af51 100644 --- a/source/common/tls/context_impl.h +++ b/source/common/tls/context_impl.h @@ -115,7 +115,8 @@ class ContextImpl : public virtual Envoy::Ssl::Context, protected: friend class ContextImplPeer; - ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, TimeSource& time_source, + ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, + Server::Configuration::CommonFactoryContext& factory_context, Ssl::ContextAdditionalInitFunc additional_init); /** @@ -151,7 +152,7 @@ class ContextImpl : public virtual Envoy::Ssl::Context, std::vector parsed_alpn_protocols_; bssl::UniquePtr cert_chain_; std::string cert_chain_file_path_; - TimeSource& time_source_; + Server::Configuration::CommonFactoryContext& factory_context_; const unsigned tls_max_version_; mutable Stats::StatNameSetPtr stat_name_set_; const Stats::StatName unknown_ssl_cipher_; @@ -173,7 +174,7 @@ using ContextImplSharedPtr = std::shared_ptr; class ClientContextImpl : public ContextImpl, public Envoy::Ssl::ClientContext { public: ClientContextImpl(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config, - TimeSource& time_source); + Server::Configuration::CommonFactoryContext& factory_context); bssl::UniquePtr newSsl(const Network::TransportSocketOptionsConstSharedPtr& options) override; @@ -195,7 +196,8 @@ enum class OcspStapleAction { Staple, NoStaple, Fail, ClientNotCapable }; class ServerContextImpl : public ContextImpl, public Envoy::Ssl::ServerContext { public: ServerContextImpl(Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, - const std::vector& server_names, TimeSource& time_source, + const std::vector& server_names, + Server::Configuration::CommonFactoryContext& factory_context, Ssl::ContextAdditionalInitFunc additional_init); // Select the TLS certificate context in SSL_CTX_set_select_certificate_cb() callback with diff --git a/source/common/tls/context_manager_impl.cc b/source/common/tls/context_manager_impl.cc index b76a81264e21..1c0e8d027aeb 100644 --- a/source/common/tls/context_manager_impl.cc +++ b/source/common/tls/context_manager_impl.cc @@ -15,17 +15,19 @@ namespace Extensions { namespace TransportSockets { namespace Tls { -ContextManagerImpl::ContextManagerImpl(TimeSource& time_source) : time_source_(time_source) {} +ContextManagerImpl::ContextManagerImpl(Server::Configuration::CommonFactoryContext& factory_context) + : factory_context_(factory_context) {} Envoy::Ssl::ClientContextSharedPtr ContextManagerImpl::createSslClientContext(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config) { + ASSERT_IS_MAIN_OR_TEST_THREAD(); if (!config.isReady()) { return nullptr; } Envoy::Ssl::ClientContextSharedPtr context = - std::make_shared(scope, config, time_source_); + std::make_shared(scope, config, factory_context_); contexts_.insert(context); return context; } @@ -33,12 +35,13 @@ ContextManagerImpl::createSslClientContext(Stats::Scope& scope, Envoy::Ssl::ServerContextSharedPtr ContextManagerImpl::createSslServerContext( Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, const std::vector& server_names, Ssl::ContextAdditionalInitFunc additional_init) { + ASSERT_IS_MAIN_OR_TEST_THREAD(); if (!config.isReady()) { return nullptr; } Envoy::Ssl::ServerContextSharedPtr context = std::make_shared( - scope, config, server_names, time_source_, std::move(additional_init)); + scope, config, server_names, factory_context_, std::move(additional_init)); contexts_.insert(context); return context; } diff --git a/source/common/tls/context_manager_impl.h b/source/common/tls/context_manager_impl.h index db72e1d308e0..31df3addd743 100644 --- a/source/common/tls/context_manager_impl.h +++ b/source/common/tls/context_manager_impl.h @@ -5,6 +5,7 @@ #include #include "envoy/common/time.h" +#include "envoy/server/factory_context.h" #include "envoy/ssl/context_manager.h" #include "envoy/ssl/private_key/private_key.h" #include "envoy/stats/scope.h" @@ -18,14 +19,13 @@ namespace Tls { /** * The SSL context manager has the following threading model: - * Contexts can be allocated via any thread (through in practice they are only allocated on the main - * thread). They can be released from any thread (and in practice are since cluster information can - * be released from any thread). Context allocation/free is a very uncommon thing so we just do a - * global lock to protect it all. + * Contexts can be allocated the main thread. They can be released from any thread (and in practice + * are since cluster information can be released from any thread). Context allocation/free is a very + * uncommon thing so we just do a global lock to protect it all. */ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { public: - explicit ContextManagerImpl(TimeSource& time_source); + explicit ContextManagerImpl(Server::Configuration::CommonFactoryContext& factory_context); ~ContextManagerImpl() override = default; // Ssl::ContextManager @@ -45,7 +45,7 @@ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { void removeContext(const Envoy::Ssl::ContextSharedPtr& old_context) override; private: - TimeSource& time_source_; + Server::Configuration::CommonFactoryContext& factory_context_; absl::flat_hash_set contexts_; PrivateKeyMethodManagerImpl private_key_method_manager_{}; }; diff --git a/source/extensions/string_matcher/lua/match.cc b/source/extensions/string_matcher/lua/match.cc index fd24f17f81f0..c6ee81e7022c 100644 --- a/source/extensions/string_matcher/lua/match.cc +++ b/source/extensions/string_matcher/lua/match.cc @@ -77,12 +77,11 @@ bool LuaStringMatcher::match(const absl::string_view value) const { // Lua state is not thread safe, so a state needs to be stored in thread local storage. class LuaStringMatcherThreadWrapper : public Matchers::StringMatcher { public: - LuaStringMatcherThreadWrapper(const std::string& code) { + LuaStringMatcherThreadWrapper(const std::string& code, ThreadLocal::SlotAllocator& tls) { // Validate that there are no errors while creating on the main thread. LuaStringMatcher validator(code); - tls_slot_ = ThreadLocal::TypedSlot::makeUnique( - *InjectableSingleton::getExisting()); + tls_slot_ = ThreadLocal::TypedSlot::makeUnique(tls); tls_slot_->set([code](Event::Dispatcher&) -> std::shared_ptr { return std::make_shared(code); }); @@ -95,18 +94,18 @@ class LuaStringMatcherThreadWrapper : public Matchers::StringMatcher { }; Matchers::StringMatcherPtr -LuaStringMatcherFactory::createStringMatcher(const ProtobufWkt::Any& message) { +LuaStringMatcherFactory::createStringMatcher(const ProtobufWkt::Any& message, + ThreadLocal::SlotAllocator& tls, Api::Api& api) { ::envoy::extensions::string_matcher::lua::v3::Lua config; Config::Utility::translateOpaqueConfig(message, ProtobufMessage::getStrictValidationVisitor(), config); - Api::Api* api = InjectableSingleton::getExisting(); absl::StatusOr result = Config::DataSource::read( - config.source_code(), false /* allow_empty */, *api, 0 /* max_size */); + config.source_code(), false /* allow_empty */, api, 0 /* max_size */); if (!result.ok()) { throw EnvoyException( fmt::format("Failed to get lua string matcher code from source: {}", result.status())); } - return std::make_unique(*result); + return std::make_unique(*result, tls); } ProtobufTypes::MessagePtr LuaStringMatcherFactory::createEmptyConfigProto() { diff --git a/source/extensions/string_matcher/lua/match.h b/source/extensions/string_matcher/lua/match.h index 8452a24b0832..2777bcf76220 100644 --- a/source/extensions/string_matcher/lua/match.h +++ b/source/extensions/string_matcher/lua/match.h @@ -29,7 +29,9 @@ class LuaStringMatcher : public Matchers::StringMatcher, public ThreadLocal::Thr class LuaStringMatcherFactory : public Matchers::StringMatcherExtensionFactory { public: - Matchers::StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& message) override; + Matchers::StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& message, + ThreadLocal::SlotAllocator& tls, + Api::Api& api) override; std::string name() const override { return "envoy.string_matcher.lua"; } ProtobufTypes::MessagePtr createEmptyConfigProto() override; }; diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc index 4c6f6fb3dc30..13f5011c7c01 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc @@ -31,8 +31,9 @@ namespace Tls { using SPIFFEConfig = envoy::extensions::transport_sockets::tls::v3::SPIFFECertValidatorConfig; SPIFFEValidator::SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) - : stats_(stats), time_source_(time_source) { + SslStats& stats, + Server::Configuration::CommonFactoryContext& context) + : stats_(stats), time_source_(context.timeSource()) { ASSERT(config != nullptr); allow_expired_certificate_ = config->allowExpiredCertificate(); @@ -48,7 +49,7 @@ SPIFFEValidator::SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextC // SAN types. See the discussion: https://github.com/envoyproxy/envoy/issues/15392 // TODO(pradeepcrao): Throw an exception when a non-URI matcher is encountered after the // deprecated field match_subject_alt_names is removed - subject_alt_name_matchers_.emplace_back(createStringSanMatcher(matcher)); + subject_alt_name_matchers_.emplace_back(createStringSanMatcher(matcher, context)); } } } @@ -310,9 +311,10 @@ Envoy::Ssl::CertificateDetailsPtr SPIFFEValidator::getCaCertInformation() const class SPIFFEValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override { - return std::make_unique(config, stats, time_source); + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override { + return std::make_unique(config, stats, context); } std::string name() const override { return "envoy.tls.cert_validator.spiffe"; } diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h index 4bb140dbe63b..0c8a93a80f16 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h @@ -35,7 +35,7 @@ class SPIFFEValidator : public CertValidator { SPIFFEValidator(SslStats& stats, TimeSource& time_source) : stats_(stats), time_source_(time_source){}; SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source); + Server::Configuration::CommonFactoryContext& context); ~SPIFFEValidator() override = default; // Tls::CertValidator diff --git a/source/extensions/transport_sockets/tls/config.cc b/source/extensions/transport_sockets/tls/config.cc index ec2f39b60aed..1d652c0d1545 100644 --- a/source/extensions/transport_sockets/tls/config.cc +++ b/source/extensions/transport_sockets/tls/config.cc @@ -51,8 +51,9 @@ ProtobufTypes::MessagePtr DownstreamSslSocketFactory::createEmptyConfigProto() { LEGACY_REGISTER_FACTORY(DownstreamSslSocketFactory, Server::Configuration::DownstreamTransportSocketConfigFactory, "tls"); -Ssl::ContextManagerPtr SslContextManagerFactory::createContextManager(TimeSource& time_source) { - return std::make_unique(time_source); +Ssl::ContextManagerPtr SslContextManagerFactory::createContextManager( + Server::Configuration::CommonFactoryContext& factory_context) { + return std::make_unique(factory_context); } static Envoy::Registry::RegisterInternalFactory(admin()->getConfigTracker()); - ssl_context_manager_ = createContextManager("ssl_context_manager", api_->timeSource()); + ssl_context_manager_ = createContextManager("ssl_context_manager", server_contexts_); cluster_manager_factory_ = std::make_unique( server_contexts_, stats(), threadLocal(), http_context_, [this]() -> Network::DnsResolverSharedPtr { return this->dnsResolver(); }, diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index fc301751e57d..9f1bb41c031d 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -123,6 +123,7 @@ class ValidationInstance final : Logger::Loggable, bool enableReusePortDefault() override { return true; } Configuration::StatsConfig& statsConfig() override { return config_.statsConfig(); } + Regex::Engine& regexEngine() override { return *regex_engine_; } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return bootstrap_; } Configuration::ServerFactoryContext& serverFactoryContext() override { return server_contexts_; } Configuration::TransportSocketFactoryContext& transportSocketFactoryContext() override { @@ -194,6 +195,7 @@ class ValidationInstance final : Logger::Loggable, Filter::TcpListenerFilterConfigProviderManagerImpl tcp_listener_config_provider_manager_; Server::DrainManagerPtr drain_manager_; HotRestartNopImpl nop_hot_restart_; + Regex::EnginePtr regex_engine_; }; } // namespace Server diff --git a/source/server/server.cc b/source/server/server.cc index 1bd625cd09dc..a570ccf16cce 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -749,7 +749,7 @@ absl::Status InstanceBase::initializeOrThrow(Network::Address::InstanceConstShar } // Once we have runtime we can initialize the SSL context manager. - ssl_context_manager_ = createContextManager("ssl_context_manager", time_source_); + ssl_context_manager_ = createContextManager("ssl_context_manager", server_contexts_); cluster_manager_factory_ = std::make_unique( serverFactoryContext(), stats_store_, thread_local_, http_context_, diff --git a/source/server/server.h b/source/server/server.h index 3afd1348aaba..0d4d76bc97f1 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -197,6 +197,7 @@ class ServerFactoryContextImpl : public Configuration::ServerFactoryContext, ProcessContextOptRef processContext() override { return server_.processContext(); } Envoy::Server::DrainManager& drainManager() override { return server_.drainManager(); } ServerLifecycleNotifier& lifecycleNotifier() override { return server_.lifecycleNotifier(); } + Regex::Engine& regexEngine() override { return server_.regexEngine(); } Configuration::StatsConfig& statsConfig() override { return server_.statsConfig(); } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return server_.bootstrap(); } OverloadManager& overloadManager() override { return server_.overloadManager(); } @@ -292,6 +293,7 @@ class InstanceBase : Logger::Loggable, TimeSource& timeSource() override { return time_source_; } void flushStats() override; Configuration::StatsConfig& statsConfig() override { return config_.statsConfig(); } + Regex::Engine& regexEngine() override { return *regex_engine_; } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return bootstrap_; } Configuration::ServerFactoryContext& serverFactoryContext() override { return server_contexts_; } Configuration::TransportSocketFactoryContext& transportSocketFactoryContext() override { diff --git a/source/server/ssl_context_manager.cc b/source/server/ssl_context_manager.cc index 8a9fec347dee..fadc32283163 100644 --- a/source/server/ssl_context_manager.cc +++ b/source/server/ssl_context_manager.cc @@ -49,12 +49,13 @@ class SslContextManagerNoTlsStub final : public Envoy::Ssl::ContextManager { } }; -Ssl::ContextManagerPtr createContextManager(const std::string& factory_name, - TimeSource& time_source) { +Ssl::ContextManagerPtr +createContextManager(const std::string& factory_name, + Server::Configuration::CommonFactoryContext& factory_context) { Ssl::ContextManagerFactory* factory = Registry::FactoryRegistry::getFactory(factory_name); if (factory != nullptr) { - return factory->createContextManager(time_source); + return factory->createContextManager(factory_context); } return std::make_unique(); diff --git a/source/server/ssl_context_manager.h b/source/server/ssl_context_manager.h index 4b618e6e64f4..c296955703fe 100644 --- a/source/server/ssl_context_manager.h +++ b/source/server/ssl_context_manager.h @@ -6,8 +6,9 @@ namespace Envoy { namespace Server { -Ssl::ContextManagerPtr createContextManager(const std::string& factory_name, - TimeSource& time_source); +Ssl::ContextManagerPtr +createContextManager(const std::string& factory_name, + Server::Configuration::CommonFactoryContext& factory_context); } // namespace Server } // namespace Envoy diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 27baab111f4f..0b52d9c55e82 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -510,7 +510,8 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { Upstream::MockClusterManager cm_; NiceMock local_info_; Runtime::MockLoader runtime_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{test_time_.timeSystem()}; + testing::NiceMock server_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; NiceMock random_; Http::AsyncClientPtr http_async_client_; Http::ConnectionPool::InstancePtr http_conn_pool_; diff --git a/test/common/quic/BUILD b/test/common/quic/BUILD index b488b190353d..19437fb2be92 100644 --- a/test/common/quic/BUILD +++ b/test/common/quic/BUILD @@ -64,6 +64,7 @@ envoy_cc_test( "//source/common/quic:envoy_quic_proof_verifier_lib", "//source/common/tls:context_config_lib", "//test/mocks/network:network_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/test_common:test_runtime_lib", "@com_github_google_quiche//:quic_core_versions_lib", @@ -108,6 +109,7 @@ envoy_cc_test( "//test/common/config:dummy_config_proto_cc_proto", "//test/common/tls/cert_validator:timed_cert_validator", "//test/mocks/event:event_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "@com_github_google_quiche//:quic_test_tools_test_certificates_lib", ], diff --git a/test/common/quic/envoy_quic_proof_source_test.cc b/test/common/quic/envoy_quic_proof_source_test.cc index a6814a40c52d..afe1e521378e 100644 --- a/test/common/quic/envoy_quic_proof_source_test.cc +++ b/test/common/quic/envoy_quic_proof_source_test.cc @@ -9,6 +9,7 @@ #include "test/common/quic/test_utils.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/test_common/test_runtime.h" @@ -71,7 +72,7 @@ class SignatureVerifier { const absl::optional nullopt = absl::nullopt; ON_CALL(cert_validation_ctx_config_, customValidatorConfig()).WillByDefault(ReturnRef(nullopt)); auto context = std::make_shared( - *store_.rootScope(), client_context_config_, time_system_); + *store_.rootScope(), client_context_config_, server_factory_context_); ON_CALL(verify_context_, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); ON_CALL(verify_context_, transportSocketOptions()) .WillByDefault(ReturnRef(transport_socket_options_)); @@ -105,6 +106,7 @@ class SignatureVerifier { NiceMock store_; Event::GlobalTimeSystem time_system_; NiceMock client_context_config_; + NiceMock server_factory_context_; NiceMock cert_validation_ctx_config_; std::unique_ptr verifier_; NiceMock tls_context_manager_; @@ -224,7 +226,8 @@ class EnvoyQuicProofSourceTest : public ::testing::Test { Network::MockFilterChainManager filter_chain_manager_; Network::MockListenSocket listen_socket_; testing::NiceMock listener_config_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{time_system_}; + Server::Configuration::MockServerFactoryContext factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{factory_context_}; Ssl::MockServerContextConfig* mock_context_config_; std::function secret_update_callback_; std::unique_ptr transport_socket_factory_; @@ -391,7 +394,9 @@ class LegacyEnvoyQuicProofSourceTest : public ::testing::Test { Network::MockFilterChainManager filter_chain_manager_; Network::MockListenSocket listen_socket_; testing::NiceMock listener_config_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{time_system_}; + testing::NiceMock server_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{ + server_factory_context_}; Ssl::MockServerContextConfig* mock_context_config_; std::unique_ptr transport_socket_factory_; Ssl::MockTlsCertificateConfig tls_cert_config_; diff --git a/test/common/quic/envoy_quic_proof_verifier_test.cc b/test/common/quic/envoy_quic_proof_verifier_test.cc index ab12465385eb..97b5bd52724a 100644 --- a/test/common/quic/envoy_quic_proof_verifier_test.cc +++ b/test/common/quic/envoy_quic_proof_verifier_test.cc @@ -9,6 +9,7 @@ #include "test/common/quic/test_utils.h" #include "test/common/tls/cert_validator/timed_cert_validator.h" #include "test/mocks/event/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/test_time.h" @@ -80,7 +81,7 @@ class EnvoyQuicProofVerifierTest : public testing::Test { EXPECT_CALL(cert_validation_ctx_config_, customValidatorConfig()) .WillRepeatedly(ReturnRef(custom_validator_config_)); auto context = std::make_shared( - *store_.rootScope(), client_context_config_, time_system_); + *store_.rootScope(), client_context_config_, factory_context_); verifier_ = std::make_unique(std::move(context)); } @@ -98,7 +99,7 @@ class EnvoyQuicProofVerifierTest : public testing::Test { absl::optional custom_validator_config_{ absl::nullopt}; NiceMock store_; - Event::GlobalTimeSystem time_system_; + Server::Configuration::MockServerFactoryContext factory_context_; NiceMock client_context_config_; Ssl::MockCertificateValidationContextConfig cert_validation_ctx_config_; std::unique_ptr verifier_; diff --git a/test/common/tls/cert_validator/BUILD b/test/common/tls/cert_validator/BUILD index f0b243a980d3..76c9d63dc831 100644 --- a/test/common/tls/cert_validator/BUILD +++ b/test/common/tls/cert_validator/BUILD @@ -21,6 +21,7 @@ envoy_cc_test( "//source/common/tls/cert_validator:cert_validator_lib", "//test/common/tls:ssl_test_utils", "//test/common/tls/cert_validator:test_common", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:environment_lib", "//test/test_common:test_runtime_lib", ], @@ -56,6 +57,7 @@ envoy_cc_test( deps = [ "//source/common/protobuf:utility_lib", "//source/common/tls/cert_validator:cert_validator_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:utility_lib", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", ], diff --git a/test/common/tls/cert_validator/default_validator_integration_test.cc b/test/common/tls/cert_validator/default_validator_integration_test.cc index a76e87ed04fd..ac5b3232863f 100644 --- a/test/common/tls/cert_validator/default_validator_integration_test.cc +++ b/test/common/tls/cert_validator/default_validator_integration_test.cc @@ -14,8 +14,8 @@ namespace Ssl { void SslCertValidatorIntegrationTest::initialize() { HttpIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + factory_context_.serverFactoryContext()); registerTestServerPorts({"http"}); test_server_->counter(listenerStatPrefix("ssl.fail_verify_error"))->reset(); diff --git a/test/common/tls/cert_validator/default_validator_test.cc b/test/common/tls/cert_validator/default_validator_test.cc index 0fa44d2d6053..aff9ed58697d 100644 --- a/test/common/tls/cert_validator/default_validator_test.cc +++ b/test/common/tls/cert_validator/default_validator_test.cc @@ -6,6 +6,7 @@ #include "test/common/tls/cert_validator/test_common.h" #include "test/common/tls/ssl_test_utility.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/environment.h" #include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" @@ -34,28 +35,34 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltNameDNSMatched) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameDNSMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameIncorrectTypeMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameWildcardDNSMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir " "}}/test/common/tls/test_data/san_multiple_dns_cert.pem")); @@ -63,11 +70,13 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameWildcardDNSMatched) { matcher.set_exact("api.example.com"); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMultiLevelMatch) { + NiceMock context; + // san_multiple_dns_cert matches *.example.com bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir " @@ -76,7 +85,7 @@ TEST(DefaultCertValidatorTest, TestMultiLevelMatch) { matcher.set_exact("foo.api.example.com"); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -98,13 +107,15 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltMultiDomain) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameURIMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_uri_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw(spiffe://lyft.com/[^/]*-team)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -117,37 +128,40 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltNameNotMatched) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameNotMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example\.net)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_IPADD, matcher)}); + SanMatcherPtr{std::make_unique(GEN_IPADD, matcher, context)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher)}); + SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, - Event::GlobalTimeSystem().timeSystem()); + /*CertificateValidationContextConfig=*/nullptr, stats, context); bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); std::vector san_matchers; - san_matchers.push_back(SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + san_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); // Verify the certificate with correct SAN regex matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, san_matchers, nullptr, nullptr), @@ -157,7 +171,7 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { matcher.MergeFrom(TestUtility::createExactMatcher("hello.example.com")); std::vector invalid_san_matchers; invalid_san_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); std::string error; // Verify the certificate with incorrect SAN exact matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, @@ -167,13 +181,13 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithNoValidationContext) { + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, - Event::GlobalTimeSystem().timeSystem()); + /*CertificateValidationContextConfig=*/nullptr, stats, context); EXPECT_EQ(default_validator->verifyCertificate(/*cert=*/nullptr, /*verify_san_list=*/{}, /*subject_alt_name_matchers=*/{}, nullptr, @@ -191,13 +205,13 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithNoValidationContex } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithEmptyCertChain) { + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, - Event::GlobalTimeSystem().timeSystem()); + /*CertificateValidationContextConfig=*/nullptr, stats, context); SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); bssl::UniquePtr cert_chain(sk_X509_new_null()); @@ -210,18 +224,20 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithEmptyCertChain) { } TEST(DefaultCertValidatorTest, NoSanInCert) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/fake_ca_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example\.net)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, WithVerifyDepth) { - + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); envoy::config::core::v3::TypedExtensionConfig typed_conf; @@ -245,8 +261,8 @@ TEST(DefaultCertValidatorTest, WithVerifyDepth) { std::make_unique(typed_conf, false, san_matchers, ca_cert_str, 2); auto default_validator = - std::make_unique( - test_config.get(), stats, Event::GlobalTimeSystem().timeSystem()); + std::make_unique(test_config.get(), + stats, context); STACK_OF(X509)* intermediates = cert_chain.get(); SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); @@ -266,7 +282,7 @@ TEST(DefaultCertValidatorTest, WithVerifyDepth) { test_config = std::make_unique(typed_conf, false, san_matchers, ca_cert_str); default_validator = std::make_unique( - test_config.get(), stats, Event::GlobalTimeSystem().timeSystem()); + test_config.get(), stats, context); // Re-initialize context ssl_ctx = SSL_CTX_new(TLS_method()); @@ -321,6 +337,8 @@ class MockCertificateValidationContextConfig : public Ssl::CertificateValidation }; TEST(DefaultCertValidatorTest, TestUnexpectedSanMatcherType) { + NiceMock context; + auto mock_context_config = std::make_unique(); EXPECT_CALL(*mock_context_config.get(), trustChainVerification()) .WillRepeatedly(testing::Return(envoy::extensions::transport_sockets::tls::v3:: @@ -329,14 +347,16 @@ TEST(DefaultCertValidatorTest, TestUnexpectedSanMatcherType) { std::vector(); Stats::TestUtil::TestStore store; auto ssl_stats = generateSslStats(*store.rootScope()); - auto validator = std::make_unique(mock_context_config.get(), ssl_stats, - Event::GlobalTimeSystem().timeSystem()); + auto validator = + std::make_unique(mock_context_config.get(), ssl_stats, context); auto ctx = std::vector(); EXPECT_THROW_WITH_REGEX(validator->initializeSslContexts(ctx, false), EnvoyException, "Failed to create string SAN matcher of type.*"); } TEST(DefaultCertValidatorTest, TestInitializeSslContextFailure) { + NiceMock context; + auto mock_context_config = std::make_unique( "-----BEGIN CERTIFICATE-----\nincomplete payload"); EXPECT_CALL(*mock_context_config.get(), trustChainVerification()) @@ -345,8 +365,8 @@ TEST(DefaultCertValidatorTest, TestInitializeSslContextFailure) { Stats::TestUtil::TestStore store; auto ssl_stats = generateSslStats(*store.rootScope()); - auto validator = std::make_unique(mock_context_config.get(), ssl_stats, - Event::GlobalTimeSystem().timeSystem()); + auto validator = + std::make_unique(mock_context_config.get(), ssl_stats, context); auto ctx = std::vector(); EXPECT_THROW_WITH_REGEX(validator->initializeSslContexts(ctx, false), EnvoyException, "Failed to load trusted CA certificates from.*"); diff --git a/test/common/tls/cert_validator/san_matcher_test.cc b/test/common/tls/cert_validator/san_matcher_test.cc index 11f67e467119..3b330866a379 100644 --- a/test/common/tls/cert_validator/san_matcher_test.cc +++ b/test/common/tls/cert_validator/san_matcher_test.cc @@ -4,6 +4,7 @@ #include "source/common/protobuf/utility.h" #include "source/common/tls/cert_validator/san_matcher.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/utility.h" #include "gmock/gmock.h" @@ -16,6 +17,8 @@ namespace Tls { // Verify that we get a valid string san matcher for all valid san types. TEST(SanMatcherConfigTest, TestValidSanType) { + NiceMock context; + // Iterate over all san type enums. for (envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType san_type = envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType_MIN; @@ -29,9 +32,9 @@ TEST(SanMatcherConfigTest, TestValidSanType) { san_matcher.set_san_type(san_type); if (san_type == envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher:: SAN_TYPE_UNSPECIFIED) { - EXPECT_DEATH(createStringSanMatcher(san_matcher), "unhandled value"); + EXPECT_DEATH(createStringSanMatcher(san_matcher, context), "unhandled value"); } else { - const SanMatcherPtr matcher = createStringSanMatcher(san_matcher); + const SanMatcherPtr matcher = createStringSanMatcher(san_matcher, context); EXPECT_NE(matcher.get(), nullptr); // Verify that the message is valid. TestUtility::validate(san_matcher); @@ -40,6 +43,7 @@ TEST(SanMatcherConfigTest, TestValidSanType) { } TEST(SanMatcherConfigTest, UnspecifiedSanType) { + NiceMock context; envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher san_matcher; san_matcher.mutable_matcher()->set_exact("foo.example"); // Do not set san_type @@ -54,7 +58,7 @@ TEST(SanMatcherConfigTest, UnspecifiedSanType) { static_cast( static_cast(123)); san_matcher.set_san_type(san_type); - EXPECT_EQ(createStringSanMatcher(san_matcher), nullptr); + EXPECT_EQ(createStringSanMatcher(san_matcher, context), nullptr); } } // namespace Tls diff --git a/test/common/tls/cert_validator/timed_cert_validator.h b/test/common/tls/cert_validator/timed_cert_validator.h index 45a57fc5eda2..2ea320d87af2 100644 --- a/test/common/tls/cert_validator/timed_cert_validator.h +++ b/test/common/tls/cert_validator/timed_cert_validator.h @@ -18,8 +18,9 @@ class TimedCertValidator : public DefaultCertValidator { public: TimedCertValidator(std::chrono::milliseconds validation_time_out_ms, const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source, absl::optional expected_host_name) - : DefaultCertValidator(config, stats, time_source), + Server::Configuration::CommonFactoryContext& context, + absl::optional expected_host_name) + : DefaultCertValidator(config, stats, context), validation_time_out_ms_(validation_time_out_ms), expected_host_name_(expected_host_name) {} ValidationResults @@ -49,10 +50,11 @@ class TimedCertValidator : public DefaultCertValidator { class TimedCertValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override { + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override { auto validator = std::make_unique(validation_time_out_ms_, config, stats, - time_source, expected_host_name_); + context, expected_host_name_); if (expected_peer_address_.has_value()) { validator->setExpectedPeerAddress(expected_peer_address_.value()); } diff --git a/test/common/tls/context_impl_test.cc b/test/common/tls/context_impl_test.cc index dadbae2c6f7b..faa1414733d7 100644 --- a/test/common/tls/context_impl_test.cc +++ b/test/common/tls/context_impl_test.cc @@ -118,8 +118,8 @@ class SslContextImplTest : public SslCertsTest { } protected: - Event::SimulatedTimeSystem time_system_; - ContextManagerImpl manager_{time_system_}; + NiceMock server_factory_context_; + ContextManagerImpl manager_{server_factory_context_}; }; TEST_F(SslContextImplTest, TestCipherSuites) { @@ -1157,8 +1157,8 @@ class ClientContextConfigImplTest : public SslCertsTest { }}; } - Event::SimulatedTimeSystem time_system_; - ContextManagerImpl manager_{time_system_}; + NiceMock server_factory_context_; + ContextManagerImpl manager_{server_factory_context_}; }; // Validate that empty SNI (according to C string rules) fails config validation. @@ -1288,8 +1288,7 @@ TEST_F(ClientContextConfigImplTest, RSA3072Cert) { TestUtility::loadFromYaml(TestEnvironment::substitute(tls_certificate_yaml), *tls_context.mutable_common_tls_context()->add_tls_certificates()); ClientContextConfigImpl client_context_config(tls_context, factory_context_); - Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(time_system); + ContextManagerImpl manager(server_factory_context_); Stats::IsolatedStoreImpl store; auto context = manager_.createSslClientContext(*store.rootScope(), client_context_config); auto cleanup = cleanUpHelper(context); @@ -1765,7 +1764,10 @@ TEST_F(ClientContextConfigImplTest, MissingStaticCertificateValidationContext) { "Unknown static certificate validation context: missing"); } -class ServerContextConfigImplTest : public SslCertsTest {}; +class ServerContextConfigImplTest : public SslCertsTest { +public: + NiceMock server_factory_context_; +}; // Multiple TLS certificates are supported. TEST_F(ServerContextConfigImplTest, MultipleTlsCertificates) { @@ -1896,8 +1898,7 @@ TEST_F(ServerContextConfigImplTest, TlsCertificateNonEmpty) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; tls_context.mutable_common_tls_context()->add_tls_certificates(); ServerContextConfigImpl client_context_config(tls_context, factory_context_); - Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(time_system); + ContextManagerImpl manager(server_factory_context_); Stats::IsolatedStoreImpl store; EXPECT_THROW_WITH_MESSAGE( Envoy::Ssl::ServerContextSharedPtr server_ctx(manager.createSslServerContext( @@ -2002,8 +2003,7 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureNoMethod) { NiceMock private_key_method_manager; auto private_key_method_provider_ptr = std::make_shared>(); - Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(time_system); + ContextManagerImpl manager(server_factory_context_); EXPECT_CALL(factory_context_, sslContextManager()).WillOnce(ReturnRef(context_manager)); EXPECT_CALL(context_manager, privateKeyMethodManager()) .WillOnce(ReturnRef(private_key_method_manager)); @@ -2201,8 +2201,8 @@ TEST_F(ServerContextConfigImplTest, Pkcs12LoadFailureBothPkcs12AndCertChain) { class TestContextImpl : public ContextImpl { public: TestContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, - TimeSource& time_source) - : ContextImpl(scope, config, time_source, nullptr), pool_(scope.symbolTable()), + Server::Configuration::ServerFactoryContext& factory_context) + : ContextImpl(scope, config, factory_context, nullptr), pool_(scope.symbolTable()), fallback_(pool_.add("fallback")) {} void incCounter(absl::string_view name, absl::string_view value) { @@ -2220,7 +2220,7 @@ class SslContextStatsTest : public SslContextImplTest { client_context_config_ = std::make_unique(tls_context_, factory_context_); context_ = std::make_unique(*store_.rootScope(), *client_context_config_, - time_system_); + server_factory_context_); } Stats::TestUtil::TestStore store_; diff --git a/test/common/tls/handshaker_factory_test.cc b/test/common/tls/handshaker_factory_test.cc index f60113708590..e161b5eea047 100644 --- a/test/common/tls/handshaker_factory_test.cc +++ b/test/common/tls/handshaker_factory_test.cc @@ -92,8 +92,8 @@ class HandshakerFactoryImplForTest class HandshakerFactoryTest : public testing::Test { protected: HandshakerFactoryTest() - : context_manager_( - std::make_unique(time_system_)), + : context_manager_(std::make_unique( + server_factory_context_)), registered_factory_(handshaker_factory_) { scoped_runtime_.mergeValues( {{"envoy.reloadable_features.no_extension_lookup_by_name", "false"}}); @@ -111,7 +111,7 @@ class HandshakerFactoryTest : public testing::Test { return SSL_get_SSL_CTX(ssl); } - Event::GlobalTimeSystem time_system_; + NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; std::unique_ptr context_manager_; HandshakerFactoryImplForTest handshaker_factory_; @@ -248,8 +248,8 @@ class HandshakerFactoryImplForDownstreamTest class HandshakerFactoryDownstreamTest : public testing::Test { protected: HandshakerFactoryDownstreamTest() - : context_manager_( - std::make_unique(time_system_)) { + : context_manager_(std::make_unique( + server_factory_context_)) { scoped_runtime_.mergeValues( {{"envoy.reloadable_features.no_extension_lookup_by_name", "false"}}); } @@ -262,7 +262,7 @@ class HandshakerFactoryDownstreamTest : public testing::Test { return SSL_get_SSL_CTX(ssl); } - Event::GlobalTimeSystem time_system_; + NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; std::unique_ptr context_manager_; envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context_; diff --git a/test/common/tls/integration/ssl_integration_test.cc b/test/common/tls/integration/ssl_integration_test.cc index 885494ce1f33..ffae471e9e90 100644 --- a/test/common/tls/integration/ssl_integration_test.cc +++ b/test/common/tls/integration/ssl_integration_test.cc @@ -66,8 +66,8 @@ void SslIntegrationTestBase::initialize() { HttpIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); registerTestServerPorts({"http"}); } diff --git a/test/common/tls/ssl_socket_test.cc b/test/common/tls/ssl_socket_test.cc index b72f7ad6596b..dc231f41eaad 100644 --- a/test/common/tls/ssl_socket_test.cc +++ b/test/common/tls/ssl_socket_test.cc @@ -47,6 +47,7 @@ #include "test/mocks/network/mocks.h" #include "test/mocks/runtime/mocks.h" #include "test/mocks/secret/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" @@ -346,8 +347,9 @@ void testUtil(const TestUtilOptions& options) { Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + transport_socket_factory_context; + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); // For private key method testing. NiceMock context_manager; @@ -356,7 +358,7 @@ void testUtil(const TestUtilOptions& options) { test_private_key_method_factory(test_factory); PrivateKeyMethodManagerImpl private_key_method_manager; if (options.expectedPrivateKeyMethod()) { - EXPECT_CALL(server_factory_context, sslContextManager()) + EXPECT_CALL(transport_socket_factory_context, sslContextManager()) .WillOnce(ReturnRef(context_manager)) .WillRepeatedly(ReturnRef(context_manager)); EXPECT_CALL(context_manager, privateKeyMethodManager()) @@ -367,9 +369,10 @@ void testUtil(const TestUtilOptions& options) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(options.serverCtxYaml()), server_tls_context); - auto server_cfg = - std::make_unique(server_tls_context, server_factory_context); - ContextManagerImpl manager(*time_system); + auto server_cfg = std::make_unique(server_tls_context, + transport_socket_factory_context); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Event::DispatcherPtr dispatcher = server_api->allocateDispatcher("test_thread"); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -707,7 +710,10 @@ class TestUtilOptionsV2 : public TestUtilOptionsBase { void testUtilV2(const TestUtilOptionsV2& options) { Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(*time_system); + NiceMock + transport_socket_factory_context; + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); // SNI-based selection logic isn't happening in SslSocket anymore. ASSERT(options.listener().filter_chains().size() == 1); @@ -716,10 +722,9 @@ void testUtilV2(const TestUtilOptionsV2& options) { filter_chain.filter_chain_match().server_names().end()); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); - testing::NiceMock - server_factory_context; NiceMock runtime; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const envoy::config::core::v3::TransportSocket& transport_socket = @@ -727,7 +732,8 @@ void testUtilV2(const TestUtilOptionsV2& options) { ASSERT(transport_socket.has_typed_config()); transport_socket.typed_config().UnpackTo(&tls_context); - auto server_cfg = std::make_unique(tls_context, server_factory_context); + auto server_cfg = + std::make_unique(tls_context, transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *server_stats_store.rootScope(), server_names); @@ -1016,7 +1022,8 @@ TEST_P(SslSocketTest, ServerTransportSocketOptions) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); auto ssl_socket = server_ssl_socket_factory.createDownstreamTransportSocket(); @@ -3065,7 +3072,8 @@ TEST_P(SslSocketTest, FlushCloseDuringHandshake) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3124,7 +3132,8 @@ TEST_P(SslSocketTest, HalfClose) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3209,7 +3218,8 @@ TEST_P(SslSocketTest, ShutdownWithCloseNotify) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3300,7 +3310,8 @@ TEST_P(SslSocketTest, ShutdownWithoutCloseNotify) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3407,7 +3418,8 @@ TEST_P(SslSocketTest, ClientAuthMultipleCAs) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3489,24 +3501,26 @@ void testTicketSessionResumption(const std::string& server_ctx_yaml1, const Network::Address::IpVersion ip_version, const uint32_t expected_lifetime_hint = 0) { Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(*time_system); + NiceMock + transport_socket_factory_context; + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; - testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context1; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml1), server_tls_context1); - auto server_cfg1 = - std::make_unique(server_tls_context1, server_factory_context); + auto server_cfg1 = std::make_unique(server_tls_context1, + transport_socket_factory_context); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context2; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml2), server_tls_context2); - auto server_cfg2 = - std::make_unique(server_tls_context2, server_factory_context); + auto server_cfg2 = std::make_unique(server_tls_context2, + transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory1(std::move(server_cfg1), manager, *server_stats_store.rootScope(), server_names1); ServerSslSocketFactory server_ssl_socket_factory2(std::move(server_cfg2), manager, @@ -3647,19 +3661,21 @@ void testSupportForSessionResumption(const std::string& server_ctx_yaml, bool expect_stateful, const Network::Address::IpVersion ip_version) { Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(*time_system); + NiceMock + transport_socket_factory_context; + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::IsolatedStoreImpl server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; - testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); - auto server_cfg = - std::make_unique(server_tls_context, server_factory_context); + auto server_cfg = std::make_unique(server_tls_context, + transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *server_stats_store.rootScope(), {}); @@ -4305,7 +4321,8 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context2; TestUtility::loadFromYaml(TestEnvironment::substitute(server2_ctx_yaml), tls_context2); auto server2_cfg = std::make_unique(tls_context2, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -4422,18 +4439,20 @@ void SslSocketTest::testClientSessionResumption(const std::string& server_ctx_ya const Network::Address::IpVersion version) { InSequence s; - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system_); testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + transport_socket_factory_context; + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_ctx_proto; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_ctx_proto); auto server_cfg = - std::make_unique(server_ctx_proto, server_factory_context); + std::make_unique(server_ctx_proto, transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -4698,7 +4717,7 @@ TEST_P(SslSocketTest, SslError) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -5230,7 +5249,7 @@ TEST_P(SslSocketTest, SetSignatureAlgorithms) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -5845,7 +5864,7 @@ TEST_P(SslSocketTest, DownstreamNotReadySslSocket) { EXPECT_TRUE(server_cfg->tlsCertificates().empty()); EXPECT_FALSE(server_cfg->isReady()); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *factory_context_.store_.rootScope(), std::vector{}); @@ -5883,7 +5902,7 @@ TEST_P(SslSocketTest, UpstreamNotReadySslSocket) { EXPECT_TRUE(client_cfg->tlsCertificates().empty()); EXPECT_FALSE(client_cfg->isReady()); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); ClientSslSocketFactory client_ssl_socket_factory(std::move(client_cfg), manager, *factory_context_.store_.rootScope()); auto transport_socket = client_ssl_socket_factory.createTransportSocket(nullptr, nullptr); @@ -5911,7 +5930,7 @@ TEST_P(SslSocketTest, TestTransportSocketCallback) { envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; auto client_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); ClientSslSocketFactory client_ssl_socket_factory(std::move(client_cfg), manager, *factory_context_.store_.rootScope()); @@ -5935,7 +5954,7 @@ class SslReadBufferLimitTest : public SslSocketTest { downstream_tls_context_); auto server_cfg = std::make_unique(downstream_tls_context_, factory_context_); - manager_ = std::make_unique(time_system_); + manager_ = std::make_unique(factory_context_.serverFactoryContext()); server_ssl_socket_factory_ = std::make_unique( std::move(server_cfg), *manager_, *server_stats_store_.rootScope(), std::vector{}); diff --git a/test/common/upstream/hds_test.cc b/test/common/upstream/hds_test.cc index 82acb9d72e8d..c58700be552a 100644 --- a/test/common/upstream/hds_test.cc +++ b/test/common/upstream/hds_test.cc @@ -61,8 +61,7 @@ class HdsTest : public testing::Test { HdsTest() : retry_timer_(new Event::MockTimer()), server_response_timer_(new Event::MockTimer()), async_client_(new Grpc::MockAsyncClient()), - api_(Api::createApiForTest(stats_store_, random_)), - ssl_context_manager_(api_->timeSource()) { + api_(Api::createApiForTest(stats_store_, random_)), ssl_context_manager_(server_context_) { ON_CALL(server_context_, api()).WillByDefault(ReturnRef(*api_)); node_.set_id("hds-node"); } diff --git a/test/common/upstream/test_cluster_manager.h b/test/common/upstream/test_cluster_manager.h index eb110f9d672e..a8e232eacb9e 100644 --- a/test/common/upstream/test_cluster_manager.h +++ b/test/common/upstream/test_cluster_manager.h @@ -146,8 +146,7 @@ class TestClusterManagerFactory : public ClusterManagerFactory { new NiceMock}; NiceMock& runtime_ = server_context_.runtime_loader_; NiceMock& dispatcher_ = server_context_.dispatcher_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{ - dispatcher_.timeSource()}; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{server_context_}; NiceMock& local_info_ = server_context_.local_info_; NiceMock& admin_ = server_context_.admin_; NiceMock secret_manager_; diff --git a/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc b/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc index 90d2e7acde2a..1b7e15d0ffe0 100644 --- a/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc +++ b/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc @@ -143,8 +143,8 @@ class TcpGrpcAccessLogIntegrationTest : public Grpc::GrpcClientIntegrationParamT const Ssl::ClientSslTransportOptions& ssl_options = {}, const std::string& curves_list = "") { // Set up the SSL client. - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); Network::Address::InstanceConstSharedPtr address = Ssl::getSslAddress(version_, lookupPort("tcp_proxy")); context_ = Ssl::createClientSslTransportSocketFactory(ssl_options, *context_manager_, *api_); diff --git a/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc b/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc index 78b57390d80d..aa2279c70839 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc +++ b/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc @@ -90,8 +90,8 @@ class TlsInspectorIntegrationTest : public testing::TestWithParam(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); } void setupConnections(bool listener_filter_disabled, bool expect_connection_open, bool ssl_client, diff --git a/test/extensions/string_matcher/lua/lua_test.cc b/test/extensions/string_matcher/lua/lua_test.cc index 4e834e0d6d71..3842b89f3469 100644 --- a/test/extensions/string_matcher/lua/lua_test.cc +++ b/test/extensions/string_matcher/lua/lua_test.cc @@ -88,21 +88,20 @@ TEST(LuaStringMatcher, LuaStdLib) { } TEST(LuaStringMatcher, NoCode) { - ScopedInjectableLoader api_inject(std::make_unique()); - ScopedInjectableLoader tls_inject( - std::make_unique()); + Api::MockApi api; + ThreadLocal::MockInstance tls; LuaStringMatcherFactory factory; ::envoy::extensions::string_matcher::lua::v3::Lua empty_config; ProtobufWkt::Any any; any.PackFrom(empty_config); - EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any, tls, api), EnvoyException, "Failed to get lua string matcher code from source: INVALID_ARGUMENT: " "Unexpected DataSource::specifier_case(): 0"); empty_config.mutable_source_code()->set_inline_string(""); any.PackFrom(empty_config); - EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any, tls, api), EnvoyException, "Failed to get lua string matcher code from source: INVALID_ARGUMENT: " "DataSource cannot be empty"); } diff --git a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc index cb2564e38286..01a27199c184 100644 --- a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc +++ b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc @@ -208,8 +208,8 @@ void StartTlsIntegrationTest::initialize() { factory->createTransportSocketFactory(*config, factory_context_)}; // Setup factories and contexts for tls transport socket. - tls_context_manager_ = - std::make_unique(timeSystem()); + tls_context_manager_ = std::make_unique( + server_factory_context_); tls_context_ = Ssl::createClientSslTransportSocketFactory({}, *tls_context_manager_, *api_); payload_reader_ = std::make_shared(*dispatcher_); diff --git a/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc index f3efba23db8d..b106d0043f30 100644 --- a/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc +++ b/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc @@ -249,8 +249,8 @@ void StartTlsIntegrationTest::initialize() { // Setup factory and context for tls transport socket. // The tls transport socket will be inserted into fake_upstream when // upstream starttls transport socket is converted to secure mode. - tls_context_manager_ = - std::make_unique(timeSystem()); + tls_context_manager_ = std::make_unique( + server_factory_context_); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext downstream_tls_context; diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD b/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD index 9f9644768aee..c95555b9be37 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD @@ -25,6 +25,7 @@ envoy_extension_cc_test( "//source/extensions/transport_sockets/tls/cert_validator/spiffe:config", "//test/common/tls:ssl_test_utils", "//test/common/tls/cert_validator:test_common", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:environment_lib", "//test/test_common:simulated_time_system_lib", "//test/test_common:test_runtime_lib", diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc index 1806d0985ed1..db9af217a43b 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc @@ -21,8 +21,8 @@ void SslSPIFFECertValidatorIntegrationTest::initialize() { .setAllowExpiredCertificate(allow_expired_cert_)); HttpIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); registerTestServerPorts({"http"}); } diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc index 51cc1ad97016..327298a3efe6 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc @@ -13,6 +13,7 @@ #include "test/common/tls/cert_validator/test_common.h" #include "test/common/tls/ssl_test_utility.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/environment.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/test_runtime.h" @@ -44,7 +45,8 @@ class TestSPIFFEValidator : public testing::Test { TestUtility::loadFromYaml(yaml, typed_conf); config_ = std::make_unique( typed_conf, allow_expired_certificate_, san_matchers_); - validator_ = std::make_unique(config_.get(), stats_, time_source); + ON_CALL(factory_context_, timeSource()).WillByDefault(testing::ReturnRef(time_source)); + validator_ = std::make_unique(config_.get(), stats_, factory_context_); } void initialize(std::string yaml) { @@ -52,8 +54,7 @@ class TestSPIFFEValidator : public testing::Test { TestUtility::loadFromYaml(yaml, typed_conf); config_ = std::make_unique( typed_conf, allow_expired_certificate_, san_matchers_); - validator_ = - std::make_unique(config_.get(), stats_, config_->api().timeSource()); + validator_ = std::make_unique(config_.get(), stats_, factory_context_); }; void initialize() { validator_ = std::make_unique(stats_, time_system_); } @@ -90,6 +91,7 @@ class TestSPIFFEValidator : public testing::Test { }; private: + NiceMock factory_context_; bool allow_expired_certificate_{false}; TestCertificateValidationContextConfigPtr config_; std::vector san_matchers_{}; diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index 1f1df3f7c765..c5eb1129d41b 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -525,7 +525,8 @@ class BaseIntegrationTest : protected Logger::Loggable { createUpstreamTlsContext(const FakeUpstreamConfig& upstream_config); testing::NiceMock thread_local_; testing::NiceMock factory_context_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{timeSystem()}; + testing::NiceMock server_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; // The fake upstreams_ are created using the context_manager, so make sure // they are destroyed before it is. diff --git a/test/integration/sds_static_integration_test.cc b/test/integration/sds_static_integration_test.cc index 49d42c1eb6c2..0e63eb3d4267 100644 --- a/test/integration/sds_static_integration_test.cc +++ b/test/integration/sds_static_integration_test.cc @@ -89,7 +89,7 @@ class SdsStaticDownstreamIntegrationTest } private: - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{timeSystem()}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; Network::UpstreamTransportSocketFactoryPtr client_ssl_ctx_; }; @@ -148,7 +148,7 @@ class SdsStaticUpstreamIntegrationTest : public testing::TestWithParam(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); payload_reader_ = std::make_shared(*dispatcher_); } diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 3d06c513d7fa..85019c6f45c2 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -239,7 +239,8 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt #ifdef ENVOY_ENABLE_QUIC testing::NiceMock threadlocal; - Extensions::TransportSockets::Tls::ContextManagerImpl manager(time_system); + NiceMock server_factory_context; + Extensions::TransportSockets::Tls::ContextManagerImpl manager(server_factory_context); Network::UpstreamTransportSocketFactoryPtr transport_socket_factory = createQuicUpstreamTransportSocketFactory(api, mock_stats_store, manager, threadlocal, "spiffe://lyft.com/backend-team"); diff --git a/test/integration/xds_integration_test.cc b/test/integration/xds_integration_test.cc index 5435bb17d55e..497c0e6522fa 100644 --- a/test/integration/xds_integration_test.cc +++ b/test/integration/xds_integration_test.cc @@ -193,7 +193,7 @@ class LdsInplaceUpdateTcpProxyIntegrationTest BaseIntegrationTest::initialize(); context_manager_ = std::make_unique( - BaseIntegrationTest::timeSystem()); + server_factory_context_); context_ = Ssl::createClientSslTransportSocketFactory({}, *context_manager_, *api_); } @@ -462,8 +462,8 @@ class LdsInplaceUpdateHttpIntegrationTest BaseIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); context_ = Ssl::createClientSslTransportSocketFactory({}, *context_manager_, *api_); address_ = Ssl::getSslAddress(version_, lookupPort("http")); } diff --git a/test/integration/xfcc_integration_test.cc b/test/integration/xfcc_integration_test.cc index eeec6cf876b5..689611d5345a 100644 --- a/test/integration/xfcc_integration_test.cc +++ b/test/integration/xfcc_integration_test.cc @@ -169,8 +169,8 @@ void XfccIntegrationTest::initialize() { config_helper_.addSslConfig(); } - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); client_tls_ssl_ctx_ = createClientSslContext(false); client_mtls_ssl_ctx_ = createClientSslContext(true); HttpIntegrationTest::initialize(); diff --git a/test/mocks/server/instance.cc b/test/mocks/server/instance.cc index e40b1e125331..eca664bd89a8 100644 --- a/test/mocks/server/instance.cc +++ b/test/mocks/server/instance.cc @@ -13,7 +13,7 @@ using ::testing::ReturnRef; MockInstance::MockInstance() : secret_manager_(std::make_unique(admin_.getConfigTracker())), - cluster_manager_(timeSource()), ssl_context_manager_(timeSource()), + cluster_manager_(timeSource()), singleton_manager_(new Singleton::ManagerImpl(Thread::threadFactoryForTest())), grpc_context_(stats_store_.symbolTable()), http_context_(stats_store_.symbolTable()), router_context_(stats_store_.symbolTable()), quic_stat_names_(stats_store_.symbolTable()), @@ -21,7 +21,8 @@ MockInstance::MockInstance() server_factory_context_( std::make_shared>()), transport_socket_factory_context_( - std::make_shared>()) { + std::make_shared>()), + ssl_context_manager_(*server_factory_context_) { ON_CALL(*this, threadLocal()).WillByDefault(ReturnRef(thread_local_)); ON_CALL(*this, stats()).WillByDefault(ReturnRef(stats_store_)); ON_CALL(*this, grpcContext()).WillByDefault(ReturnRef(grpc_context_)); diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index a8561d534b9c..de109c9c5a97 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -52,6 +52,7 @@ class MockInstance : public Instance { MOCK_METHOD(ThreadLocal::Instance&, threadLocal, ()); MOCK_METHOD(LocalInfo::LocalInfo&, localInfo, (), (const)); MOCK_METHOD(Configuration::StatsConfig&, statsConfig, (), ()); + MOCK_METHOD(Regex::Engine&, regexEngine, ()); MOCK_METHOD(void, flushStats, ()); MOCK_METHOD(ProtobufMessage::ValidationContext&, messageValidationContext, ()); MOCK_METHOD(Configuration::ServerFactoryContext&, serverFactoryContext, ()); @@ -78,7 +79,6 @@ class MockInstance : public Instance { testing::NiceMock cluster_manager_; Thread::MutexBasicLockable access_log_lock_; testing::NiceMock runtime_loader_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_; testing::NiceMock dispatcher_; testing::NiceMock drain_manager_; testing::NiceMock access_log_manager_; @@ -101,6 +101,7 @@ class MockInstance : public Instance { server_factory_context_; std::shared_ptr> transport_socket_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_; }; } // namespace Server diff --git a/test/mocks/server/server_factory_context.h b/test/mocks/server/server_factory_context.h index 7e69dbad447e..128afe221c26 100644 --- a/test/mocks/server/server_factory_context.h +++ b/test/mocks/server/server_factory_context.h @@ -79,6 +79,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(Server::DrainManager&, drainManager, ()); MOCK_METHOD(Init::Manager&, initManager, ()); MOCK_METHOD(ServerLifecycleNotifier&, lifecycleNotifier, ()); + Regex::Engine& regexEngine() override { return regex_engine_; } MOCK_METHOD(StatsConfig&, statsConfig, (), ()); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), ()); MOCK_METHOD(OverloadManager&, overloadManager, ()); @@ -107,6 +108,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { Router::ContextImpl router_context_; envoy::config::bootstrap::v3::Bootstrap bootstrap_; testing::NiceMock options_; + Regex::GoogleReEngine regex_engine_; }; class MockGenericFactoryContext : public GenericFactoryContext { @@ -155,6 +157,7 @@ class StatelessMockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(Server::DrainManager&, drainManager, ()); MOCK_METHOD(Init::Manager&, initManager, ()); MOCK_METHOD(ServerLifecycleNotifier&, lifecycleNotifier, ()); + MOCK_METHOD(Regex::Engine&, regexEngine, ()); MOCK_METHOD(StatsConfig&, statsConfig, (), ()); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), ()); MOCK_METHOD(OverloadManager&, overloadManager, ()); diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index 5ab396837967..cd8604d486dc 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -62,7 +62,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/listener_managers/validation_listener_manager:70.5" "source/extensions/watchdog/profile_action:83.3" "source/server:91.0" # flaky: be careful adjusting. See https://github.com/envoyproxy/envoy/issues/15239 -"source/server/config_validation:89.2" +"source/server/config_validation:88.9" "source/extensions/health_checkers:96.0" "source/extensions/health_checkers/http:93.9" "source/extensions/health_checkers/grpc:92.0" diff --git a/test/server/BUILD b/test/server/BUILD index 9cceee1474dc..2938f1dd3429 100644 --- a/test/server/BUILD +++ b/test/server/BUILD @@ -368,6 +368,7 @@ envoy_cc_test( srcs = ["ssl_context_manager_test.cc"], deps = [ "//source/server:ssl_context_manager_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/mocks/stats:stats_mocks", "//test/test_common:simulated_time_system_lib", diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index 02442808b230..12db1d70e553 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -40,7 +40,8 @@ TEST(ValidationClusterManagerTest, MockedMethods) { testing::NiceMock secret_manager; auto dns_resolver = std::make_shared>(); - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager{api->timeSource()}; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager{ + *server.server_factory_context_}; Http::ContextImpl http_context(stats_store.symbolTable()); Quic::QuicStatNames quic_stat_names(stats_store.symbolTable()); diff --git a/test/server/ssl_context_manager_test.cc b/test/server/ssl_context_manager_test.cc index dbe2bdc8db6e..4a651e61dc31 100644 --- a/test/server/ssl_context_manager_test.cc +++ b/test/server/ssl_context_manager_test.cc @@ -2,9 +2,9 @@ #include "source/server/ssl_context_manager.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" -#include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" @@ -14,14 +14,15 @@ namespace Server { namespace { TEST(SslContextManager, createStub) { - Event::SimulatedTimeSystem time_system; Stats::MockStore store; Stats::Scope& scope(*store.rootScope()); Ssl::MockClientContextConfig client_config; Ssl::MockServerContextConfig server_config; std::vector server_names; + NiceMock server_factory_context; - Ssl::ContextManagerPtr manager = createContextManager("fake_factory_name", time_system); + Ssl::ContextManagerPtr manager = + createContextManager("fake_factory_name", server_factory_context); // Check we've created a stub, not real manager. EXPECT_EQ(manager->daysUntilFirstCertExpires().value(), std::numeric_limits::max()); From d5bfc06f110647127edc5ddc3bd6cfda78f2760b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 09:14:14 +0000 Subject: [PATCH 021/124] build(deps): bump aioquic from 0.9.25 to 1.0.0 in /tools/base (#32863) Bumps [aioquic](https://github.com/aiortc/aioquic) from 0.9.25 to 1.0.0. - [Changelog](https://github.com/aiortc/aioquic/blob/main/docs/changelog.rst) - [Commits](https://github.com/aiortc/aioquic/compare/0.9.25...1.0.0) --- updated-dependencies: - dependency-name: aioquic dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 53 +++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 26f39ac48a40..019da779c73f 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -173,30 +173,30 @@ aiohttp==3.9.3 \ # envoy-github-abstract # envoy-github-release # google-auth -aioquic==0.9.25 \ - --hash=sha256:0066c0867c7c78aad05cd1f7ebcc1a61b61f3dbc57e65823df26edc0098b6c75 \ - --hash=sha256:18658be4dc06eb1cba9a7bbc80b716b25d3dcbfb89360575de9e2b66c0bee6a7 \ - --hash=sha256:3fad05736e0152e698a3fd18d421bab1a77f379ff085b953e306e53df00d0b9e \ - --hash=sha256:4032a718dea1cc670379dcac15da6ee49440ffaffca565d4505c74f6ac56bb34 \ - --hash=sha256:45241ac5b9b6d4cd976109220dfecddc377d610d4675fffb69869bedcdfa841c \ - --hash=sha256:4ab61fe290e3eed71e2f0ee1dd6916040adc087fc2d4f9dc0dfd037c09a6defc \ - --hash=sha256:4d4641eee9cdd05b9c11088077b376423f8ed148f198d491d72d8189596f1aaf \ - --hash=sha256:4d8b00e2fbf6fee5c9bb5e6d481f1d414f9a3318ae500f673470f6571f2455dd \ - --hash=sha256:70795c78905326d855c2ae524072234aae586c789b81292e272d021e9b0430a3 \ - --hash=sha256:7e4f592f0ad0d57753c7d3851f75041052528b76a7255011294b208c6a9e360b \ - --hash=sha256:7fb11167019d24ec9b5c62e40cef365a4911cd74f5fb23a1283d772e92c8ef7d \ - --hash=sha256:7fd3b0e42e3dab1ca7396fbb6810deb3a0d9324bfc730fb4a7697de08f1b4dc3 \ - --hash=sha256:827652aa7b52ac069fc1fc9b1d8308f6c19adcfb86cd7f563c0ce5be8b416ce9 \ - --hash=sha256:9852358f7bbb52c56e1151fa054505a3880f1d2cffef8a83a1bbb653a2faaab0 \ - --hash=sha256:9a416579f78177ea3590fdb16933f6168f425f9109fcad00e09b3ac3f991d0bb \ - --hash=sha256:9a7a69f4396540e38caf2cf3f69f42844a9130e3dac2590fd8713d5dc77b3a1f \ - --hash=sha256:bac804af55b230acaebefc33eb04356df1844cc77da5f4a7f860cbe41052553d \ - --hash=sha256:bb187080955b026da4d3c9ea5fa1be32c4413e27bd8e458f66d94bf9a2b42e72 \ - --hash=sha256:cbd60cec8cc8e134dc1e2ebb79047827298b84d3b5ff011c36ee101110da63b8 \ - --hash=sha256:d8637030a95f68454cdaa58c0a7d0cbee5eca1e694a5cb8d6c179846f7d4d86c \ - --hash=sha256:da07befc3fa186621a6ff34695d9bf51c803e49f6c02fec53f50c86b74cdd55f \ - --hash=sha256:dd1cda94f7c5e1a4bb75a2f195c0f20839b54b014e3d81eeab47d6a625c7a761 \ - --hash=sha256:f73db85db29e35260f85961840d5089c3da3e404c6b7dfdaadbd9842a53c10a1 +aioquic==1.0.0 \ + --hash=sha256:0697ee1c293296e9673ed03888ce5eed978d0070edbdb7b7ec53ea84162f64ed \ + --hash=sha256:188feedbc4eb7062cfda5095b372261dffa73e08e570fd6d72c65d7760792266 \ + --hash=sha256:195ea3b15d6d2874afea641b2b0ba3eb3b598810ccff1646fa2bb0f1a2f79fde \ + --hash=sha256:27a372943b95b4ecf7dd5091640cf55e685a27fba55f5b2d14c6f0efb0a8da6a \ + --hash=sha256:2848b4bd3d7e59baa4aa164af325fad4aa14fb5071c8082419a9881aa78cfe42 \ + --hash=sha256:45076b6fa4e4e7bf7e812effa68427746626b1d6d8d894d41b60c3ef2c7f79d2 \ + --hash=sha256:47f33359929bf7255afe63ceaeed120fa8ba25cfa78b4a85dff1afb7bb4519d0 \ + --hash=sha256:49693b883ffaba31b47a59b3d3f22486299afbc7162711271e225aae18bc703b \ + --hash=sha256:67d169567d5deb6b6ade67b08520f713b20406724e32102fc257bcb153e4bc2f \ + --hash=sha256:741949d0feaded5ef0ba75b0b11e99f8d9e11ebe2a6516336a483bd4f7e6dd95 \ + --hash=sha256:78a0492d1d122d8aa399a0168b37aa2390500c9719117750b213fd16187475da \ + --hash=sha256:7bcaf62b1727eb4187f8167e0827224b047eda4010d3d6c2f451edf5beb9f8e1 \ + --hash=sha256:8656a70d352bdc580f8433ffd32566d1868e5be8ff4abade6a2fd858a3dcc0c0 \ + --hash=sha256:8ea030d2edf6f8bf37c57b3c25929b405af4d5f0f71ac122ca3f60a5bbebe9b1 \ + --hash=sha256:948d1f524057f1675c1cb5f77aaa29f32073bfb77ede810ecbbb798cd2f5b551 \ + --hash=sha256:9509fefef63abd9956832fb52f2f2ec8bf90a8767126d19c868409dc6ce5f1a5 \ + --hash=sha256:96798b8186f192155246c1d9d2662bbe1768aa11fb707418a6887b19f5ef8621 \ + --hash=sha256:9cc4a5319729ea19c407b5f301cc1692c3c887bbbd5d9fc715445843129fa689 \ + --hash=sha256:abd3c01cf1079431d9b4a1740cb92e5d57d24cd7a5bed8d6b4af0ecf21176782 \ + --hash=sha256:bfc4cbf8a40952b6d3916b21101b2d36f3be96ca670f28ffe6315f1947958325 \ + --hash=sha256:c4cf8069e6ef3065a9888a81a0f8fa2ac21dc4f88833c910567c16fc3846f1f6 \ + --hash=sha256:d3f3a89f92fb23df4999c8216b4fed8a7e08a760dad5fcc9cecae436d8d90343 \ + --hash=sha256:ed31c2b5afa98c5b6cafa4f36149deaf1dff6c5a69701eadd27167415f9f1660 # via -r requirements.in aiosignal==1.3.1 \ --hash=sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc \ @@ -700,9 +700,7 @@ google-apitools==0.5.32 \ google-auth[aiohttp]==2.27.0 \ --hash=sha256:8e4bad367015430ff253fe49d500fdc3396c1a434db5740828c728e45bcce245 \ --hash=sha256:e863a56ccc2d8efa83df7a80272601e43487fa9a728a376205c86c26aaefa821 - # via - # google-auth - # gsutil + # via gsutil google-reauth==0.1.1 \ --hash=sha256:cb39074488d74c8853074dde47368bbf8f739d4a4338b89aab696c895b6d8368 \ --hash=sha256:f9f6852a55c2c5453d581cd01f3d1278e86147c03d008409800390a834235892 @@ -1063,7 +1061,6 @@ pyjwt[crypto]==2.8.0 \ # via # gidgethub # pygithub - # pyjwt pylsqpack==0.3.18 \ --hash=sha256:005ddce84bdcbf5c3cf99f764504208e1aa0a91a8331bf47108f2708f2a315e6 \ --hash=sha256:06e1bbe47514b83cd03158e5558ef8cc44f578169c1820098be9f3cc4137f16a \ From 9ac0dd8ab61b255077933b41d9eeaab7560b5717 Mon Sep 17 00:00:00 2001 From: phlax Date: Thu, 14 Mar 2024 13:25:18 +0000 Subject: [PATCH 022/124] =?UTF-8?q?Revert=20"Begin=20process=20of=20removi?= =?UTF-8?q?ng=20singleton=20use=20by=20StringMatcher=20(#32=E2=80=A6=20(#3?= =?UTF-8?q?2902)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Begin process of removing singleton use by StringMatcher (#32829)" This reverts commit b7818b0df716af47ec22982c5a1cbbace5f2ae15. Signed-off-by: Ryan Northey --- .../network/test/postgres_integration_test.cc | 6 +- envoy/server/factory_context.h | 10 -- envoy/server/instance.h | 5 - envoy/ssl/context_manager.h | 10 +- .../cert_validator/platform_bridge/config.cc | 2 +- .../cert_validator/platform_bridge/config.h | 5 +- mobile/test/common/integration/BUILD | 2 - mobile/test/common/integration/test_server.cc | 2 +- mobile/test/common/integration/test_server.h | 4 +- .../test/common/integration/xds_test_server.h | 4 +- .../integration/xds_test_server_interface.cc | 4 - source/common/common/matchers.cc | 5 +- source/common/common/matchers.h | 57 ++-------- source/common/common/regex.h | 10 -- .../tls/cert_validator/default_validator.cc | 17 ++- .../tls/cert_validator/default_validator.h | 4 +- source/common/tls/cert_validator/factory.h | 2 +- .../common/tls/cert_validator/san_matcher.cc | 11 +- .../common/tls/cert_validator/san_matcher.h | 10 +- source/common/tls/context_impl.cc | 22 ++-- source/common/tls/context_impl.h | 10 +- source/common/tls/context_manager_impl.cc | 9 +- source/common/tls/context_manager_impl.h | 12 +- source/extensions/string_matcher/lua/match.cc | 13 ++- source/extensions/string_matcher/lua/match.h | 4 +- .../cert_validator/spiffe/spiffe_validator.cc | 14 +-- .../cert_validator/spiffe/spiffe_validator.h | 2 +- .../transport_sockets/tls/config.cc | 5 +- .../extensions/transport_sockets/tls/config.h | 3 +- source/server/config_validation/server.cc | 4 +- source/server/config_validation/server.h | 2 - source/server/server.cc | 2 +- source/server/server.h | 2 - source/server/ssl_context_manager.cc | 7 +- source/server/ssl_context_manager.h | 5 +- .../grpc_client_integration_test_harness.h | 3 +- test/common/quic/BUILD | 2 - .../quic/envoy_quic_proof_source_test.cc | 11 +- .../quic/envoy_quic_proof_verifier_test.cc | 5 +- test/common/tls/cert_validator/BUILD | 2 - .../default_validator_integration_test.cc | 4 +- .../cert_validator/default_validator_test.cc | 72 +++++------- .../tls/cert_validator/san_matcher_test.cc | 10 +- .../tls/cert_validator/timed_cert_validator.h | 12 +- test/common/tls/context_impl_test.cc | 28 ++--- test/common/tls/handshaker_factory_test.cc | 12 +- .../tls/integration/ssl_integration_test.cc | 4 +- test/common/tls/ssl_socket_test.cc | 103 +++++++----------- test/common/upstream/hds_test.cc | 3 +- test/common/upstream/test_cluster_manager.h | 3 +- .../tcp_grpc_access_log_integration_test.cc | 4 +- .../tls_inspector_integration_test.cc | 4 +- .../extensions/string_matcher/lua/lua_test.cc | 9 +- .../starttls/starttls_integration_test.cc | 4 +- .../upstream_starttls_integration_test.cc | 4 +- .../tls/cert_validator/spiffe/BUILD | 1 - .../spiffe_validator_integration_test.cc | 4 +- .../spiffe/spiffe_validator_test.cc | 8 +- test/integration/base_integration_test.h | 3 +- .../sds_static_integration_test.cc | 4 +- .../integration/tcp_proxy_integration_test.cc | 4 +- test/integration/utility.cc | 3 +- test/integration/xds_integration_test.cc | 6 +- test/integration/xfcc_integration_test.cc | 4 +- test/mocks/server/instance.cc | 5 +- test/mocks/server/instance.h | 3 +- test/mocks/server/server_factory_context.h | 3 - test/per_file_coverage.sh | 2 +- test/server/BUILD | 1 - .../config_validation/cluster_manager_test.cc | 3 +- test/server/ssl_context_manager_test.cc | 7 +- 71 files changed, 236 insertions(+), 410 deletions(-) diff --git a/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc b/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc index f027cfec1074..fb47ccde35d0 100644 --- a/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc +++ b/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc @@ -295,8 +295,7 @@ class UpstreamSSLBaseIntegrationTest : public PostgresBaseIntegrationTest { // The tls transport socket will be inserted into fake_upstream when // Envoy's upstream starttls transport socket is converted to secure mode. std::unique_ptr tls_context_manager = - std::make_unique( - server_factory_context_); + std::make_unique(timeSystem()); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext downstream_tls_context; @@ -528,8 +527,7 @@ class UpstreamAndDownstreamSSLIntegrationTest : public UpstreamSSLBaseIntegratio // The tls transport socket will be inserted into fake_upstream when // Envoy's upstream starttls transport socket is converted to secure mode. std::unique_ptr tls_context_manager = - std::make_unique( - server_factory_context_); + std::make_unique(timeSystem()); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext upstream_tls_context; diff --git a/envoy/server/factory_context.h b/envoy/server/factory_context.h index f6a34d53cf87..8a139fbcb0c4 100644 --- a/envoy/server/factory_context.h +++ b/envoy/server/factory_context.h @@ -36,11 +36,6 @@ #include "source/common/protobuf/protobuf.h" namespace Envoy { - -namespace Regex { -class Engine; -} - namespace Server { namespace Configuration { @@ -134,11 +129,6 @@ class CommonFactoryContext { * @return ServerLifecycleNotifier& the lifecycle notifier for the server. */ virtual ServerLifecycleNotifier& lifecycleNotifier() PURE; - - /** - * @return the server regex engine. - */ - virtual Regex::Engine& regexEngine() PURE; }; /** diff --git a/envoy/server/instance.h b/envoy/server/instance.h index a6fe23cd4c75..683fef31b60d 100644 --- a/envoy/server/instance.h +++ b/envoy/server/instance.h @@ -253,11 +253,6 @@ class Instance { */ virtual Configuration::StatsConfig& statsConfig() PURE; - /** - * @return the server regex engine. - */ - virtual Regex::Engine& regexEngine() PURE; - /** * @return envoy::config::bootstrap::v3::Bootstrap& the servers bootstrap configuration. */ diff --git a/envoy/ssl/context_manager.h b/envoy/ssl/context_manager.h index 1cd6f4054472..8c7fae3707b2 100644 --- a/envoy/ssl/context_manager.h +++ b/envoy/ssl/context_manager.h @@ -10,13 +10,6 @@ #include "envoy/stats/scope.h" namespace Envoy { - -namespace Server { -namespace Configuration { -class CommonFactoryContext; -} // namespace Configuration -} // namespace Server - namespace Ssl { // Opaque type defined and used by the ``ServerContext``. @@ -80,8 +73,7 @@ using ContextManagerPtr = std::unique_ptr; class ContextManagerFactory : public Config::UntypedFactory { public: ~ContextManagerFactory() override = default; - virtual ContextManagerPtr - createContextManager(Server::Configuration::CommonFactoryContext& factory_context) PURE; + virtual ContextManagerPtr createContextManager(TimeSource& time_source) PURE; // There could be only one factory thus the name is static. std::string name() const override { return "ssl_context_manager"; } diff --git a/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc b/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc index 807ec2dcfef7..223e6ff8e925 100644 --- a/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc +++ b/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc @@ -9,7 +9,7 @@ namespace Tls { CertValidatorPtr PlatformBridgeCertValidatorFactory::createCertValidator( const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - Server::Configuration::CommonFactoryContext& /*context*/) { + TimeSource& /*time_source*/) { return std::make_unique(config, stats); } diff --git a/mobile/library/common/extensions/cert_validator/platform_bridge/config.h b/mobile/library/common/extensions/cert_validator/platform_bridge/config.h index d070c3fc99af..bc884fddc3ac 100644 --- a/mobile/library/common/extensions/cert_validator/platform_bridge/config.h +++ b/mobile/library/common/extensions/cert_validator/platform_bridge/config.h @@ -15,9 +15,8 @@ namespace Tls { class PlatformBridgeCertValidatorFactory : public CertValidatorFactory, public Config::TypedFactory { public: - CertValidatorPtr - createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - Server::Configuration::CommonFactoryContext& context) override; + CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, + SslStats& stats, TimeSource& time_source) override; std::string name() const override { return "envoy_mobile.cert_validator.platform_bridge_cert_validator"; diff --git a/mobile/test/common/integration/BUILD b/mobile/test/common/integration/BUILD index e2fd61c2abda..a60cc0281793 100644 --- a/mobile/test/common/integration/BUILD +++ b/mobile/test/common/integration/BUILD @@ -184,7 +184,6 @@ envoy_cc_test_library( "@envoy//source/exe:process_wide_lib", "@envoy//test/integration:autonomous_upstream_lib", "@envoy//test/integration:utility_lib", - "@envoy//test/mocks/server:server_factory_context_mocks", "@envoy//test/mocks/server:transport_socket_factory_context_mocks", "@envoy//test/test_common:environment_lib", "@envoy_build_config//:extension_registry", @@ -214,7 +213,6 @@ envoy_cc_test_library( "@envoy//source/exe:process_wide_lib", "@envoy//test/integration:autonomous_upstream_lib", "@envoy//test/integration:utility_lib", - "@envoy//test/mocks/server:server_factory_context_mocks", "@envoy//test/mocks/server:transport_socket_factory_context_mocks", "@envoy//test/test_common:environment_lib", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", diff --git a/mobile/test/common/integration/test_server.cc b/mobile/test/common/integration/test_server.cc index 6e840f9c8545..5c478560fa55 100644 --- a/mobile/test/common/integration/test_server.cc +++ b/mobile/test/common/integration/test_server.cc @@ -25,7 +25,7 @@ namespace Envoy { Network::DownstreamTransportSocketFactoryPtr TestServer::createQuicUpstreamTlsContext( testing::NiceMock& factory_context) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager{server_factory_context_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager{time_system_}; tls_context.mutable_common_tls_context()->add_alpn_protocols("h3"); envoy::extensions::transport_sockets::tls::v3::TlsCertificate* certs = tls_context.mutable_common_tls_context()->add_tls_certificates(); diff --git a/mobile/test/common/integration/test_server.h b/mobile/test/common/integration/test_server.h index 6c7c0506b6f0..dc3e7786e87e 100644 --- a/mobile/test/common/integration/test_server.h +++ b/mobile/test/common/integration/test_server.h @@ -8,7 +8,6 @@ #include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.h" #include "test/integration/autonomous_upstream.h" -#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/integration/server.h" @@ -27,7 +26,6 @@ enum class TestServerType { class TestServer : public ListenerHooks { private: testing::NiceMock factory_context_; - testing::NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; Event::GlobalTimeSystem time_system_; Api::ApiPtr api_; @@ -37,7 +35,7 @@ class TestServer : public ListenerHooks { Thread::SkipAsserts skip_asserts_; ProcessWide process_wide; Thread::MutexBasicLockable lock; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{time_system_}; std::unique_ptr runfiles_; // Either test_server_ will be set for test_server_type is a proxy, otherwise upstream_ will be diff --git a/mobile/test/common/integration/xds_test_server.h b/mobile/test/common/integration/xds_test_server.h index f00bc8cb4030..de2fa1252721 100644 --- a/mobile/test/common/integration/xds_test_server.h +++ b/mobile/test/common/integration/xds_test_server.h @@ -7,7 +7,6 @@ #include "test/integration/fake_upstream.h" #include "test/integration/server.h" -#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/test_common/test_time.h" @@ -37,7 +36,6 @@ class XdsTestServer { private: testing::NiceMock factory_context_; - testing::NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; Event::GlobalTimeSystem time_system_; Api::ApiPtr api_; @@ -46,7 +44,7 @@ class XdsTestServer { Event::DispatcherPtr dispatcher_; FakeUpstreamConfig upstream_config_; Thread::MutexBasicLockable lock_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{time_system_}; std::unique_ptr runfiles_; std::unique_ptr xds_upstream_; FakeHttpConnectionPtr xds_connection_; diff --git a/mobile/test/common/integration/xds_test_server_interface.cc b/mobile/test/common/integration/xds_test_server_interface.cc index d4476e768327..12152d5d9071 100644 --- a/mobile/test/common/integration/xds_test_server_interface.cc +++ b/mobile/test/common/integration/xds_test_server_interface.cc @@ -12,10 +12,6 @@ static std::weak_ptr weak_test_server_; static std::shared_ptr testServer() { return weak_test_server_.lock(); } void initXdsServer() { - // This is called via JNI from kotlin tests, and Envoy doesn't consider it a test thread - // which triggers some failures of `ASSERT_IS_MAIN_OR_TEST_THREAD()`. - Envoy::Thread::SkipAsserts skip; - Envoy::ExtensionRegistry::registerFactories(); strong_test_server_ = std::make_shared(); weak_test_server_ = strong_test_server_; diff --git a/source/common/common/matchers.cc b/source/common/common/matchers.cc index 4dded56b26f0..d69fe3c72822 100644 --- a/source/common/common/matchers.cc +++ b/source/common/common/matchers.cc @@ -201,10 +201,9 @@ bool PathMatcher::match(const absl::string_view path) const { return matcher_.match(Http::PathUtil::removeQueryAndFragment(path)); } -StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config, - ThreadLocal::SlotAllocator& tls, Api::Api& api) { +StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config) { auto factory = Config::Utility::getAndCheckFactory(config, false); - return factory->createStringMatcher(config.typed_config(), tls, api); + return factory->createStringMatcher(config.typed_config()); } } // namespace Matchers diff --git a/source/common/common/matchers.h b/source/common/common/matchers.h index 49b68c705a42..ae66633b6660 100644 --- a/source/common/common/matchers.h +++ b/source/common/common/matchers.h @@ -86,36 +86,24 @@ class UniversalStringMatcher : public StringMatcher { bool match(absl::string_view) const override { return true; } }; -StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config, - ThreadLocal::SlotAllocator& tls, Api::Api& api); +StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config); template -class PrivateStringMatcherImpl : public ValueMatcher, public StringMatcher { +class StringMatcherImpl : public ValueMatcher, public StringMatcher { public: - // TODO(ggreenway): convert all but the first parameter into - // `Server::Configuration::CommonFactoryContext`. - explicit PrivateStringMatcherImpl(const StringMatcherType& matcher, Regex::Engine* regex_engine, - ThreadLocal::SlotAllocator* tls, Api::Api* api) - : matcher_(matcher) { + explicit StringMatcherImpl(const StringMatcherType& matcher) : matcher_(matcher) { if (matcher.match_pattern_case() == StringMatcherType::MatchPatternCase::kSafeRegex) { if (matcher.ignore_case()) { ExceptionUtil::throwEnvoyException("ignore_case has no effect for safe_regex."); } - if (regex_engine != nullptr) { - regex_ = Regex::Utility::parseRegex(matcher_.safe_regex(), *regex_engine); - } else { - // TODO(ggreenway): remove this branch when we always have an engine. This is only - // needed to make tests not complain about dereferencing a null pointer, even though - // the reference isn't actually used. - regex_ = Regex::Utility::parseRegex(matcher_.safe_regex()); - } + regex_ = Regex::Utility::parseRegex(matcher_.safe_regex()); } else if (matcher.match_pattern_case() == StringMatcherType::MatchPatternCase::kContains) { if (matcher_.ignore_case()) { // Cache the lowercase conversion of the Contains matcher for future use lowercase_contains_match_ = absl::AsciiStrToLower(matcher_.contains()); } } else { - initialize(matcher, tls, api); + initialize(matcher); } } @@ -155,13 +143,11 @@ class PrivateStringMatcherImpl : public ValueMatcher, public StringMatcher { // overloading to only handle that case for type `envoy::type::matcher::v3::StringMatcher` to // prevent compilation errors on use of `kCustom`. - void initialize(const xds::type::matcher::v3::StringMatcher&, ThreadLocal::SlotAllocator*, - Api::Api*) {} + void initialize(const xds::type::matcher::v3::StringMatcher&) {} - void initialize(const envoy::type::matcher::v3::StringMatcher& matcher, - ThreadLocal::SlotAllocator* tls, Api::Api* api) { + void initialize(const envoy::type::matcher::v3::StringMatcher& matcher) { if (matcher.has_custom()) { - custom_ = getExtensionStringMatcher(matcher.custom(), *tls, *api); + custom_ = getExtensionStringMatcher(matcher.custom()); } } @@ -207,34 +193,9 @@ class PrivateStringMatcherImpl : public ValueMatcher, public StringMatcher { StringMatcherPtr custom_; }; -// Temporarily create two separate types with different constructors, inheriting from the same -// implementation, to make it easier to find and replace all usage of the old one. -// TODO(ggreenway): delete these two extra classes, make `PrivateStringMatcherImpl` back into -// `StringMatcherImpl`. -template -class StringMatcherImplWithContext : public PrivateStringMatcherImpl { -public: - explicit StringMatcherImplWithContext(const StringMatcherType& matcher, - Server::Configuration::CommonFactoryContext& context) - : PrivateStringMatcherImpl(matcher, &context.regexEngine(), - &context.threadLocal(), &context.api()) {} -}; - -template -class StringMatcherImpl : public PrivateStringMatcherImpl { -public: - explicit StringMatcherImpl(const StringMatcherType& matcher) - : PrivateStringMatcherImpl( - matcher, Regex::EngineSingleton::getExisting(), - InjectableSingleton::getExisting(), - InjectableSingleton::getExisting()) {} -}; - class StringMatcherExtensionFactory : public Config::TypedFactory { public: - // TODO(ggreenway): Convert all but first parameter to `CommonFactoryContext`. - virtual StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& config, - ThreadLocal::SlotAllocator& tls, Api::Api& api) PURE; + virtual StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& config) PURE; std::string category() const override { return "envoy.string_matcher"; } }; diff --git a/source/common/common/regex.h b/source/common/common/regex.h index e64fcf90f2ec..4189df75fc24 100644 --- a/source/common/common/regex.h +++ b/source/common/common/regex.h @@ -79,16 +79,6 @@ class Utility { return EngineSingleton::get().matcher(matcher.regex()); } - - template - static CompiledMatcherPtr parseRegex(const RegexMatcherType& matcher, Engine& engine) { - // Fallback deprecated engine type in regex matcher. - if (matcher.has_google_re2()) { - return std::make_unique(matcher); - } - - return engine.matcher(matcher.regex()); - } }; } // namespace Regex diff --git a/source/common/tls/cert_validator/default_validator.cc b/source/common/tls/cert_validator/default_validator.cc index 39319a7312b3..8d049f7ba0c9 100644 --- a/source/common/tls/cert_validator/default_validator.cc +++ b/source/common/tls/cert_validator/default_validator.cc @@ -44,8 +44,8 @@ namespace Tls { DefaultCertValidator::DefaultCertValidator( const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - Server::Configuration::CommonFactoryContext& context) - : config_(config), stats_(stats), context_(context) { + TimeSource& time_source) + : config_(config), stats_(stats), time_source_(time_source) { if (config_ != nullptr) { allow_untrusted_certificate_ = config_->trustChainVerification() == envoy::extensions::transport_sockets::tls::v3:: @@ -155,7 +155,7 @@ int DefaultCertValidator::initializeSslContexts(std::vector contexts, if (!cert_validation_config->subjectAltNameMatchers().empty()) { for (const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher : cert_validation_config->subjectAltNameMatchers()) { - auto san_matcher = createStringSanMatcher(matcher, context_); + auto san_matcher = createStringSanMatcher(matcher); if (san_matcher == nullptr) { throwEnvoyExceptionOrPanic( absl::StrCat("Failed to create string SAN matcher of type ", matcher.san_type())); @@ -548,19 +548,18 @@ Envoy::Ssl::CertificateDetailsPtr DefaultCertValidator::getCaCertInformation() c if (ca_cert_ == nullptr) { return nullptr; } - return Utility::certificateDetails(ca_cert_.get(), getCaFileName(), context_.timeSource()); + return Utility::certificateDetails(ca_cert_.get(), getCaFileName(), time_source_); } absl::optional DefaultCertValidator::daysUntilFirstCertExpires() const { - return Utility::getDaysUntilExpiration(ca_cert_.get(), context_.timeSource()); + return Utility::getDaysUntilExpiration(ca_cert_.get(), time_source_); } class DefaultCertValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr - createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - Server::Configuration::CommonFactoryContext& context) override { - return std::make_unique(config, stats, context); + CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, + SslStats& stats, TimeSource& time_source) override { + return std::make_unique(config, stats, time_source); } std::string name() const override { return "envoy.tls.cert_validator.default"; } diff --git a/source/common/tls/cert_validator/default_validator.h b/source/common/tls/cert_validator/default_validator.h index f17a01dd3d5d..c3e88bd09ca1 100644 --- a/source/common/tls/cert_validator/default_validator.h +++ b/source/common/tls/cert_validator/default_validator.h @@ -35,7 +35,7 @@ namespace Tls { class DefaultCertValidator : public CertValidator, Logger::Loggable { public: DefaultCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, Server::Configuration::CommonFactoryContext& context); + SslStats& stats, TimeSource& time_source); ~DefaultCertValidator() override = default; @@ -110,7 +110,7 @@ class DefaultCertValidator : public CertValidator, Logger::Loggable ca_cert_; diff --git a/source/common/tls/cert_validator/factory.h b/source/common/tls/cert_validator/factory.h index 8f6aebbd6b4c..40f3fc3de92b 100644 --- a/source/common/tls/cert_validator/factory.h +++ b/source/common/tls/cert_validator/factory.h @@ -21,7 +21,7 @@ class CertValidatorFactory : public Config::UntypedFactory { public: virtual CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - Server::Configuration::CommonFactoryContext& context) PURE; + TimeSource& time_source) PURE; std::string category() const override { return "envoy.tls.cert_validator"; } }; diff --git a/source/common/tls/cert_validator/san_matcher.cc b/source/common/tls/cert_validator/san_matcher.cc index 0229ca1c1273..13429c3fcdcc 100644 --- a/source/common/tls/cert_validator/san_matcher.cc +++ b/source/common/tls/cert_validator/san_matcher.cc @@ -28,8 +28,7 @@ bool StringSanMatcher::match(const GENERAL_NAME* general_name) const { } SanMatcherPtr createStringSanMatcher( - envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher const& matcher, - Server::Configuration::CommonFactoryContext& context) { + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher const& matcher) { // Verify that a new san type has not been added. static_assert(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType_MAX == 4); @@ -37,13 +36,13 @@ SanMatcherPtr createStringSanMatcher( switch (matcher.san_type()) { PANIC_ON_PROTO_ENUM_SENTINEL_VALUES; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS: - return SanMatcherPtr{std::make_unique(GEN_DNS, matcher.matcher(), context)}; + return SanMatcherPtr{std::make_unique(GEN_DNS, matcher.matcher())}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL: - return SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher.matcher(), context)}; + return SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher.matcher())}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI: - return SanMatcherPtr{std::make_unique(GEN_URI, matcher.matcher(), context)}; + return SanMatcherPtr{std::make_unique(GEN_URI, matcher.matcher())}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS: - return SanMatcherPtr{std::make_unique(GEN_IPADD, matcher.matcher(), context)}; + return SanMatcherPtr{std::make_unique(GEN_IPADD, matcher.matcher())}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SAN_TYPE_UNSPECIFIED: PANIC("unhandled value"); } diff --git a/source/common/tls/cert_validator/san_matcher.h b/source/common/tls/cert_validator/san_matcher.h index b9409555209e..260e9cc3075e 100644 --- a/source/common/tls/cert_validator/san_matcher.h +++ b/source/common/tls/cert_validator/san_matcher.h @@ -34,18 +34,16 @@ class StringSanMatcher : public SanMatcher { public: bool match(const GENERAL_NAME* general_name) const override; ~StringSanMatcher() override = default; - StringSanMatcher(int general_name_type, envoy::type::matcher::v3::StringMatcher matcher, - Server::Configuration::CommonFactoryContext& context) - : general_name_type_(general_name_type), matcher_(matcher, context) {} + StringSanMatcher(int general_name_type, envoy::type::matcher::v3::StringMatcher matcher) + : general_name_type_(general_name_type), matcher_(matcher) {} private: const int general_name_type_; - const Matchers::StringMatcherImplWithContext matcher_; + const Matchers::StringMatcherImpl matcher_; }; SanMatcherPtr createStringSanMatcher( - const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher, - Server::Configuration::CommonFactoryContext& context); + const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher); } // namespace Tls } // namespace TransportSockets diff --git a/source/common/tls/context_impl.cc b/source/common/tls/context_impl.cc index 2669fd8559f5..7d86c94bd5f6 100644 --- a/source/common/tls/context_impl.cc +++ b/source/common/tls/context_impl.cc @@ -80,9 +80,8 @@ int ContextImpl::sslExtendedSocketInfoIndex() { } ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, - Server::Configuration::CommonFactoryContext& factory_context, - Ssl::ContextAdditionalInitFunc additional_init) - : scope_(scope), stats_(generateSslStats(scope)), factory_context_(factory_context), + TimeSource& time_source, Ssl::ContextAdditionalInitFunc additional_init) + : scope_(scope), stats_(generateSslStats(scope)), time_source_(time_source), tls_max_version_(config.maxProtocolVersion()), stat_name_set_(scope.symbolTable().makeSet("TransportSockets::Tls")), unknown_ssl_cipher_(stat_name_set_->add("unknown_ssl_cipher")), @@ -105,7 +104,7 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c } cert_validator_ = cert_validator_factory->createCertValidator( - config.certificateValidationContext(), stats_, factory_context_); + config.certificateValidationContext(), stats_, time_source_); const auto tls_certificates = config.tlsCertificates(); tls_contexts_.resize(std::max(static_cast(1), tls_certificates.size())); @@ -610,7 +609,7 @@ absl::optional ContextImpl::daysUntilFirstCertExpires() const { } for (auto& ctx : tls_contexts_) { const absl::optional tmp = - Utility::getDaysUntilExpiration(ctx.cert_chain_.get(), factory_context_.timeSource()); + Utility::getDaysUntilExpiration(ctx.cert_chain_.get(), time_source_); if (!tmp.has_value()) { return absl::nullopt; } @@ -644,7 +643,7 @@ std::vector ContextImpl::getCertChainInformat } auto detail = Utility::certificateDetails(ctx.cert_chain_.get(), ctx.getCertChainFileName(), - factory_context_.timeSource()); + time_source_); auto ocsp_resp = ctx.ocsp_response_.get(); if (ocsp_resp) { auto* ocsp_details = detail->mutable_ocsp_details(); @@ -660,8 +659,8 @@ std::vector ContextImpl::getCertChainInformat ClientContextImpl::ClientContextImpl(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config, - Server::Configuration::CommonFactoryContext& factory_context) - : ContextImpl(scope, config, factory_context, nullptr /* additional_init */), + TimeSource& time_source) + : ContextImpl(scope, config, time_source, nullptr /* additional_init */), server_name_indication_(config.serverNameIndication()), allow_renegotiation_(config.allowRenegotiation()), enforce_rsa_key_usage_(config.enforceRsaKeyUsage()), @@ -790,9 +789,9 @@ int ClientContextImpl::newSessionKey(SSL_SESSION* session) { ServerContextImpl::ServerContextImpl(Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, const std::vector& server_names, - Server::Configuration::CommonFactoryContext& factory_context, + TimeSource& time_source, Ssl::ContextAdditionalInitFunc additional_init) - : ContextImpl(scope, config, factory_context, additional_init), + : ContextImpl(scope, config, time_source, additional_init), session_ticket_keys_(config.sessionTicketKeys()), ocsp_staple_policy_(config.ocspStaplePolicy()), full_scan_certs_on_sni_mismatch_(config.fullScanCertsOnSNIMismatch()) { @@ -889,8 +888,7 @@ ServerContextImpl::ServerContextImpl(Stats::Scope& scope, throwEnvoyExceptionOrPanic("Required OCSP response is missing from TLS context"); } } else { - auto response = std::make_unique(ocsp_resp_bytes, - factory_context_.timeSource()); + auto response = std::make_unique(ocsp_resp_bytes, time_source_); if (!response->matchesCertificate(*ctx.cert_chain_)) { throwEnvoyExceptionOrPanic("OCSP response does not match its TLS certificate"); } diff --git a/source/common/tls/context_impl.h b/source/common/tls/context_impl.h index 71f04229af51..9bffa93a74a4 100644 --- a/source/common/tls/context_impl.h +++ b/source/common/tls/context_impl.h @@ -115,8 +115,7 @@ class ContextImpl : public virtual Envoy::Ssl::Context, protected: friend class ContextImplPeer; - ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, - Server::Configuration::CommonFactoryContext& factory_context, + ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, TimeSource& time_source, Ssl::ContextAdditionalInitFunc additional_init); /** @@ -152,7 +151,7 @@ class ContextImpl : public virtual Envoy::Ssl::Context, std::vector parsed_alpn_protocols_; bssl::UniquePtr cert_chain_; std::string cert_chain_file_path_; - Server::Configuration::CommonFactoryContext& factory_context_; + TimeSource& time_source_; const unsigned tls_max_version_; mutable Stats::StatNameSetPtr stat_name_set_; const Stats::StatName unknown_ssl_cipher_; @@ -174,7 +173,7 @@ using ContextImplSharedPtr = std::shared_ptr; class ClientContextImpl : public ContextImpl, public Envoy::Ssl::ClientContext { public: ClientContextImpl(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config, - Server::Configuration::CommonFactoryContext& factory_context); + TimeSource& time_source); bssl::UniquePtr newSsl(const Network::TransportSocketOptionsConstSharedPtr& options) override; @@ -196,8 +195,7 @@ enum class OcspStapleAction { Staple, NoStaple, Fail, ClientNotCapable }; class ServerContextImpl : public ContextImpl, public Envoy::Ssl::ServerContext { public: ServerContextImpl(Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, - const std::vector& server_names, - Server::Configuration::CommonFactoryContext& factory_context, + const std::vector& server_names, TimeSource& time_source, Ssl::ContextAdditionalInitFunc additional_init); // Select the TLS certificate context in SSL_CTX_set_select_certificate_cb() callback with diff --git a/source/common/tls/context_manager_impl.cc b/source/common/tls/context_manager_impl.cc index 1c0e8d027aeb..b76a81264e21 100644 --- a/source/common/tls/context_manager_impl.cc +++ b/source/common/tls/context_manager_impl.cc @@ -15,19 +15,17 @@ namespace Extensions { namespace TransportSockets { namespace Tls { -ContextManagerImpl::ContextManagerImpl(Server::Configuration::CommonFactoryContext& factory_context) - : factory_context_(factory_context) {} +ContextManagerImpl::ContextManagerImpl(TimeSource& time_source) : time_source_(time_source) {} Envoy::Ssl::ClientContextSharedPtr ContextManagerImpl::createSslClientContext(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config) { - ASSERT_IS_MAIN_OR_TEST_THREAD(); if (!config.isReady()) { return nullptr; } Envoy::Ssl::ClientContextSharedPtr context = - std::make_shared(scope, config, factory_context_); + std::make_shared(scope, config, time_source_); contexts_.insert(context); return context; } @@ -35,13 +33,12 @@ ContextManagerImpl::createSslClientContext(Stats::Scope& scope, Envoy::Ssl::ServerContextSharedPtr ContextManagerImpl::createSslServerContext( Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, const std::vector& server_names, Ssl::ContextAdditionalInitFunc additional_init) { - ASSERT_IS_MAIN_OR_TEST_THREAD(); if (!config.isReady()) { return nullptr; } Envoy::Ssl::ServerContextSharedPtr context = std::make_shared( - scope, config, server_names, factory_context_, std::move(additional_init)); + scope, config, server_names, time_source_, std::move(additional_init)); contexts_.insert(context); return context; } diff --git a/source/common/tls/context_manager_impl.h b/source/common/tls/context_manager_impl.h index 31df3addd743..db72e1d308e0 100644 --- a/source/common/tls/context_manager_impl.h +++ b/source/common/tls/context_manager_impl.h @@ -5,7 +5,6 @@ #include #include "envoy/common/time.h" -#include "envoy/server/factory_context.h" #include "envoy/ssl/context_manager.h" #include "envoy/ssl/private_key/private_key.h" #include "envoy/stats/scope.h" @@ -19,13 +18,14 @@ namespace Tls { /** * The SSL context manager has the following threading model: - * Contexts can be allocated the main thread. They can be released from any thread (and in practice - * are since cluster information can be released from any thread). Context allocation/free is a very - * uncommon thing so we just do a global lock to protect it all. + * Contexts can be allocated via any thread (through in practice they are only allocated on the main + * thread). They can be released from any thread (and in practice are since cluster information can + * be released from any thread). Context allocation/free is a very uncommon thing so we just do a + * global lock to protect it all. */ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { public: - explicit ContextManagerImpl(Server::Configuration::CommonFactoryContext& factory_context); + explicit ContextManagerImpl(TimeSource& time_source); ~ContextManagerImpl() override = default; // Ssl::ContextManager @@ -45,7 +45,7 @@ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { void removeContext(const Envoy::Ssl::ContextSharedPtr& old_context) override; private: - Server::Configuration::CommonFactoryContext& factory_context_; + TimeSource& time_source_; absl::flat_hash_set contexts_; PrivateKeyMethodManagerImpl private_key_method_manager_{}; }; diff --git a/source/extensions/string_matcher/lua/match.cc b/source/extensions/string_matcher/lua/match.cc index c6ee81e7022c..fd24f17f81f0 100644 --- a/source/extensions/string_matcher/lua/match.cc +++ b/source/extensions/string_matcher/lua/match.cc @@ -77,11 +77,12 @@ bool LuaStringMatcher::match(const absl::string_view value) const { // Lua state is not thread safe, so a state needs to be stored in thread local storage. class LuaStringMatcherThreadWrapper : public Matchers::StringMatcher { public: - LuaStringMatcherThreadWrapper(const std::string& code, ThreadLocal::SlotAllocator& tls) { + LuaStringMatcherThreadWrapper(const std::string& code) { // Validate that there are no errors while creating on the main thread. LuaStringMatcher validator(code); - tls_slot_ = ThreadLocal::TypedSlot::makeUnique(tls); + tls_slot_ = ThreadLocal::TypedSlot::makeUnique( + *InjectableSingleton::getExisting()); tls_slot_->set([code](Event::Dispatcher&) -> std::shared_ptr { return std::make_shared(code); }); @@ -94,18 +95,18 @@ class LuaStringMatcherThreadWrapper : public Matchers::StringMatcher { }; Matchers::StringMatcherPtr -LuaStringMatcherFactory::createStringMatcher(const ProtobufWkt::Any& message, - ThreadLocal::SlotAllocator& tls, Api::Api& api) { +LuaStringMatcherFactory::createStringMatcher(const ProtobufWkt::Any& message) { ::envoy::extensions::string_matcher::lua::v3::Lua config; Config::Utility::translateOpaqueConfig(message, ProtobufMessage::getStrictValidationVisitor(), config); + Api::Api* api = InjectableSingleton::getExisting(); absl::StatusOr result = Config::DataSource::read( - config.source_code(), false /* allow_empty */, api, 0 /* max_size */); + config.source_code(), false /* allow_empty */, *api, 0 /* max_size */); if (!result.ok()) { throw EnvoyException( fmt::format("Failed to get lua string matcher code from source: {}", result.status())); } - return std::make_unique(*result, tls); + return std::make_unique(*result); } ProtobufTypes::MessagePtr LuaStringMatcherFactory::createEmptyConfigProto() { diff --git a/source/extensions/string_matcher/lua/match.h b/source/extensions/string_matcher/lua/match.h index 2777bcf76220..8452a24b0832 100644 --- a/source/extensions/string_matcher/lua/match.h +++ b/source/extensions/string_matcher/lua/match.h @@ -29,9 +29,7 @@ class LuaStringMatcher : public Matchers::StringMatcher, public ThreadLocal::Thr class LuaStringMatcherFactory : public Matchers::StringMatcherExtensionFactory { public: - Matchers::StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& message, - ThreadLocal::SlotAllocator& tls, - Api::Api& api) override; + Matchers::StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& message) override; std::string name() const override { return "envoy.string_matcher.lua"; } ProtobufTypes::MessagePtr createEmptyConfigProto() override; }; diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc index 13f5011c7c01..4c6f6fb3dc30 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc @@ -31,9 +31,8 @@ namespace Tls { using SPIFFEConfig = envoy::extensions::transport_sockets::tls::v3::SPIFFECertValidatorConfig; SPIFFEValidator::SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, - Server::Configuration::CommonFactoryContext& context) - : stats_(stats), time_source_(context.timeSource()) { + SslStats& stats, TimeSource& time_source) + : stats_(stats), time_source_(time_source) { ASSERT(config != nullptr); allow_expired_certificate_ = config->allowExpiredCertificate(); @@ -49,7 +48,7 @@ SPIFFEValidator::SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextC // SAN types. See the discussion: https://github.com/envoyproxy/envoy/issues/15392 // TODO(pradeepcrao): Throw an exception when a non-URI matcher is encountered after the // deprecated field match_subject_alt_names is removed - subject_alt_name_matchers_.emplace_back(createStringSanMatcher(matcher, context)); + subject_alt_name_matchers_.emplace_back(createStringSanMatcher(matcher)); } } } @@ -311,10 +310,9 @@ Envoy::Ssl::CertificateDetailsPtr SPIFFEValidator::getCaCertInformation() const class SPIFFEValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr - createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - Server::Configuration::CommonFactoryContext& context) override { - return std::make_unique(config, stats, context); + CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, + SslStats& stats, TimeSource& time_source) override { + return std::make_unique(config, stats, time_source); } std::string name() const override { return "envoy.tls.cert_validator.spiffe"; } diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h index 0c8a93a80f16..4bb140dbe63b 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h @@ -35,7 +35,7 @@ class SPIFFEValidator : public CertValidator { SPIFFEValidator(SslStats& stats, TimeSource& time_source) : stats_(stats), time_source_(time_source){}; SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - Server::Configuration::CommonFactoryContext& context); + TimeSource& time_source); ~SPIFFEValidator() override = default; // Tls::CertValidator diff --git a/source/extensions/transport_sockets/tls/config.cc b/source/extensions/transport_sockets/tls/config.cc index 1d652c0d1545..ec2f39b60aed 100644 --- a/source/extensions/transport_sockets/tls/config.cc +++ b/source/extensions/transport_sockets/tls/config.cc @@ -51,9 +51,8 @@ ProtobufTypes::MessagePtr DownstreamSslSocketFactory::createEmptyConfigProto() { LEGACY_REGISTER_FACTORY(DownstreamSslSocketFactory, Server::Configuration::DownstreamTransportSocketConfigFactory, "tls"); -Ssl::ContextManagerPtr SslContextManagerFactory::createContextManager( - Server::Configuration::CommonFactoryContext& factory_context) { - return std::make_unique(factory_context); +Ssl::ContextManagerPtr SslContextManagerFactory::createContextManager(TimeSource& time_source) { + return std::make_unique(time_source); } static Envoy::Registry::RegisterInternalFactory(admin()->getConfigTracker()); - ssl_context_manager_ = createContextManager("ssl_context_manager", server_contexts_); + ssl_context_manager_ = createContextManager("ssl_context_manager", api_->timeSource()); cluster_manager_factory_ = std::make_unique( server_contexts_, stats(), threadLocal(), http_context_, [this]() -> Network::DnsResolverSharedPtr { return this->dnsResolver(); }, diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 9f1bb41c031d..fc301751e57d 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -123,7 +123,6 @@ class ValidationInstance final : Logger::Loggable, bool enableReusePortDefault() override { return true; } Configuration::StatsConfig& statsConfig() override { return config_.statsConfig(); } - Regex::Engine& regexEngine() override { return *regex_engine_; } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return bootstrap_; } Configuration::ServerFactoryContext& serverFactoryContext() override { return server_contexts_; } Configuration::TransportSocketFactoryContext& transportSocketFactoryContext() override { @@ -195,7 +194,6 @@ class ValidationInstance final : Logger::Loggable, Filter::TcpListenerFilterConfigProviderManagerImpl tcp_listener_config_provider_manager_; Server::DrainManagerPtr drain_manager_; HotRestartNopImpl nop_hot_restart_; - Regex::EnginePtr regex_engine_; }; } // namespace Server diff --git a/source/server/server.cc b/source/server/server.cc index a570ccf16cce..1bd625cd09dc 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -749,7 +749,7 @@ absl::Status InstanceBase::initializeOrThrow(Network::Address::InstanceConstShar } // Once we have runtime we can initialize the SSL context manager. - ssl_context_manager_ = createContextManager("ssl_context_manager", server_contexts_); + ssl_context_manager_ = createContextManager("ssl_context_manager", time_source_); cluster_manager_factory_ = std::make_unique( serverFactoryContext(), stats_store_, thread_local_, http_context_, diff --git a/source/server/server.h b/source/server/server.h index 0d4d76bc97f1..3afd1348aaba 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -197,7 +197,6 @@ class ServerFactoryContextImpl : public Configuration::ServerFactoryContext, ProcessContextOptRef processContext() override { return server_.processContext(); } Envoy::Server::DrainManager& drainManager() override { return server_.drainManager(); } ServerLifecycleNotifier& lifecycleNotifier() override { return server_.lifecycleNotifier(); } - Regex::Engine& regexEngine() override { return server_.regexEngine(); } Configuration::StatsConfig& statsConfig() override { return server_.statsConfig(); } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return server_.bootstrap(); } OverloadManager& overloadManager() override { return server_.overloadManager(); } @@ -293,7 +292,6 @@ class InstanceBase : Logger::Loggable, TimeSource& timeSource() override { return time_source_; } void flushStats() override; Configuration::StatsConfig& statsConfig() override { return config_.statsConfig(); } - Regex::Engine& regexEngine() override { return *regex_engine_; } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return bootstrap_; } Configuration::ServerFactoryContext& serverFactoryContext() override { return server_contexts_; } Configuration::TransportSocketFactoryContext& transportSocketFactoryContext() override { diff --git a/source/server/ssl_context_manager.cc b/source/server/ssl_context_manager.cc index fadc32283163..8a9fec347dee 100644 --- a/source/server/ssl_context_manager.cc +++ b/source/server/ssl_context_manager.cc @@ -49,13 +49,12 @@ class SslContextManagerNoTlsStub final : public Envoy::Ssl::ContextManager { } }; -Ssl::ContextManagerPtr -createContextManager(const std::string& factory_name, - Server::Configuration::CommonFactoryContext& factory_context) { +Ssl::ContextManagerPtr createContextManager(const std::string& factory_name, + TimeSource& time_source) { Ssl::ContextManagerFactory* factory = Registry::FactoryRegistry::getFactory(factory_name); if (factory != nullptr) { - return factory->createContextManager(factory_context); + return factory->createContextManager(time_source); } return std::make_unique(); diff --git a/source/server/ssl_context_manager.h b/source/server/ssl_context_manager.h index c296955703fe..4b618e6e64f4 100644 --- a/source/server/ssl_context_manager.h +++ b/source/server/ssl_context_manager.h @@ -6,9 +6,8 @@ namespace Envoy { namespace Server { -Ssl::ContextManagerPtr -createContextManager(const std::string& factory_name, - Server::Configuration::CommonFactoryContext& factory_context); +Ssl::ContextManagerPtr createContextManager(const std::string& factory_name, + TimeSource& time_source); } // namespace Server } // namespace Envoy diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 0b52d9c55e82..27baab111f4f 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -510,8 +510,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { Upstream::MockClusterManager cm_; NiceMock local_info_; Runtime::MockLoader runtime_; - testing::NiceMock server_factory_context_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{test_time_.timeSystem()}; NiceMock random_; Http::AsyncClientPtr http_async_client_; Http::ConnectionPool::InstancePtr http_conn_pool_; diff --git a/test/common/quic/BUILD b/test/common/quic/BUILD index 19437fb2be92..b488b190353d 100644 --- a/test/common/quic/BUILD +++ b/test/common/quic/BUILD @@ -64,7 +64,6 @@ envoy_cc_test( "//source/common/quic:envoy_quic_proof_verifier_lib", "//source/common/tls:context_config_lib", "//test/mocks/network:network_mocks", - "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/test_common:test_runtime_lib", "@com_github_google_quiche//:quic_core_versions_lib", @@ -109,7 +108,6 @@ envoy_cc_test( "//test/common/config:dummy_config_proto_cc_proto", "//test/common/tls/cert_validator:timed_cert_validator", "//test/mocks/event:event_mocks", - "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "@com_github_google_quiche//:quic_test_tools_test_certificates_lib", ], diff --git a/test/common/quic/envoy_quic_proof_source_test.cc b/test/common/quic/envoy_quic_proof_source_test.cc index afe1e521378e..a6814a40c52d 100644 --- a/test/common/quic/envoy_quic_proof_source_test.cc +++ b/test/common/quic/envoy_quic_proof_source_test.cc @@ -9,7 +9,6 @@ #include "test/common/quic/test_utils.h" #include "test/mocks/network/mocks.h" -#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/test_common/test_runtime.h" @@ -72,7 +71,7 @@ class SignatureVerifier { const absl::optional nullopt = absl::nullopt; ON_CALL(cert_validation_ctx_config_, customValidatorConfig()).WillByDefault(ReturnRef(nullopt)); auto context = std::make_shared( - *store_.rootScope(), client_context_config_, server_factory_context_); + *store_.rootScope(), client_context_config_, time_system_); ON_CALL(verify_context_, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); ON_CALL(verify_context_, transportSocketOptions()) .WillByDefault(ReturnRef(transport_socket_options_)); @@ -106,7 +105,6 @@ class SignatureVerifier { NiceMock store_; Event::GlobalTimeSystem time_system_; NiceMock client_context_config_; - NiceMock server_factory_context_; NiceMock cert_validation_ctx_config_; std::unique_ptr verifier_; NiceMock tls_context_manager_; @@ -226,8 +224,7 @@ class EnvoyQuicProofSourceTest : public ::testing::Test { Network::MockFilterChainManager filter_chain_manager_; Network::MockListenSocket listen_socket_; testing::NiceMock listener_config_; - Server::Configuration::MockServerFactoryContext factory_context_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{factory_context_}; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{time_system_}; Ssl::MockServerContextConfig* mock_context_config_; std::function secret_update_callback_; std::unique_ptr transport_socket_factory_; @@ -394,9 +391,7 @@ class LegacyEnvoyQuicProofSourceTest : public ::testing::Test { Network::MockFilterChainManager filter_chain_manager_; Network::MockListenSocket listen_socket_; testing::NiceMock listener_config_; - testing::NiceMock server_factory_context_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{ - server_factory_context_}; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{time_system_}; Ssl::MockServerContextConfig* mock_context_config_; std::unique_ptr transport_socket_factory_; Ssl::MockTlsCertificateConfig tls_cert_config_; diff --git a/test/common/quic/envoy_quic_proof_verifier_test.cc b/test/common/quic/envoy_quic_proof_verifier_test.cc index 97b5bd52724a..ab12465385eb 100644 --- a/test/common/quic/envoy_quic_proof_verifier_test.cc +++ b/test/common/quic/envoy_quic_proof_verifier_test.cc @@ -9,7 +9,6 @@ #include "test/common/quic/test_utils.h" #include "test/common/tls/cert_validator/timed_cert_validator.h" #include "test/mocks/event/mocks.h" -#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/test_time.h" @@ -81,7 +80,7 @@ class EnvoyQuicProofVerifierTest : public testing::Test { EXPECT_CALL(cert_validation_ctx_config_, customValidatorConfig()) .WillRepeatedly(ReturnRef(custom_validator_config_)); auto context = std::make_shared( - *store_.rootScope(), client_context_config_, factory_context_); + *store_.rootScope(), client_context_config_, time_system_); verifier_ = std::make_unique(std::move(context)); } @@ -99,7 +98,7 @@ class EnvoyQuicProofVerifierTest : public testing::Test { absl::optional custom_validator_config_{ absl::nullopt}; NiceMock store_; - Server::Configuration::MockServerFactoryContext factory_context_; + Event::GlobalTimeSystem time_system_; NiceMock client_context_config_; Ssl::MockCertificateValidationContextConfig cert_validation_ctx_config_; std::unique_ptr verifier_; diff --git a/test/common/tls/cert_validator/BUILD b/test/common/tls/cert_validator/BUILD index 76c9d63dc831..f0b243a980d3 100644 --- a/test/common/tls/cert_validator/BUILD +++ b/test/common/tls/cert_validator/BUILD @@ -21,7 +21,6 @@ envoy_cc_test( "//source/common/tls/cert_validator:cert_validator_lib", "//test/common/tls:ssl_test_utils", "//test/common/tls/cert_validator:test_common", - "//test/mocks/server:server_factory_context_mocks", "//test/test_common:environment_lib", "//test/test_common:test_runtime_lib", ], @@ -57,7 +56,6 @@ envoy_cc_test( deps = [ "//source/common/protobuf:utility_lib", "//source/common/tls/cert_validator:cert_validator_lib", - "//test/mocks/server:server_factory_context_mocks", "//test/test_common:utility_lib", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", ], diff --git a/test/common/tls/cert_validator/default_validator_integration_test.cc b/test/common/tls/cert_validator/default_validator_integration_test.cc index ac5b3232863f..a76e87ed04fd 100644 --- a/test/common/tls/cert_validator/default_validator_integration_test.cc +++ b/test/common/tls/cert_validator/default_validator_integration_test.cc @@ -14,8 +14,8 @@ namespace Ssl { void SslCertValidatorIntegrationTest::initialize() { HttpIntegrationTest::initialize(); - context_manager_ = std::make_unique( - factory_context_.serverFactoryContext()); + context_manager_ = + std::make_unique(timeSystem()); registerTestServerPorts({"http"}); test_server_->counter(listenerStatPrefix("ssl.fail_verify_error"))->reset(); diff --git a/test/common/tls/cert_validator/default_validator_test.cc b/test/common/tls/cert_validator/default_validator_test.cc index aff9ed58697d..0fa44d2d6053 100644 --- a/test/common/tls/cert_validator/default_validator_test.cc +++ b/test/common/tls/cert_validator/default_validator_test.cc @@ -6,7 +6,6 @@ #include "test/common/tls/cert_validator/test_common.h" #include "test/common/tls/ssl_test_utility.h" -#include "test/mocks/server/server_factory_context.h" #include "test/test_common/environment.h" #include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" @@ -35,34 +34,28 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltNameDNSMatched) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameDNSMatched) { - NiceMock context; - bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameIncorrectTypeMatched) { - NiceMock context; - bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameWildcardDNSMatched) { - NiceMock context; - bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir " "}}/test/common/tls/test_data/san_multiple_dns_cert.pem")); @@ -70,13 +63,11 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameWildcardDNSMatched) { matcher.set_exact("api.example.com"); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMultiLevelMatch) { - NiceMock context; - // san_multiple_dns_cert matches *.example.com bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir " @@ -85,7 +76,7 @@ TEST(DefaultCertValidatorTest, TestMultiLevelMatch) { matcher.set_exact("foo.api.example.com"); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -107,15 +98,13 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltMultiDomain) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameURIMatched) { - NiceMock context; - bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_uri_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw(spiffe://lyft.com/[^/]*-team)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -128,40 +117,37 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltNameNotMatched) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameNotMatched) { - NiceMock context; - bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example\.net)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_IPADD, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_IPADD, matcher)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { - NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, context); + /*CertificateValidationContextConfig=*/nullptr, stats, + Event::GlobalTimeSystem().timeSystem()); bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); std::vector san_matchers; - san_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); + san_matchers.push_back(SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); // Verify the certificate with correct SAN regex matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, san_matchers, nullptr, nullptr), @@ -171,7 +157,7 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { matcher.MergeFrom(TestUtility::createExactMatcher("hello.example.com")); std::vector invalid_san_matchers; invalid_san_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); std::string error; // Verify the certificate with incorrect SAN exact matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, @@ -181,13 +167,13 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithNoValidationContext) { - NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, context); + /*CertificateValidationContextConfig=*/nullptr, stats, + Event::GlobalTimeSystem().timeSystem()); EXPECT_EQ(default_validator->verifyCertificate(/*cert=*/nullptr, /*verify_san_list=*/{}, /*subject_alt_name_matchers=*/{}, nullptr, @@ -205,13 +191,13 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithNoValidationContex } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithEmptyCertChain) { - NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, context); + /*CertificateValidationContextConfig=*/nullptr, stats, + Event::GlobalTimeSystem().timeSystem()); SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); bssl::UniquePtr cert_chain(sk_X509_new_null()); @@ -224,20 +210,18 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithEmptyCertChain) { } TEST(DefaultCertValidatorTest, NoSanInCert) { - NiceMock context; - bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/fake_ca_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example\.net)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, WithVerifyDepth) { - NiceMock context; + Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); envoy::config::core::v3::TypedExtensionConfig typed_conf; @@ -261,8 +245,8 @@ TEST(DefaultCertValidatorTest, WithVerifyDepth) { std::make_unique(typed_conf, false, san_matchers, ca_cert_str, 2); auto default_validator = - std::make_unique(test_config.get(), - stats, context); + std::make_unique( + test_config.get(), stats, Event::GlobalTimeSystem().timeSystem()); STACK_OF(X509)* intermediates = cert_chain.get(); SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); @@ -282,7 +266,7 @@ TEST(DefaultCertValidatorTest, WithVerifyDepth) { test_config = std::make_unique(typed_conf, false, san_matchers, ca_cert_str); default_validator = std::make_unique( - test_config.get(), stats, context); + test_config.get(), stats, Event::GlobalTimeSystem().timeSystem()); // Re-initialize context ssl_ctx = SSL_CTX_new(TLS_method()); @@ -337,8 +321,6 @@ class MockCertificateValidationContextConfig : public Ssl::CertificateValidation }; TEST(DefaultCertValidatorTest, TestUnexpectedSanMatcherType) { - NiceMock context; - auto mock_context_config = std::make_unique(); EXPECT_CALL(*mock_context_config.get(), trustChainVerification()) .WillRepeatedly(testing::Return(envoy::extensions::transport_sockets::tls::v3:: @@ -347,16 +329,14 @@ TEST(DefaultCertValidatorTest, TestUnexpectedSanMatcherType) { std::vector(); Stats::TestUtil::TestStore store; auto ssl_stats = generateSslStats(*store.rootScope()); - auto validator = - std::make_unique(mock_context_config.get(), ssl_stats, context); + auto validator = std::make_unique(mock_context_config.get(), ssl_stats, + Event::GlobalTimeSystem().timeSystem()); auto ctx = std::vector(); EXPECT_THROW_WITH_REGEX(validator->initializeSslContexts(ctx, false), EnvoyException, "Failed to create string SAN matcher of type.*"); } TEST(DefaultCertValidatorTest, TestInitializeSslContextFailure) { - NiceMock context; - auto mock_context_config = std::make_unique( "-----BEGIN CERTIFICATE-----\nincomplete payload"); EXPECT_CALL(*mock_context_config.get(), trustChainVerification()) @@ -365,8 +345,8 @@ TEST(DefaultCertValidatorTest, TestInitializeSslContextFailure) { Stats::TestUtil::TestStore store; auto ssl_stats = generateSslStats(*store.rootScope()); - auto validator = - std::make_unique(mock_context_config.get(), ssl_stats, context); + auto validator = std::make_unique(mock_context_config.get(), ssl_stats, + Event::GlobalTimeSystem().timeSystem()); auto ctx = std::vector(); EXPECT_THROW_WITH_REGEX(validator->initializeSslContexts(ctx, false), EnvoyException, "Failed to load trusted CA certificates from.*"); diff --git a/test/common/tls/cert_validator/san_matcher_test.cc b/test/common/tls/cert_validator/san_matcher_test.cc index 3b330866a379..11f67e467119 100644 --- a/test/common/tls/cert_validator/san_matcher_test.cc +++ b/test/common/tls/cert_validator/san_matcher_test.cc @@ -4,7 +4,6 @@ #include "source/common/protobuf/utility.h" #include "source/common/tls/cert_validator/san_matcher.h" -#include "test/mocks/server/server_factory_context.h" #include "test/test_common/utility.h" #include "gmock/gmock.h" @@ -17,8 +16,6 @@ namespace Tls { // Verify that we get a valid string san matcher for all valid san types. TEST(SanMatcherConfigTest, TestValidSanType) { - NiceMock context; - // Iterate over all san type enums. for (envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType san_type = envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType_MIN; @@ -32,9 +29,9 @@ TEST(SanMatcherConfigTest, TestValidSanType) { san_matcher.set_san_type(san_type); if (san_type == envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher:: SAN_TYPE_UNSPECIFIED) { - EXPECT_DEATH(createStringSanMatcher(san_matcher, context), "unhandled value"); + EXPECT_DEATH(createStringSanMatcher(san_matcher), "unhandled value"); } else { - const SanMatcherPtr matcher = createStringSanMatcher(san_matcher, context); + const SanMatcherPtr matcher = createStringSanMatcher(san_matcher); EXPECT_NE(matcher.get(), nullptr); // Verify that the message is valid. TestUtility::validate(san_matcher); @@ -43,7 +40,6 @@ TEST(SanMatcherConfigTest, TestValidSanType) { } TEST(SanMatcherConfigTest, UnspecifiedSanType) { - NiceMock context; envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher san_matcher; san_matcher.mutable_matcher()->set_exact("foo.example"); // Do not set san_type @@ -58,7 +54,7 @@ TEST(SanMatcherConfigTest, UnspecifiedSanType) { static_cast( static_cast(123)); san_matcher.set_san_type(san_type); - EXPECT_EQ(createStringSanMatcher(san_matcher, context), nullptr); + EXPECT_EQ(createStringSanMatcher(san_matcher), nullptr); } } // namespace Tls diff --git a/test/common/tls/cert_validator/timed_cert_validator.h b/test/common/tls/cert_validator/timed_cert_validator.h index 2ea320d87af2..45a57fc5eda2 100644 --- a/test/common/tls/cert_validator/timed_cert_validator.h +++ b/test/common/tls/cert_validator/timed_cert_validator.h @@ -18,9 +18,8 @@ class TimedCertValidator : public DefaultCertValidator { public: TimedCertValidator(std::chrono::milliseconds validation_time_out_ms, const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - Server::Configuration::CommonFactoryContext& context, - absl::optional expected_host_name) - : DefaultCertValidator(config, stats, context), + TimeSource& time_source, absl::optional expected_host_name) + : DefaultCertValidator(config, stats, time_source), validation_time_out_ms_(validation_time_out_ms), expected_host_name_(expected_host_name) {} ValidationResults @@ -50,11 +49,10 @@ class TimedCertValidator : public DefaultCertValidator { class TimedCertValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr - createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - Server::Configuration::CommonFactoryContext& context) override { + CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, + SslStats& stats, TimeSource& time_source) override { auto validator = std::make_unique(validation_time_out_ms_, config, stats, - context, expected_host_name_); + time_source, expected_host_name_); if (expected_peer_address_.has_value()) { validator->setExpectedPeerAddress(expected_peer_address_.value()); } diff --git a/test/common/tls/context_impl_test.cc b/test/common/tls/context_impl_test.cc index faa1414733d7..dadbae2c6f7b 100644 --- a/test/common/tls/context_impl_test.cc +++ b/test/common/tls/context_impl_test.cc @@ -118,8 +118,8 @@ class SslContextImplTest : public SslCertsTest { } protected: - NiceMock server_factory_context_; - ContextManagerImpl manager_{server_factory_context_}; + Event::SimulatedTimeSystem time_system_; + ContextManagerImpl manager_{time_system_}; }; TEST_F(SslContextImplTest, TestCipherSuites) { @@ -1157,8 +1157,8 @@ class ClientContextConfigImplTest : public SslCertsTest { }}; } - NiceMock server_factory_context_; - ContextManagerImpl manager_{server_factory_context_}; + Event::SimulatedTimeSystem time_system_; + ContextManagerImpl manager_{time_system_}; }; // Validate that empty SNI (according to C string rules) fails config validation. @@ -1288,7 +1288,8 @@ TEST_F(ClientContextConfigImplTest, RSA3072Cert) { TestUtility::loadFromYaml(TestEnvironment::substitute(tls_certificate_yaml), *tls_context.mutable_common_tls_context()->add_tls_certificates()); ClientContextConfigImpl client_context_config(tls_context, factory_context_); - ContextManagerImpl manager(server_factory_context_); + Event::SimulatedTimeSystem time_system; + ContextManagerImpl manager(time_system); Stats::IsolatedStoreImpl store; auto context = manager_.createSslClientContext(*store.rootScope(), client_context_config); auto cleanup = cleanUpHelper(context); @@ -1764,10 +1765,7 @@ TEST_F(ClientContextConfigImplTest, MissingStaticCertificateValidationContext) { "Unknown static certificate validation context: missing"); } -class ServerContextConfigImplTest : public SslCertsTest { -public: - NiceMock server_factory_context_; -}; +class ServerContextConfigImplTest : public SslCertsTest {}; // Multiple TLS certificates are supported. TEST_F(ServerContextConfigImplTest, MultipleTlsCertificates) { @@ -1898,7 +1896,8 @@ TEST_F(ServerContextConfigImplTest, TlsCertificateNonEmpty) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; tls_context.mutable_common_tls_context()->add_tls_certificates(); ServerContextConfigImpl client_context_config(tls_context, factory_context_); - ContextManagerImpl manager(server_factory_context_); + Event::SimulatedTimeSystem time_system; + ContextManagerImpl manager(time_system); Stats::IsolatedStoreImpl store; EXPECT_THROW_WITH_MESSAGE( Envoy::Ssl::ServerContextSharedPtr server_ctx(manager.createSslServerContext( @@ -2003,7 +2002,8 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureNoMethod) { NiceMock private_key_method_manager; auto private_key_method_provider_ptr = std::make_shared>(); - ContextManagerImpl manager(server_factory_context_); + Event::SimulatedTimeSystem time_system; + ContextManagerImpl manager(time_system); EXPECT_CALL(factory_context_, sslContextManager()).WillOnce(ReturnRef(context_manager)); EXPECT_CALL(context_manager, privateKeyMethodManager()) .WillOnce(ReturnRef(private_key_method_manager)); @@ -2201,8 +2201,8 @@ TEST_F(ServerContextConfigImplTest, Pkcs12LoadFailureBothPkcs12AndCertChain) { class TestContextImpl : public ContextImpl { public: TestContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, - Server::Configuration::ServerFactoryContext& factory_context) - : ContextImpl(scope, config, factory_context, nullptr), pool_(scope.symbolTable()), + TimeSource& time_source) + : ContextImpl(scope, config, time_source, nullptr), pool_(scope.symbolTable()), fallback_(pool_.add("fallback")) {} void incCounter(absl::string_view name, absl::string_view value) { @@ -2220,7 +2220,7 @@ class SslContextStatsTest : public SslContextImplTest { client_context_config_ = std::make_unique(tls_context_, factory_context_); context_ = std::make_unique(*store_.rootScope(), *client_context_config_, - server_factory_context_); + time_system_); } Stats::TestUtil::TestStore store_; diff --git a/test/common/tls/handshaker_factory_test.cc b/test/common/tls/handshaker_factory_test.cc index e161b5eea047..f60113708590 100644 --- a/test/common/tls/handshaker_factory_test.cc +++ b/test/common/tls/handshaker_factory_test.cc @@ -92,8 +92,8 @@ class HandshakerFactoryImplForTest class HandshakerFactoryTest : public testing::Test { protected: HandshakerFactoryTest() - : context_manager_(std::make_unique( - server_factory_context_)), + : context_manager_( + std::make_unique(time_system_)), registered_factory_(handshaker_factory_) { scoped_runtime_.mergeValues( {{"envoy.reloadable_features.no_extension_lookup_by_name", "false"}}); @@ -111,7 +111,7 @@ class HandshakerFactoryTest : public testing::Test { return SSL_get_SSL_CTX(ssl); } - NiceMock server_factory_context_; + Event::GlobalTimeSystem time_system_; Stats::IsolatedStoreImpl stats_store_; std::unique_ptr context_manager_; HandshakerFactoryImplForTest handshaker_factory_; @@ -248,8 +248,8 @@ class HandshakerFactoryImplForDownstreamTest class HandshakerFactoryDownstreamTest : public testing::Test { protected: HandshakerFactoryDownstreamTest() - : context_manager_(std::make_unique( - server_factory_context_)) { + : context_manager_( + std::make_unique(time_system_)) { scoped_runtime_.mergeValues( {{"envoy.reloadable_features.no_extension_lookup_by_name", "false"}}); } @@ -262,7 +262,7 @@ class HandshakerFactoryDownstreamTest : public testing::Test { return SSL_get_SSL_CTX(ssl); } - NiceMock server_factory_context_; + Event::GlobalTimeSystem time_system_; Stats::IsolatedStoreImpl stats_store_; std::unique_ptr context_manager_; envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context_; diff --git a/test/common/tls/integration/ssl_integration_test.cc b/test/common/tls/integration/ssl_integration_test.cc index ffae471e9e90..885494ce1f33 100644 --- a/test/common/tls/integration/ssl_integration_test.cc +++ b/test/common/tls/integration/ssl_integration_test.cc @@ -66,8 +66,8 @@ void SslIntegrationTestBase::initialize() { HttpIntegrationTest::initialize(); - context_manager_ = std::make_unique( - server_factory_context_); + context_manager_ = + std::make_unique(timeSystem()); registerTestServerPorts({"http"}); } diff --git a/test/common/tls/ssl_socket_test.cc b/test/common/tls/ssl_socket_test.cc index dc231f41eaad..b72f7ad6596b 100644 --- a/test/common/tls/ssl_socket_test.cc +++ b/test/common/tls/ssl_socket_test.cc @@ -47,7 +47,6 @@ #include "test/mocks/network/mocks.h" #include "test/mocks/runtime/mocks.h" #include "test/mocks/secret/mocks.h" -#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" @@ -347,9 +346,8 @@ void testUtil(const TestUtilOptions& options) { Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; testing::NiceMock - transport_socket_factory_context; - ON_CALL(transport_socket_factory_context.server_context_, api()) - .WillByDefault(ReturnRef(*server_api)); + server_factory_context; + ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); // For private key method testing. NiceMock context_manager; @@ -358,7 +356,7 @@ void testUtil(const TestUtilOptions& options) { test_private_key_method_factory(test_factory); PrivateKeyMethodManagerImpl private_key_method_manager; if (options.expectedPrivateKeyMethod()) { - EXPECT_CALL(transport_socket_factory_context, sslContextManager()) + EXPECT_CALL(server_factory_context, sslContextManager()) .WillOnce(ReturnRef(context_manager)) .WillRepeatedly(ReturnRef(context_manager)); EXPECT_CALL(context_manager, privateKeyMethodManager()) @@ -369,10 +367,9 @@ void testUtil(const TestUtilOptions& options) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(options.serverCtxYaml()), server_tls_context); - auto server_cfg = std::make_unique(server_tls_context, - transport_socket_factory_context); - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + auto server_cfg = + std::make_unique(server_tls_context, server_factory_context); + ContextManagerImpl manager(*time_system); Event::DispatcherPtr dispatcher = server_api->allocateDispatcher("test_thread"); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -710,10 +707,7 @@ class TestUtilOptionsV2 : public TestUtilOptionsBase { void testUtilV2(const TestUtilOptionsV2& options) { Event::SimulatedTimeSystem time_system; - NiceMock - transport_socket_factory_context; - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(*time_system); // SNI-based selection logic isn't happening in SslSocket anymore. ASSERT(options.listener().filter_chains().size() == 1); @@ -722,9 +716,10 @@ void testUtilV2(const TestUtilOptionsV2& options) { filter_chain.filter_chain_match().server_names().end()); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); + testing::NiceMock + server_factory_context; NiceMock runtime; - ON_CALL(transport_socket_factory_context.server_context_, api()) - .WillByDefault(ReturnRef(*server_api)); + ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const envoy::config::core::v3::TransportSocket& transport_socket = @@ -732,8 +727,7 @@ void testUtilV2(const TestUtilOptionsV2& options) { ASSERT(transport_socket.has_typed_config()); transport_socket.typed_config().UnpackTo(&tls_context); - auto server_cfg = - std::make_unique(tls_context, transport_socket_factory_context); + auto server_cfg = std::make_unique(tls_context, server_factory_context); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *server_stats_store.rootScope(), server_names); @@ -1022,8 +1016,7 @@ TEST_P(SslSocketTest, ServerTransportSocketOptions) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(time_system_); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); auto ssl_socket = server_ssl_socket_factory.createDownstreamTransportSocket(); @@ -3072,8 +3065,7 @@ TEST_P(SslSocketTest, FlushCloseDuringHandshake) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(time_system_); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3132,8 +3124,7 @@ TEST_P(SslSocketTest, HalfClose) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(time_system_); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3218,8 +3209,7 @@ TEST_P(SslSocketTest, ShutdownWithCloseNotify) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(time_system_); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3310,8 +3300,7 @@ TEST_P(SslSocketTest, ShutdownWithoutCloseNotify) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(time_system_); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3418,8 +3407,7 @@ TEST_P(SslSocketTest, ClientAuthMultipleCAs) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(time_system_); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3501,26 +3489,24 @@ void testTicketSessionResumption(const std::string& server_ctx_yaml1, const Network::Address::IpVersion ip_version, const uint32_t expected_lifetime_hint = 0) { Event::SimulatedTimeSystem time_system; - NiceMock - transport_socket_factory_context; - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(*time_system); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; - ON_CALL(transport_socket_factory_context.server_context_, api()) - .WillByDefault(ReturnRef(*server_api)); + testing::NiceMock + server_factory_context; + ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context1; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml1), server_tls_context1); - auto server_cfg1 = std::make_unique(server_tls_context1, - transport_socket_factory_context); + auto server_cfg1 = + std::make_unique(server_tls_context1, server_factory_context); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context2; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml2), server_tls_context2); - auto server_cfg2 = std::make_unique(server_tls_context2, - transport_socket_factory_context); + auto server_cfg2 = + std::make_unique(server_tls_context2, server_factory_context); ServerSslSocketFactory server_ssl_socket_factory1(std::move(server_cfg1), manager, *server_stats_store.rootScope(), server_names1); ServerSslSocketFactory server_ssl_socket_factory2(std::move(server_cfg2), manager, @@ -3661,21 +3647,19 @@ void testSupportForSessionResumption(const std::string& server_ctx_yaml, bool expect_stateful, const Network::Address::IpVersion ip_version) { Event::SimulatedTimeSystem time_system; - NiceMock - transport_socket_factory_context; - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(*time_system); Stats::IsolatedStoreImpl server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; - ON_CALL(transport_socket_factory_context.server_context_, api()) - .WillByDefault(ReturnRef(*server_api)); + testing::NiceMock + server_factory_context; + ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); - auto server_cfg = std::make_unique(server_tls_context, - transport_socket_factory_context); + auto server_cfg = + std::make_unique(server_tls_context, server_factory_context); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *server_stats_store.rootScope(), {}); @@ -4321,8 +4305,7 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context2; TestUtility::loadFromYaml(TestEnvironment::substitute(server2_ctx_yaml), tls_context2); auto server2_cfg = std::make_unique(tls_context2, factory_context_); - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(time_system_); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -4439,20 +4422,18 @@ void SslSocketTest::testClientSessionResumption(const std::string& server_ctx_ya const Network::Address::IpVersion version) { InSequence s; - NiceMock server_factory_context; - ContextManagerImpl manager(server_factory_context); + ContextManagerImpl manager(time_system_); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system_); testing::NiceMock - transport_socket_factory_context; - ON_CALL(transport_socket_factory_context.server_context_, api()) - .WillByDefault(ReturnRef(*server_api)); + server_factory_context; + ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_ctx_proto; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_ctx_proto); auto server_cfg = - std::make_unique(server_ctx_proto, transport_socket_factory_context); + std::make_unique(server_ctx_proto, server_factory_context); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -4717,7 +4698,7 @@ TEST_P(SslSocketTest, SslError) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(factory_context_.serverFactoryContext()); + ContextManagerImpl manager(time_system_); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -5249,7 +5230,7 @@ TEST_P(SslSocketTest, SetSignatureAlgorithms) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(factory_context_.serverFactoryContext()); + ContextManagerImpl manager(time_system_); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -5864,7 +5845,7 @@ TEST_P(SslSocketTest, DownstreamNotReadySslSocket) { EXPECT_TRUE(server_cfg->tlsCertificates().empty()); EXPECT_FALSE(server_cfg->isReady()); - ContextManagerImpl manager(factory_context_.serverFactoryContext()); + ContextManagerImpl manager(time_system_); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *factory_context_.store_.rootScope(), std::vector{}); @@ -5902,7 +5883,7 @@ TEST_P(SslSocketTest, UpstreamNotReadySslSocket) { EXPECT_TRUE(client_cfg->tlsCertificates().empty()); EXPECT_FALSE(client_cfg->isReady()); - ContextManagerImpl manager(factory_context_.serverFactoryContext()); + ContextManagerImpl manager(time_system_); ClientSslSocketFactory client_ssl_socket_factory(std::move(client_cfg), manager, *factory_context_.store_.rootScope()); auto transport_socket = client_ssl_socket_factory.createTransportSocket(nullptr, nullptr); @@ -5930,7 +5911,7 @@ TEST_P(SslSocketTest, TestTransportSocketCallback) { envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; auto client_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(factory_context_.serverFactoryContext()); + ContextManagerImpl manager(time_system_); ClientSslSocketFactory client_ssl_socket_factory(std::move(client_cfg), manager, *factory_context_.store_.rootScope()); @@ -5954,7 +5935,7 @@ class SslReadBufferLimitTest : public SslSocketTest { downstream_tls_context_); auto server_cfg = std::make_unique(downstream_tls_context_, factory_context_); - manager_ = std::make_unique(factory_context_.serverFactoryContext()); + manager_ = std::make_unique(time_system_); server_ssl_socket_factory_ = std::make_unique( std::move(server_cfg), *manager_, *server_stats_store_.rootScope(), std::vector{}); diff --git a/test/common/upstream/hds_test.cc b/test/common/upstream/hds_test.cc index c58700be552a..82acb9d72e8d 100644 --- a/test/common/upstream/hds_test.cc +++ b/test/common/upstream/hds_test.cc @@ -61,7 +61,8 @@ class HdsTest : public testing::Test { HdsTest() : retry_timer_(new Event::MockTimer()), server_response_timer_(new Event::MockTimer()), async_client_(new Grpc::MockAsyncClient()), - api_(Api::createApiForTest(stats_store_, random_)), ssl_context_manager_(server_context_) { + api_(Api::createApiForTest(stats_store_, random_)), + ssl_context_manager_(api_->timeSource()) { ON_CALL(server_context_, api()).WillByDefault(ReturnRef(*api_)); node_.set_id("hds-node"); } diff --git a/test/common/upstream/test_cluster_manager.h b/test/common/upstream/test_cluster_manager.h index a8e232eacb9e..eb110f9d672e 100644 --- a/test/common/upstream/test_cluster_manager.h +++ b/test/common/upstream/test_cluster_manager.h @@ -146,7 +146,8 @@ class TestClusterManagerFactory : public ClusterManagerFactory { new NiceMock}; NiceMock& runtime_ = server_context_.runtime_loader_; NiceMock& dispatcher_ = server_context_.dispatcher_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{server_context_}; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{ + dispatcher_.timeSource()}; NiceMock& local_info_ = server_context_.local_info_; NiceMock& admin_ = server_context_.admin_; NiceMock secret_manager_; diff --git a/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc b/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc index 1b7e15d0ffe0..90d2e7acde2a 100644 --- a/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc +++ b/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc @@ -143,8 +143,8 @@ class TcpGrpcAccessLogIntegrationTest : public Grpc::GrpcClientIntegrationParamT const Ssl::ClientSslTransportOptions& ssl_options = {}, const std::string& curves_list = "") { // Set up the SSL client. - context_manager_ = std::make_unique( - server_factory_context_); + context_manager_ = + std::make_unique(timeSystem()); Network::Address::InstanceConstSharedPtr address = Ssl::getSslAddress(version_, lookupPort("tcp_proxy")); context_ = Ssl::createClientSslTransportSocketFactory(ssl_options, *context_manager_, *api_); diff --git a/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc b/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc index aa2279c70839..78b57390d80d 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc +++ b/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc @@ -90,8 +90,8 @@ class TlsInspectorIntegrationTest : public testing::TestWithParam( - server_factory_context_); + context_manager_ = + std::make_unique(timeSystem()); } void setupConnections(bool listener_filter_disabled, bool expect_connection_open, bool ssl_client, diff --git a/test/extensions/string_matcher/lua/lua_test.cc b/test/extensions/string_matcher/lua/lua_test.cc index 3842b89f3469..4e834e0d6d71 100644 --- a/test/extensions/string_matcher/lua/lua_test.cc +++ b/test/extensions/string_matcher/lua/lua_test.cc @@ -88,20 +88,21 @@ TEST(LuaStringMatcher, LuaStdLib) { } TEST(LuaStringMatcher, NoCode) { - Api::MockApi api; - ThreadLocal::MockInstance tls; + ScopedInjectableLoader api_inject(std::make_unique()); + ScopedInjectableLoader tls_inject( + std::make_unique()); LuaStringMatcherFactory factory; ::envoy::extensions::string_matcher::lua::v3::Lua empty_config; ProtobufWkt::Any any; any.PackFrom(empty_config); - EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any, tls, api), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any), EnvoyException, "Failed to get lua string matcher code from source: INVALID_ARGUMENT: " "Unexpected DataSource::specifier_case(): 0"); empty_config.mutable_source_code()->set_inline_string(""); any.PackFrom(empty_config); - EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any, tls, api), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any), EnvoyException, "Failed to get lua string matcher code from source: INVALID_ARGUMENT: " "DataSource cannot be empty"); } diff --git a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc index 01a27199c184..cb2564e38286 100644 --- a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc +++ b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc @@ -208,8 +208,8 @@ void StartTlsIntegrationTest::initialize() { factory->createTransportSocketFactory(*config, factory_context_)}; // Setup factories and contexts for tls transport socket. - tls_context_manager_ = std::make_unique( - server_factory_context_); + tls_context_manager_ = + std::make_unique(timeSystem()); tls_context_ = Ssl::createClientSslTransportSocketFactory({}, *tls_context_manager_, *api_); payload_reader_ = std::make_shared(*dispatcher_); diff --git a/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc index b106d0043f30..f3efba23db8d 100644 --- a/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc +++ b/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc @@ -249,8 +249,8 @@ void StartTlsIntegrationTest::initialize() { // Setup factory and context for tls transport socket. // The tls transport socket will be inserted into fake_upstream when // upstream starttls transport socket is converted to secure mode. - tls_context_manager_ = std::make_unique( - server_factory_context_); + tls_context_manager_ = + std::make_unique(timeSystem()); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext downstream_tls_context; diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD b/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD index c95555b9be37..9f9644768aee 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD @@ -25,7 +25,6 @@ envoy_extension_cc_test( "//source/extensions/transport_sockets/tls/cert_validator/spiffe:config", "//test/common/tls:ssl_test_utils", "//test/common/tls/cert_validator:test_common", - "//test/mocks/server:server_factory_context_mocks", "//test/test_common:environment_lib", "//test/test_common:simulated_time_system_lib", "//test/test_common:test_runtime_lib", diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc index db9af217a43b..1806d0985ed1 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc @@ -21,8 +21,8 @@ void SslSPIFFECertValidatorIntegrationTest::initialize() { .setAllowExpiredCertificate(allow_expired_cert_)); HttpIntegrationTest::initialize(); - context_manager_ = std::make_unique( - server_factory_context_); + context_manager_ = + std::make_unique(timeSystem()); registerTestServerPorts({"http"}); } diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc index 327298a3efe6..51cc1ad97016 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc @@ -13,7 +13,6 @@ #include "test/common/tls/cert_validator/test_common.h" #include "test/common/tls/ssl_test_utility.h" -#include "test/mocks/server/server_factory_context.h" #include "test/test_common/environment.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/test_runtime.h" @@ -45,8 +44,7 @@ class TestSPIFFEValidator : public testing::Test { TestUtility::loadFromYaml(yaml, typed_conf); config_ = std::make_unique( typed_conf, allow_expired_certificate_, san_matchers_); - ON_CALL(factory_context_, timeSource()).WillByDefault(testing::ReturnRef(time_source)); - validator_ = std::make_unique(config_.get(), stats_, factory_context_); + validator_ = std::make_unique(config_.get(), stats_, time_source); } void initialize(std::string yaml) { @@ -54,7 +52,8 @@ class TestSPIFFEValidator : public testing::Test { TestUtility::loadFromYaml(yaml, typed_conf); config_ = std::make_unique( typed_conf, allow_expired_certificate_, san_matchers_); - validator_ = std::make_unique(config_.get(), stats_, factory_context_); + validator_ = + std::make_unique(config_.get(), stats_, config_->api().timeSource()); }; void initialize() { validator_ = std::make_unique(stats_, time_system_); } @@ -91,7 +90,6 @@ class TestSPIFFEValidator : public testing::Test { }; private: - NiceMock factory_context_; bool allow_expired_certificate_{false}; TestCertificateValidationContextConfigPtr config_; std::vector san_matchers_{}; diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index c5eb1129d41b..1f1df3f7c765 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -525,8 +525,7 @@ class BaseIntegrationTest : protected Logger::Loggable { createUpstreamTlsContext(const FakeUpstreamConfig& upstream_config); testing::NiceMock thread_local_; testing::NiceMock factory_context_; - testing::NiceMock server_factory_context_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{timeSystem()}; // The fake upstreams_ are created using the context_manager, so make sure // they are destroyed before it is. diff --git a/test/integration/sds_static_integration_test.cc b/test/integration/sds_static_integration_test.cc index 0e63eb3d4267..49d42c1eb6c2 100644 --- a/test/integration/sds_static_integration_test.cc +++ b/test/integration/sds_static_integration_test.cc @@ -89,7 +89,7 @@ class SdsStaticDownstreamIntegrationTest } private: - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{timeSystem()}; Network::UpstreamTransportSocketFactoryPtr client_ssl_ctx_; }; @@ -148,7 +148,7 @@ class SdsStaticUpstreamIntegrationTest : public testing::TestWithParam( - server_factory_context_); + context_manager_ = + std::make_unique(timeSystem()); payload_reader_ = std::make_shared(*dispatcher_); } diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 85019c6f45c2..3d06c513d7fa 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -239,8 +239,7 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt #ifdef ENVOY_ENABLE_QUIC testing::NiceMock threadlocal; - NiceMock server_factory_context; - Extensions::TransportSockets::Tls::ContextManagerImpl manager(server_factory_context); + Extensions::TransportSockets::Tls::ContextManagerImpl manager(time_system); Network::UpstreamTransportSocketFactoryPtr transport_socket_factory = createQuicUpstreamTransportSocketFactory(api, mock_stats_store, manager, threadlocal, "spiffe://lyft.com/backend-team"); diff --git a/test/integration/xds_integration_test.cc b/test/integration/xds_integration_test.cc index 497c0e6522fa..5435bb17d55e 100644 --- a/test/integration/xds_integration_test.cc +++ b/test/integration/xds_integration_test.cc @@ -193,7 +193,7 @@ class LdsInplaceUpdateTcpProxyIntegrationTest BaseIntegrationTest::initialize(); context_manager_ = std::make_unique( - server_factory_context_); + BaseIntegrationTest::timeSystem()); context_ = Ssl::createClientSslTransportSocketFactory({}, *context_manager_, *api_); } @@ -462,8 +462,8 @@ class LdsInplaceUpdateHttpIntegrationTest BaseIntegrationTest::initialize(); - context_manager_ = std::make_unique( - server_factory_context_); + context_manager_ = + std::make_unique(timeSystem()); context_ = Ssl::createClientSslTransportSocketFactory({}, *context_manager_, *api_); address_ = Ssl::getSslAddress(version_, lookupPort("http")); } diff --git a/test/integration/xfcc_integration_test.cc b/test/integration/xfcc_integration_test.cc index 689611d5345a..eeec6cf876b5 100644 --- a/test/integration/xfcc_integration_test.cc +++ b/test/integration/xfcc_integration_test.cc @@ -169,8 +169,8 @@ void XfccIntegrationTest::initialize() { config_helper_.addSslConfig(); } - context_manager_ = std::make_unique( - server_factory_context_); + context_manager_ = + std::make_unique(timeSystem()); client_tls_ssl_ctx_ = createClientSslContext(false); client_mtls_ssl_ctx_ = createClientSslContext(true); HttpIntegrationTest::initialize(); diff --git a/test/mocks/server/instance.cc b/test/mocks/server/instance.cc index eca664bd89a8..e40b1e125331 100644 --- a/test/mocks/server/instance.cc +++ b/test/mocks/server/instance.cc @@ -13,7 +13,7 @@ using ::testing::ReturnRef; MockInstance::MockInstance() : secret_manager_(std::make_unique(admin_.getConfigTracker())), - cluster_manager_(timeSource()), + cluster_manager_(timeSource()), ssl_context_manager_(timeSource()), singleton_manager_(new Singleton::ManagerImpl(Thread::threadFactoryForTest())), grpc_context_(stats_store_.symbolTable()), http_context_(stats_store_.symbolTable()), router_context_(stats_store_.symbolTable()), quic_stat_names_(stats_store_.symbolTable()), @@ -21,8 +21,7 @@ MockInstance::MockInstance() server_factory_context_( std::make_shared>()), transport_socket_factory_context_( - std::make_shared>()), - ssl_context_manager_(*server_factory_context_) { + std::make_shared>()) { ON_CALL(*this, threadLocal()).WillByDefault(ReturnRef(thread_local_)); ON_CALL(*this, stats()).WillByDefault(ReturnRef(stats_store_)); ON_CALL(*this, grpcContext()).WillByDefault(ReturnRef(grpc_context_)); diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index de109c9c5a97..a8561d534b9c 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -52,7 +52,6 @@ class MockInstance : public Instance { MOCK_METHOD(ThreadLocal::Instance&, threadLocal, ()); MOCK_METHOD(LocalInfo::LocalInfo&, localInfo, (), (const)); MOCK_METHOD(Configuration::StatsConfig&, statsConfig, (), ()); - MOCK_METHOD(Regex::Engine&, regexEngine, ()); MOCK_METHOD(void, flushStats, ()); MOCK_METHOD(ProtobufMessage::ValidationContext&, messageValidationContext, ()); MOCK_METHOD(Configuration::ServerFactoryContext&, serverFactoryContext, ()); @@ -79,6 +78,7 @@ class MockInstance : public Instance { testing::NiceMock cluster_manager_; Thread::MutexBasicLockable access_log_lock_; testing::NiceMock runtime_loader_; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_; testing::NiceMock dispatcher_; testing::NiceMock drain_manager_; testing::NiceMock access_log_manager_; @@ -101,7 +101,6 @@ class MockInstance : public Instance { server_factory_context_; std::shared_ptr> transport_socket_factory_context_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_; }; } // namespace Server diff --git a/test/mocks/server/server_factory_context.h b/test/mocks/server/server_factory_context.h index 128afe221c26..7e69dbad447e 100644 --- a/test/mocks/server/server_factory_context.h +++ b/test/mocks/server/server_factory_context.h @@ -79,7 +79,6 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(Server::DrainManager&, drainManager, ()); MOCK_METHOD(Init::Manager&, initManager, ()); MOCK_METHOD(ServerLifecycleNotifier&, lifecycleNotifier, ()); - Regex::Engine& regexEngine() override { return regex_engine_; } MOCK_METHOD(StatsConfig&, statsConfig, (), ()); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), ()); MOCK_METHOD(OverloadManager&, overloadManager, ()); @@ -108,7 +107,6 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { Router::ContextImpl router_context_; envoy::config::bootstrap::v3::Bootstrap bootstrap_; testing::NiceMock options_; - Regex::GoogleReEngine regex_engine_; }; class MockGenericFactoryContext : public GenericFactoryContext { @@ -157,7 +155,6 @@ class StatelessMockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(Server::DrainManager&, drainManager, ()); MOCK_METHOD(Init::Manager&, initManager, ()); MOCK_METHOD(ServerLifecycleNotifier&, lifecycleNotifier, ()); - MOCK_METHOD(Regex::Engine&, regexEngine, ()); MOCK_METHOD(StatsConfig&, statsConfig, (), ()); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), ()); MOCK_METHOD(OverloadManager&, overloadManager, ()); diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index cd8604d486dc..5ab396837967 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -62,7 +62,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/listener_managers/validation_listener_manager:70.5" "source/extensions/watchdog/profile_action:83.3" "source/server:91.0" # flaky: be careful adjusting. See https://github.com/envoyproxy/envoy/issues/15239 -"source/server/config_validation:88.9" +"source/server/config_validation:89.2" "source/extensions/health_checkers:96.0" "source/extensions/health_checkers/http:93.9" "source/extensions/health_checkers/grpc:92.0" diff --git a/test/server/BUILD b/test/server/BUILD index 2938f1dd3429..9cceee1474dc 100644 --- a/test/server/BUILD +++ b/test/server/BUILD @@ -368,7 +368,6 @@ envoy_cc_test( srcs = ["ssl_context_manager_test.cc"], deps = [ "//source/server:ssl_context_manager_lib", - "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/mocks/stats:stats_mocks", "//test/test_common:simulated_time_system_lib", diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index 12db1d70e553..02442808b230 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -40,8 +40,7 @@ TEST(ValidationClusterManagerTest, MockedMethods) { testing::NiceMock secret_manager; auto dns_resolver = std::make_shared>(); - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager{ - *server.server_factory_context_}; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager{api->timeSource()}; Http::ContextImpl http_context(stats_store.symbolTable()); Quic::QuicStatNames quic_stat_names(stats_store.symbolTable()); diff --git a/test/server/ssl_context_manager_test.cc b/test/server/ssl_context_manager_test.cc index 4a651e61dc31..dbe2bdc8db6e 100644 --- a/test/server/ssl_context_manager_test.cc +++ b/test/server/ssl_context_manager_test.cc @@ -2,9 +2,9 @@ #include "source/server/ssl_context_manager.h" -#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" +#include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" @@ -14,15 +14,14 @@ namespace Server { namespace { TEST(SslContextManager, createStub) { + Event::SimulatedTimeSystem time_system; Stats::MockStore store; Stats::Scope& scope(*store.rootScope()); Ssl::MockClientContextConfig client_config; Ssl::MockServerContextConfig server_config; std::vector server_names; - NiceMock server_factory_context; - Ssl::ContextManagerPtr manager = - createContextManager("fake_factory_name", server_factory_context); + Ssl::ContextManagerPtr manager = createContextManager("fake_factory_name", time_system); // Check we've created a stub, not real manager. EXPECT_EQ(manager->daysUntilFirstCertExpires().value(), std::numeric_limits::max()); From 2bf682636dbaffc6859c42cfd50777f9a73a40c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:32:00 +0000 Subject: [PATCH 023/124] build(deps): bump google.golang.org/protobuf from 1.32.0 to 1.33.0 in /contrib/golang/filters/http/test/test_data/action (#32891) build(deps): bump google.golang.org/protobuf Bumps google.golang.org/protobuf from 1.32.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- contrib/golang/filters/http/test/test_data/action/go.mod | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/golang/filters/http/test/test_data/action/go.mod b/contrib/golang/filters/http/test/test_data/action/go.mod index bda83466971f..56a3bc4ad157 100644 --- a/contrib/golang/filters/http/test/test_data/action/go.mod +++ b/contrib/golang/filters/http/test/test_data/action/go.mod @@ -4,8 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require github.com/google/go-cmp v0.5.9 // indirect - -require google.golang.org/protobuf v1.32.0 // indirect +require google.golang.org/protobuf v1.33.0 // indirect replace github.com/envoyproxy/envoy => ../../../../../../../ From 6142523b14ff01fa6cd11f948676405f51629681 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:32:11 +0000 Subject: [PATCH 024/124] build(deps): bump google.golang.org/protobuf from 1.30.0 to 1.33.0 in /contrib/golang/filters/http/test/test_data/metric (#32889) build(deps): bump google.golang.org/protobuf Bumps google.golang.org/protobuf from 1.30.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- contrib/golang/filters/http/test/test_data/metric/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/golang/filters/http/test/test_data/metric/go.mod b/contrib/golang/filters/http/test/test_data/metric/go.mod index b70054d4863a..2d4e1c1f68ee 100644 --- a/contrib/golang/filters/http/test/test_data/metric/go.mod +++ b/contrib/golang/filters/http/test/test_data/metric/go.mod @@ -4,6 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 replace github.com/envoyproxy/envoy => ../../../../../../../ From 17efb8ac7d75a28ac76b31ae309448432738b76d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:32:18 +0000 Subject: [PATCH 025/124] build(deps): bump google.golang.org/protobuf from 1.30.0 to 1.33.0 in /contrib/golang/filters/http/test/test_data/access_log (#32888) build(deps): bump google.golang.org/protobuf Bumps google.golang.org/protobuf from 1.30.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- contrib/golang/filters/http/test/test_data/access_log/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/golang/filters/http/test/test_data/access_log/go.mod b/contrib/golang/filters/http/test/test_data/access_log/go.mod index 01693aa92cd2..ed46c345ae77 100644 --- a/contrib/golang/filters/http/test/test_data/access_log/go.mod +++ b/contrib/golang/filters/http/test/test_data/access_log/go.mod @@ -4,6 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 replace github.com/envoyproxy/envoy => ../../../../../../../ From 85a24e93bdc81ba14b25d8cb16ca6f6c81d4dead Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:32:25 +0000 Subject: [PATCH 026/124] build(deps): bump google.golang.org/protobuf from 1.30.0 to 1.33.0 in /contrib/golang/filters/network/test/test_data (#32887) build(deps): bump google.golang.org/protobuf Bumps google.golang.org/protobuf from 1.30.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- contrib/golang/filters/network/test/test_data/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/golang/filters/network/test/test_data/go.mod b/contrib/golang/filters/network/test/test_data/go.mod index 8c20e9bd14f6..e1a332e4375c 100644 --- a/contrib/golang/filters/network/test/test_data/go.mod +++ b/contrib/golang/filters/network/test/test_data/go.mod @@ -4,6 +4,6 @@ go 1.18 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 // indirect replace github.com/envoyproxy/envoy => ../../../../../../ From 329f42c923af64ef3f834992482670bd3764b44a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:32:31 +0000 Subject: [PATCH 027/124] build(deps): bump google.golang.org/protobuf from 1.30.0 to 1.33.0 in /contrib/golang/filters/http/test/test_data/dummy (#32886) build(deps): bump google.golang.org/protobuf Bumps google.golang.org/protobuf from 1.30.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- contrib/golang/filters/http/test/test_data/dummy/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/golang/filters/http/test/test_data/dummy/go.mod b/contrib/golang/filters/http/test/test_data/dummy/go.mod index beaf9d59c743..7f3cef141617 100644 --- a/contrib/golang/filters/http/test/test_data/dummy/go.mod +++ b/contrib/golang/filters/http/test/test_data/dummy/go.mod @@ -4,6 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 // indirect replace github.com/envoyproxy/envoy => ../../../../../../../ From 4f5e9eb44a4c4e6b13af389f12e3342a811e03e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:32:37 +0000 Subject: [PATCH 028/124] build(deps): bump google.golang.org/protobuf from 1.30.0 to 1.33.0 in /contrib/golang/filters/http/test/test_data/basic (#32885) build(deps): bump google.golang.org/protobuf Bumps google.golang.org/protobuf from 1.30.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- contrib/golang/filters/http/test/test_data/basic/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/golang/filters/http/test/test_data/basic/go.mod b/contrib/golang/filters/http/test/test_data/basic/go.mod index b70054d4863a..77ecc15879bb 100644 --- a/contrib/golang/filters/http/test/test_data/basic/go.mod +++ b/contrib/golang/filters/http/test/test_data/basic/go.mod @@ -4,6 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 // indirect replace github.com/envoyproxy/envoy => ../../../../../../../ From a94291c3d16a0d4449607ab3b9d379713eda727d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:32:59 +0000 Subject: [PATCH 029/124] build(deps): bump google.golang.org/protobuf from 1.30.0 to 1.33.0 in /contrib/golang/filters/http/test/test_data/passthrough (#32884) build(deps): bump google.golang.org/protobuf Bumps google.golang.org/protobuf from 1.30.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- contrib/golang/filters/http/test/test_data/passthrough/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/golang/filters/http/test/test_data/passthrough/go.mod b/contrib/golang/filters/http/test/test_data/passthrough/go.mod index d6c620393300..7830ee8c6273 100644 --- a/contrib/golang/filters/http/test/test_data/passthrough/go.mod +++ b/contrib/golang/filters/http/test/test_data/passthrough/go.mod @@ -4,6 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 // indirect replace github.com/envoyproxy/envoy => ../../../../../../../ From 065710fbe7f453d7f8ba44871330af33b0a0d978 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Thu, 14 Mar 2024 07:14:53 -0700 Subject: [PATCH 030/124] Update QUICHE from cc9724b90 to 70bc4dfde (#32892) https://github.com/google/quiche/compare/cc9724b90..70bc4dfde ``` $ git log cc9724b90..70bc4dfde --date=short --no-merges --format="%ad %al %s" 2024-03-13 rch Return false from QuicSpdyStream::OnMetadataFrameEnd if the stream has been reset. 2024-03-13 martinduke Update MOQT to draft-03 wire image. Does not add ANNOUNCE_CANCEL because it does not have a code point. 2024-03-13 martinduke Rename kGenericError to kInternalError. ``` Signed-off-by: Ryan Hamilton --- bazel/repository_locations.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 1ecfa2a69763..7d52b7ac94e0 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -1210,8 +1210,8 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "QUICHE", project_desc = "QUICHE (QUIC, HTTP/2, Etc) is Google‘s implementation of QUIC and related protocols", project_url = "https://github.com/google/quiche", - version = "cc9724b902e0e3392434ea3f985a50c27d99fed4", - sha256 = "adc449ecd70248777565f9a5797f6443728d8edf4d23f6d70f5ea4a10ddb1745", + version = "70bc4dfde98dd4feb7de4ce4b68b220e8cdfee9b", + sha256 = "33c6a54f605eb5348004f66af4682ae050216e1da5601d860ec634b255256d6e", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["controlplane", "dataplane_core"], From 640f016a2e99ab44e97dec71b60afec91404dadd Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Thu, 14 Mar 2024 10:31:02 -0700 Subject: [PATCH 031/124] http3: Add support for HTTP/3 METADATA (#32568) http3: Add support for HTTP/3 METADATA Adds a new allow_metadata option to Http3ProtocolOptions. Risk Level: Low, protected by new config option Testing: New integration tests Docs Changes: N/A Release Notes: Updated Signed-off-by: Ryan Hamilton --- api/envoy/config/core/v3/protocol.proto | 14 +- changelogs/current.yaml | 3 + .../common/quic/envoy_quic_client_stream.cc | 14 +- source/common/quic/envoy_quic_client_stream.h | 8 +- .../common/quic/envoy_quic_server_stream.cc | 14 +- source/common/quic/envoy_quic_server_stream.h | 8 +- source/common/quic/envoy_quic_stream.cc | 85 +++++++++++- source/common/quic/envoy_quic_stream.h | 21 ++- .../quic/platform/quiche_flags_constants.h | 2 + .../common/quic/platform/quiche_flags_impl.cc | 2 +- test/integration/fake_upstream.h | 1 + test/integration/http_integration.cc | 11 +- .../multiplexed_integration_test.cc | 123 +++++++++++------- 13 files changed, 239 insertions(+), 67 deletions(-) diff --git a/api/envoy/config/core/v3/protocol.proto b/api/envoy/config/core/v3/protocol.proto index 8a4e292af508..70af4851b8e7 100644 --- a/api/envoy/config/core/v3/protocol.proto +++ b/api/envoy/config/core/v3/protocol.proto @@ -486,10 +486,10 @@ message Http2ProtocolOptions { // Allows proxying Websocket and other upgrades over H2 connect. bool allow_connect = 5; - // [#not-implemented-hide:] Hiding until envoy has full metadata support. + // [#not-implemented-hide:] Hiding until Envoy has full metadata support. // Still under implementation. DO NOT USE. // - // Allows metadata. See [metadata + // Allows sending and receiving HTTP/2 METADATA frames. See [metadata // docs](https://github.com/envoyproxy/envoy/blob/main/source/docs/h2_metadata.md) for more // information. bool allow_metadata = 6; @@ -618,7 +618,7 @@ message GrpcProtocolOptions { } // A message which allows using HTTP/3. -// [#next-free-field: 6] +// [#next-free-field: 7] message Http3ProtocolOptions { QuicProtocolOptions quic_protocol_options = 1; @@ -637,6 +637,14 @@ message Http3ProtocolOptions { // `_ // Note that HTTP/3 CONNECT is not yet an RFC. bool allow_extended_connect = 5 [(xds.annotations.v3.field_status).work_in_progress = true]; + + // [#not-implemented-hide:] Hiding until Envoy has full metadata support. + // Still under implementation. DO NOT USE. + // + // Allows sending and receiving HTTP/3 METADATA frames. See [metadata + // docs](https://github.com/envoyproxy/envoy/blob/main/source/docs/h2_metadata.md) for more + // information. + bool allow_metadata = 6; } // A message to control transformations to the :scheme header diff --git a/changelogs/current.yaml b/changelogs/current.yaml index e13ab82f5fb5..6d2dff7d9edb 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -239,6 +239,9 @@ new_features: change: | Added :ref:`host_rewrite ` config to be used during signature. +- area: http3 + change: | + Added experimental support for sending and receiving HTTP/3 METADATA frames. - area: ext_proc change: | added diff --git a/source/common/quic/envoy_quic_client_stream.cc b/source/common/quic/envoy_quic_client_stream.cc index 18e02bb0bdc3..1e659ac61e4c 100644 --- a/source/common/quic/envoy_quic_client_stream.cc +++ b/source/common/quic/envoy_quic_client_stream.cc @@ -23,7 +23,7 @@ EnvoyQuicClientStream::EnvoyQuicClientStream( const envoy::config::core::v3::Http3ProtocolOptions& http3_options) : quic::QuicSpdyClientStream(id, client_session, type), EnvoyQuicStream( - *this, + *this, *client_session, // Flow control receive window should be larger than 8k so that the send buffer can fully // utilize congestion control window before it reaches the high watermark. static_cast(GetReceiveWindow().value()), *filterManagerConnection(), @@ -31,6 +31,7 @@ EnvoyQuicClientStream::EnvoyQuicClientStream( stats, http3_options) { ASSERT(static_cast(GetReceiveWindow().value()) > 8 * 1024, "Send buffer limit should be larger than 8KB."); + RegisterMetadataVisitor(this); } Http::Status EnvoyQuicClientStream::encodeHeaders(const Http::RequestHeaderMap& headers, @@ -411,6 +412,17 @@ QuicFilterManagerConnectionImpl* EnvoyQuicClientStream::filterManagerConnection( return dynamic_cast(session()); } +void EnvoyQuicClientStream::OnMetadataComplete(size_t /*frame_len*/, + const quic::QuicHeaderList& header_list) { + if (mustRejectMetadata(header_list.uncompressed_header_bytes())) { + onStreamError(true, quic::QUIC_HEADERS_TOO_LARGE); + return; + } + if (!header_list.empty()) { + response_decoder_->decodeMetadata(metadataMapFromHeaderList(header_list)); + } +} + void EnvoyQuicClientStream::onStreamError(absl::optional should_close_connection, quic::QuicRstStreamErrorCode rst_code) { if (details_.empty()) { diff --git a/source/common/quic/envoy_quic_client_stream.h b/source/common/quic/envoy_quic_client_stream.h index 98607867ea5b..2cb1c5105909 100644 --- a/source/common/quic/envoy_quic_client_stream.h +++ b/source/common/quic/envoy_quic_client_stream.h @@ -9,6 +9,8 @@ #endif #include "quiche/common/simple_buffer_allocator.h" #include "quiche/quic/core/http/quic_spdy_client_stream.h" +#include "quiche/quic/core/qpack/qpack_encoder.h" +#include "quiche/quic/core/qpack/qpack_instruction_encoder.h" namespace Envoy { namespace Quic { @@ -16,7 +18,8 @@ namespace Quic { // This class is a quic stream and also a request encoder. class EnvoyQuicClientStream : public quic::QuicSpdyClientStream, public EnvoyQuicStream, - public Http::RequestEncoder { + public Http::RequestEncoder, + public quic::QuicSpdyStream::MetadataVisitor { public: EnvoyQuicClientStream(quic::QuicStreamId id, quic::QuicSpdyClientSession* client_session, quic::StreamType type, Http::Http3::CodecStats& stats, @@ -52,6 +55,9 @@ class EnvoyQuicClientStream : public quic::QuicSpdyClientStream, void clearWatermarkBuffer(); + // quic::QuicSpdyStream::MetadataVisitor + void OnMetadataComplete(size_t frame_len, const quic::QuicHeaderList& header_list) override; + protected: // EnvoyQuicStream void switchStreamBlockState() override; diff --git a/source/common/quic/envoy_quic_server_stream.cc b/source/common/quic/envoy_quic_server_stream.cc index 1932010257f7..94a82cce10d7 100644 --- a/source/common/quic/envoy_quic_server_stream.cc +++ b/source/common/quic/envoy_quic_server_stream.cc @@ -30,7 +30,7 @@ EnvoyQuicServerStream::EnvoyQuicServerStream( headers_with_underscores_action) : quic::QuicSpdyServerStreamBase(id, session, type), EnvoyQuicStream( - *this, + *this, *session, // Flow control receive window should be larger than 8k to fully utilize congestion // control window before it reaches the high watermark. static_cast(GetReceiveWindow().value()), *filterManagerConnection(), @@ -42,6 +42,7 @@ EnvoyQuicServerStream::EnvoyQuicServerStream( stats_gatherer_ = new QuicStatsGatherer(&filterManagerConnection()->dispatcher().timeSource()); set_ack_listener(stats_gatherer_); + RegisterMetadataVisitor(this); } void EnvoyQuicServerStream::encode1xxHeaders(const Http::ResponseHeaderMap& headers) { @@ -449,6 +450,17 @@ EnvoyQuicServerStream::validateHeader(absl::string_view header_name, return result; } +void EnvoyQuicServerStream::OnMetadataComplete(size_t /*frame_len*/, + const quic::QuicHeaderList& header_list) { + if (mustRejectMetadata(header_list.uncompressed_header_bytes())) { + onStreamError(true, quic::QUIC_HEADERS_TOO_LARGE); + return; + } + if (!header_list.empty()) { + request_decoder_->decodeMetadata(metadataMapFromHeaderList(header_list)); + } +} + void EnvoyQuicServerStream::onStreamError(absl::optional should_close_connection, quic::QuicRstStreamErrorCode rst) { if (details_.empty()) { diff --git a/source/common/quic/envoy_quic_server_stream.h b/source/common/quic/envoy_quic_server_stream.h index bb9c32003b07..9153217efcb1 100644 --- a/source/common/quic/envoy_quic_server_stream.h +++ b/source/common/quic/envoy_quic_server_stream.h @@ -8,6 +8,8 @@ #include "quiche/common/platform/api/quiche_reference_counted.h" #include "quiche/quic/core/http/quic_spdy_server_stream_base.h" +#include "quiche/quic/core/qpack/qpack_encoder.h" +#include "quiche/quic/core/qpack/qpack_instruction_encoder.h" namespace Envoy { namespace Quic { @@ -15,7 +17,8 @@ namespace Quic { // This class is a quic stream and also a response encoder. class EnvoyQuicServerStream : public quic::QuicSpdyServerStreamBase, public EnvoyQuicStream, - public Http::ResponseEncoder { + public Http::ResponseEncoder, + public quic::QuicSpdyStream::MetadataVisitor { public: EnvoyQuicServerStream(quic::QuicStreamId id, quic::QuicSpdySession* session, quic::StreamType type, Http::Http3::CodecStats& stats, @@ -78,6 +81,9 @@ class EnvoyQuicServerStream : public quic::QuicSpdyServerStreamBase, Http::HeaderUtility::HeaderValidationResult validateHeader(absl::string_view header_name, absl::string_view header_value) override; + // quic::QuicSpdyStream::MetadataVisitor + void OnMetadataComplete(size_t frame_len, const quic::QuicHeaderList& header_list) override; + protected: // EnvoyQuicStream void switchStreamBlockState() override; diff --git a/source/common/quic/envoy_quic_stream.cc b/source/common/quic/envoy_quic_stream.cc index 8ca1e7950466..280c7ab91513 100644 --- a/source/common/quic/envoy_quic_stream.cc +++ b/source/common/quic/envoy_quic_stream.cc @@ -102,10 +102,87 @@ void EnvoyQuicStream::encodeTrailersImpl(spdy::Http2HeaderBlock&& trailers) { onLocalEndStream(); } -void EnvoyQuicStream::encodeMetadata(const Http::MetadataMapVector& /*metadata_map_vector*/) { - // Metadata Frame is not supported in QUICHE. - ENVOY_STREAM_LOG(debug, "METADATA is not supported in Http3.", *this); - stats_.metadata_not_supported_error_.inc(); +std::unique_ptr +EnvoyQuicStream::metadataMapFromHeaderList(const quic::QuicHeaderList& header_list) { + auto metadata_map = std::make_unique(); + for (const auto& [key, value] : header_list) { + (*metadata_map)[key] = value; + } + return metadata_map; +} + +namespace { + +// Returns a new `unique_ptr` containing the characters copied from `str`. +std::unique_ptr dataFromString(const std::string& str) { + auto data = std::make_unique(str.length()); + memcpy(&data[0], str.data(), str.length()); // NOLINT(safe-memcpy) + return data; +} + +void serializeMetadata(const Http::MetadataMapPtr& metadata, quic::QuicStreamId id, + absl::InlinedVector& slices) { + quic::NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; + quic::QpackEncoder qpack_encoder(&decoder_stream_error_delegate, + quic::HuffmanEncoding::kDisabled); + + spdy::Http2HeaderBlock header_block; + for (const auto& [key, value] : *metadata) { + header_block.AppendValueOrAddHeader(key, value); + } + + // The METADATA frame consist of a frame header, which includes payload + // length, and a payload, which is the QPACK-encoded metadata block. In order + // to generate the frame header, the payload needs to be generated first. + std::string metadata_frame_payload = + qpack_encoder.EncodeHeaderList(id, header_block, + /* encoder_stream_sent_byte_count = */ nullptr); + std::string metadata_frame_header = + quic::HttpEncoder::SerializeMetadataFrameHeader(metadata_frame_payload.size()); + + slices.emplace_back(dataFromString(metadata_frame_header), metadata_frame_header.length()); + slices.emplace_back(dataFromString(metadata_frame_payload), metadata_frame_payload.length()); +} + +} // namespace + +void EnvoyQuicStream::encodeMetadata(const Http::MetadataMapVector& metadata_map_vector) { + if (!http3_options_.allow_metadata()) { + ENVOY_STREAM_LOG(debug, "METADATA not supported by config.", *this); + stats_.metadata_not_supported_error_.inc(); + return; + } + if (quic_stream_.write_side_closed()) { + return; + } + ASSERT(!local_end_stream_); + + for (const Http::MetadataMapPtr& metadata : metadata_map_vector) { + absl::InlinedVector quic_slices; + quic_slices.reserve(2); + serializeMetadata(metadata, quic_stream_.id(), quic_slices); + absl::Span metadata_frame(quic_slices); + + SendBufferMonitor::ScopedWatermarkBufferUpdater updater(&quic_stream_, this); + quic::QuicConsumedData result{0, false}; + { + IncrementalBytesSentTracker tracker(quic_stream_, *mutableBytesMeter(), false); + result = quic_stream_.WriteMemSlices(metadata_frame, /*end_stream=*/false); + } + // QUIC stream must take all. + if (result.bytes_consumed == 0) { + IS_ENVOY_BUG(fmt::format("Send buffer didn't take all the data. Stream is write {} with {} " + "bytes in send buffer. Current write was rejected.", + quic_stream_.write_side_closed() ? "closed" : "open", + quic_stream_.BufferedDataBytes())); + quic_stream_.Reset(quic::QUIC_ERROR_PROCESSING_STREAM); + return; + } + if (!quic_session_.connection()->connected()) { + // Return early if sending METADATA caused the connection to close. + return; + } + } } } // namespace Quic diff --git a/source/common/quic/envoy_quic_stream.h b/source/common/quic/envoy_quic_stream.h index 360788d2fdcd..bbe4759339ae 100644 --- a/source/common/quic/envoy_quic_stream.h +++ b/source/common/quic/envoy_quic_stream.h @@ -33,13 +33,13 @@ class EnvoyQuicStream : public virtual Http::StreamEncoder, public: // |buffer_limit| is the high watermark of the stream send buffer, and the low // watermark will be half of it. - EnvoyQuicStream(quic::QuicSpdyStream& quic_stream, uint32_t buffer_limit, - QuicFilterManagerConnectionImpl& filter_manager_connection, + EnvoyQuicStream(quic::QuicSpdyStream& quic_stream, quic::QuicSession& quic_session, + uint32_t buffer_limit, QuicFilterManagerConnectionImpl& filter_manager_connection, std::function below_low_watermark, std::function above_high_watermark, Http::Http3::CodecStats& stats, const envoy::config::core::v3::Http3ProtocolOptions& http3_options) : Http::MultiplexedStreamImplBase(filter_manager_connection.dispatcher()), stats_(stats), - http3_options_(http3_options), quic_stream_(quic_stream), + http3_options_(http3_options), quic_stream_(quic_stream), quic_session_(quic_session), send_buffer_simulation_(buffer_limit / 2, buffer_limit, std::move(below_low_watermark), std::move(above_high_watermark), ENVOY_LOGGER()), filter_manager_connection_(filter_manager_connection), @@ -180,6 +180,17 @@ class EnvoyQuicStream : public virtual Http::StreamEncoder, void encodeTrailersImpl(spdy::Http2HeaderBlock&& trailers); + // Converts `header_list` into a new `Http::MetadataMap`. + std::unique_ptr + metadataMapFromHeaderList(const quic::QuicHeaderList& header_list); + + // Returns true if the cumulative limit on METADATA headers has been reached + // after adding `bytes`. + bool mustRejectMetadata(size_t bytes) { + received_metadata_bytes_ += bytes; + return received_metadata_bytes_ > 1 << 20; + } + #ifdef ENVOY_ENABLE_HTTP_DATAGRAMS // Setting |http_datagram_handler_| enables HTTP Datagram support. std::unique_ptr http_datagram_handler_; @@ -210,8 +221,9 @@ class EnvoyQuicStream : public virtual Http::StreamEncoder, bool saw_regular_headers_{false}; private: - // QUIC stream that this EnvoyQuicStream wraps. + // QUIC stream and session that this EnvoyQuicStream wraps. quic::QuicSpdyStream& quic_stream_; + quic::QuicSession& quic_session_; // Keeps track of bytes buffered in the stream send buffer in QUICHE and reacts // upon crossing high and low watermarks. @@ -232,6 +244,7 @@ class EnvoyQuicStream : public virtual Http::StreamEncoder, absl::optional content_length_; size_t received_content_bytes_{0}; http2::adapter::HeaderValidator header_validator_; + size_t received_metadata_bytes_{0}; }; // Object used for updating a BytesMeter to track bytes sent on a QuicStream since this object was diff --git a/source/common/quic/platform/quiche_flags_constants.h b/source/common/quic/platform/quiche_flags_constants.h index cff579bad68c..a38bd99a0fc6 100644 --- a/source/common/quic/platform/quiche_flags_constants.h +++ b/source/common/quic/platform/quiche_flags_constants.h @@ -17,6 +17,8 @@ /* Envoy only supports RFC-v1 in the long term, so disable IETF draft 29 implementation by \ * default. */ \ KEY_VALUE_PAIR(quic_disable_version_draft_29, true) \ + /* Enable support for HTTP/3 metadata decoding in QUICHE. */ \ + KEY_VALUE_PAIR(quic_enable_http3_metadata_decoding, true) \ /* This flag enables BBR, otherwise QUIC will use Cubic which is less performant */ \ KEY_VALUE_PAIR(quic_default_to_bbr, true) diff --git a/source/common/quic/platform/quiche_flags_impl.cc b/source/common/quic/platform/quiche_flags_impl.cc index f676005f91b7..0f8d64fcfce9 100644 --- a/source/common/quic/platform/quiche_flags_impl.cc +++ b/source/common/quic/platform/quiche_flags_impl.cc @@ -117,7 +117,7 @@ FlagRegistry::FlagRegistry() : reloadable_flags_(makeReloadableFlagMap()) {} FlagRegistry& FlagRegistry::getInstance() { static auto* instance = new FlagRegistry(); ASSERT(sizeof(quiche_reloadable_flag_overrides) / sizeof(std::pair) == - 2); + 3); ASSERT(sizeof(quiche_protocol_flag_overrides) / sizeof(std::pair>) == 3); diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index 24a89f96d7c0..78bc9b1566aa 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -644,6 +644,7 @@ struct FakeUpstreamConfig { http2_options_.set_allow_connect(true); http2_options_.set_allow_metadata(true); http3_options_.set_allow_extended_connect(true); + http3_options_.set_allow_metadata(true); } Event::TestTimeSystem& time_system_; diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index c25103ffabb0..2064c9629c45 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -276,13 +276,14 @@ IntegrationCodecClientPtr HttpIntegrationTest::makeRawHttpConnection( .value(); http2_options.value().set_allow_connect(true); http2_options.value().set_allow_metadata(true); + } #ifdef ENVOY_ENABLE_QUIC - } else { - cluster->http3_options_ = ConfigHelper::http2ToHttp3ProtocolOptions( - http2_options.value(), quic::kStreamReceiveWindowLimit); - cluster->http3_options_.set_allow_extended_connect(true); + cluster->http3_options_ = ConfigHelper::http2ToHttp3ProtocolOptions( + http2_options.value(), quic::kStreamReceiveWindowLimit); + cluster->http3_options_.set_allow_extended_connect(true); + cluster->http3_options_.set_allow_metadata(true); #endif - } + cluster->http2_options_ = http2_options.value(); cluster->http1_settings_.enable_trailers_ = true; diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc index d83e2e395ef2..4f534ecaf40d 100644 --- a/test/integration/multiplexed_integration_test.cc +++ b/test/integration/multiplexed_integration_test.cc @@ -276,7 +276,7 @@ static std::string response_metadata_filter = R"EOF( name: response-metadata-filter )EOF"; -class Http2MetadataIntegrationTest : public HttpProtocolIntegrationTest { +class MetadataIntegrationTest : public HttpProtocolIntegrationTest { public: void SetUp() override { HttpProtocolIntegrationTest::SetUp(); @@ -284,15 +284,25 @@ class Http2MetadataIntegrationTest : public HttpProtocolIntegrationTest { [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { RELEASE_ASSERT(bootstrap.mutable_static_resources()->clusters_size() >= 1, ""); ConfigHelper::HttpProtocolOptions protocol_options; - protocol_options.mutable_explicit_http_config() - ->mutable_http2_protocol_options() - ->set_allow_metadata(true); + if (GetParam().upstream_protocol == Http::CodecType::HTTP3) { + protocol_options.mutable_explicit_http_config() + ->mutable_http3_protocol_options() + ->set_allow_metadata(true); + protocol_options.mutable_upstream_http_protocol_options()->set_auto_sni(true); + } else { + protocol_options.mutable_explicit_http_config() + ->mutable_http2_protocol_options() + ->set_allow_metadata(true); + } ConfigHelper::setProtocolOptions( *bootstrap.mutable_static_resources()->mutable_clusters(0), protocol_options); }); config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - hcm) -> void { hcm.mutable_http2_protocol_options()->set_allow_metadata(true); }); + hcm) -> void { + hcm.mutable_http2_protocol_options()->set_allow_metadata(true); + hcm.mutable_http3_protocol_options()->set_allow_metadata(true); + }); } void testRequestMetadataWithStopAllFilter(); @@ -301,6 +311,8 @@ class Http2MetadataIntegrationTest : public HttpProtocolIntegrationTest { void runHeaderOnlyTest(bool send_request_body, size_t body_size); + Http::CodecClient::Type upstreamProtocol() { return GetParam().upstream_protocol; } + protected: // Utility function to prepend filters. Note that the filters // are added in reverse order. @@ -312,7 +324,7 @@ class Http2MetadataIntegrationTest : public HttpProtocolIntegrationTest { }; // Verifies metadata can be sent at different locations of the responses. -TEST_P(Http2MetadataIntegrationTest, ProxyMetadataInResponse) { +TEST_P(MetadataIntegrationTest, ProxyMetadataInResponse) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -445,7 +457,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxyMetadataInResponse) { test_server_->waitForCounterEq(counter, 1); } -TEST_P(Http2MetadataIntegrationTest, ProxyMultipleMetadata) { +TEST_P(MetadataIntegrationTest, ProxyMultipleMetadata) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -485,7 +497,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxyMultipleMetadata) { // Disabled temporarily see #19040 #if 0 -TEST_P(Http2MetadataIntegrationTest, ProxyInvalidMetadata) { +TEST_P(MetadataIntegrationTest, ProxyInvalidMetadata) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -516,14 +528,16 @@ TEST_P(Http2MetadataIntegrationTest, ProxyInvalidMetadata) { #endif void verifyExpectedMetadata(Http::MetadataMap metadata_map, std::set keys) { + EXPECT_EQ(metadata_map.size(), keys.size()); for (const auto& key : keys) { // keys are the same as their corresponding values. + auto it = metadata_map.find(key); + ASSERT_FALSE(it == metadata_map.end()) << "key: " << key; EXPECT_EQ(metadata_map.find(key)->second, key); } - EXPECT_EQ(metadata_map.size(), keys.size()); } -TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { +TEST_P(MetadataIntegrationTest, TestResponseMetadata) { prependFilters({response_metadata_filter}); config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& @@ -540,6 +554,11 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { ASSERT_TRUE(response->waitForEndStream()); ASSERT_TRUE(response->complete()); std::set expected_metadata_keys = {"headers", "duplicate"}; + if (upstreamProtocol() == Http::CodecType::HTTP3) { + // HTTP/3 Sends "end stream" in an empty DATA frame which results in the test filter + // adding the "data" metadata header. + expected_metadata_keys.insert("data"); + } verifyExpectedMetadata(response->metadataMap(), expected_metadata_keys); // Upstream responds with headers and data. @@ -571,13 +590,9 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { EXPECT_EQ(4, response->metadataMapsDecodedCount()); // Upstream responds with headers, 100-continue and data. - response = - codec_client_->makeRequestWithBody(Http::TestRequestHeaderMapImpl{{":method", "GET"}, - {":path", "/dynamo/url"}, - {":scheme", "http"}, - {":authority", "host"}, - {"expect", "100-contINUE"}}, - 10); + Http::TestRequestHeaderMapImpl headers = default_request_headers_; + headers.addCopy("expect", "100-contINUE"); + response = codec_client_->makeRequestWithBody(headers, 10); waitForNextUpstreamRequest(); upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); @@ -609,8 +624,17 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { expected_metadata_keys.erase("100-continue"); expected_metadata_keys.insert("aaa"); expected_metadata_keys.insert("keep"); + if (upstreamProtocol() == Http::CodecType::HTTP3) { + // HTTP/3 Sends "end stream" in an empty DATA frame which results in the test filter + // adding the "data" metadata header. + expected_metadata_keys.insert("data"); + } verifyExpectedMetadata(response->metadataMap(), expected_metadata_keys); - EXPECT_EQ(2, response->metadataMapsDecodedCount()); + if (upstreamProtocol() == Http::CodecType::HTTP3) { + EXPECT_EQ(3, response->metadataMapsDecodedCount()); + } else { + EXPECT_EQ(2, response->metadataMapsDecodedCount()); + } // Upstream responds with headers, data and metadata that will be consumed. response = codec_client_->makeRequestWithBody(default_request_headers_, 10); @@ -633,7 +657,7 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { EXPECT_EQ(3, response->metadataMapsDecodedCount()); } -TEST_P(Http2MetadataIntegrationTest, ProxyMultipleMetadataReachSizeLimit) { +TEST_P(MetadataIntegrationTest, ProxyMultipleMetadataReachSizeLimit) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -659,7 +683,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxyMultipleMetadataReachSizeLimit) { } // Verifies small metadata can be sent at different locations of a request. -TEST_P(Http2MetadataIntegrationTest, ProxySmallMetadataInRequest) { +TEST_P(MetadataIntegrationTest, ProxySmallMetadataInRequest) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -688,7 +712,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxySmallMetadataInRequest) { } // Verifies large metadata can be sent at different locations of a request. -TEST_P(Http2MetadataIntegrationTest, ProxyLargeMetadataInRequest) { +TEST_P(MetadataIntegrationTest, ProxyLargeMetadataInRequest) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -717,7 +741,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxyLargeMetadataInRequest) { ASSERT_TRUE(response->complete()); } -TEST_P(Http2MetadataIntegrationTest, RequestMetadataReachSizeLimit) { +TEST_P(MetadataIntegrationTest, RequestMetadataReachSizeLimit) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -742,7 +766,7 @@ TEST_P(Http2MetadataIntegrationTest, RequestMetadataReachSizeLimit) { ASSERT_FALSE(response->complete()); } -TEST_P(Http2MetadataIntegrationTest, RequestMetadataThenTrailers) { +TEST_P(MetadataIntegrationTest, RequestMetadataThenTrailers) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -765,7 +789,7 @@ static std::string request_metadata_filter = R"EOF( name: request-metadata-filter )EOF"; -TEST_P(Http2MetadataIntegrationTest, ConsumeAndInsertRequestMetadata) { +TEST_P(MetadataIntegrationTest, ConsumeAndInsertRequestMetadata) { prependFilters({request_metadata_filter}); config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& @@ -784,6 +808,11 @@ TEST_P(Http2MetadataIntegrationTest, ConsumeAndInsertRequestMetadata) { // Verifies a headers metadata added. std::set expected_metadata_keys = {"headers"}; expected_metadata_keys.insert("metadata"); + if (downstreamProtocol() == Http::CodecType::HTTP3) { + // HTTP/3 Sends "end stream" in an empty DATA frame which results in the test filter + // adding the "data" metadata header. + expected_metadata_keys.insert("data"); + } verifyExpectedMetadata(upstream_request_->metadataMap(), expected_metadata_keys); // Sends a headers only request with metadata. An empty data frame carries end_stream. @@ -869,7 +898,7 @@ TEST_P(Http2MetadataIntegrationTest, ConsumeAndInsertRequestMetadata) { EXPECT_EQ(upstream_request_->duplicatedMetadataKeyCount().find("metadata")->second, 6); } -void Http2MetadataIntegrationTest::runHeaderOnlyTest(bool send_request_body, size_t body_size) { +void MetadataIntegrationTest::runHeaderOnlyTest(bool send_request_body, size_t body_size) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { hcm.set_proxy_100_continue(true); }); @@ -880,18 +909,9 @@ void Http2MetadataIntegrationTest::runHeaderOnlyTest(bool send_request_body, siz // Sends a request with body. Only headers will pass through filters. IntegrationStreamDecoderPtr response; if (send_request_body) { - response = codec_client_->makeRequestWithBody( - Http::TestRequestHeaderMapImpl{{":method", "POST"}, - {":path", "/test/long/url"}, - {":scheme", "http"}, - {":authority", "host"}}, - body_size); + response = codec_client_->makeRequestWithBody(default_request_headers_, body_size); } else { - response = codec_client_->makeHeaderOnlyRequest( - Http::TestRequestHeaderMapImpl{{":method", "POST"}, - {":path", "/test/long/url"}, - {":scheme", "http"}, - {":authority", "host"}}); + response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); } waitForNextUpstreamRequest(); @@ -900,10 +920,15 @@ void Http2MetadataIntegrationTest::runHeaderOnlyTest(bool send_request_body, siz ASSERT_TRUE(response->complete()); } -void Http2MetadataIntegrationTest::verifyHeadersOnlyTest() { +void MetadataIntegrationTest::verifyHeadersOnlyTest() { // Verifies a headers metadata added. std::set expected_metadata_keys = {"headers"}; expected_metadata_keys.insert("metadata"); + if (downstreamProtocol() == Http::CodecType::HTTP3) { + // HTTP/3 Sends "end stream" in an empty DATA frame which results in the test filter + // adding the "data" metadata header. + expected_metadata_keys.insert("data"); + } verifyExpectedMetadata(upstream_request_->metadataMap(), expected_metadata_keys); // Verifies zero length data received, and end_stream is true. @@ -912,14 +937,14 @@ void Http2MetadataIntegrationTest::verifyHeadersOnlyTest() { EXPECT_EQ(true, upstream_request_->complete()); } -TEST_P(Http2MetadataIntegrationTest, HeadersOnlyRequestWithRequestMetadata) { +TEST_P(MetadataIntegrationTest, HeadersOnlyRequestWithRequestMetadata) { prependFilters({request_metadata_filter}); // Send a headers only request. runHeaderOnlyTest(false, 0); verifyHeadersOnlyTest(); } -void Http2MetadataIntegrationTest::testRequestMetadataWithStopAllFilter() { +void MetadataIntegrationTest::testRequestMetadataWithStopAllFilter() { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -953,17 +978,17 @@ static std::string metadata_stop_all_filter = R"EOF( name: metadata-stop-all-filter )EOF"; -TEST_P(Http2MetadataIntegrationTest, RequestMetadataWithStopAllFilterBeforeMetadataFilter) { +TEST_P(MetadataIntegrationTest, RequestMetadataWithStopAllFilterBeforeMetadataFilter) { prependFilters({request_metadata_filter, metadata_stop_all_filter}); testRequestMetadataWithStopAllFilter(); } -TEST_P(Http2MetadataIntegrationTest, RequestMetadataWithStopAllFilterAfterMetadataFilter) { +TEST_P(MetadataIntegrationTest, RequestMetadataWithStopAllFilterAfterMetadataFilter) { prependFilters({metadata_stop_all_filter, request_metadata_filter}); testRequestMetadataWithStopAllFilter(); } -TEST_P(Http2MetadataIntegrationTest, TestAddEncodedMetadata) { +TEST_P(MetadataIntegrationTest, TestAddEncodedMetadata) { config_helper_.prependFilter(R"EOF( name: encode-headers-return-stop-all-filter )EOF"); @@ -1704,9 +1729,10 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, MultiplexedRingHashIntegrationTest, {Http::CodecType::HTTP1})), HttpProtocolIntegrationTest::protocolTestParamsToString); -INSTANTIATE_TEST_SUITE_P(IpVersions, Http2MetadataIntegrationTest, +INSTANTIATE_TEST_SUITE_P(IpVersions, MetadataIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( - {Http::CodecType::HTTP2}, {Http::CodecType::HTTP2})), + {Http::CodecType::HTTP2, Http::CodecType::HTTP3}, + {Http::CodecType::HTTP2, Http::CodecType::HTTP3})), HttpProtocolIntegrationTest::protocolTestParamsToString); void MultiplexedRingHashIntegrationTest::sendMultipleRequests( @@ -2700,7 +2726,11 @@ TEST_P(Http2FrameIntegrationTest, DownstreamSendingEmptyMetadata) { } // Tests that an empty metadata map from upstream is ignored. -TEST_P(Http2MetadataIntegrationTest, UpstreamSendingEmptyMetadata) { +TEST_P(MetadataIntegrationTest, UpstreamSendingEmptyMetadata) { + if (upstreamProtocol() == Http::CodecType::HTTP3) { + // rawWriteConnection is not available for QUIC. + return; + } initialize(); // Send a request and make sure an upstream connection is established. @@ -2728,7 +2758,7 @@ TEST_P(Http2MetadataIntegrationTest, UpstreamSendingEmptyMetadata) { } // Tests upstream sending a metadata frame after ending a stream. -TEST_P(Http2MetadataIntegrationTest, UpstreamMetadataAfterEndStream) { +TEST_P(MetadataIntegrationTest, UpstreamMetadataAfterEndStream) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -2758,6 +2788,7 @@ TEST_P(Http2MetadataIntegrationTest, UpstreamMetadataAfterEndStream) { ASSERT_TRUE(fake_upstream_connection_->close()); ASSERT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().getStatusValue()); + cleanupUpstreamAndDownstream(); } TEST_P(MultiplexedIntegrationTest, InvalidTrailers) { From cc5a9d09b9f0cacd6d2824e703b10828bb5809bb Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Thu, 14 Mar 2024 12:41:57 -0500 Subject: [PATCH 032/124] mobile: Remove unused Java imports (#32907) Signed-off-by: Fredy Wijaya --- .../envoymobile/engine/AndroidJniLibrary.java | 1 - .../envoymobile/engine/EnvoyHTTPStream.java | 2 -- .../envoymobile/engine/HeaderMatchConfig.java | 2 -- .../envoymobile/engine/JniBridgeUtility.java | 1 - .../utilities/AndroidNetworkLibrary.java | 4 ---- .../envoymobile/utilities/ContextUtils.java | 4 ---- .../envoymobile/utilities/FakeX509Util.java | 1 - .../org/chromium/net/BidirectionalStream.java | 1 - .../net/impl/CronvoyBidirectionalStream.java | 2 -- .../AndroidEngineSocketTagTest.java | 21 +------------------ .../AndroidEnvoyExplicitFlowTest.java | 14 +------------ .../integration/AndroidEnvoyFlowTest.java | 15 ------------- .../engine/testing/QuicTestServerTest.java | 14 +------------ .../engine/testing/RequestScenario.java | 19 +---------------- .../envoymobile/engine/testing/Response.java | 7 ------- .../chromium/net/CronetUrlRequestTest.java | 2 -- .../org/chromium/net/DiskStorageTest.java | 2 +- .../chromium/net/impl/CronvoyEngineTest.java | 1 - .../chromium/net/testing/CronetTestRule.java | 4 +--- .../net/testing/CronetTestRuleTest.java | 1 - .../net/testing/MockUrlRequestJobFactory.java | 2 -- 21 files changed, 6 insertions(+), 114 deletions(-) diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidJniLibrary.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidJniLibrary.java index dae533e009a0..7bcd24764c52 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidJniLibrary.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidJniLibrary.java @@ -1,7 +1,6 @@ package io.envoyproxy.envoymobile.engine; import android.content.Context; -import android.net.ConnectivityManager; public class AndroidJniLibrary { // Internal reference to helper object used to load and initialize the native diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyHTTPStream.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyHTTPStream.java index dfa3e2c371e4..6284fff1d674 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyHTTPStream.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyHTTPStream.java @@ -2,9 +2,7 @@ import io.envoyproxy.envoymobile.engine.types.EnvoyHTTPCallbacks; -import java.nio.charset.StandardCharsets; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/HeaderMatchConfig.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/HeaderMatchConfig.java index 708cbf9e4713..c8764eba4682 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/HeaderMatchConfig.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/HeaderMatchConfig.java @@ -1,7 +1,5 @@ package io.envoyproxy.envoymobile.engine; -import java.util.List; - /* Datatype used by the EnvoyConfiguration to header matches for virtual clusters * * All the fields here map to the fields by the respective names in the HeaderMatcher message diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/JniBridgeUtility.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/JniBridgeUtility.java index 6adb49b36b0a..bcd5cd7c4839 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/JniBridgeUtility.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/JniBridgeUtility.java @@ -1,7 +1,6 @@ package io.envoyproxy.envoymobile.engine; import java.nio.charset.StandardCharsets; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/mobile/library/java/io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary.java b/mobile/library/java/io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary.java index d7b07e05265e..99ac5265f428 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary.java @@ -13,12 +13,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.Socket; import java.net.SocketAddress; -import java.net.SocketException; import java.net.SocketImpl; -import java.net.URLConnection; import java.net.Socket; import java.security.KeyStoreException; diff --git a/mobile/library/java/io/envoyproxy/envoymobile/utilities/ContextUtils.java b/mobile/library/java/io/envoyproxy/envoymobile/utilities/ContextUtils.java index ce05e83efdd0..c738ee51a092 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/utilities/ContextUtils.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/utilities/ContextUtils.java @@ -1,6 +1,5 @@ package io.envoyproxy.envoymobile.utilities; -import android.app.Activity; import android.app.Application; import android.content.BroadcastReceiver; import android.content.Context; @@ -8,13 +7,10 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.res.AssetManager; import android.os.Build; import android.os.Handler; -import android.os.Process; import android.preference.PreferenceManager; -import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; /** diff --git a/mobile/library/java/io/envoyproxy/envoymobile/utilities/FakeX509Util.java b/mobile/library/java/io/envoyproxy/envoymobile/utilities/FakeX509Util.java index 42db7a5e4f66..afa33586eb1c 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/utilities/FakeX509Util.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/utilities/FakeX509Util.java @@ -1,6 +1,5 @@ package io.envoyproxy.envoymobile.utilities; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Set; diff --git a/mobile/library/java/org/chromium/net/BidirectionalStream.java b/mobile/library/java/org/chromium/net/BidirectionalStream.java index e99ba187d189..03424dddebdc 100644 --- a/mobile/library/java/org/chromium/net/BidirectionalStream.java +++ b/mobile/library/java/org/chromium/net/BidirectionalStream.java @@ -1,6 +1,5 @@ package org.chromium.net; -import android.annotation.SuppressLint; import java.nio.ByteBuffer; import java.util.concurrent.Executor; diff --git a/mobile/library/java/org/chromium/net/impl/CronvoyBidirectionalStream.java b/mobile/library/java/org/chromium/net/impl/CronvoyBidirectionalStream.java index 19870e3b31e8..9cd0a34d6846 100644 --- a/mobile/library/java/org/chromium/net/impl/CronvoyBidirectionalStream.java +++ b/mobile/library/java/org/chromium/net/impl/CronvoyBidirectionalStream.java @@ -13,7 +13,6 @@ import org.chromium.net.CallbackException; import org.chromium.net.CronetException; import org.chromium.net.ExperimentalBidirectionalStream; -import org.chromium.net.NetworkException; import org.chromium.net.RequestFinishedInfo; import org.chromium.net.UrlResponseInfo; import org.chromium.net.impl.Annotations.RequestPriority; @@ -27,7 +26,6 @@ import java.nio.ByteBuffer; import java.util.AbstractMap; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; diff --git a/mobile/test/java/integration/AndroidEngineSocketTagTest.java b/mobile/test/java/integration/AndroidEngineSocketTagTest.java index e5f7a2f8d3c9..96bc516676a3 100644 --- a/mobile/test/java/integration/AndroidEngineSocketTagTest.java +++ b/mobile/test/java/integration/AndroidEngineSocketTagTest.java @@ -6,34 +6,15 @@ import androidx.test.core.app.ApplicationProvider; import io.envoyproxy.envoymobile.AndroidEngineBuilder; import io.envoyproxy.envoymobile.Engine; -import io.envoyproxy.envoymobile.EnvoyError; -import io.envoyproxy.envoymobile.FinalStreamIntel; import io.envoyproxy.envoymobile.LogLevel; -import io.envoyproxy.envoymobile.RequestHeaders; -import io.envoyproxy.envoymobile.RequestHeadersBuilder; import io.envoyproxy.envoymobile.RequestMethod; -import io.envoyproxy.envoymobile.ResponseHeaders; -import io.envoyproxy.envoymobile.ResponseTrailers; import io.envoyproxy.envoymobile.Stream; -import io.envoyproxy.envoymobile.StreamIntel; import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; import io.envoyproxy.envoymobile.engine.testing.RequestScenario; import io.envoyproxy.envoymobile.engine.testing.Response; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.ByteBuffer; -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; + import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; diff --git a/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java b/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java index fa0e0e8a29bb..fc8553a66b82 100644 --- a/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java +++ b/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java @@ -6,34 +6,22 @@ import androidx.test.core.app.ApplicationProvider; import io.envoyproxy.envoymobile.AndroidEngineBuilder; import io.envoyproxy.envoymobile.Engine; -import io.envoyproxy.envoymobile.EnvoyError; -import io.envoyproxy.envoymobile.FinalStreamIntel; import io.envoyproxy.envoymobile.LogLevel; -import io.envoyproxy.envoymobile.RequestHeaders; -import io.envoyproxy.envoymobile.RequestHeadersBuilder; import io.envoyproxy.envoymobile.RequestMethod; -import io.envoyproxy.envoymobile.ResponseHeaders; -import io.envoyproxy.envoymobile.ResponseTrailers; import io.envoyproxy.envoymobile.Stream; -import io.envoyproxy.envoymobile.StreamIntel; import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; import io.envoyproxy.envoymobile.engine.testing.RequestScenario; import io.envoyproxy.envoymobile.engine.testing.Response; -import java.net.MalformedURLException; -import java.net.URL; + import java.nio.ByteBuffer; -import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; diff --git a/mobile/test/java/integration/AndroidEnvoyFlowTest.java b/mobile/test/java/integration/AndroidEnvoyFlowTest.java index fbdbcd446cbc..3bb9bcb1cedd 100644 --- a/mobile/test/java/integration/AndroidEnvoyFlowTest.java +++ b/mobile/test/java/integration/AndroidEnvoyFlowTest.java @@ -6,37 +6,22 @@ import androidx.test.core.app.ApplicationProvider; import io.envoyproxy.envoymobile.AndroidEngineBuilder; import io.envoyproxy.envoymobile.Engine; -import io.envoyproxy.envoymobile.EnvoyError; -import io.envoyproxy.envoymobile.RequestHeaders; -import io.envoyproxy.envoymobile.RequestHeadersBuilder; import io.envoyproxy.envoymobile.RequestMethod; -import io.envoyproxy.envoymobile.ResponseHeaders; -import io.envoyproxy.envoymobile.ResponseTrailers; import io.envoyproxy.envoymobile.Stream; import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; import io.envoyproxy.envoymobile.engine.testing.RequestScenario; import io.envoyproxy.envoymobile.engine.testing.Response; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.ByteBuffer; -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java index 43c2e025e4c9..5c1054697574 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java @@ -7,28 +7,16 @@ import io.envoyproxy.envoymobile.AndroidEngineBuilder; import io.envoyproxy.envoymobile.Custom; import io.envoyproxy.envoymobile.Engine; -import io.envoyproxy.envoymobile.EnvoyError; import io.envoyproxy.envoymobile.LogLevel; -import io.envoyproxy.envoymobile.RequestHeaders; -import io.envoyproxy.envoymobile.RequestHeadersBuilder; import io.envoyproxy.envoymobile.RequestMethod; -import io.envoyproxy.envoymobile.ResponseHeaders; -import io.envoyproxy.envoymobile.ResponseTrailers; import io.envoyproxy.envoymobile.Stream; import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; import io.envoyproxy.envoymobile.engine.JniLibrary; import io.envoyproxy.envoymobile.engine.testing.RequestScenario; import io.envoyproxy.envoymobile.engine.testing.Response; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.junit.After; import org.junit.Before; diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/RequestScenario.java b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/RequestScenario.java index 9f7d6b2758e1..e8922c7099af 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/RequestScenario.java +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/RequestScenario.java @@ -1,35 +1,18 @@ package io.envoyproxy.envoymobile.engine.testing; -import io.envoyproxy.envoymobile.AndroidEngineBuilder; -import io.envoyproxy.envoymobile.Engine; -import io.envoyproxy.envoymobile.EnvoyError; -import io.envoyproxy.envoymobile.FinalStreamIntel; -import io.envoyproxy.envoymobile.LogLevel; import io.envoyproxy.envoymobile.RequestHeaders; import io.envoyproxy.envoymobile.RequestHeadersBuilder; import io.envoyproxy.envoymobile.RequestMethod; -import io.envoyproxy.envoymobile.ResponseHeaders; -import io.envoyproxy.envoymobile.ResponseTrailers; -import io.envoyproxy.envoymobile.Stream; -import io.envoyproxy.envoymobile.StreamIntel; -import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; + import java.net.MalformedURLException; import java.net.URL; import java.nio.ByteBuffer; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; public final class RequestScenario { public int responseBufferSize = 1000; diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/Response.java b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/Response.java index d60bb5778940..eb53b93e2187 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/Response.java +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/Response.java @@ -1,16 +1,9 @@ package io.envoyproxy.envoymobile.engine.testing; -import io.envoyproxy.envoymobile.AndroidEngineBuilder; -import io.envoyproxy.envoymobile.Engine; import io.envoyproxy.envoymobile.EnvoyError; import io.envoyproxy.envoymobile.FinalStreamIntel; -import io.envoyproxy.envoymobile.LogLevel; -import io.envoyproxy.envoymobile.RequestHeaders; -import io.envoyproxy.envoymobile.RequestHeadersBuilder; -import io.envoyproxy.envoymobile.RequestMethod; import io.envoyproxy.envoymobile.ResponseHeaders; import io.envoyproxy.envoymobile.ResponseTrailers; -import io.envoyproxy.envoymobile.Stream; import io.envoyproxy.envoymobile.StreamIntel; import java.nio.ByteBuffer; import java.util.ArrayList; diff --git a/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java b/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java index a15bbc8f8d2d..1c984c747df1 100644 --- a/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java +++ b/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java @@ -10,7 +10,6 @@ import static org.junit.Assert.fail; import static org.robolectric.Shadows.shadowOf; -import android.content.Context; import android.content.Intent; import android.Manifest; import android.net.ConnectivityManager; @@ -22,7 +21,6 @@ import androidx.test.filters.SmallTest; import androidx.test.rule.GrantPermissionRule; import java.io.IOException; -import java.net.ConnectException; import java.nio.ByteBuffer; import java.util.AbstractMap; import java.util.ArrayList; diff --git a/mobile/test/java/org/chromium/net/DiskStorageTest.java b/mobile/test/java/org/chromium/net/DiskStorageTest.java index 72b925afb603..297839316d03 100644 --- a/mobile/test/java/org/chromium/net/DiskStorageTest.java +++ b/mobile/test/java/org/chromium/net/DiskStorageTest.java @@ -14,7 +14,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; -import java.util.Arrays; + import org.chromium.net.testing.CronetTestRule; import org.chromium.net.testing.Feature; import org.chromium.net.testing.FileUtils; diff --git a/mobile/test/java/org/chromium/net/impl/CronvoyEngineTest.java b/mobile/test/java/org/chromium/net/impl/CronvoyEngineTest.java index 862b73e281b1..b9df8f5eb9f7 100644 --- a/mobile/test/java/org/chromium/net/impl/CronvoyEngineTest.java +++ b/mobile/test/java/org/chromium/net/impl/CronvoyEngineTest.java @@ -8,7 +8,6 @@ import static org.junit.Assert.assertThrows; -import android.content.Context; import androidx.test.core.app.ApplicationProvider; import io.envoyproxy.envoymobile.RequestMethod; import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; diff --git a/mobile/test/java/org/chromium/net/testing/CronetTestRule.java b/mobile/test/java/org/chromium/net/testing/CronetTestRule.java index 1dfeb1921303..692d9c628db7 100644 --- a/mobile/test/java/org/chromium/net/testing/CronetTestRule.java +++ b/mobile/test/java/org/chromium/net/testing/CronetTestRule.java @@ -9,7 +9,7 @@ import android.os.StrictMode; import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; -import io.envoyproxy.envoymobile.LogLevel; + import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; import java.io.File; import java.lang.annotation.Annotation; @@ -24,9 +24,7 @@ import org.chromium.net.CronetEngine; import org.chromium.net.ExperimentalCronetEngine; import org.chromium.net.UrlResponseInfo; -import org.chromium.net.impl.CronvoyEngineBuilderImpl; import org.chromium.net.impl.NativeCronvoyProvider; -import org.chromium.net.impl.CronvoyUserAgent; import org.junit.Assert; import org.junit.rules.TestRule; import org.junit.runner.Description; diff --git a/mobile/test/java/org/chromium/net/testing/CronetTestRuleTest.java b/mobile/test/java/org/chromium/net/testing/CronetTestRuleTest.java index 8519dd113b48..895c581316f9 100644 --- a/mobile/test/java/org/chromium/net/testing/CronetTestRuleTest.java +++ b/mobile/test/java/org/chromium/net/testing/CronetTestRuleTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import androidx.test.ext.junit.runners.AndroidJUnit4; diff --git a/mobile/test/java/org/chromium/net/testing/MockUrlRequestJobFactory.java b/mobile/test/java/org/chromium/net/testing/MockUrlRequestJobFactory.java index a65cf41cb984..4c358bbf5e0c 100644 --- a/mobile/test/java/org/chromium/net/testing/MockUrlRequestJobFactory.java +++ b/mobile/test/java/org/chromium/net/testing/MockUrlRequestJobFactory.java @@ -1,7 +1,5 @@ package org.chromium.net.testing; -import static junit.framework.Assert.assertTrue; - import org.chromium.net.CronetEngine; import org.chromium.net.ExperimentalCronetEngine; From 8714c2e537746862e459c154f7db586135c26e04 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 14 Mar 2024 13:47:53 -0400 Subject: [PATCH 033/124] test: improving bad header testing (#32876) Signed-off-by: Alyssa Wilk --- test/integration/protocol_integration_test.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index e13e5a493b72..70ecf10ac2dd 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -4634,6 +4634,7 @@ TEST_P(ProtocolIntegrationTest, InvalidResponseHeaderName) { // } is invalid character in header name upstream_request_->encodeHeaders( Http::TestResponseHeaderMapImpl{{":status", "200"}, {"foo}name", "foo_value"}}, false); + upstream_request_->encodeData(1, true); ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); ASSERT_TRUE(response->waitForEndStream()); From 63cf70129f06e53f0915e7cefc4ead637784a183 Mon Sep 17 00:00:00 2001 From: Matt Jones Date: Thu, 14 Mar 2024 10:54:47 -0700 Subject: [PATCH 034/124] Add token lifetime constraints to JwtProvider (#32853) Adds new max_lifetime config field to restrict token lifetime accepted from a JwtProvider. Risk Level: Low Testing: Unit testing Docs Changes: Added subjects description inline in proto. Release Notes: Attached Optional [API Considerations](https://github.com/envoyproxy/envoy/blob/main/api/review_checklist.md): Feature is opt in, without specifying the config, there's no behavior change. Fixes #31455 Signed-off-by: Matthew Jones --- .../filters/http/jwt_authn/v3/config.proto | 30 +++++++++++++- changelogs/current.yaml | 6 +++ .../filters/http/jwt_authn/authenticator.cc | 13 ++++++ .../filters/http/jwt_authn/jwks_cache.cc | 33 +++++++++++++++ .../filters/http/jwt_authn/jwks_cache.h | 3 ++ .../http/jwt_authn/authenticator_test.cc | 28 +++++++++++++ .../filters/http/jwt_authn/jwks_cache_test.cc | 41 +++++++++++++++++++ test/extensions/filters/http/jwt_authn/mock.h | 2 + .../filters/http/jwt_authn/test_common.h | 33 +++++++++++++++ 9 files changed, 188 insertions(+), 1 deletion(-) diff --git a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto index 1256a40bdc2f..3db921a94818 100644 --- a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto +++ b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto @@ -54,7 +54,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // cache_duration: // seconds: 300 // -// [#next-free-field: 20] +// [#next-free-field: 22] message JwtProvider { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.jwt_authn.v2alpha.JwtProvider"; @@ -119,6 +119,34 @@ message JwtProvider { // type.matcher.v3.StringMatcher subjects = 19; + // Requires that the credential contains an `expiration `_. + // For instance, this could implement JWT-SVID + // `expiration restrictions `_. + // Unlike ``max_lifetime``, this only requires that expiration is present, where ``max_lifetime`` also checks the value. + // + // Example: + // + // .. code-block:: yaml + // + // require_expiration: true + // + bool require_expiration = 20; + + // Restrict the maximum remaining lifetime of a credential from the JwtProvider. Credential lifetime + // is the difference between the current time and the expiration of the credential. For instance, + // the following example will reject credentials that have a lifetime longer than 24 hours. If not set, + // expiration checking still occurs, but there is no limit on credential lifetime. If set, takes precedence + // over ``require_expiration``. + // + // Example: + // + // .. code-block:: yaml + // + // max_lifetime: + // seconds: 86400 + // + google.protobuf.Duration max_lifetime = 21; + // `JSON Web Key Set (JWKS) `_ is needed to // validate signature of a JWT. This field specifies where to fetch JWKS. oneof jwks_source_specifier { diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 6d2dff7d9edb..80b9ab473346 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -149,6 +149,12 @@ bug_fixes: - area: jwt_authn change: | Fixed JWT extractor, which concatenated headers with a comma, resultig in invalid tokens. +- area: jwt_authn + change: | + Added + :ref:`max_lifetime ` and + :ref:`require_expiration ` + to limit the maximum remaining lifetime of a token from a ``JwtProvider`` and implement restrictions for JWT-SVIDs. - area: router change: | Fix a timing issue when upstream requests are empty when decoding data and send local reply when that happens. This is diff --git a/source/extensions/filters/http/jwt_authn/authenticator.cc b/source/extensions/filters/http/jwt_authn/authenticator.cc index 56f72f426837..237f147ee5d0 100644 --- a/source/extensions/filters/http/jwt_authn/authenticator.cc +++ b/source/extensions/filters/http/jwt_authn/authenticator.cc @@ -13,6 +13,7 @@ #include "source/common/tracing/http_tracer_impl.h" #include "absl/strings/str_split.h" +#include "absl/time/time.h" #include "jwt_verify_lib/jwt.h" #include "jwt_verify_lib/struct_utils.h" #include "jwt_verify_lib/verify.h" @@ -248,6 +249,18 @@ void AuthenticatorImpl::startVerify() { return; } + absl::optional exp; + if (jwt_->exp_) { + exp = absl::FromUnixSeconds(jwt_->exp_); + } + const bool exp_allowed = jwks_data_->isLifetimeAllowed( + absl::FromChrono(timeSource().systemTime()), exp ? &exp.value() : nullptr); + + if (!exp_allowed) { + doneWithStatus(Status::JwtVerificationFail); + return; + } + if (use_jwt_cache) { handleGoodJwt(/*cache_hit=*/true); return; diff --git a/source/extensions/filters/http/jwt_authn/jwks_cache.cc b/source/extensions/filters/http/jwt_authn/jwks_cache.cc index 053d662e36d9..37bad44e10e9 100644 --- a/source/extensions/filters/http/jwt_authn/jwks_cache.cc +++ b/source/extensions/filters/http/jwt_authn/jwks_cache.cc @@ -14,6 +14,7 @@ #include "absl/container/node_hash_map.h" #include "absl/strings/string_view.h" +#include "absl/time/time.h" #include "absl/types/optional.h" #include "jwt_verify_lib/check_audience.h" @@ -65,6 +66,16 @@ class JwksDataImpl : public JwksCache::JwksData, public Logger::Loggablematch(jwt_subject); } + bool isLifetimeAllowed(const absl::Time& now, const absl::Time* exp) const override { + // This function takes the current time and calculates the remaining lifetime of the JWT. + // Then it compares that with the max lifetime in the config. Using issue time or not before + // claims would be better, but optional according to the spec. + + // Without a max lifetime, any exp is allowed. + if (!max_exp_.has_value()) { + return true; + } + + // If there's no exp field and we have a max set, then this isn't allowed. + if (exp == nullptr) { + return false; + } + + // Take the remaining credential lifetime and return if it's less + // than the max. + absl::Duration lifetime = *exp - now; + return lifetime < *max_exp_; + } + const Jwks* getJwksObj() const override { return tls_->jwks_.get(); } bool isExpired() const override { return time_source_.monotonicTime() >= tls_->expire_; } @@ -158,6 +190,7 @@ class JwksDataImpl : public JwksCache::JwksData, public Logger::Loggable> sub_matcher_; + absl::optional max_exp_; }; using JwksDataImplPtr = std::unique_ptr; diff --git a/source/extensions/filters/http/jwt_authn/jwks_cache.h b/source/extensions/filters/http/jwt_authn/jwks_cache.h index b9a149f9a64c..9cd0aac6926a 100644 --- a/source/extensions/filters/http/jwt_authn/jwks_cache.h +++ b/source/extensions/filters/http/jwt_authn/jwks_cache.h @@ -59,6 +59,9 @@ class JwksCache { // Check if a subject is allowed. virtual bool isSubjectAllowed(absl::string_view sub) const PURE; + // Check if the current credential lifetime is allowed. + virtual bool isLifetimeAllowed(const absl::Time& now, const absl::Time* exp) const PURE; + // Get the cached config: JWT rule. virtual const envoy::extensions::filters::http::jwt_authn::v3::JwtProvider& getJwtProvider() const PURE; diff --git a/test/extensions/filters/http/jwt_authn/authenticator_test.cc b/test/extensions/filters/http/jwt_authn/authenticator_test.cc index 902455fc4331..7adbf27588e0 100644 --- a/test/extensions/filters/http/jwt_authn/authenticator_test.cc +++ b/test/extensions/filters/http/jwt_authn/authenticator_test.cc @@ -128,6 +128,34 @@ TEST_F(AuthenticatorTest, TestSubject) { } } +// Test authenticator constraints for subjects +TEST_F(AuthenticatorTest, TestLifetimeConstraint) { + TestUtility::loadFromYaml(ExpirationConfig, proto_config_); + + { + // Test that GoodToken fails because the expiration time is more than 24h in the future. + createAuthenticator(); + Http::TestRequestHeaderMapImpl headers{{"Authorization", "Bearer " + std::string(GoodToken)}}; + expectVerifyStatus(Status::JwtVerificationFail, headers); + } + + { + // spiffe_provider has an inf expiration time, so any expiration works. + createAuthenticator(nullptr, "spiffe_provider"); + EXPECT_CALL(*raw_fetcher_, fetch(_, _)) + .WillOnce(Invoke([this](Tracing::Span&, JwksFetcher::JwksReceiver& receiver) { + receiver.onJwksSuccess(std::move(jwks_)); + })); + Http::TestRequestHeaderMapImpl headers{{"Authorization", "Bearer " + std::string(GoodToken)}}; + expectVerifyStatus(Status::Ok, headers); + + // Tokens without expiration should fail for infinite constraints + Http::TestRequestHeaderMapImpl non_expiring_headers{ + {"Authorization", "Bearer " + std::string(NonExpiringToken)}}; + expectVerifyStatus(Status::JwtVerificationFail, non_expiring_headers); + } +} + // This test validates a good JWT authentication with a remote Jwks. // It also verifies Jwks cache with 10 JWT authentications, but only one Jwks fetch. TEST_F(AuthenticatorTest, TestOkJWTandCache) { diff --git a/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc b/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc index 2a9e9b866ef8..b1da3485a5f6 100644 --- a/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc +++ b/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc @@ -11,6 +11,8 @@ #include "test/mocks/server/factory_context.h" #include "test/test_common/utility.h" +#include "absl/time/time.h" + using envoy::extensions::filters::http::jwt_authn::v3::JwtAuthentication; using envoy::extensions::filters::http::jwt_authn::v3::RemoteJwks; using ::google::jwt_verify::Status; @@ -221,6 +223,45 @@ TEST_F(JwksCacheTest, TestSubjects) { } } +// Test lifetime constraints for JwtProvider +TEST_F(JwksCacheTest, TestLifetime) { + ScopedInjectableLoader engine(std::make_unique()); + setupCache(ExpirationConfig); + + { + auto jwks = cache_->findByIssuer("https://example.com"); + + absl::Time created; + absl::Time good_exp = created + absl::Minutes(30); + absl::Time bad_exp = created + absl::Hours(25); + // Issuer has a lifetime constraint of 24 hours, so 30 minutes is good. + EXPECT_TRUE(jwks->isLifetimeAllowed(created, &good_exp)); + // 25 hours should fail based on lifetime + EXPECT_FALSE(jwks->isLifetimeAllowed(created, &bad_exp)); + // Tokens without expiration should also fail + EXPECT_FALSE(jwks->isLifetimeAllowed(created, nullptr)); + } + + { + auto jwks = cache_->findByIssuer("https://spiffe.example.com"); + + absl::Time created; + absl::Time long_exp = created + absl::Hours(2500); + // Spiffe provider has a infinite constraint, so any time should work. + EXPECT_TRUE(jwks->isLifetimeAllowed(created, &long_exp)); + // Infinite constraints require an expiration, so null should fail. + EXPECT_FALSE(jwks->isLifetimeAllowed(created, nullptr)); + } + + { + auto jwks = cache_->findByIssuer("https://noexp.example.com"); + + absl::Time created; + // Require_expiration set to false, so this should pass. + EXPECT_TRUE(jwks->isLifetimeAllowed(created, nullptr)); + } +} + } // namespace } // namespace JwtAuthn } // namespace HttpFilters diff --git a/test/extensions/filters/http/jwt_authn/mock.h b/test/extensions/filters/http/jwt_authn/mock.h index 533990cdb04d..3167fcd67a08 100644 --- a/test/extensions/filters/http/jwt_authn/mock.h +++ b/test/extensions/filters/http/jwt_authn/mock.h @@ -78,10 +78,12 @@ class MockJwksData : public JwksCache::JwksData { ON_CALL(*this, isExpired()).WillByDefault(::testing::Return(false)); ON_CALL(*this, getJwtCache()).WillByDefault(::testing::ReturnRef(jwt_cache_)); ON_CALL(*this, isSubjectAllowed(_)).WillByDefault(::testing::Return(true)); + ON_CALL(*this, isLifetimeAllowed(_, _)).WillByDefault(::testing::Return(true)); } MOCK_METHOD(bool, areAudiencesAllowed, (const std::vector&), (const)); MOCK_METHOD(bool, isSubjectAllowed, (const absl::string_view), (const)); + MOCK_METHOD(bool, isLifetimeAllowed, (const absl::Time&, const absl::Time*), (const)); MOCK_METHOD(const envoy::extensions::filters::http::jwt_authn::v3::JwtProvider&, getJwtProvider, (), (const)); MOCK_METHOD(const ::google::jwt_verify::Jwks*, getJwksObj, (), (const)); diff --git a/test/extensions/filters/http/jwt_authn/test_common.h b/test/extensions/filters/http/jwt_authn/test_common.h index cd679510e0f7..813565e26bc6 100644 --- a/test/extensions/filters/http/jwt_authn/test_common.h +++ b/test/extensions/filters/http/jwt_authn/test_common.h @@ -112,6 +112,39 @@ const char SubjectConfig[] = R"( )"; +// Provider config with various subject constraints +const char ExpirationConfig[] = R"( +providers: + example_provider: + issuer: https://example.com + max_lifetime: + seconds: 86400 + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + spiffe_provider: + issuer: https://spiffe.example.com + require_expiration: true + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + noexp_provider: + issuer: https://noexp.example.com + require_expiration: false + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 +)"; + // A good config. const char ExampleConfig[] = R"( providers: From 975d4107061ea92a62e99490c9474ace17d9609a Mon Sep 17 00:00:00 2001 From: ohadvano <49730675+ohadvano@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:58:04 +0200 Subject: [PATCH 035/124] health_checks: add option to always log successful health checks (#32674) Resolves #32119. This allows the option to always log successful health checks. On the first successful health check, only ``logAddHealthy`` is called. On consecutive successful health checks, ``logSuccessfulHealthCheck`` is called. Risk Level: low (config guarded) Testing: unit tests Docs Changes: API docs Release Notes: added Platform Specific Features: none Signed-off-by: ohadvano --- api/envoy/config/core/v3/health_check.proto | 7 +- .../data/core/v3/health_check_event.proto | 11 ++- changelogs/current.yaml | 4 + envoy/upstream/health_checker.h | 10 ++- .../upstream/health_checker_event_logger.cc | 7 ++ .../upstream/health_checker_event_logger.h | 2 + .../common/health_checker_base_impl.cc | 9 ++- .../common/health_checker_base_impl.h | 1 + .../upstream/health_checker_impl_test.cc | 79 +++++++++++++++++++ .../upstream/health_check_event_logger.h | 2 + 10 files changed, 127 insertions(+), 5 deletions(-) diff --git a/api/envoy/config/core/v3/health_check.proto b/api/envoy/config/core/v3/health_check.proto index 2ec258d8ac09..739d5db1e108 100644 --- a/api/envoy/config/core/v3/health_check.proto +++ b/api/envoy/config/core/v3/health_check.proto @@ -62,7 +62,7 @@ message HealthStatusSet { [(validate.rules).repeated = {items {enum {defined_only: true}}}]; } -// [#next-free-field: 26] +// [#next-free-field: 27] message HealthCheck { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.core.HealthCheck"; @@ -392,6 +392,11 @@ message HealthCheck { // The default value is false. bool always_log_health_check_failures = 19; + // If set to true, health check success events will always be logged. If set to false, only host addition event will be logged + // if it is the first successful health check, or if the healthy threshold is reached. + // The default value is false. + bool always_log_health_check_success = 26; + // This allows overriding the cluster TLS settings, just for health check connections. TlsOptions tls_options = 21; diff --git a/api/envoy/data/core/v3/health_check_event.proto b/api/envoy/data/core/v3/health_check_event.proto index 17e78ea5ecb1..2f8c32d92caf 100644 --- a/api/envoy/data/core/v3/health_check_event.proto +++ b/api/envoy/data/core/v3/health_check_event.proto @@ -35,7 +35,7 @@ enum HealthCheckerType { THRIFT = 4; } -// [#next-free-field: 12] +// [#next-free-field: 13] message HealthCheckEvent { option (udpa.annotations.versioning).previous_message_type = "envoy.data.core.v2alpha.HealthCheckEvent"; @@ -55,6 +55,12 @@ message HealthCheckEvent { // Host addition. HealthCheckAddHealthy add_healthy_event = 5; + // A health check was successful. Note: a host will be considered healthy either if it is + // the first ever health check, or if the healthy threshold is reached. This kind of event + // indicate that a health check was successful, but does not indicates that the host is + // considered healthy. A host is considered healthy if HealthCheckAddHealthy kind of event is sent. + HealthCheckSuccessful successful_health_check_event = 12; + // Host failure. HealthCheckFailure health_check_failure_event = 7; @@ -93,6 +99,9 @@ message HealthCheckAddHealthy { bool first_check = 1; } +message HealthCheckSuccessful { +} + message HealthCheckFailure { option (udpa.annotations.versioning).previous_message_type = "envoy.data.core.v2alpha.HealthCheckFailure"; diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 80b9ab473346..1829a73c28c2 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -292,6 +292,10 @@ new_features: change: | Added load shed point ``envoy.load_shed_points.hcm_ondata_creating_codec`` that closes connections before creating codec if Envoy is under pressure, typically memory. +- area: health_checks + change: | + added a :ref:`configuration option ` that enables + health check logs on each successful health check. - area: string matcher change: | Added an :ref:`extension point for custom string matcher implementations `. diff --git a/envoy/upstream/health_checker.h b/envoy/upstream/health_checker.h index 901401a6cf25..f5e3045da79b 100644 --- a/envoy/upstream/health_checker.h +++ b/envoy/upstream/health_checker.h @@ -94,12 +94,20 @@ class HealthCheckEventLogger { * Log a healthy host addition event. * @param health_checker_type supplies the type of health checker that generated the event. * @param host supplies the host that generated the event. - * @param healthy_threshold supplied the configured healthy threshold for this health check. * @param first_check whether this is a fast path success on the first health check for this host. */ virtual void logAddHealthy(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host, bool first_check) PURE; + /** + * A health check was successful. + * @param health_checker_type supplies the type of health checker that generated the event. + * @param host supplies the host that generated the event. + */ + virtual void + logSuccessfulHealthCheck(envoy::data::core::v3::HealthCheckerType health_checker_type, + const HostDescriptionConstSharedPtr& host) PURE; + /** * Log a degraded healthy host event. * @param health_checker_type supplies the type of health checker that generated the event. diff --git a/source/common/upstream/health_checker_event_logger.cc b/source/common/upstream/health_checker_event_logger.cc index c75d3a55e6db..b6218cb0c07c 100644 --- a/source/common/upstream/health_checker_event_logger.cc +++ b/source/common/upstream/health_checker_event_logger.cc @@ -37,6 +37,13 @@ void HealthCheckEventLoggerImpl::logAddHealthy( }); } +void HealthCheckEventLoggerImpl::logSuccessfulHealthCheck( + envoy::data::core::v3::HealthCheckerType health_checker_type, + const HostDescriptionConstSharedPtr& host) { + createHealthCheckEvent(health_checker_type, *host, + [](auto& event) { event.mutable_successful_health_check_event(); }); +} + void HealthCheckEventLoggerImpl::logDegraded( envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host) { diff --git a/source/common/upstream/health_checker_event_logger.h b/source/common/upstream/health_checker_event_logger.h index 808e2ea0dec2..38bd0ed9d0a6 100644 --- a/source/common/upstream/health_checker_event_logger.h +++ b/source/common/upstream/health_checker_event_logger.h @@ -48,6 +48,8 @@ class HealthCheckEventLoggerImpl : public HealthCheckEventLogger { envoy::data::core::v3::HealthCheckFailureType failure_type) override; void logAddHealthy(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host, bool first_check) override; + void logSuccessfulHealthCheck(envoy::data::core::v3::HealthCheckerType health_checker_type, + const HostDescriptionConstSharedPtr& host) override; void logUnhealthy(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host, envoy::data::core::v3::HealthCheckFailureType failure_type, diff --git a/source/extensions/health_checkers/common/health_checker_base_impl.cc b/source/extensions/health_checkers/common/health_checker_base_impl.cc index f774af6f9aca..262ad36ba947 100644 --- a/source/extensions/health_checkers/common/health_checker_base_impl.cc +++ b/source/extensions/health_checkers/common/health_checker_base_impl.cc @@ -18,8 +18,8 @@ HealthCheckerImplBase::HealthCheckerImplBase(const Cluster& cluster, Random::RandomGenerator& random, HealthCheckEventLoggerPtr&& event_logger) : always_log_health_check_failures_(config.always_log_health_check_failures()), - cluster_(cluster), dispatcher_(dispatcher), - timeout_(PROTOBUF_GET_MS_REQUIRED(config, timeout)), + always_log_health_check_success_(config.always_log_health_check_success()), cluster_(cluster), + dispatcher_(dispatcher), timeout_(PROTOBUF_GET_MS_REQUIRED(config, timeout)), unhealthy_threshold_(PROTOBUF_GET_WRAPPED_REQUIRED(config, unhealthy_threshold)), healthy_threshold_(PROTOBUF_GET_WRAPPED_REQUIRED(config, healthy_threshold)), stats_(generateStats(cluster.info()->statsScope())), runtime_(runtime), random_(random), @@ -305,6 +305,11 @@ void HealthCheckerImplBase::ActiveHealthCheckSession::handleSuccess(bool degrade host_->setLastHcPassTime(time_source_.monotonicTime()); } + if (changed_state != HealthTransition::Changed && parent_.always_log_health_check_success_ && + parent_.event_logger_) { + parent_.event_logger_->logSuccessfulHealthCheck(parent_.healthCheckerType(), host_); + } + changed_state = clearPendingFlag(changed_state); if (degraded != host_->healthFlagGet(Host::HealthFlag::DEGRADED_ACTIVE_HC)) { diff --git a/source/extensions/health_checkers/common/health_checker_base_impl.h b/source/extensions/health_checkers/common/health_checker_base_impl.h index 1e7e308348cd..4527e1440a0e 100644 --- a/source/extensions/health_checkers/common/health_checker_base_impl.h +++ b/source/extensions/health_checkers/common/health_checker_base_impl.h @@ -104,6 +104,7 @@ class HealthCheckerImplBase : public HealthChecker, virtual envoy::data::core::v3::HealthCheckerType healthCheckerType() const PURE; const bool always_log_health_check_failures_; + const bool always_log_health_check_success_; const Cluster& cluster_; Event::Dispatcher& dispatcher_; const std::chrono::milliseconds timeout_; diff --git a/test/common/upstream/health_checker_impl_test.cc b/test/common/upstream/health_checker_impl_test.cc index 9558e88d7e39..a3349b98667e 100644 --- a/test/common/upstream/health_checker_impl_test.cc +++ b/test/common/upstream/health_checker_impl_test.cc @@ -309,6 +309,25 @@ class HttpHealthCheckerImplTest : public Event::TestUsingSimulatedTime, addCompletionCallback(); } + void setupNoServiceValidationHCAlwaysLogSuccess() { + const std::string yaml = R"EOF( + timeout: 1s + interval: 1s + no_traffic_interval: 5s + interval_jitter: 1s + unhealthy_threshold: 2 + healthy_threshold: 1 + http_health_check: + service_name_matcher: + prefix: locations + path: /healthcheck + always_log_health_check_success: true + )EOF"; + + allocHealthChecker(yaml); + addCompletionCallback(); + } + void setupNoServiceValidationNoReuseConnectionHC() { std::string yaml = R"EOF( timeout: 1s @@ -2348,6 +2367,42 @@ TEST_F(HttpHealthCheckerImplTest, HttpFailLogError) { cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->coarseHealth()); } +TEST_F(HttpHealthCheckerImplTest, HttpAlwaysLogSuccess) { + setupNoServiceValidationHCAlwaysLogSuccess(); + cluster_->prioritySet().getMockHostSet(0)->hosts_ = { + makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; + cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->healthFlagSet( + Host::HealthFlag::FAILED_ACTIVE_HC); + cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->healthFlagSet( + Host::HealthFlag::PENDING_ACTIVE_HC); + expectSessionCreate(); + expectStreamCreate(0); + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_, _)); + health_checker_->start(); + + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)); + EXPECT_CALL(event_logger_, logAddHealthy(_, _, true)); + EXPECT_CALL(event_logger_, logSuccessfulHealthCheck(_, _)).Times(0); + EXPECT_CALL(*test_sessions_[0]->interval_timer_, enableTimer(_, _)); + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, disableTimer()); + respond(0, "200", false); + EXPECT_EQ(Host::Health::Healthy, + cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->coarseHealth()); + + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_, _)); + expectStreamCreate(0); + test_sessions_[0]->interval_timer_->invokeCallback(); + + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); + EXPECT_CALL(event_logger_, logAddHealthy(_, _, _)).Times(0); + EXPECT_CALL(event_logger_, logSuccessfulHealthCheck(_, _)); + EXPECT_CALL(*test_sessions_[0]->interval_timer_, enableTimer(_, _)); + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, disableTimer()); + respond(0, "200", false); + EXPECT_EQ(Host::Health::Healthy, + cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->coarseHealth()); +} + TEST_F(HttpHealthCheckerImplTest, Disconnect) { setupNoServiceValidationHC(); EXPECT_CALL(event_logger_, logUnhealthy(_, _, _, true)); @@ -6429,6 +6484,19 @@ TEST(HealthCheckEventLoggerImplTest, All) { "{\"region\":\"\",\"zone\":\"\",\"sub_zone\":\"\"}}\n"})); event_logger.logAddHealthy(envoy::data::core::v3::HTTP, host, false); + EXPECT_CALL(*file, write(absl::string_view{ + "{\"health_checker_type\":\"HTTP\",\"host\":{\"socket_address\":{" + "\"protocol\":\"TCP\",\"address\":\"10.0.0.1\",\"port_value\":443," + "\"resolver_name\":\"\"," + "\"ipv4_compat\":false}},\"cluster_name\":\"fake_" + "cluster\"," + "\"timestamp\":\"2009-02-13T23:31:31.234Z\"," + "\"metadata\":" + "{\"filter_metadata\":{},\"typed_filter_metadata\":{}},\"locality\":" + "{\"region\":\"\",\"zone\":\"\",\"sub_zone\":\"\"}," + "\"successful_health_check_event\":{}}\n"})); + event_logger.logSuccessfulHealthCheck(envoy::data::core::v3::HTTP, host); + EXPECT_CALL(*file, write(absl::string_view{ "{\"health_checker_type\":\"HTTP\",\"host\":{\"socket_address\":{" "\"protocol\":\"TCP\",\"address\":\"10.0.0.1\",\"port_value\":443," @@ -6516,6 +6584,17 @@ TEST(HealthCheckEventLoggerImplTest, OneEventLogger) { "{\"filter_metadata\":{},\"typed_filter_metadata\":{}},\"locality\":" "{\"region\":\"\",\"zone\":\"\",\"sub_zone\":\"\"}}\n"); + event_logger.logSuccessfulHealthCheck(envoy::data::core::v3::HTTP, host); + EXPECT_EQ( + file_log_data.value(), + "{\"health_checker_type\":\"HTTP\",\"host\":{\"socket_address\":{" + "\"protocol\":\"TCP\",\"address\":\"10.0.0.1\",\"port_value\":443,\"resolver_name\":\"\"," + "\"ipv4_compat\":false}},\"cluster_name\":\"fake_" + "cluster\",\"timestamp\":\"2009-02-13T23:31:31.234Z\"," + "\"metadata\":" + "{\"filter_metadata\":{},\"typed_filter_metadata\":{}},\"locality\":" + "{\"region\":\"\",\"zone\":\"\",\"sub_zone\":\"\"},\"successful_health_check_event\":{}}\n"); + event_logger.logUnhealthy(envoy::data::core::v3::HTTP, host, envoy::data::core::v3::ACTIVE, false); EXPECT_EQ( diff --git a/test/mocks/upstream/health_check_event_logger.h b/test/mocks/upstream/health_check_event_logger.h index 1d6e75819b3b..200512ec9e28 100644 --- a/test/mocks/upstream/health_check_event_logger.h +++ b/test/mocks/upstream/health_check_event_logger.h @@ -22,6 +22,8 @@ class MockHealthCheckEventLogger : public HealthCheckEventLogger { MOCK_METHOD(void, logAddHealthy, (envoy::data::core::v3::HealthCheckerType, const HostDescriptionConstSharedPtr&, bool)); + MOCK_METHOD(void, logSuccessfulHealthCheck, + (envoy::data::core::v3::HealthCheckerType, const HostDescriptionConstSharedPtr&)); MOCK_METHOD(void, logUnhealthy, (envoy::data::core::v3::HealthCheckerType, const HostDescriptionConstSharedPtr&, envoy::data::core::v3::HealthCheckFailureType, bool)); From 5bb37a927dcaf1354a989071e8ab273979c459df Mon Sep 17 00:00:00 2001 From: "Adi (Suissa) Peleg" Date: Thu, 14 Mar 2024 14:21:05 -0400 Subject: [PATCH 036/124] EDS-caching: multiple clusters using the same EDS resource fix (#32821) Signed-off-by: Adi Suissa-Peleg --- changelogs/current.yaml | 5 + .../grpc/eds_resources_cache_impl.cc | 11 ++- .../grpc/eds_resources_cache_impl_test.cc | 43 +++++++++ test/integration/ads_integration_test.cc | 96 +++++++++++++++++++ 4 files changed, 152 insertions(+), 3 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 1829a73c28c2..4bb71b22796c 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -146,6 +146,11 @@ bug_fixes: to support half close semantics during TCP tunneling. This behavioral change can be temporarily reverted by setting runtime guard ``envoy.reloadable_features.tcp_tunneling_send_downstream_fin_on_upstream_trailers`` to false. +- area: eds-caching + change: | + Fixing an issue where EDS caching is used (protected by the ``envoy.restart_features.use_eds_cache_for_ads`` + runtime flag that is false by default), when multiple clusters use the same EDS resource, and that cached EDS + resource is used. - area: jwt_authn change: | Fixed JWT extractor, which concatenated headers with a comma, resultig in invalid tokens. diff --git a/source/extensions/config_subscription/grpc/eds_resources_cache_impl.cc b/source/extensions/config_subscription/grpc/eds_resources_cache_impl.cc index b527c2b6b404..bf3709a54172 100644 --- a/source/extensions/config_subscription/grpc/eds_resources_cache_impl.cc +++ b/source/extensions/config_subscription/grpc/eds_resources_cache_impl.cc @@ -1,5 +1,7 @@ #include "source/extensions/config_subscription/grpc/eds_resources_cache_impl.h" +#include + #include "source/common/common/logger.h" namespace Envoy { @@ -49,9 +51,12 @@ void EdsResourcesCacheImpl::removeCallback(absl::string_view resource_name, resource_it != resources_map_.end()) { ENVOY_LOG_MISC(trace, "Removing callback for resource {} from the xDS resource cache", resource_name); - resource_it->second.removal_cbs_.erase(std::remove(resource_it->second.removal_cbs_.begin(), - resource_it->second.removal_cbs_.end(), - removal_cb)); + auto& callbacks = resource_it->second.removal_cbs_; + // Using the Erase-Remove idiom, in which all entries to be removed are + // moved to the end of the vector by the remove() call, and then the erase() call + // will erase these entries from the first element to be removed all the + // way to the end of the vector. + callbacks.erase(std::remove(callbacks.begin(), callbacks.end(), removal_cb), callbacks.end()); } } diff --git a/test/extensions/config_subscription/grpc/eds_resources_cache_impl_test.cc b/test/extensions/config_subscription/grpc/eds_resources_cache_impl_test.cc index 4a769c8b25f0..ae65e05b390b 100644 --- a/test/extensions/config_subscription/grpc/eds_resources_cache_impl_test.cc +++ b/test/extensions/config_subscription/grpc/eds_resources_cache_impl_test.cc @@ -224,6 +224,49 @@ TEST_F(EdsResourcesCacheImplTest, ExplicitRemoveCallback) { EXPECT_EQ(0, callback.calls_counter_map_["foo_cla"]); } +// Validate explicit callback removal of multiple callbacks with the same name, +// and a call to setResource in between is executed properly. +TEST_F(EdsResourcesCacheImplTest, ExplicitSameNameRemoveCallbacks) { + ResourceRemovalCallbackCounter callback1; + ResourceRemovalCallbackCounter callback2; + + // Emulate receiving a resource. + ClusterLoadAssignment resource; + resource.set_cluster_name("foo"); + // Set the CLA resource to some resource_name. + resources_cache_.setResource("foo_cla", resource); + EXPECT_EQ(1, resources_cache_.cacheSizeForTest()); + + // Emulate resource fetched from cache with 2 different callbacks. + // Fetch the resource by the first name and register a callback. + const auto& fetched_resource1 = resources_cache_.getResource("foo_cla", &callback1); + EXPECT_TRUE(fetched_resource1.has_value()); + EXPECT_EQ("foo", fetched_resource1->cluster_name()); + EXPECT_EQ(0, callback1.calls_counter_map_["foo_cla"]); + // Fetch the resource by the second name and register the same callback. + const auto& fetched_resource2 = resources_cache_.getResource("foo_cla", &callback2); + EXPECT_TRUE(fetched_resource2.has_value()); + EXPECT_EQ("foo", fetched_resource2->cluster_name()); + EXPECT_EQ(0, callback2.calls_counter_map_["foo_cla"]); + + // Emulate receiving the resource again, and invoking the callbacks removal. + resources_cache_.removeCallback("foo_cla", &callback1); + + // Set the resource again (an ongoing update). + ClusterLoadAssignment resource2; + resource2.set_cluster_name("foo"); + resources_cache_.setResource("foo_cla", resource2); + + // Remove the callback using the second name. + resources_cache_.removeCallback("foo_cla", &callback2); + + // Remove the resource using the first name, callback not invoked. + resources_cache_.removeResource("foo_cla"); + EXPECT_EQ(0, resources_cache_.cacheSizeForTest()); + EXPECT_EQ(0, callback1.calls_counter_map_["foo_cla"]); + EXPECT_EQ(0, callback2.calls_counter_map_["foo_cla"]); +} + // Validate correct removal callback gets notified when multiple resources are used. TEST_F(EdsResourcesCacheImplTest, MultipleResourcesRemoveCallbackCalled) { ResourceRemovalCallbackCounter callback; diff --git a/test/integration/ads_integration_test.cc b/test/integration/ads_integration_test.cc index 024247a0e569..0fb8c4ecb006 100644 --- a/test/integration/ads_integration_test.cc +++ b/test/integration/ads_integration_test.cc @@ -717,6 +717,102 @@ TEST_P(AdsIntegrationTest, CdsKeepEdsAfterWarmingFailure) { makeSingleRequest(); } +// Validate that an update to 2 Clusters that have the same ClusterLoadAssignment, and +// that don't receive updated ClusterLoadAssignment use the previous (cached) cluster +// load assignment. +TEST_P(AdsIntegrationTest, DoubleClustersCachedLoadAssignment) { + // TODO(adisuissa): this test should be kept after the runtime guard is deprecated + // (only the runtime guard should be removed). + config_helper_.addRuntimeOverride("envoy.restart_features.use_eds_cache_for_ads", "true"); + initialize(); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "", {}, {}, {}, true)); + envoy::config::cluster::v3::Cluster cluster0 = buildCluster("cluster_0"); + envoy::config::cluster::v3::Cluster cluster1 = buildCluster("cluster_1"); + // Set a small EDS subscription expiration. + cluster0.mutable_eds_cluster_config() + ->mutable_eds_config() + ->mutable_initial_fetch_timeout() + ->set_nanos(100 * 1000 * 1000); + cluster1.mutable_eds_cluster_config() + ->mutable_eds_config() + ->mutable_initial_fetch_timeout() + ->set_nanos(100 * 1000 * 1000); + // Set the EDS service of cluster0 and cluster1 to be the same. + cluster0.mutable_eds_cluster_config()->set_service_name("same_eds"); + cluster1.mutable_eds_cluster_config()->set_service_name("same_eds"); + sendDiscoveryResponse( + Config::TypeUrl::get().Cluster, {cluster0, cluster1}, {cluster0, cluster1}, {}, "1"); + + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "", + {"same_eds"}, {"same_eds"}, {})); + auto cla_0 = buildClusterLoadAssignment("same_eds"); + sendDiscoveryResponse( + Config::TypeUrl::get().ClusterLoadAssignment, {cla_0}, {cla_0}, {}, "1"); + + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "1", {}, {}, {})); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Listener, "", {}, {}, {})); + sendDiscoveryResponse( + Config::TypeUrl::get().Listener, {buildListener("listener_0", "route_config_0")}, + {buildListener("listener_0", "route_config_0")}, {}, "1"); + + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "1", + {"same_eds"}, {}, {})); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().RouteConfiguration, "", + {"route_config_0"}, {"route_config_0"}, {})); + sendDiscoveryResponse( + Config::TypeUrl::get().RouteConfiguration, {buildRouteConfig("route_config_0", "cluster_0")}, + {buildRouteConfig("route_config_0", "cluster_0")}, {}, "1"); + + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Listener, "1", {}, {}, {})); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().RouteConfiguration, "1", + {"route_config_0"}, {}, {})); + test_server_->waitForCounterGe("listener_manager.listener_create_success", 1); + makeSingleRequest(); + + // Update a field of the clusters (connect_timeout) so the clusters in Envoy will be explicitly + // updated. + cluster0.mutable_connect_timeout()->set_seconds(7); + cluster1.mutable_connect_timeout()->set_seconds(7); + sendDiscoveryResponse( + Config::TypeUrl::get().Cluster, {cluster0, cluster1}, {cluster0, cluster1}, {}, "2"); + // Inconsistent SotW and delta behaviors for warming, see + // https://github.com/envoyproxy/envoy/issues/11477#issuecomment-657855029. + // TODO (dmitri-d) this should be remove when legacy mux implementations have been removed. + if (sotw_or_delta_ == Grpc::SotwOrDelta::Sotw) { + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "1", + {"same_eds"}, {}, {})); + } + + // Avoid sending an EDS update, and wait for EDS update timeout (that results in + // a cluster update without resources). + test_server_->waitForCounterGe("cluster.cluster_0.init_fetch_timeout", 1); + + if (sotw_or_delta_ == Grpc::SotwOrDelta::Sotw) { + // Expect another EDS request after the previous one wasn't answered and timed out. + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "1", + {"same_eds"}, {}, {})); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "1", + {"same_eds"}, {}, {})); + } + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "2", {}, {}, {})); + + // Envoy uses the cached resource. + EXPECT_EQ(1, test_server_->counter("cluster.cluster_0.assignment_use_cached")->value()); + // A single message should be successfully sent to the upstream. + makeSingleRequest(); + + // Now send an EDS update. + cla_0.mutable_policy()->mutable_overprovisioning_factor()->set_value(141); + sendDiscoveryResponse( + Config::TypeUrl::get().ClusterLoadAssignment, {cla_0}, {cla_0}, {}, "2"); + + // Wait for ingesting the update. + test_server_->waitForCounterEq("cluster.cluster_0.update_success", 2); + + // A single message should be successfully sent to the upstream. + makeSingleRequest(); +} + // Validate that the request with duplicate clusters in the initial request during server init is // rejected. TEST_P(AdsIntegrationTest, DuplicateInitialClusters) { From aad448bb5851e2e86c8dd332bd546e4075d6cf96 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Thu, 14 Mar 2024 13:22:59 -0500 Subject: [PATCH 037/124] mobile: Delete outdated mobile CODEOWNERS (#32911) Signed-off-by: Fredy Wijaya --- mobile/.github/CODEOWNERS | 1 - 1 file changed, 1 deletion(-) delete mode 100644 mobile/.github/CODEOWNERS diff --git a/mobile/.github/CODEOWNERS b/mobile/.github/CODEOWNERS deleted file mode 100644 index f24bf5f43472..000000000000 --- a/mobile/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @lyft/envoy-mobile-admin From bdb78f0f176bb769569e21bf96839dcc5ad8af3f Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Thu, 14 Mar 2024 14:33:58 -0500 Subject: [PATCH 038/124] mobile: Fix Java package name (#32913) Signed-off-by: Fredy Wijaya --- mobile/bazel/EnvoyMobileTestSuite.kt | 2 +- mobile/test/java/integration/AndroidEngineSocketTagTest.java | 2 +- mobile/test/java/integration/AndroidEnvoyEngineStartUpTest.java | 2 +- mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java | 2 +- mobile/test/java/integration/AndroidEnvoyFlowTest.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mobile/bazel/EnvoyMobileTestSuite.kt b/mobile/bazel/EnvoyMobileTestSuite.kt index 04a4130a5ddc..8941e4bdcdf9 100644 --- a/mobile/bazel/EnvoyMobileTestSuite.kt +++ b/mobile/bazel/EnvoyMobileTestSuite.kt @@ -18,7 +18,7 @@ object EnvoyMobileTestSuite { .enableAnnotationInfo() .enableMethodInfo() .ignoreClassVisibility() - .acceptPackages("io.envoyproxy", "test.kotlin.integration", "org.chromium.net") + .acceptPackages("io.envoyproxy", "test.java.integration", "test.kotlin.integration", "org.chromium.net") .scan() scan.getClassesWithMethodAnnotation(junitTestAnnotation) .asSequence() diff --git a/mobile/test/java/integration/AndroidEngineSocketTagTest.java b/mobile/test/java/integration/AndroidEngineSocketTagTest.java index 96bc516676a3..0bbd3014166f 100644 --- a/mobile/test/java/integration/AndroidEngineSocketTagTest.java +++ b/mobile/test/java/integration/AndroidEngineSocketTagTest.java @@ -1,4 +1,4 @@ -package test.kotlin.integration; +package test.java.integration; import static com.google.common.truth.Truth.assertThat; diff --git a/mobile/test/java/integration/AndroidEnvoyEngineStartUpTest.java b/mobile/test/java/integration/AndroidEnvoyEngineStartUpTest.java index fbfad680af6b..f90ea60e1d3b 100644 --- a/mobile/test/java/integration/AndroidEnvoyEngineStartUpTest.java +++ b/mobile/test/java/integration/AndroidEnvoyEngineStartUpTest.java @@ -1,4 +1,4 @@ -package test.kotlin.integration; +package test.java.integration; import android.content.Context; import androidx.test.core.app.ApplicationProvider; diff --git a/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java b/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java index fc8553a66b82..ede4a7c2ccbe 100644 --- a/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java +++ b/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java @@ -1,4 +1,4 @@ -package test.kotlin.integration; +package test.java.integration; import static com.google.common.truth.Truth.assertThat; diff --git a/mobile/test/java/integration/AndroidEnvoyFlowTest.java b/mobile/test/java/integration/AndroidEnvoyFlowTest.java index 3bb9bcb1cedd..0451ba5d5dc8 100644 --- a/mobile/test/java/integration/AndroidEnvoyFlowTest.java +++ b/mobile/test/java/integration/AndroidEnvoyFlowTest.java @@ -1,4 +1,4 @@ -package test.kotlin.integration; +package test.java.integration; import static com.google.common.truth.Truth.assertThat; From 8ec0c7f96f6ebd0c10883c9a24d295a93a9ffda0 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 14 Mar 2024 16:03:38 -0400 Subject: [PATCH 039/124] test: fixing a slow tst (#32879) Signed-off-by: Alyssa Wilk --- test/integration/fake_upstream.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 1453b47044e2..e8ae3c677553 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -579,7 +579,11 @@ void FakeConnectionBase::postToConnectionThread(std::function cb) { ++pending_cbs_; dispatcher_.post([this, cb]() { cb(); - --pending_cbs_; + { + // Snag this lock not because it's needed but so waitForNoPost doesn't stall + absl::MutexLock lock(&lock_); + --pending_cbs_; + } }); } From c85ec7ae0d234533ec346f68068bdcfc94ecf4bc Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Thu, 14 Mar 2024 16:18:23 -0400 Subject: [PATCH 040/124] fuzz: Add router fuzz corpus with empty config (#32877) Signed-off-by: Yan Avlasov --- .../route_corpus/testcase-6001880544444416 | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/common/router/route_corpus/testcase-6001880544444416 diff --git a/test/common/router/route_corpus/testcase-6001880544444416 b/test/common/router/route_corpus/testcase-6001880544444416 new file mode 100644 index 000000000000..657bdff08b77 --- /dev/null +++ b/test/common/router/route_corpus/testcase-6001880544444416 @@ -0,0 +1,29 @@ +config { + virtual_hosts { + name: " " + domains: "" + matcher { + matcher_list { + matchers { + on_match { + action { + name: "route" + typed_config { + type_url: "type.googleapis.com/envoy.config.route.v3.Route" + } + } + } + } + } + on_no_match { + action { + name: "route" + typed_config { + type_url: "type.googleapis.com/envoy.config.route.v3.Route" + } + } + } + } + } +} +random_value: 8388608 From f4b2d4a412dd70ea5afa74e81e1e320b5f2f5d88 Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Thu, 14 Mar 2024 13:38:52 -0700 Subject: [PATCH 041/124] Begin process of removing singleton use by StringMatcher (#32908) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "Revert "Begin process of removing singleton use by StringMatcher (#32… (#32902)" This reverts commit 9ac0dd8ab61b255077933b41d9eeaab7560b5717. Signed-off-by: Greg Greenway * try increasing shard count to attempt fix of test timeout Signed-off-by: Greg Greenway --------- Signed-off-by: Greg Greenway --- .../network/test/postgres_integration_test.cc | 6 +- envoy/server/factory_context.h | 10 ++ envoy/server/instance.h | 5 + envoy/ssl/context_manager.h | 10 +- .../cert_validator/platform_bridge/config.cc | 2 +- .../cert_validator/platform_bridge/config.h | 5 +- mobile/test/common/integration/BUILD | 2 + mobile/test/common/integration/test_server.cc | 2 +- mobile/test/common/integration/test_server.h | 4 +- .../test/common/integration/xds_test_server.h | 4 +- .../integration/xds_test_server_interface.cc | 4 + source/common/common/matchers.cc | 5 +- source/common/common/matchers.h | 57 ++++++++-- source/common/common/regex.h | 10 ++ .../tls/cert_validator/default_validator.cc | 17 +-- .../tls/cert_validator/default_validator.h | 4 +- source/common/tls/cert_validator/factory.h | 2 +- .../common/tls/cert_validator/san_matcher.cc | 11 +- .../common/tls/cert_validator/san_matcher.h | 10 +- source/common/tls/context_impl.cc | 22 ++-- source/common/tls/context_impl.h | 10 +- source/common/tls/context_manager_impl.cc | 9 +- source/common/tls/context_manager_impl.h | 12 +- source/extensions/string_matcher/lua/match.cc | 13 +-- source/extensions/string_matcher/lua/match.h | 4 +- .../cert_validator/spiffe/spiffe_validator.cc | 14 ++- .../cert_validator/spiffe/spiffe_validator.h | 2 +- .../transport_sockets/tls/config.cc | 5 +- .../extensions/transport_sockets/tls/config.h | 3 +- source/server/config_validation/server.cc | 4 +- source/server/config_validation/server.h | 2 + source/server/server.cc | 2 +- source/server/server.h | 2 + source/server/ssl_context_manager.cc | 7 +- source/server/ssl_context_manager.h | 5 +- .../grpc_client_integration_test_harness.h | 3 +- test/common/quic/BUILD | 2 + .../quic/envoy_quic_proof_source_test.cc | 11 +- .../quic/envoy_quic_proof_verifier_test.cc | 5 +- test/common/tls/cert_validator/BUILD | 2 + .../default_validator_integration_test.cc | 4 +- .../cert_validator/default_validator_test.cc | 72 +++++++----- .../tls/cert_validator/san_matcher_test.cc | 10 +- .../tls/cert_validator/timed_cert_validator.h | 12 +- test/common/tls/context_impl_test.cc | 28 ++--- test/common/tls/handshaker_factory_test.cc | 12 +- .../tls/integration/ssl_integration_test.cc | 4 +- test/common/tls/ssl_socket_test.cc | 103 +++++++++++------- test/common/upstream/hds_test.cc | 3 +- test/common/upstream/test_cluster_manager.h | 3 +- .../tcp_grpc_access_log_integration_test.cc | 4 +- test/extensions/filters/http/rbac/BUILD | 2 +- .../tls_inspector_integration_test.cc | 4 +- .../extensions/string_matcher/lua/lua_test.cc | 9 +- .../starttls/starttls_integration_test.cc | 4 +- .../upstream_starttls_integration_test.cc | 4 +- .../tls/cert_validator/spiffe/BUILD | 1 + .../spiffe_validator_integration_test.cc | 4 +- .../spiffe/spiffe_validator_test.cc | 8 +- test/integration/base_integration_test.h | 3 +- .../sds_static_integration_test.cc | 4 +- .../integration/tcp_proxy_integration_test.cc | 4 +- test/integration/utility.cc | 3 +- test/integration/xds_integration_test.cc | 6 +- test/integration/xfcc_integration_test.cc | 4 +- test/mocks/server/instance.cc | 5 +- test/mocks/server/instance.h | 3 +- test/mocks/server/server_factory_context.h | 3 + test/per_file_coverage.sh | 2 +- test/server/BUILD | 1 + .../config_validation/cluster_manager_test.cc | 3 +- test/server/ssl_context_manager_test.cc | 7 +- 72 files changed, 411 insertions(+), 237 deletions(-) diff --git a/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc b/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc index fb47ccde35d0..f027cfec1074 100644 --- a/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc +++ b/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc @@ -295,7 +295,8 @@ class UpstreamSSLBaseIntegrationTest : public PostgresBaseIntegrationTest { // The tls transport socket will be inserted into fake_upstream when // Envoy's upstream starttls transport socket is converted to secure mode. std::unique_ptr tls_context_manager = - std::make_unique(timeSystem()); + std::make_unique( + server_factory_context_); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext downstream_tls_context; @@ -527,7 +528,8 @@ class UpstreamAndDownstreamSSLIntegrationTest : public UpstreamSSLBaseIntegratio // The tls transport socket will be inserted into fake_upstream when // Envoy's upstream starttls transport socket is converted to secure mode. std::unique_ptr tls_context_manager = - std::make_unique(timeSystem()); + std::make_unique( + server_factory_context_); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext upstream_tls_context; diff --git a/envoy/server/factory_context.h b/envoy/server/factory_context.h index 8a139fbcb0c4..f6a34d53cf87 100644 --- a/envoy/server/factory_context.h +++ b/envoy/server/factory_context.h @@ -36,6 +36,11 @@ #include "source/common/protobuf/protobuf.h" namespace Envoy { + +namespace Regex { +class Engine; +} + namespace Server { namespace Configuration { @@ -129,6 +134,11 @@ class CommonFactoryContext { * @return ServerLifecycleNotifier& the lifecycle notifier for the server. */ virtual ServerLifecycleNotifier& lifecycleNotifier() PURE; + + /** + * @return the server regex engine. + */ + virtual Regex::Engine& regexEngine() PURE; }; /** diff --git a/envoy/server/instance.h b/envoy/server/instance.h index 683fef31b60d..a6fe23cd4c75 100644 --- a/envoy/server/instance.h +++ b/envoy/server/instance.h @@ -253,6 +253,11 @@ class Instance { */ virtual Configuration::StatsConfig& statsConfig() PURE; + /** + * @return the server regex engine. + */ + virtual Regex::Engine& regexEngine() PURE; + /** * @return envoy::config::bootstrap::v3::Bootstrap& the servers bootstrap configuration. */ diff --git a/envoy/ssl/context_manager.h b/envoy/ssl/context_manager.h index 8c7fae3707b2..1cd6f4054472 100644 --- a/envoy/ssl/context_manager.h +++ b/envoy/ssl/context_manager.h @@ -10,6 +10,13 @@ #include "envoy/stats/scope.h" namespace Envoy { + +namespace Server { +namespace Configuration { +class CommonFactoryContext; +} // namespace Configuration +} // namespace Server + namespace Ssl { // Opaque type defined and used by the ``ServerContext``. @@ -73,7 +80,8 @@ using ContextManagerPtr = std::unique_ptr; class ContextManagerFactory : public Config::UntypedFactory { public: ~ContextManagerFactory() override = default; - virtual ContextManagerPtr createContextManager(TimeSource& time_source) PURE; + virtual ContextManagerPtr + createContextManager(Server::Configuration::CommonFactoryContext& factory_context) PURE; // There could be only one factory thus the name is static. std::string name() const override { return "ssl_context_manager"; } diff --git a/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc b/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc index 223e6ff8e925..807ec2dcfef7 100644 --- a/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc +++ b/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc @@ -9,7 +9,7 @@ namespace Tls { CertValidatorPtr PlatformBridgeCertValidatorFactory::createCertValidator( const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& /*time_source*/) { + Server::Configuration::CommonFactoryContext& /*context*/) { return std::make_unique(config, stats); } diff --git a/mobile/library/common/extensions/cert_validator/platform_bridge/config.h b/mobile/library/common/extensions/cert_validator/platform_bridge/config.h index bc884fddc3ac..d070c3fc99af 100644 --- a/mobile/library/common/extensions/cert_validator/platform_bridge/config.h +++ b/mobile/library/common/extensions/cert_validator/platform_bridge/config.h @@ -15,8 +15,9 @@ namespace Tls { class PlatformBridgeCertValidatorFactory : public CertValidatorFactory, public Config::TypedFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override; + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override; std::string name() const override { return "envoy_mobile.cert_validator.platform_bridge_cert_validator"; diff --git a/mobile/test/common/integration/BUILD b/mobile/test/common/integration/BUILD index a60cc0281793..e2fd61c2abda 100644 --- a/mobile/test/common/integration/BUILD +++ b/mobile/test/common/integration/BUILD @@ -184,6 +184,7 @@ envoy_cc_test_library( "@envoy//source/exe:process_wide_lib", "@envoy//test/integration:autonomous_upstream_lib", "@envoy//test/integration:utility_lib", + "@envoy//test/mocks/server:server_factory_context_mocks", "@envoy//test/mocks/server:transport_socket_factory_context_mocks", "@envoy//test/test_common:environment_lib", "@envoy_build_config//:extension_registry", @@ -213,6 +214,7 @@ envoy_cc_test_library( "@envoy//source/exe:process_wide_lib", "@envoy//test/integration:autonomous_upstream_lib", "@envoy//test/integration:utility_lib", + "@envoy//test/mocks/server:server_factory_context_mocks", "@envoy//test/mocks/server:transport_socket_factory_context_mocks", "@envoy//test/test_common:environment_lib", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", diff --git a/mobile/test/common/integration/test_server.cc b/mobile/test/common/integration/test_server.cc index 5c478560fa55..6e840f9c8545 100644 --- a/mobile/test/common/integration/test_server.cc +++ b/mobile/test/common/integration/test_server.cc @@ -25,7 +25,7 @@ namespace Envoy { Network::DownstreamTransportSocketFactoryPtr TestServer::createQuicUpstreamTlsContext( testing::NiceMock& factory_context) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager{time_system_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager{server_factory_context_}; tls_context.mutable_common_tls_context()->add_alpn_protocols("h3"); envoy::extensions::transport_sockets::tls::v3::TlsCertificate* certs = tls_context.mutable_common_tls_context()->add_tls_certificates(); diff --git a/mobile/test/common/integration/test_server.h b/mobile/test/common/integration/test_server.h index dc3e7786e87e..6c7c0506b6f0 100644 --- a/mobile/test/common/integration/test_server.h +++ b/mobile/test/common/integration/test_server.h @@ -8,6 +8,7 @@ #include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.h" #include "test/integration/autonomous_upstream.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/integration/server.h" @@ -26,6 +27,7 @@ enum class TestServerType { class TestServer : public ListenerHooks { private: testing::NiceMock factory_context_; + testing::NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; Event::GlobalTimeSystem time_system_; Api::ApiPtr api_; @@ -35,7 +37,7 @@ class TestServer : public ListenerHooks { Thread::SkipAsserts skip_asserts_; ProcessWide process_wide; Thread::MutexBasicLockable lock; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{time_system_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; std::unique_ptr runfiles_; // Either test_server_ will be set for test_server_type is a proxy, otherwise upstream_ will be diff --git a/mobile/test/common/integration/xds_test_server.h b/mobile/test/common/integration/xds_test_server.h index de2fa1252721..f00bc8cb4030 100644 --- a/mobile/test/common/integration/xds_test_server.h +++ b/mobile/test/common/integration/xds_test_server.h @@ -7,6 +7,7 @@ #include "test/integration/fake_upstream.h" #include "test/integration/server.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/test_common/test_time.h" @@ -36,6 +37,7 @@ class XdsTestServer { private: testing::NiceMock factory_context_; + testing::NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; Event::GlobalTimeSystem time_system_; Api::ApiPtr api_; @@ -44,7 +46,7 @@ class XdsTestServer { Event::DispatcherPtr dispatcher_; FakeUpstreamConfig upstream_config_; Thread::MutexBasicLockable lock_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{time_system_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; std::unique_ptr runfiles_; std::unique_ptr xds_upstream_; FakeHttpConnectionPtr xds_connection_; diff --git a/mobile/test/common/integration/xds_test_server_interface.cc b/mobile/test/common/integration/xds_test_server_interface.cc index 12152d5d9071..d4476e768327 100644 --- a/mobile/test/common/integration/xds_test_server_interface.cc +++ b/mobile/test/common/integration/xds_test_server_interface.cc @@ -12,6 +12,10 @@ static std::weak_ptr weak_test_server_; static std::shared_ptr testServer() { return weak_test_server_.lock(); } void initXdsServer() { + // This is called via JNI from kotlin tests, and Envoy doesn't consider it a test thread + // which triggers some failures of `ASSERT_IS_MAIN_OR_TEST_THREAD()`. + Envoy::Thread::SkipAsserts skip; + Envoy::ExtensionRegistry::registerFactories(); strong_test_server_ = std::make_shared(); weak_test_server_ = strong_test_server_; diff --git a/source/common/common/matchers.cc b/source/common/common/matchers.cc index d69fe3c72822..4dded56b26f0 100644 --- a/source/common/common/matchers.cc +++ b/source/common/common/matchers.cc @@ -201,9 +201,10 @@ bool PathMatcher::match(const absl::string_view path) const { return matcher_.match(Http::PathUtil::removeQueryAndFragment(path)); } -StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config) { +StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config, + ThreadLocal::SlotAllocator& tls, Api::Api& api) { auto factory = Config::Utility::getAndCheckFactory(config, false); - return factory->createStringMatcher(config.typed_config()); + return factory->createStringMatcher(config.typed_config(), tls, api); } } // namespace Matchers diff --git a/source/common/common/matchers.h b/source/common/common/matchers.h index ae66633b6660..49b68c705a42 100644 --- a/source/common/common/matchers.h +++ b/source/common/common/matchers.h @@ -86,24 +86,36 @@ class UniversalStringMatcher : public StringMatcher { bool match(absl::string_view) const override { return true; } }; -StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config); +StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config, + ThreadLocal::SlotAllocator& tls, Api::Api& api); template -class StringMatcherImpl : public ValueMatcher, public StringMatcher { +class PrivateStringMatcherImpl : public ValueMatcher, public StringMatcher { public: - explicit StringMatcherImpl(const StringMatcherType& matcher) : matcher_(matcher) { + // TODO(ggreenway): convert all but the first parameter into + // `Server::Configuration::CommonFactoryContext`. + explicit PrivateStringMatcherImpl(const StringMatcherType& matcher, Regex::Engine* regex_engine, + ThreadLocal::SlotAllocator* tls, Api::Api* api) + : matcher_(matcher) { if (matcher.match_pattern_case() == StringMatcherType::MatchPatternCase::kSafeRegex) { if (matcher.ignore_case()) { ExceptionUtil::throwEnvoyException("ignore_case has no effect for safe_regex."); } - regex_ = Regex::Utility::parseRegex(matcher_.safe_regex()); + if (regex_engine != nullptr) { + regex_ = Regex::Utility::parseRegex(matcher_.safe_regex(), *regex_engine); + } else { + // TODO(ggreenway): remove this branch when we always have an engine. This is only + // needed to make tests not complain about dereferencing a null pointer, even though + // the reference isn't actually used. + regex_ = Regex::Utility::parseRegex(matcher_.safe_regex()); + } } else if (matcher.match_pattern_case() == StringMatcherType::MatchPatternCase::kContains) { if (matcher_.ignore_case()) { // Cache the lowercase conversion of the Contains matcher for future use lowercase_contains_match_ = absl::AsciiStrToLower(matcher_.contains()); } } else { - initialize(matcher); + initialize(matcher, tls, api); } } @@ -143,11 +155,13 @@ class StringMatcherImpl : public ValueMatcher, public StringMatcher { // overloading to only handle that case for type `envoy::type::matcher::v3::StringMatcher` to // prevent compilation errors on use of `kCustom`. - void initialize(const xds::type::matcher::v3::StringMatcher&) {} + void initialize(const xds::type::matcher::v3::StringMatcher&, ThreadLocal::SlotAllocator*, + Api::Api*) {} - void initialize(const envoy::type::matcher::v3::StringMatcher& matcher) { + void initialize(const envoy::type::matcher::v3::StringMatcher& matcher, + ThreadLocal::SlotAllocator* tls, Api::Api* api) { if (matcher.has_custom()) { - custom_ = getExtensionStringMatcher(matcher.custom()); + custom_ = getExtensionStringMatcher(matcher.custom(), *tls, *api); } } @@ -193,9 +207,34 @@ class StringMatcherImpl : public ValueMatcher, public StringMatcher { StringMatcherPtr custom_; }; +// Temporarily create two separate types with different constructors, inheriting from the same +// implementation, to make it easier to find and replace all usage of the old one. +// TODO(ggreenway): delete these two extra classes, make `PrivateStringMatcherImpl` back into +// `StringMatcherImpl`. +template +class StringMatcherImplWithContext : public PrivateStringMatcherImpl { +public: + explicit StringMatcherImplWithContext(const StringMatcherType& matcher, + Server::Configuration::CommonFactoryContext& context) + : PrivateStringMatcherImpl(matcher, &context.regexEngine(), + &context.threadLocal(), &context.api()) {} +}; + +template +class StringMatcherImpl : public PrivateStringMatcherImpl { +public: + explicit StringMatcherImpl(const StringMatcherType& matcher) + : PrivateStringMatcherImpl( + matcher, Regex::EngineSingleton::getExisting(), + InjectableSingleton::getExisting(), + InjectableSingleton::getExisting()) {} +}; + class StringMatcherExtensionFactory : public Config::TypedFactory { public: - virtual StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& config) PURE; + // TODO(ggreenway): Convert all but first parameter to `CommonFactoryContext`. + virtual StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& config, + ThreadLocal::SlotAllocator& tls, Api::Api& api) PURE; std::string category() const override { return "envoy.string_matcher"; } }; diff --git a/source/common/common/regex.h b/source/common/common/regex.h index 4189df75fc24..e64fcf90f2ec 100644 --- a/source/common/common/regex.h +++ b/source/common/common/regex.h @@ -79,6 +79,16 @@ class Utility { return EngineSingleton::get().matcher(matcher.regex()); } + + template + static CompiledMatcherPtr parseRegex(const RegexMatcherType& matcher, Engine& engine) { + // Fallback deprecated engine type in regex matcher. + if (matcher.has_google_re2()) { + return std::make_unique(matcher); + } + + return engine.matcher(matcher.regex()); + } }; } // namespace Regex diff --git a/source/common/tls/cert_validator/default_validator.cc b/source/common/tls/cert_validator/default_validator.cc index 8d049f7ba0c9..39319a7312b3 100644 --- a/source/common/tls/cert_validator/default_validator.cc +++ b/source/common/tls/cert_validator/default_validator.cc @@ -44,8 +44,8 @@ namespace Tls { DefaultCertValidator::DefaultCertValidator( const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source) - : config_(config), stats_(stats), time_source_(time_source) { + Server::Configuration::CommonFactoryContext& context) + : config_(config), stats_(stats), context_(context) { if (config_ != nullptr) { allow_untrusted_certificate_ = config_->trustChainVerification() == envoy::extensions::transport_sockets::tls::v3:: @@ -155,7 +155,7 @@ int DefaultCertValidator::initializeSslContexts(std::vector contexts, if (!cert_validation_config->subjectAltNameMatchers().empty()) { for (const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher : cert_validation_config->subjectAltNameMatchers()) { - auto san_matcher = createStringSanMatcher(matcher); + auto san_matcher = createStringSanMatcher(matcher, context_); if (san_matcher == nullptr) { throwEnvoyExceptionOrPanic( absl::StrCat("Failed to create string SAN matcher of type ", matcher.san_type())); @@ -548,18 +548,19 @@ Envoy::Ssl::CertificateDetailsPtr DefaultCertValidator::getCaCertInformation() c if (ca_cert_ == nullptr) { return nullptr; } - return Utility::certificateDetails(ca_cert_.get(), getCaFileName(), time_source_); + return Utility::certificateDetails(ca_cert_.get(), getCaFileName(), context_.timeSource()); } absl::optional DefaultCertValidator::daysUntilFirstCertExpires() const { - return Utility::getDaysUntilExpiration(ca_cert_.get(), time_source_); + return Utility::getDaysUntilExpiration(ca_cert_.get(), context_.timeSource()); } class DefaultCertValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override { - return std::make_unique(config, stats, time_source); + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override { + return std::make_unique(config, stats, context); } std::string name() const override { return "envoy.tls.cert_validator.default"; } diff --git a/source/common/tls/cert_validator/default_validator.h b/source/common/tls/cert_validator/default_validator.h index c3e88bd09ca1..f17a01dd3d5d 100644 --- a/source/common/tls/cert_validator/default_validator.h +++ b/source/common/tls/cert_validator/default_validator.h @@ -35,7 +35,7 @@ namespace Tls { class DefaultCertValidator : public CertValidator, Logger::Loggable { public: DefaultCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source); + SslStats& stats, Server::Configuration::CommonFactoryContext& context); ~DefaultCertValidator() override = default; @@ -110,7 +110,7 @@ class DefaultCertValidator : public CertValidator, Logger::Loggable ca_cert_; diff --git a/source/common/tls/cert_validator/factory.h b/source/common/tls/cert_validator/factory.h index 40f3fc3de92b..8f6aebbd6b4c 100644 --- a/source/common/tls/cert_validator/factory.h +++ b/source/common/tls/cert_validator/factory.h @@ -21,7 +21,7 @@ class CertValidatorFactory : public Config::UntypedFactory { public: virtual CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source) PURE; + Server::Configuration::CommonFactoryContext& context) PURE; std::string category() const override { return "envoy.tls.cert_validator"; } }; diff --git a/source/common/tls/cert_validator/san_matcher.cc b/source/common/tls/cert_validator/san_matcher.cc index 13429c3fcdcc..0229ca1c1273 100644 --- a/source/common/tls/cert_validator/san_matcher.cc +++ b/source/common/tls/cert_validator/san_matcher.cc @@ -28,7 +28,8 @@ bool StringSanMatcher::match(const GENERAL_NAME* general_name) const { } SanMatcherPtr createStringSanMatcher( - envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher const& matcher) { + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher const& matcher, + Server::Configuration::CommonFactoryContext& context) { // Verify that a new san type has not been added. static_assert(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType_MAX == 4); @@ -36,13 +37,13 @@ SanMatcherPtr createStringSanMatcher( switch (matcher.san_type()) { PANIC_ON_PROTO_ENUM_SENTINEL_VALUES; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS: - return SanMatcherPtr{std::make_unique(GEN_DNS, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_DNS, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL: - return SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI: - return SanMatcherPtr{std::make_unique(GEN_URI, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_URI, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS: - return SanMatcherPtr{std::make_unique(GEN_IPADD, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_IPADD, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SAN_TYPE_UNSPECIFIED: PANIC("unhandled value"); } diff --git a/source/common/tls/cert_validator/san_matcher.h b/source/common/tls/cert_validator/san_matcher.h index 260e9cc3075e..b9409555209e 100644 --- a/source/common/tls/cert_validator/san_matcher.h +++ b/source/common/tls/cert_validator/san_matcher.h @@ -34,16 +34,18 @@ class StringSanMatcher : public SanMatcher { public: bool match(const GENERAL_NAME* general_name) const override; ~StringSanMatcher() override = default; - StringSanMatcher(int general_name_type, envoy::type::matcher::v3::StringMatcher matcher) - : general_name_type_(general_name_type), matcher_(matcher) {} + StringSanMatcher(int general_name_type, envoy::type::matcher::v3::StringMatcher matcher, + Server::Configuration::CommonFactoryContext& context) + : general_name_type_(general_name_type), matcher_(matcher, context) {} private: const int general_name_type_; - const Matchers::StringMatcherImpl matcher_; + const Matchers::StringMatcherImplWithContext matcher_; }; SanMatcherPtr createStringSanMatcher( - const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher); + const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher, + Server::Configuration::CommonFactoryContext& context); } // namespace Tls } // namespace TransportSockets diff --git a/source/common/tls/context_impl.cc b/source/common/tls/context_impl.cc index 7d86c94bd5f6..2669fd8559f5 100644 --- a/source/common/tls/context_impl.cc +++ b/source/common/tls/context_impl.cc @@ -80,8 +80,9 @@ int ContextImpl::sslExtendedSocketInfoIndex() { } ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, - TimeSource& time_source, Ssl::ContextAdditionalInitFunc additional_init) - : scope_(scope), stats_(generateSslStats(scope)), time_source_(time_source), + Server::Configuration::CommonFactoryContext& factory_context, + Ssl::ContextAdditionalInitFunc additional_init) + : scope_(scope), stats_(generateSslStats(scope)), factory_context_(factory_context), tls_max_version_(config.maxProtocolVersion()), stat_name_set_(scope.symbolTable().makeSet("TransportSockets::Tls")), unknown_ssl_cipher_(stat_name_set_->add("unknown_ssl_cipher")), @@ -104,7 +105,7 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c } cert_validator_ = cert_validator_factory->createCertValidator( - config.certificateValidationContext(), stats_, time_source_); + config.certificateValidationContext(), stats_, factory_context_); const auto tls_certificates = config.tlsCertificates(); tls_contexts_.resize(std::max(static_cast(1), tls_certificates.size())); @@ -609,7 +610,7 @@ absl::optional ContextImpl::daysUntilFirstCertExpires() const { } for (auto& ctx : tls_contexts_) { const absl::optional tmp = - Utility::getDaysUntilExpiration(ctx.cert_chain_.get(), time_source_); + Utility::getDaysUntilExpiration(ctx.cert_chain_.get(), factory_context_.timeSource()); if (!tmp.has_value()) { return absl::nullopt; } @@ -643,7 +644,7 @@ std::vector ContextImpl::getCertChainInformat } auto detail = Utility::certificateDetails(ctx.cert_chain_.get(), ctx.getCertChainFileName(), - time_source_); + factory_context_.timeSource()); auto ocsp_resp = ctx.ocsp_response_.get(); if (ocsp_resp) { auto* ocsp_details = detail->mutable_ocsp_details(); @@ -659,8 +660,8 @@ std::vector ContextImpl::getCertChainInformat ClientContextImpl::ClientContextImpl(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config, - TimeSource& time_source) - : ContextImpl(scope, config, time_source, nullptr /* additional_init */), + Server::Configuration::CommonFactoryContext& factory_context) + : ContextImpl(scope, config, factory_context, nullptr /* additional_init */), server_name_indication_(config.serverNameIndication()), allow_renegotiation_(config.allowRenegotiation()), enforce_rsa_key_usage_(config.enforceRsaKeyUsage()), @@ -789,9 +790,9 @@ int ClientContextImpl::newSessionKey(SSL_SESSION* session) { ServerContextImpl::ServerContextImpl(Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, const std::vector& server_names, - TimeSource& time_source, + Server::Configuration::CommonFactoryContext& factory_context, Ssl::ContextAdditionalInitFunc additional_init) - : ContextImpl(scope, config, time_source, additional_init), + : ContextImpl(scope, config, factory_context, additional_init), session_ticket_keys_(config.sessionTicketKeys()), ocsp_staple_policy_(config.ocspStaplePolicy()), full_scan_certs_on_sni_mismatch_(config.fullScanCertsOnSNIMismatch()) { @@ -888,7 +889,8 @@ ServerContextImpl::ServerContextImpl(Stats::Scope& scope, throwEnvoyExceptionOrPanic("Required OCSP response is missing from TLS context"); } } else { - auto response = std::make_unique(ocsp_resp_bytes, time_source_); + auto response = std::make_unique(ocsp_resp_bytes, + factory_context_.timeSource()); if (!response->matchesCertificate(*ctx.cert_chain_)) { throwEnvoyExceptionOrPanic("OCSP response does not match its TLS certificate"); } diff --git a/source/common/tls/context_impl.h b/source/common/tls/context_impl.h index 9bffa93a74a4..71f04229af51 100644 --- a/source/common/tls/context_impl.h +++ b/source/common/tls/context_impl.h @@ -115,7 +115,8 @@ class ContextImpl : public virtual Envoy::Ssl::Context, protected: friend class ContextImplPeer; - ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, TimeSource& time_source, + ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, + Server::Configuration::CommonFactoryContext& factory_context, Ssl::ContextAdditionalInitFunc additional_init); /** @@ -151,7 +152,7 @@ class ContextImpl : public virtual Envoy::Ssl::Context, std::vector parsed_alpn_protocols_; bssl::UniquePtr cert_chain_; std::string cert_chain_file_path_; - TimeSource& time_source_; + Server::Configuration::CommonFactoryContext& factory_context_; const unsigned tls_max_version_; mutable Stats::StatNameSetPtr stat_name_set_; const Stats::StatName unknown_ssl_cipher_; @@ -173,7 +174,7 @@ using ContextImplSharedPtr = std::shared_ptr; class ClientContextImpl : public ContextImpl, public Envoy::Ssl::ClientContext { public: ClientContextImpl(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config, - TimeSource& time_source); + Server::Configuration::CommonFactoryContext& factory_context); bssl::UniquePtr newSsl(const Network::TransportSocketOptionsConstSharedPtr& options) override; @@ -195,7 +196,8 @@ enum class OcspStapleAction { Staple, NoStaple, Fail, ClientNotCapable }; class ServerContextImpl : public ContextImpl, public Envoy::Ssl::ServerContext { public: ServerContextImpl(Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, - const std::vector& server_names, TimeSource& time_source, + const std::vector& server_names, + Server::Configuration::CommonFactoryContext& factory_context, Ssl::ContextAdditionalInitFunc additional_init); // Select the TLS certificate context in SSL_CTX_set_select_certificate_cb() callback with diff --git a/source/common/tls/context_manager_impl.cc b/source/common/tls/context_manager_impl.cc index b76a81264e21..1c0e8d027aeb 100644 --- a/source/common/tls/context_manager_impl.cc +++ b/source/common/tls/context_manager_impl.cc @@ -15,17 +15,19 @@ namespace Extensions { namespace TransportSockets { namespace Tls { -ContextManagerImpl::ContextManagerImpl(TimeSource& time_source) : time_source_(time_source) {} +ContextManagerImpl::ContextManagerImpl(Server::Configuration::CommonFactoryContext& factory_context) + : factory_context_(factory_context) {} Envoy::Ssl::ClientContextSharedPtr ContextManagerImpl::createSslClientContext(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config) { + ASSERT_IS_MAIN_OR_TEST_THREAD(); if (!config.isReady()) { return nullptr; } Envoy::Ssl::ClientContextSharedPtr context = - std::make_shared(scope, config, time_source_); + std::make_shared(scope, config, factory_context_); contexts_.insert(context); return context; } @@ -33,12 +35,13 @@ ContextManagerImpl::createSslClientContext(Stats::Scope& scope, Envoy::Ssl::ServerContextSharedPtr ContextManagerImpl::createSslServerContext( Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, const std::vector& server_names, Ssl::ContextAdditionalInitFunc additional_init) { + ASSERT_IS_MAIN_OR_TEST_THREAD(); if (!config.isReady()) { return nullptr; } Envoy::Ssl::ServerContextSharedPtr context = std::make_shared( - scope, config, server_names, time_source_, std::move(additional_init)); + scope, config, server_names, factory_context_, std::move(additional_init)); contexts_.insert(context); return context; } diff --git a/source/common/tls/context_manager_impl.h b/source/common/tls/context_manager_impl.h index db72e1d308e0..31df3addd743 100644 --- a/source/common/tls/context_manager_impl.h +++ b/source/common/tls/context_manager_impl.h @@ -5,6 +5,7 @@ #include #include "envoy/common/time.h" +#include "envoy/server/factory_context.h" #include "envoy/ssl/context_manager.h" #include "envoy/ssl/private_key/private_key.h" #include "envoy/stats/scope.h" @@ -18,14 +19,13 @@ namespace Tls { /** * The SSL context manager has the following threading model: - * Contexts can be allocated via any thread (through in practice they are only allocated on the main - * thread). They can be released from any thread (and in practice are since cluster information can - * be released from any thread). Context allocation/free is a very uncommon thing so we just do a - * global lock to protect it all. + * Contexts can be allocated the main thread. They can be released from any thread (and in practice + * are since cluster information can be released from any thread). Context allocation/free is a very + * uncommon thing so we just do a global lock to protect it all. */ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { public: - explicit ContextManagerImpl(TimeSource& time_source); + explicit ContextManagerImpl(Server::Configuration::CommonFactoryContext& factory_context); ~ContextManagerImpl() override = default; // Ssl::ContextManager @@ -45,7 +45,7 @@ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { void removeContext(const Envoy::Ssl::ContextSharedPtr& old_context) override; private: - TimeSource& time_source_; + Server::Configuration::CommonFactoryContext& factory_context_; absl::flat_hash_set contexts_; PrivateKeyMethodManagerImpl private_key_method_manager_{}; }; diff --git a/source/extensions/string_matcher/lua/match.cc b/source/extensions/string_matcher/lua/match.cc index fd24f17f81f0..c6ee81e7022c 100644 --- a/source/extensions/string_matcher/lua/match.cc +++ b/source/extensions/string_matcher/lua/match.cc @@ -77,12 +77,11 @@ bool LuaStringMatcher::match(const absl::string_view value) const { // Lua state is not thread safe, so a state needs to be stored in thread local storage. class LuaStringMatcherThreadWrapper : public Matchers::StringMatcher { public: - LuaStringMatcherThreadWrapper(const std::string& code) { + LuaStringMatcherThreadWrapper(const std::string& code, ThreadLocal::SlotAllocator& tls) { // Validate that there are no errors while creating on the main thread. LuaStringMatcher validator(code); - tls_slot_ = ThreadLocal::TypedSlot::makeUnique( - *InjectableSingleton::getExisting()); + tls_slot_ = ThreadLocal::TypedSlot::makeUnique(tls); tls_slot_->set([code](Event::Dispatcher&) -> std::shared_ptr { return std::make_shared(code); }); @@ -95,18 +94,18 @@ class LuaStringMatcherThreadWrapper : public Matchers::StringMatcher { }; Matchers::StringMatcherPtr -LuaStringMatcherFactory::createStringMatcher(const ProtobufWkt::Any& message) { +LuaStringMatcherFactory::createStringMatcher(const ProtobufWkt::Any& message, + ThreadLocal::SlotAllocator& tls, Api::Api& api) { ::envoy::extensions::string_matcher::lua::v3::Lua config; Config::Utility::translateOpaqueConfig(message, ProtobufMessage::getStrictValidationVisitor(), config); - Api::Api* api = InjectableSingleton::getExisting(); absl::StatusOr result = Config::DataSource::read( - config.source_code(), false /* allow_empty */, *api, 0 /* max_size */); + config.source_code(), false /* allow_empty */, api, 0 /* max_size */); if (!result.ok()) { throw EnvoyException( fmt::format("Failed to get lua string matcher code from source: {}", result.status())); } - return std::make_unique(*result); + return std::make_unique(*result, tls); } ProtobufTypes::MessagePtr LuaStringMatcherFactory::createEmptyConfigProto() { diff --git a/source/extensions/string_matcher/lua/match.h b/source/extensions/string_matcher/lua/match.h index 8452a24b0832..2777bcf76220 100644 --- a/source/extensions/string_matcher/lua/match.h +++ b/source/extensions/string_matcher/lua/match.h @@ -29,7 +29,9 @@ class LuaStringMatcher : public Matchers::StringMatcher, public ThreadLocal::Thr class LuaStringMatcherFactory : public Matchers::StringMatcherExtensionFactory { public: - Matchers::StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& message) override; + Matchers::StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& message, + ThreadLocal::SlotAllocator& tls, + Api::Api& api) override; std::string name() const override { return "envoy.string_matcher.lua"; } ProtobufTypes::MessagePtr createEmptyConfigProto() override; }; diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc index 4c6f6fb3dc30..13f5011c7c01 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc @@ -31,8 +31,9 @@ namespace Tls { using SPIFFEConfig = envoy::extensions::transport_sockets::tls::v3::SPIFFECertValidatorConfig; SPIFFEValidator::SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) - : stats_(stats), time_source_(time_source) { + SslStats& stats, + Server::Configuration::CommonFactoryContext& context) + : stats_(stats), time_source_(context.timeSource()) { ASSERT(config != nullptr); allow_expired_certificate_ = config->allowExpiredCertificate(); @@ -48,7 +49,7 @@ SPIFFEValidator::SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextC // SAN types. See the discussion: https://github.com/envoyproxy/envoy/issues/15392 // TODO(pradeepcrao): Throw an exception when a non-URI matcher is encountered after the // deprecated field match_subject_alt_names is removed - subject_alt_name_matchers_.emplace_back(createStringSanMatcher(matcher)); + subject_alt_name_matchers_.emplace_back(createStringSanMatcher(matcher, context)); } } } @@ -310,9 +311,10 @@ Envoy::Ssl::CertificateDetailsPtr SPIFFEValidator::getCaCertInformation() const class SPIFFEValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override { - return std::make_unique(config, stats, time_source); + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override { + return std::make_unique(config, stats, context); } std::string name() const override { return "envoy.tls.cert_validator.spiffe"; } diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h index 4bb140dbe63b..0c8a93a80f16 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h @@ -35,7 +35,7 @@ class SPIFFEValidator : public CertValidator { SPIFFEValidator(SslStats& stats, TimeSource& time_source) : stats_(stats), time_source_(time_source){}; SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source); + Server::Configuration::CommonFactoryContext& context); ~SPIFFEValidator() override = default; // Tls::CertValidator diff --git a/source/extensions/transport_sockets/tls/config.cc b/source/extensions/transport_sockets/tls/config.cc index ec2f39b60aed..1d652c0d1545 100644 --- a/source/extensions/transport_sockets/tls/config.cc +++ b/source/extensions/transport_sockets/tls/config.cc @@ -51,8 +51,9 @@ ProtobufTypes::MessagePtr DownstreamSslSocketFactory::createEmptyConfigProto() { LEGACY_REGISTER_FACTORY(DownstreamSslSocketFactory, Server::Configuration::DownstreamTransportSocketConfigFactory, "tls"); -Ssl::ContextManagerPtr SslContextManagerFactory::createContextManager(TimeSource& time_source) { - return std::make_unique(time_source); +Ssl::ContextManagerPtr SslContextManagerFactory::createContextManager( + Server::Configuration::CommonFactoryContext& factory_context) { + return std::make_unique(factory_context); } static Envoy::Registry::RegisterInternalFactory(admin()->getConfigTracker()); - ssl_context_manager_ = createContextManager("ssl_context_manager", api_->timeSource()); + ssl_context_manager_ = createContextManager("ssl_context_manager", server_contexts_); cluster_manager_factory_ = std::make_unique( server_contexts_, stats(), threadLocal(), http_context_, [this]() -> Network::DnsResolverSharedPtr { return this->dnsResolver(); }, diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index fc301751e57d..9f1bb41c031d 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -123,6 +123,7 @@ class ValidationInstance final : Logger::Loggable, bool enableReusePortDefault() override { return true; } Configuration::StatsConfig& statsConfig() override { return config_.statsConfig(); } + Regex::Engine& regexEngine() override { return *regex_engine_; } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return bootstrap_; } Configuration::ServerFactoryContext& serverFactoryContext() override { return server_contexts_; } Configuration::TransportSocketFactoryContext& transportSocketFactoryContext() override { @@ -194,6 +195,7 @@ class ValidationInstance final : Logger::Loggable, Filter::TcpListenerFilterConfigProviderManagerImpl tcp_listener_config_provider_manager_; Server::DrainManagerPtr drain_manager_; HotRestartNopImpl nop_hot_restart_; + Regex::EnginePtr regex_engine_; }; } // namespace Server diff --git a/source/server/server.cc b/source/server/server.cc index 1bd625cd09dc..a570ccf16cce 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -749,7 +749,7 @@ absl::Status InstanceBase::initializeOrThrow(Network::Address::InstanceConstShar } // Once we have runtime we can initialize the SSL context manager. - ssl_context_manager_ = createContextManager("ssl_context_manager", time_source_); + ssl_context_manager_ = createContextManager("ssl_context_manager", server_contexts_); cluster_manager_factory_ = std::make_unique( serverFactoryContext(), stats_store_, thread_local_, http_context_, diff --git a/source/server/server.h b/source/server/server.h index 3afd1348aaba..0d4d76bc97f1 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -197,6 +197,7 @@ class ServerFactoryContextImpl : public Configuration::ServerFactoryContext, ProcessContextOptRef processContext() override { return server_.processContext(); } Envoy::Server::DrainManager& drainManager() override { return server_.drainManager(); } ServerLifecycleNotifier& lifecycleNotifier() override { return server_.lifecycleNotifier(); } + Regex::Engine& regexEngine() override { return server_.regexEngine(); } Configuration::StatsConfig& statsConfig() override { return server_.statsConfig(); } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return server_.bootstrap(); } OverloadManager& overloadManager() override { return server_.overloadManager(); } @@ -292,6 +293,7 @@ class InstanceBase : Logger::Loggable, TimeSource& timeSource() override { return time_source_; } void flushStats() override; Configuration::StatsConfig& statsConfig() override { return config_.statsConfig(); } + Regex::Engine& regexEngine() override { return *regex_engine_; } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return bootstrap_; } Configuration::ServerFactoryContext& serverFactoryContext() override { return server_contexts_; } Configuration::TransportSocketFactoryContext& transportSocketFactoryContext() override { diff --git a/source/server/ssl_context_manager.cc b/source/server/ssl_context_manager.cc index 8a9fec347dee..fadc32283163 100644 --- a/source/server/ssl_context_manager.cc +++ b/source/server/ssl_context_manager.cc @@ -49,12 +49,13 @@ class SslContextManagerNoTlsStub final : public Envoy::Ssl::ContextManager { } }; -Ssl::ContextManagerPtr createContextManager(const std::string& factory_name, - TimeSource& time_source) { +Ssl::ContextManagerPtr +createContextManager(const std::string& factory_name, + Server::Configuration::CommonFactoryContext& factory_context) { Ssl::ContextManagerFactory* factory = Registry::FactoryRegistry::getFactory(factory_name); if (factory != nullptr) { - return factory->createContextManager(time_source); + return factory->createContextManager(factory_context); } return std::make_unique(); diff --git a/source/server/ssl_context_manager.h b/source/server/ssl_context_manager.h index 4b618e6e64f4..c296955703fe 100644 --- a/source/server/ssl_context_manager.h +++ b/source/server/ssl_context_manager.h @@ -6,8 +6,9 @@ namespace Envoy { namespace Server { -Ssl::ContextManagerPtr createContextManager(const std::string& factory_name, - TimeSource& time_source); +Ssl::ContextManagerPtr +createContextManager(const std::string& factory_name, + Server::Configuration::CommonFactoryContext& factory_context); } // namespace Server } // namespace Envoy diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 27baab111f4f..0b52d9c55e82 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -510,7 +510,8 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { Upstream::MockClusterManager cm_; NiceMock local_info_; Runtime::MockLoader runtime_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{test_time_.timeSystem()}; + testing::NiceMock server_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; NiceMock random_; Http::AsyncClientPtr http_async_client_; Http::ConnectionPool::InstancePtr http_conn_pool_; diff --git a/test/common/quic/BUILD b/test/common/quic/BUILD index b488b190353d..19437fb2be92 100644 --- a/test/common/quic/BUILD +++ b/test/common/quic/BUILD @@ -64,6 +64,7 @@ envoy_cc_test( "//source/common/quic:envoy_quic_proof_verifier_lib", "//source/common/tls:context_config_lib", "//test/mocks/network:network_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/test_common:test_runtime_lib", "@com_github_google_quiche//:quic_core_versions_lib", @@ -108,6 +109,7 @@ envoy_cc_test( "//test/common/config:dummy_config_proto_cc_proto", "//test/common/tls/cert_validator:timed_cert_validator", "//test/mocks/event:event_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "@com_github_google_quiche//:quic_test_tools_test_certificates_lib", ], diff --git a/test/common/quic/envoy_quic_proof_source_test.cc b/test/common/quic/envoy_quic_proof_source_test.cc index a6814a40c52d..afe1e521378e 100644 --- a/test/common/quic/envoy_quic_proof_source_test.cc +++ b/test/common/quic/envoy_quic_proof_source_test.cc @@ -9,6 +9,7 @@ #include "test/common/quic/test_utils.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/test_common/test_runtime.h" @@ -71,7 +72,7 @@ class SignatureVerifier { const absl::optional nullopt = absl::nullopt; ON_CALL(cert_validation_ctx_config_, customValidatorConfig()).WillByDefault(ReturnRef(nullopt)); auto context = std::make_shared( - *store_.rootScope(), client_context_config_, time_system_); + *store_.rootScope(), client_context_config_, server_factory_context_); ON_CALL(verify_context_, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); ON_CALL(verify_context_, transportSocketOptions()) .WillByDefault(ReturnRef(transport_socket_options_)); @@ -105,6 +106,7 @@ class SignatureVerifier { NiceMock store_; Event::GlobalTimeSystem time_system_; NiceMock client_context_config_; + NiceMock server_factory_context_; NiceMock cert_validation_ctx_config_; std::unique_ptr verifier_; NiceMock tls_context_manager_; @@ -224,7 +226,8 @@ class EnvoyQuicProofSourceTest : public ::testing::Test { Network::MockFilterChainManager filter_chain_manager_; Network::MockListenSocket listen_socket_; testing::NiceMock listener_config_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{time_system_}; + Server::Configuration::MockServerFactoryContext factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{factory_context_}; Ssl::MockServerContextConfig* mock_context_config_; std::function secret_update_callback_; std::unique_ptr transport_socket_factory_; @@ -391,7 +394,9 @@ class LegacyEnvoyQuicProofSourceTest : public ::testing::Test { Network::MockFilterChainManager filter_chain_manager_; Network::MockListenSocket listen_socket_; testing::NiceMock listener_config_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{time_system_}; + testing::NiceMock server_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{ + server_factory_context_}; Ssl::MockServerContextConfig* mock_context_config_; std::unique_ptr transport_socket_factory_; Ssl::MockTlsCertificateConfig tls_cert_config_; diff --git a/test/common/quic/envoy_quic_proof_verifier_test.cc b/test/common/quic/envoy_quic_proof_verifier_test.cc index ab12465385eb..97b5bd52724a 100644 --- a/test/common/quic/envoy_quic_proof_verifier_test.cc +++ b/test/common/quic/envoy_quic_proof_verifier_test.cc @@ -9,6 +9,7 @@ #include "test/common/quic/test_utils.h" #include "test/common/tls/cert_validator/timed_cert_validator.h" #include "test/mocks/event/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/test_time.h" @@ -80,7 +81,7 @@ class EnvoyQuicProofVerifierTest : public testing::Test { EXPECT_CALL(cert_validation_ctx_config_, customValidatorConfig()) .WillRepeatedly(ReturnRef(custom_validator_config_)); auto context = std::make_shared( - *store_.rootScope(), client_context_config_, time_system_); + *store_.rootScope(), client_context_config_, factory_context_); verifier_ = std::make_unique(std::move(context)); } @@ -98,7 +99,7 @@ class EnvoyQuicProofVerifierTest : public testing::Test { absl::optional custom_validator_config_{ absl::nullopt}; NiceMock store_; - Event::GlobalTimeSystem time_system_; + Server::Configuration::MockServerFactoryContext factory_context_; NiceMock client_context_config_; Ssl::MockCertificateValidationContextConfig cert_validation_ctx_config_; std::unique_ptr verifier_; diff --git a/test/common/tls/cert_validator/BUILD b/test/common/tls/cert_validator/BUILD index f0b243a980d3..76c9d63dc831 100644 --- a/test/common/tls/cert_validator/BUILD +++ b/test/common/tls/cert_validator/BUILD @@ -21,6 +21,7 @@ envoy_cc_test( "//source/common/tls/cert_validator:cert_validator_lib", "//test/common/tls:ssl_test_utils", "//test/common/tls/cert_validator:test_common", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:environment_lib", "//test/test_common:test_runtime_lib", ], @@ -56,6 +57,7 @@ envoy_cc_test( deps = [ "//source/common/protobuf:utility_lib", "//source/common/tls/cert_validator:cert_validator_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:utility_lib", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", ], diff --git a/test/common/tls/cert_validator/default_validator_integration_test.cc b/test/common/tls/cert_validator/default_validator_integration_test.cc index a76e87ed04fd..ac5b3232863f 100644 --- a/test/common/tls/cert_validator/default_validator_integration_test.cc +++ b/test/common/tls/cert_validator/default_validator_integration_test.cc @@ -14,8 +14,8 @@ namespace Ssl { void SslCertValidatorIntegrationTest::initialize() { HttpIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + factory_context_.serverFactoryContext()); registerTestServerPorts({"http"}); test_server_->counter(listenerStatPrefix("ssl.fail_verify_error"))->reset(); diff --git a/test/common/tls/cert_validator/default_validator_test.cc b/test/common/tls/cert_validator/default_validator_test.cc index 0fa44d2d6053..aff9ed58697d 100644 --- a/test/common/tls/cert_validator/default_validator_test.cc +++ b/test/common/tls/cert_validator/default_validator_test.cc @@ -6,6 +6,7 @@ #include "test/common/tls/cert_validator/test_common.h" #include "test/common/tls/ssl_test_utility.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/environment.h" #include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" @@ -34,28 +35,34 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltNameDNSMatched) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameDNSMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameIncorrectTypeMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameWildcardDNSMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir " "}}/test/common/tls/test_data/san_multiple_dns_cert.pem")); @@ -63,11 +70,13 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameWildcardDNSMatched) { matcher.set_exact("api.example.com"); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMultiLevelMatch) { + NiceMock context; + // san_multiple_dns_cert matches *.example.com bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir " @@ -76,7 +85,7 @@ TEST(DefaultCertValidatorTest, TestMultiLevelMatch) { matcher.set_exact("foo.api.example.com"); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -98,13 +107,15 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltMultiDomain) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameURIMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_uri_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw(spiffe://lyft.com/[^/]*-team)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -117,37 +128,40 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltNameNotMatched) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameNotMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example\.net)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_IPADD, matcher)}); + SanMatcherPtr{std::make_unique(GEN_IPADD, matcher, context)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher)}); + SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, - Event::GlobalTimeSystem().timeSystem()); + /*CertificateValidationContextConfig=*/nullptr, stats, context); bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); std::vector san_matchers; - san_matchers.push_back(SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + san_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); // Verify the certificate with correct SAN regex matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, san_matchers, nullptr, nullptr), @@ -157,7 +171,7 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { matcher.MergeFrom(TestUtility::createExactMatcher("hello.example.com")); std::vector invalid_san_matchers; invalid_san_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); std::string error; // Verify the certificate with incorrect SAN exact matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, @@ -167,13 +181,13 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithNoValidationContext) { + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, - Event::GlobalTimeSystem().timeSystem()); + /*CertificateValidationContextConfig=*/nullptr, stats, context); EXPECT_EQ(default_validator->verifyCertificate(/*cert=*/nullptr, /*verify_san_list=*/{}, /*subject_alt_name_matchers=*/{}, nullptr, @@ -191,13 +205,13 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithNoValidationContex } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithEmptyCertChain) { + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, - Event::GlobalTimeSystem().timeSystem()); + /*CertificateValidationContextConfig=*/nullptr, stats, context); SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); bssl::UniquePtr cert_chain(sk_X509_new_null()); @@ -210,18 +224,20 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithEmptyCertChain) { } TEST(DefaultCertValidatorTest, NoSanInCert) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/fake_ca_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example\.net)raw")); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, WithVerifyDepth) { - + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); envoy::config::core::v3::TypedExtensionConfig typed_conf; @@ -245,8 +261,8 @@ TEST(DefaultCertValidatorTest, WithVerifyDepth) { std::make_unique(typed_conf, false, san_matchers, ca_cert_str, 2); auto default_validator = - std::make_unique( - test_config.get(), stats, Event::GlobalTimeSystem().timeSystem()); + std::make_unique(test_config.get(), + stats, context); STACK_OF(X509)* intermediates = cert_chain.get(); SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); @@ -266,7 +282,7 @@ TEST(DefaultCertValidatorTest, WithVerifyDepth) { test_config = std::make_unique(typed_conf, false, san_matchers, ca_cert_str); default_validator = std::make_unique( - test_config.get(), stats, Event::GlobalTimeSystem().timeSystem()); + test_config.get(), stats, context); // Re-initialize context ssl_ctx = SSL_CTX_new(TLS_method()); @@ -321,6 +337,8 @@ class MockCertificateValidationContextConfig : public Ssl::CertificateValidation }; TEST(DefaultCertValidatorTest, TestUnexpectedSanMatcherType) { + NiceMock context; + auto mock_context_config = std::make_unique(); EXPECT_CALL(*mock_context_config.get(), trustChainVerification()) .WillRepeatedly(testing::Return(envoy::extensions::transport_sockets::tls::v3:: @@ -329,14 +347,16 @@ TEST(DefaultCertValidatorTest, TestUnexpectedSanMatcherType) { std::vector(); Stats::TestUtil::TestStore store; auto ssl_stats = generateSslStats(*store.rootScope()); - auto validator = std::make_unique(mock_context_config.get(), ssl_stats, - Event::GlobalTimeSystem().timeSystem()); + auto validator = + std::make_unique(mock_context_config.get(), ssl_stats, context); auto ctx = std::vector(); EXPECT_THROW_WITH_REGEX(validator->initializeSslContexts(ctx, false), EnvoyException, "Failed to create string SAN matcher of type.*"); } TEST(DefaultCertValidatorTest, TestInitializeSslContextFailure) { + NiceMock context; + auto mock_context_config = std::make_unique( "-----BEGIN CERTIFICATE-----\nincomplete payload"); EXPECT_CALL(*mock_context_config.get(), trustChainVerification()) @@ -345,8 +365,8 @@ TEST(DefaultCertValidatorTest, TestInitializeSslContextFailure) { Stats::TestUtil::TestStore store; auto ssl_stats = generateSslStats(*store.rootScope()); - auto validator = std::make_unique(mock_context_config.get(), ssl_stats, - Event::GlobalTimeSystem().timeSystem()); + auto validator = + std::make_unique(mock_context_config.get(), ssl_stats, context); auto ctx = std::vector(); EXPECT_THROW_WITH_REGEX(validator->initializeSslContexts(ctx, false), EnvoyException, "Failed to load trusted CA certificates from.*"); diff --git a/test/common/tls/cert_validator/san_matcher_test.cc b/test/common/tls/cert_validator/san_matcher_test.cc index 11f67e467119..3b330866a379 100644 --- a/test/common/tls/cert_validator/san_matcher_test.cc +++ b/test/common/tls/cert_validator/san_matcher_test.cc @@ -4,6 +4,7 @@ #include "source/common/protobuf/utility.h" #include "source/common/tls/cert_validator/san_matcher.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/utility.h" #include "gmock/gmock.h" @@ -16,6 +17,8 @@ namespace Tls { // Verify that we get a valid string san matcher for all valid san types. TEST(SanMatcherConfigTest, TestValidSanType) { + NiceMock context; + // Iterate over all san type enums. for (envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType san_type = envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType_MIN; @@ -29,9 +32,9 @@ TEST(SanMatcherConfigTest, TestValidSanType) { san_matcher.set_san_type(san_type); if (san_type == envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher:: SAN_TYPE_UNSPECIFIED) { - EXPECT_DEATH(createStringSanMatcher(san_matcher), "unhandled value"); + EXPECT_DEATH(createStringSanMatcher(san_matcher, context), "unhandled value"); } else { - const SanMatcherPtr matcher = createStringSanMatcher(san_matcher); + const SanMatcherPtr matcher = createStringSanMatcher(san_matcher, context); EXPECT_NE(matcher.get(), nullptr); // Verify that the message is valid. TestUtility::validate(san_matcher); @@ -40,6 +43,7 @@ TEST(SanMatcherConfigTest, TestValidSanType) { } TEST(SanMatcherConfigTest, UnspecifiedSanType) { + NiceMock context; envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher san_matcher; san_matcher.mutable_matcher()->set_exact("foo.example"); // Do not set san_type @@ -54,7 +58,7 @@ TEST(SanMatcherConfigTest, UnspecifiedSanType) { static_cast( static_cast(123)); san_matcher.set_san_type(san_type); - EXPECT_EQ(createStringSanMatcher(san_matcher), nullptr); + EXPECT_EQ(createStringSanMatcher(san_matcher, context), nullptr); } } // namespace Tls diff --git a/test/common/tls/cert_validator/timed_cert_validator.h b/test/common/tls/cert_validator/timed_cert_validator.h index 45a57fc5eda2..2ea320d87af2 100644 --- a/test/common/tls/cert_validator/timed_cert_validator.h +++ b/test/common/tls/cert_validator/timed_cert_validator.h @@ -18,8 +18,9 @@ class TimedCertValidator : public DefaultCertValidator { public: TimedCertValidator(std::chrono::milliseconds validation_time_out_ms, const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source, absl::optional expected_host_name) - : DefaultCertValidator(config, stats, time_source), + Server::Configuration::CommonFactoryContext& context, + absl::optional expected_host_name) + : DefaultCertValidator(config, stats, context), validation_time_out_ms_(validation_time_out_ms), expected_host_name_(expected_host_name) {} ValidationResults @@ -49,10 +50,11 @@ class TimedCertValidator : public DefaultCertValidator { class TimedCertValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override { + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override { auto validator = std::make_unique(validation_time_out_ms_, config, stats, - time_source, expected_host_name_); + context, expected_host_name_); if (expected_peer_address_.has_value()) { validator->setExpectedPeerAddress(expected_peer_address_.value()); } diff --git a/test/common/tls/context_impl_test.cc b/test/common/tls/context_impl_test.cc index dadbae2c6f7b..faa1414733d7 100644 --- a/test/common/tls/context_impl_test.cc +++ b/test/common/tls/context_impl_test.cc @@ -118,8 +118,8 @@ class SslContextImplTest : public SslCertsTest { } protected: - Event::SimulatedTimeSystem time_system_; - ContextManagerImpl manager_{time_system_}; + NiceMock server_factory_context_; + ContextManagerImpl manager_{server_factory_context_}; }; TEST_F(SslContextImplTest, TestCipherSuites) { @@ -1157,8 +1157,8 @@ class ClientContextConfigImplTest : public SslCertsTest { }}; } - Event::SimulatedTimeSystem time_system_; - ContextManagerImpl manager_{time_system_}; + NiceMock server_factory_context_; + ContextManagerImpl manager_{server_factory_context_}; }; // Validate that empty SNI (according to C string rules) fails config validation. @@ -1288,8 +1288,7 @@ TEST_F(ClientContextConfigImplTest, RSA3072Cert) { TestUtility::loadFromYaml(TestEnvironment::substitute(tls_certificate_yaml), *tls_context.mutable_common_tls_context()->add_tls_certificates()); ClientContextConfigImpl client_context_config(tls_context, factory_context_); - Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(time_system); + ContextManagerImpl manager(server_factory_context_); Stats::IsolatedStoreImpl store; auto context = manager_.createSslClientContext(*store.rootScope(), client_context_config); auto cleanup = cleanUpHelper(context); @@ -1765,7 +1764,10 @@ TEST_F(ClientContextConfigImplTest, MissingStaticCertificateValidationContext) { "Unknown static certificate validation context: missing"); } -class ServerContextConfigImplTest : public SslCertsTest {}; +class ServerContextConfigImplTest : public SslCertsTest { +public: + NiceMock server_factory_context_; +}; // Multiple TLS certificates are supported. TEST_F(ServerContextConfigImplTest, MultipleTlsCertificates) { @@ -1896,8 +1898,7 @@ TEST_F(ServerContextConfigImplTest, TlsCertificateNonEmpty) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; tls_context.mutable_common_tls_context()->add_tls_certificates(); ServerContextConfigImpl client_context_config(tls_context, factory_context_); - Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(time_system); + ContextManagerImpl manager(server_factory_context_); Stats::IsolatedStoreImpl store; EXPECT_THROW_WITH_MESSAGE( Envoy::Ssl::ServerContextSharedPtr server_ctx(manager.createSslServerContext( @@ -2002,8 +2003,7 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureNoMethod) { NiceMock private_key_method_manager; auto private_key_method_provider_ptr = std::make_shared>(); - Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(time_system); + ContextManagerImpl manager(server_factory_context_); EXPECT_CALL(factory_context_, sslContextManager()).WillOnce(ReturnRef(context_manager)); EXPECT_CALL(context_manager, privateKeyMethodManager()) .WillOnce(ReturnRef(private_key_method_manager)); @@ -2201,8 +2201,8 @@ TEST_F(ServerContextConfigImplTest, Pkcs12LoadFailureBothPkcs12AndCertChain) { class TestContextImpl : public ContextImpl { public: TestContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, - TimeSource& time_source) - : ContextImpl(scope, config, time_source, nullptr), pool_(scope.symbolTable()), + Server::Configuration::ServerFactoryContext& factory_context) + : ContextImpl(scope, config, factory_context, nullptr), pool_(scope.symbolTable()), fallback_(pool_.add("fallback")) {} void incCounter(absl::string_view name, absl::string_view value) { @@ -2220,7 +2220,7 @@ class SslContextStatsTest : public SslContextImplTest { client_context_config_ = std::make_unique(tls_context_, factory_context_); context_ = std::make_unique(*store_.rootScope(), *client_context_config_, - time_system_); + server_factory_context_); } Stats::TestUtil::TestStore store_; diff --git a/test/common/tls/handshaker_factory_test.cc b/test/common/tls/handshaker_factory_test.cc index f60113708590..e161b5eea047 100644 --- a/test/common/tls/handshaker_factory_test.cc +++ b/test/common/tls/handshaker_factory_test.cc @@ -92,8 +92,8 @@ class HandshakerFactoryImplForTest class HandshakerFactoryTest : public testing::Test { protected: HandshakerFactoryTest() - : context_manager_( - std::make_unique(time_system_)), + : context_manager_(std::make_unique( + server_factory_context_)), registered_factory_(handshaker_factory_) { scoped_runtime_.mergeValues( {{"envoy.reloadable_features.no_extension_lookup_by_name", "false"}}); @@ -111,7 +111,7 @@ class HandshakerFactoryTest : public testing::Test { return SSL_get_SSL_CTX(ssl); } - Event::GlobalTimeSystem time_system_; + NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; std::unique_ptr context_manager_; HandshakerFactoryImplForTest handshaker_factory_; @@ -248,8 +248,8 @@ class HandshakerFactoryImplForDownstreamTest class HandshakerFactoryDownstreamTest : public testing::Test { protected: HandshakerFactoryDownstreamTest() - : context_manager_( - std::make_unique(time_system_)) { + : context_manager_(std::make_unique( + server_factory_context_)) { scoped_runtime_.mergeValues( {{"envoy.reloadable_features.no_extension_lookup_by_name", "false"}}); } @@ -262,7 +262,7 @@ class HandshakerFactoryDownstreamTest : public testing::Test { return SSL_get_SSL_CTX(ssl); } - Event::GlobalTimeSystem time_system_; + NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; std::unique_ptr context_manager_; envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context_; diff --git a/test/common/tls/integration/ssl_integration_test.cc b/test/common/tls/integration/ssl_integration_test.cc index 885494ce1f33..ffae471e9e90 100644 --- a/test/common/tls/integration/ssl_integration_test.cc +++ b/test/common/tls/integration/ssl_integration_test.cc @@ -66,8 +66,8 @@ void SslIntegrationTestBase::initialize() { HttpIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); registerTestServerPorts({"http"}); } diff --git a/test/common/tls/ssl_socket_test.cc b/test/common/tls/ssl_socket_test.cc index b72f7ad6596b..dc231f41eaad 100644 --- a/test/common/tls/ssl_socket_test.cc +++ b/test/common/tls/ssl_socket_test.cc @@ -47,6 +47,7 @@ #include "test/mocks/network/mocks.h" #include "test/mocks/runtime/mocks.h" #include "test/mocks/secret/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" @@ -346,8 +347,9 @@ void testUtil(const TestUtilOptions& options) { Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + transport_socket_factory_context; + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); // For private key method testing. NiceMock context_manager; @@ -356,7 +358,7 @@ void testUtil(const TestUtilOptions& options) { test_private_key_method_factory(test_factory); PrivateKeyMethodManagerImpl private_key_method_manager; if (options.expectedPrivateKeyMethod()) { - EXPECT_CALL(server_factory_context, sslContextManager()) + EXPECT_CALL(transport_socket_factory_context, sslContextManager()) .WillOnce(ReturnRef(context_manager)) .WillRepeatedly(ReturnRef(context_manager)); EXPECT_CALL(context_manager, privateKeyMethodManager()) @@ -367,9 +369,10 @@ void testUtil(const TestUtilOptions& options) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(options.serverCtxYaml()), server_tls_context); - auto server_cfg = - std::make_unique(server_tls_context, server_factory_context); - ContextManagerImpl manager(*time_system); + auto server_cfg = std::make_unique(server_tls_context, + transport_socket_factory_context); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Event::DispatcherPtr dispatcher = server_api->allocateDispatcher("test_thread"); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -707,7 +710,10 @@ class TestUtilOptionsV2 : public TestUtilOptionsBase { void testUtilV2(const TestUtilOptionsV2& options) { Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(*time_system); + NiceMock + transport_socket_factory_context; + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); // SNI-based selection logic isn't happening in SslSocket anymore. ASSERT(options.listener().filter_chains().size() == 1); @@ -716,10 +722,9 @@ void testUtilV2(const TestUtilOptionsV2& options) { filter_chain.filter_chain_match().server_names().end()); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); - testing::NiceMock - server_factory_context; NiceMock runtime; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const envoy::config::core::v3::TransportSocket& transport_socket = @@ -727,7 +732,8 @@ void testUtilV2(const TestUtilOptionsV2& options) { ASSERT(transport_socket.has_typed_config()); transport_socket.typed_config().UnpackTo(&tls_context); - auto server_cfg = std::make_unique(tls_context, server_factory_context); + auto server_cfg = + std::make_unique(tls_context, transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *server_stats_store.rootScope(), server_names); @@ -1016,7 +1022,8 @@ TEST_P(SslSocketTest, ServerTransportSocketOptions) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); auto ssl_socket = server_ssl_socket_factory.createDownstreamTransportSocket(); @@ -3065,7 +3072,8 @@ TEST_P(SslSocketTest, FlushCloseDuringHandshake) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3124,7 +3132,8 @@ TEST_P(SslSocketTest, HalfClose) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3209,7 +3218,8 @@ TEST_P(SslSocketTest, ShutdownWithCloseNotify) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3300,7 +3310,8 @@ TEST_P(SslSocketTest, ShutdownWithoutCloseNotify) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3407,7 +3418,8 @@ TEST_P(SslSocketTest, ClientAuthMultipleCAs) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3489,24 +3501,26 @@ void testTicketSessionResumption(const std::string& server_ctx_yaml1, const Network::Address::IpVersion ip_version, const uint32_t expected_lifetime_hint = 0) { Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(*time_system); + NiceMock + transport_socket_factory_context; + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; - testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context1; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml1), server_tls_context1); - auto server_cfg1 = - std::make_unique(server_tls_context1, server_factory_context); + auto server_cfg1 = std::make_unique(server_tls_context1, + transport_socket_factory_context); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context2; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml2), server_tls_context2); - auto server_cfg2 = - std::make_unique(server_tls_context2, server_factory_context); + auto server_cfg2 = std::make_unique(server_tls_context2, + transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory1(std::move(server_cfg1), manager, *server_stats_store.rootScope(), server_names1); ServerSslSocketFactory server_ssl_socket_factory2(std::move(server_cfg2), manager, @@ -3647,19 +3661,21 @@ void testSupportForSessionResumption(const std::string& server_ctx_yaml, bool expect_stateful, const Network::Address::IpVersion ip_version) { Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(*time_system); + NiceMock + transport_socket_factory_context; + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::IsolatedStoreImpl server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; - testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); - auto server_cfg = - std::make_unique(server_tls_context, server_factory_context); + auto server_cfg = std::make_unique(server_tls_context, + transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *server_stats_store.rootScope(), {}); @@ -4305,7 +4321,8 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context2; TestUtility::loadFromYaml(TestEnvironment::substitute(server2_ctx_yaml), tls_context2); auto server2_cfg = std::make_unique(tls_context2, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -4422,18 +4439,20 @@ void SslSocketTest::testClientSessionResumption(const std::string& server_ctx_ya const Network::Address::IpVersion version) { InSequence s; - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system_); testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + transport_socket_factory_context; + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_ctx_proto; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_ctx_proto); auto server_cfg = - std::make_unique(server_ctx_proto, server_factory_context); + std::make_unique(server_ctx_proto, transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -4698,7 +4717,7 @@ TEST_P(SslSocketTest, SslError) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -5230,7 +5249,7 @@ TEST_P(SslSocketTest, SetSignatureAlgorithms) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -5845,7 +5864,7 @@ TEST_P(SslSocketTest, DownstreamNotReadySslSocket) { EXPECT_TRUE(server_cfg->tlsCertificates().empty()); EXPECT_FALSE(server_cfg->isReady()); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *factory_context_.store_.rootScope(), std::vector{}); @@ -5883,7 +5902,7 @@ TEST_P(SslSocketTest, UpstreamNotReadySslSocket) { EXPECT_TRUE(client_cfg->tlsCertificates().empty()); EXPECT_FALSE(client_cfg->isReady()); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); ClientSslSocketFactory client_ssl_socket_factory(std::move(client_cfg), manager, *factory_context_.store_.rootScope()); auto transport_socket = client_ssl_socket_factory.createTransportSocket(nullptr, nullptr); @@ -5911,7 +5930,7 @@ TEST_P(SslSocketTest, TestTransportSocketCallback) { envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; auto client_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); ClientSslSocketFactory client_ssl_socket_factory(std::move(client_cfg), manager, *factory_context_.store_.rootScope()); @@ -5935,7 +5954,7 @@ class SslReadBufferLimitTest : public SslSocketTest { downstream_tls_context_); auto server_cfg = std::make_unique(downstream_tls_context_, factory_context_); - manager_ = std::make_unique(time_system_); + manager_ = std::make_unique(factory_context_.serverFactoryContext()); server_ssl_socket_factory_ = std::make_unique( std::move(server_cfg), *manager_, *server_stats_store_.rootScope(), std::vector{}); diff --git a/test/common/upstream/hds_test.cc b/test/common/upstream/hds_test.cc index 82acb9d72e8d..c58700be552a 100644 --- a/test/common/upstream/hds_test.cc +++ b/test/common/upstream/hds_test.cc @@ -61,8 +61,7 @@ class HdsTest : public testing::Test { HdsTest() : retry_timer_(new Event::MockTimer()), server_response_timer_(new Event::MockTimer()), async_client_(new Grpc::MockAsyncClient()), - api_(Api::createApiForTest(stats_store_, random_)), - ssl_context_manager_(api_->timeSource()) { + api_(Api::createApiForTest(stats_store_, random_)), ssl_context_manager_(server_context_) { ON_CALL(server_context_, api()).WillByDefault(ReturnRef(*api_)); node_.set_id("hds-node"); } diff --git a/test/common/upstream/test_cluster_manager.h b/test/common/upstream/test_cluster_manager.h index eb110f9d672e..a8e232eacb9e 100644 --- a/test/common/upstream/test_cluster_manager.h +++ b/test/common/upstream/test_cluster_manager.h @@ -146,8 +146,7 @@ class TestClusterManagerFactory : public ClusterManagerFactory { new NiceMock}; NiceMock& runtime_ = server_context_.runtime_loader_; NiceMock& dispatcher_ = server_context_.dispatcher_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{ - dispatcher_.timeSource()}; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{server_context_}; NiceMock& local_info_ = server_context_.local_info_; NiceMock& admin_ = server_context_.admin_; NiceMock secret_manager_; diff --git a/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc b/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc index 90d2e7acde2a..1b7e15d0ffe0 100644 --- a/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc +++ b/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc @@ -143,8 +143,8 @@ class TcpGrpcAccessLogIntegrationTest : public Grpc::GrpcClientIntegrationParamT const Ssl::ClientSslTransportOptions& ssl_options = {}, const std::string& curves_list = "") { // Set up the SSL client. - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); Network::Address::InstanceConstSharedPtr address = Ssl::getSslAddress(version_, lookupPort("tcp_proxy")); context_ = Ssl::createClientSslTransportSocketFactory(ssl_options, *context_manager_, *api_); diff --git a/test/extensions/filters/http/rbac/BUILD b/test/extensions/filters/http/rbac/BUILD index a2fb02c8fe24..4c397d1e0389 100644 --- a/test/extensions/filters/http/rbac/BUILD +++ b/test/extensions/filters/http/rbac/BUILD @@ -58,7 +58,7 @@ envoy_extension_cc_test( size = "large", srcs = ["rbac_filter_integration_test.cc"], extension_names = ["envoy.filters.http.rbac"], - shard_count = 2, + shard_count = 3, tags = ["skip_on_windows"], deps = [ "//source/extensions/clusters/dynamic_forward_proxy:cluster", diff --git a/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc b/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc index 78b57390d80d..aa2279c70839 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc +++ b/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc @@ -90,8 +90,8 @@ class TlsInspectorIntegrationTest : public testing::TestWithParam(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); } void setupConnections(bool listener_filter_disabled, bool expect_connection_open, bool ssl_client, diff --git a/test/extensions/string_matcher/lua/lua_test.cc b/test/extensions/string_matcher/lua/lua_test.cc index 4e834e0d6d71..3842b89f3469 100644 --- a/test/extensions/string_matcher/lua/lua_test.cc +++ b/test/extensions/string_matcher/lua/lua_test.cc @@ -88,21 +88,20 @@ TEST(LuaStringMatcher, LuaStdLib) { } TEST(LuaStringMatcher, NoCode) { - ScopedInjectableLoader api_inject(std::make_unique()); - ScopedInjectableLoader tls_inject( - std::make_unique()); + Api::MockApi api; + ThreadLocal::MockInstance tls; LuaStringMatcherFactory factory; ::envoy::extensions::string_matcher::lua::v3::Lua empty_config; ProtobufWkt::Any any; any.PackFrom(empty_config); - EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any, tls, api), EnvoyException, "Failed to get lua string matcher code from source: INVALID_ARGUMENT: " "Unexpected DataSource::specifier_case(): 0"); empty_config.mutable_source_code()->set_inline_string(""); any.PackFrom(empty_config); - EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any, tls, api), EnvoyException, "Failed to get lua string matcher code from source: INVALID_ARGUMENT: " "DataSource cannot be empty"); } diff --git a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc index cb2564e38286..01a27199c184 100644 --- a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc +++ b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc @@ -208,8 +208,8 @@ void StartTlsIntegrationTest::initialize() { factory->createTransportSocketFactory(*config, factory_context_)}; // Setup factories and contexts for tls transport socket. - tls_context_manager_ = - std::make_unique(timeSystem()); + tls_context_manager_ = std::make_unique( + server_factory_context_); tls_context_ = Ssl::createClientSslTransportSocketFactory({}, *tls_context_manager_, *api_); payload_reader_ = std::make_shared(*dispatcher_); diff --git a/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc index f3efba23db8d..b106d0043f30 100644 --- a/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc +++ b/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc @@ -249,8 +249,8 @@ void StartTlsIntegrationTest::initialize() { // Setup factory and context for tls transport socket. // The tls transport socket will be inserted into fake_upstream when // upstream starttls transport socket is converted to secure mode. - tls_context_manager_ = - std::make_unique(timeSystem()); + tls_context_manager_ = std::make_unique( + server_factory_context_); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext downstream_tls_context; diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD b/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD index 9f9644768aee..c95555b9be37 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD @@ -25,6 +25,7 @@ envoy_extension_cc_test( "//source/extensions/transport_sockets/tls/cert_validator/spiffe:config", "//test/common/tls:ssl_test_utils", "//test/common/tls/cert_validator:test_common", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:environment_lib", "//test/test_common:simulated_time_system_lib", "//test/test_common:test_runtime_lib", diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc index 1806d0985ed1..db9af217a43b 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc @@ -21,8 +21,8 @@ void SslSPIFFECertValidatorIntegrationTest::initialize() { .setAllowExpiredCertificate(allow_expired_cert_)); HttpIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); registerTestServerPorts({"http"}); } diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc index 51cc1ad97016..327298a3efe6 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc @@ -13,6 +13,7 @@ #include "test/common/tls/cert_validator/test_common.h" #include "test/common/tls/ssl_test_utility.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/environment.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/test_runtime.h" @@ -44,7 +45,8 @@ class TestSPIFFEValidator : public testing::Test { TestUtility::loadFromYaml(yaml, typed_conf); config_ = std::make_unique( typed_conf, allow_expired_certificate_, san_matchers_); - validator_ = std::make_unique(config_.get(), stats_, time_source); + ON_CALL(factory_context_, timeSource()).WillByDefault(testing::ReturnRef(time_source)); + validator_ = std::make_unique(config_.get(), stats_, factory_context_); } void initialize(std::string yaml) { @@ -52,8 +54,7 @@ class TestSPIFFEValidator : public testing::Test { TestUtility::loadFromYaml(yaml, typed_conf); config_ = std::make_unique( typed_conf, allow_expired_certificate_, san_matchers_); - validator_ = - std::make_unique(config_.get(), stats_, config_->api().timeSource()); + validator_ = std::make_unique(config_.get(), stats_, factory_context_); }; void initialize() { validator_ = std::make_unique(stats_, time_system_); } @@ -90,6 +91,7 @@ class TestSPIFFEValidator : public testing::Test { }; private: + NiceMock factory_context_; bool allow_expired_certificate_{false}; TestCertificateValidationContextConfigPtr config_; std::vector san_matchers_{}; diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index 1f1df3f7c765..c5eb1129d41b 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -525,7 +525,8 @@ class BaseIntegrationTest : protected Logger::Loggable { createUpstreamTlsContext(const FakeUpstreamConfig& upstream_config); testing::NiceMock thread_local_; testing::NiceMock factory_context_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{timeSystem()}; + testing::NiceMock server_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; // The fake upstreams_ are created using the context_manager, so make sure // they are destroyed before it is. diff --git a/test/integration/sds_static_integration_test.cc b/test/integration/sds_static_integration_test.cc index 49d42c1eb6c2..0e63eb3d4267 100644 --- a/test/integration/sds_static_integration_test.cc +++ b/test/integration/sds_static_integration_test.cc @@ -89,7 +89,7 @@ class SdsStaticDownstreamIntegrationTest } private: - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{timeSystem()}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; Network::UpstreamTransportSocketFactoryPtr client_ssl_ctx_; }; @@ -148,7 +148,7 @@ class SdsStaticUpstreamIntegrationTest : public testing::TestWithParam(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); payload_reader_ = std::make_shared(*dispatcher_); } diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 3d06c513d7fa..85019c6f45c2 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -239,7 +239,8 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt #ifdef ENVOY_ENABLE_QUIC testing::NiceMock threadlocal; - Extensions::TransportSockets::Tls::ContextManagerImpl manager(time_system); + NiceMock server_factory_context; + Extensions::TransportSockets::Tls::ContextManagerImpl manager(server_factory_context); Network::UpstreamTransportSocketFactoryPtr transport_socket_factory = createQuicUpstreamTransportSocketFactory(api, mock_stats_store, manager, threadlocal, "spiffe://lyft.com/backend-team"); diff --git a/test/integration/xds_integration_test.cc b/test/integration/xds_integration_test.cc index 5435bb17d55e..497c0e6522fa 100644 --- a/test/integration/xds_integration_test.cc +++ b/test/integration/xds_integration_test.cc @@ -193,7 +193,7 @@ class LdsInplaceUpdateTcpProxyIntegrationTest BaseIntegrationTest::initialize(); context_manager_ = std::make_unique( - BaseIntegrationTest::timeSystem()); + server_factory_context_); context_ = Ssl::createClientSslTransportSocketFactory({}, *context_manager_, *api_); } @@ -462,8 +462,8 @@ class LdsInplaceUpdateHttpIntegrationTest BaseIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); context_ = Ssl::createClientSslTransportSocketFactory({}, *context_manager_, *api_); address_ = Ssl::getSslAddress(version_, lookupPort("http")); } diff --git a/test/integration/xfcc_integration_test.cc b/test/integration/xfcc_integration_test.cc index eeec6cf876b5..689611d5345a 100644 --- a/test/integration/xfcc_integration_test.cc +++ b/test/integration/xfcc_integration_test.cc @@ -169,8 +169,8 @@ void XfccIntegrationTest::initialize() { config_helper_.addSslConfig(); } - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); client_tls_ssl_ctx_ = createClientSslContext(false); client_mtls_ssl_ctx_ = createClientSslContext(true); HttpIntegrationTest::initialize(); diff --git a/test/mocks/server/instance.cc b/test/mocks/server/instance.cc index e40b1e125331..eca664bd89a8 100644 --- a/test/mocks/server/instance.cc +++ b/test/mocks/server/instance.cc @@ -13,7 +13,7 @@ using ::testing::ReturnRef; MockInstance::MockInstance() : secret_manager_(std::make_unique(admin_.getConfigTracker())), - cluster_manager_(timeSource()), ssl_context_manager_(timeSource()), + cluster_manager_(timeSource()), singleton_manager_(new Singleton::ManagerImpl(Thread::threadFactoryForTest())), grpc_context_(stats_store_.symbolTable()), http_context_(stats_store_.symbolTable()), router_context_(stats_store_.symbolTable()), quic_stat_names_(stats_store_.symbolTable()), @@ -21,7 +21,8 @@ MockInstance::MockInstance() server_factory_context_( std::make_shared>()), transport_socket_factory_context_( - std::make_shared>()) { + std::make_shared>()), + ssl_context_manager_(*server_factory_context_) { ON_CALL(*this, threadLocal()).WillByDefault(ReturnRef(thread_local_)); ON_CALL(*this, stats()).WillByDefault(ReturnRef(stats_store_)); ON_CALL(*this, grpcContext()).WillByDefault(ReturnRef(grpc_context_)); diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index a8561d534b9c..de109c9c5a97 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -52,6 +52,7 @@ class MockInstance : public Instance { MOCK_METHOD(ThreadLocal::Instance&, threadLocal, ()); MOCK_METHOD(LocalInfo::LocalInfo&, localInfo, (), (const)); MOCK_METHOD(Configuration::StatsConfig&, statsConfig, (), ()); + MOCK_METHOD(Regex::Engine&, regexEngine, ()); MOCK_METHOD(void, flushStats, ()); MOCK_METHOD(ProtobufMessage::ValidationContext&, messageValidationContext, ()); MOCK_METHOD(Configuration::ServerFactoryContext&, serverFactoryContext, ()); @@ -78,7 +79,6 @@ class MockInstance : public Instance { testing::NiceMock cluster_manager_; Thread::MutexBasicLockable access_log_lock_; testing::NiceMock runtime_loader_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_; testing::NiceMock dispatcher_; testing::NiceMock drain_manager_; testing::NiceMock access_log_manager_; @@ -101,6 +101,7 @@ class MockInstance : public Instance { server_factory_context_; std::shared_ptr> transport_socket_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_; }; } // namespace Server diff --git a/test/mocks/server/server_factory_context.h b/test/mocks/server/server_factory_context.h index 7e69dbad447e..128afe221c26 100644 --- a/test/mocks/server/server_factory_context.h +++ b/test/mocks/server/server_factory_context.h @@ -79,6 +79,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(Server::DrainManager&, drainManager, ()); MOCK_METHOD(Init::Manager&, initManager, ()); MOCK_METHOD(ServerLifecycleNotifier&, lifecycleNotifier, ()); + Regex::Engine& regexEngine() override { return regex_engine_; } MOCK_METHOD(StatsConfig&, statsConfig, (), ()); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), ()); MOCK_METHOD(OverloadManager&, overloadManager, ()); @@ -107,6 +108,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { Router::ContextImpl router_context_; envoy::config::bootstrap::v3::Bootstrap bootstrap_; testing::NiceMock options_; + Regex::GoogleReEngine regex_engine_; }; class MockGenericFactoryContext : public GenericFactoryContext { @@ -155,6 +157,7 @@ class StatelessMockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(Server::DrainManager&, drainManager, ()); MOCK_METHOD(Init::Manager&, initManager, ()); MOCK_METHOD(ServerLifecycleNotifier&, lifecycleNotifier, ()); + MOCK_METHOD(Regex::Engine&, regexEngine, ()); MOCK_METHOD(StatsConfig&, statsConfig, (), ()); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), ()); MOCK_METHOD(OverloadManager&, overloadManager, ()); diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index 5ab396837967..cd8604d486dc 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -62,7 +62,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/listener_managers/validation_listener_manager:70.5" "source/extensions/watchdog/profile_action:83.3" "source/server:91.0" # flaky: be careful adjusting. See https://github.com/envoyproxy/envoy/issues/15239 -"source/server/config_validation:89.2" +"source/server/config_validation:88.9" "source/extensions/health_checkers:96.0" "source/extensions/health_checkers/http:93.9" "source/extensions/health_checkers/grpc:92.0" diff --git a/test/server/BUILD b/test/server/BUILD index 9cceee1474dc..2938f1dd3429 100644 --- a/test/server/BUILD +++ b/test/server/BUILD @@ -368,6 +368,7 @@ envoy_cc_test( srcs = ["ssl_context_manager_test.cc"], deps = [ "//source/server:ssl_context_manager_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/mocks/stats:stats_mocks", "//test/test_common:simulated_time_system_lib", diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index 02442808b230..12db1d70e553 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -40,7 +40,8 @@ TEST(ValidationClusterManagerTest, MockedMethods) { testing::NiceMock secret_manager; auto dns_resolver = std::make_shared>(); - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager{api->timeSource()}; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager{ + *server.server_factory_context_}; Http::ContextImpl http_context(stats_store.symbolTable()); Quic::QuicStatNames quic_stat_names(stats_store.symbolTable()); diff --git a/test/server/ssl_context_manager_test.cc b/test/server/ssl_context_manager_test.cc index dbe2bdc8db6e..4a651e61dc31 100644 --- a/test/server/ssl_context_manager_test.cc +++ b/test/server/ssl_context_manager_test.cc @@ -2,9 +2,9 @@ #include "source/server/ssl_context_manager.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" -#include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" @@ -14,14 +14,15 @@ namespace Server { namespace { TEST(SslContextManager, createStub) { - Event::SimulatedTimeSystem time_system; Stats::MockStore store; Stats::Scope& scope(*store.rootScope()); Ssl::MockClientContextConfig client_config; Ssl::MockServerContextConfig server_config; std::vector server_names; + NiceMock server_factory_context; - Ssl::ContextManagerPtr manager = createContextManager("fake_factory_name", time_system); + Ssl::ContextManagerPtr manager = + createContextManager("fake_factory_name", server_factory_context); // Check we've created a stub, not real manager. EXPECT_EQ(manager->daysUntilFirstCertExpires().value(), std::numeric_limits::max()); From 84dc857cea5ae0676403eac011dcf35ddaf9411d Mon Sep 17 00:00:00 2001 From: phlax Date: Thu, 14 Mar 2024 20:41:52 +0000 Subject: [PATCH 042/124] ci: Disable windows (#32904) Signed-off-by: Ryan Northey --- .github/config.yml | 19 ---- .github/workflows/envoy-windows.yml | 140 ---------------------------- 2 files changed, 159 deletions(-) delete mode 100644 .github/workflows/envoy-windows.yml diff --git a/.github/config.yml b/.github/config.yml index 316b204fe4a0..b8d00e19072d 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -150,27 +150,8 @@ checks: - publish - verify required: true - windows: - name: Envoy/Windows - required: true - on-run: - - build-windows run: - build-windows: - paths: - - .bazelrc - - .bazelversion - - .github/config.yml - - api/**/* - - bazel/**/* - - ci/**/* - - configs/**/* - - contrib/**/* - - envoy/**/* - - source/**/* - - test/**/* - - VERSION.txt build-macos: paths: - .bazelrc diff --git a/.github/workflows/envoy-windows.yml b/.github/workflows/envoy-windows.yml deleted file mode 100644 index fbd01b992a95..000000000000 --- a/.github/workflows/envoy-windows.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: Envoy/Windows - -permissions: - contents: read - -on: - workflow_run: - workflows: - - Request - types: - - completed - -concurrency: - group: ${{ github.head_ref || github.run_id }}-${{ github.workflow }} - cancel-in-progress: true - - -jobs: - load: - secrets: - app-key: ${{ secrets.ENVOY_CI_APP_KEY }} - app-id: ${{ secrets.ENVOY_CI_APP_ID }} - lock-app-key: ${{ secrets.ENVOY_CI_MUTEX_APP_KEY }} - lock-app-id: ${{ secrets.ENVOY_CI_MUTEX_APP_ID }} - permissions: - actions: read - contents: read - packages: read - pull-requests: read - if: ${{ github.event.workflow_run.conclusion == 'success' }} - uses: ./.github/workflows/_load.yml - with: - cache-docker: false - check-name: windows - - windows: - permissions: - contents: read - packages: read - secrets: - rbe-key: ${{ secrets.GCP_SERVICE_ACCOUNT_KEY }} - if: ${{ fromJSON(needs.load.outputs.request).run.build-windows }} - needs: - - load - uses: ./.github/workflows/_run.yml - name: CI ${{ matrix.name || matrix.target }} - with: - command: - request: ${{ needs.load.outputs.request }} - runs-on: envoy-win19-small - source: | - export ENVOY_SHARED_TMP_DIR="C:\Users\runner\AppData\Local\Temp\bazel-shared" - export ENVOY_DOCKER_BUILD_DIR="C:\Users\runner\AppData\Local\Temp" - mkdir -p "$ENVOY_SHARED_TMP_DIR" - GCP_SERVICE_ACCOUNT_KEY_PATH=$(mktemp -p "${ENVOY_SHARED_TMP_DIR}" -t gcp_service_account.XXXXXX.json) - bash -c "echo \"${RBE_KEY}\" | base64 --decode > \"${GCP_SERVICE_ACCOUNT_KEY_PATH}\"" - _BAZEL_BUILD_EXTRA_OPTIONS=( - --config=remote-ci - --config=rbe-google - --config=remote-msvc-cl - --google_credentials=${GCP_SERVICE_ACCOUNT_KEY_PATH} - --jobs=75 - --flaky_test_attempts=2) - export BAZEL_BUILD_EXTRA_OPTIONS=${_BAZEL_BUILD_EXTRA_OPTIONS[*]} - steps-post: - target: ${{ matrix.target }} - temp-dir: 'C:\Users\runner\AppData\Local\Temp\bazel-shared' - timeout-minutes: 120 - trusted: ${{ fromJSON(needs.load.outputs.trusted) }} - upload-name: windows.release - upload-path: 'C:\Users\runner\AppData\Local\Temp\envoy' - strategy: - fail-fast: false - matrix: - include: - - target: ci/windows_ci_steps.sh - name: Windows 2019 - - docker: - needs: - - load - - windows - strategy: - fail-fast: false - matrix: - include: - - target: windows2019 - name: Windows 2019 - runs-on: envoy-win19-small - build-type: windows - image-base: mcr.microsoft.com/windows/servercore - image-tag: ltsc2019 - - target: windows2022 - name: Windows 2022 - runs-on: envoy-win22-small - build-type: windows-ltsc2022 - image-base: mcr.microsoft.com/windows/nanoserver - image-tag: ltsc2022 - runs-on: ${{ matrix.runs-on }} - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ needs.load.outputs.repo_ref }} - - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 - with: - name: windows.release - - run: | - # Convert to Unix-style path so tar doesn't think drive letter is a hostname - STAGING_DIR="$(echo $PWD | tr -d ':' | tr '\\' '/')" - mkdir -p windows/amd64 && tar zxf "${STAGING_DIR}/envoy_binary.tar.gz" -C ./windows/amd64 - CI_SHA1=$(git rev-parse head) - export CI_SHA1 - ci/docker_ci.sh - shell: bash - env: - CI_BRANCH: ${{ github.ref }} - DOCKERHUB_USERNAME: ${{ fromJSON(needs.load.outputs.trusted) && secrets.DOCKERHUB_USERNAME || '' }} - DOCKERHUB_PASSWORD: ${{ fromJSON(needs.load.outputs.trusted) && secrets.DOCKERHUB_PASSWORD || '' }} - WINDOWS_BUILD_TYPE: ${{ matrix.build-type }} - WINDOWS_IMAGE_BASE: ${{ matrix.image-base }} - WINDOWS_IMAGE_TAG: ${{ matrix.image-tag }} - request: - permissions: - actions: read - contents: read - pull-requests: read - secrets: - app-id: ${{ secrets.ENVOY_CI_APP_ID }} - app-key: ${{ secrets.ENVOY_CI_APP_KEY }} - if: >- - ${{ always() - && github.event.workflow_run.conclusion == 'success' - && fromJSON(needs.load.outputs.request).run.build-windows }} - needs: - - load - - windows - - docker - uses: ./.github/workflows/_finish.yml - with: - needs: ${{ toJSON(needs) }} From c9fb17a6fabd1d2030ce8f5192780e947a335e01 Mon Sep 17 00:00:00 2001 From: phlax Date: Thu, 14 Mar 2024 20:53:21 +0000 Subject: [PATCH 043/124] deps: Bump VPP/VCL (#32873) Signed-off-by: Ryan Northey --- bazel/foreign_cc/vpp_vcl.patch | 26 +++++++++++++------------- bazel/repository_locations.bzl | 6 +++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bazel/foreign_cc/vpp_vcl.patch b/bazel/foreign_cc/vpp_vcl.patch index 07916e8a4167..2ce6b455ed04 100644 --- a/bazel/foreign_cc/vpp_vcl.patch +++ b/bazel/foreign_cc/vpp_vcl.patch @@ -1,8 +1,8 @@ diff --git src/CMakeLists.txt src/CMakeLists.txt -index fb4463e33..de1ef8990 100644 +index 68d0a4fe6..958918ef1 100644 --- src/CMakeLists.txt +++ src/CMakeLists.txt -@@ -31,12 +31,8 @@ include(cmake/ccache.cmake) +@@ -50,13 +50,8 @@ include(cmake/ccache.cmake) ############################################################################## # VPP Version ############################################################################## @@ -12,12 +12,12 @@ index fb4463e33..de1ef8990 100644 - OUTPUT_VARIABLE VPP_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE -) -+ -+set(VPP_VERSION 23.06-release) - string(REPLACE "-" ";" VPP_LIB_VERSION ${VPP_VERSION}) - list(GET VPP_LIB_VERSION 0 VPP_LIB_VERSION) - -@@ -215,8 +211,7 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + ++set(VPP_VERSION 24.03-dev) + if (VPP_PLATFORM) + set(VPP_VERSION ${VPP_VERSION}-${VPP_PLATFORM_NAME}) + endif() +@@ -277,8 +272,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD") find_package(OpenSSL) set(SUBDIRS vppinfra svm vlib vlibmemory vlibapi vnet vpp vat vat2 vcl vpp-api @@ -47,11 +47,11 @@ index 45b3944eb..b1dcc56e1 100644 @@ -24,7 +24,7 @@ macro(add_vpp_library lib) set_target_properties(${lo} PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_options(${lo} PUBLIC ${VPP_DEFAULT_MARCH_FLAGS}) - + - add_library(${lib} SHARED) + add_library(${lib} STATIC) target_sources(${lib} PRIVATE $) - + if(VPP_LIB_VERSION) diff --git src/tools/vppapigen/CMakeLists.txt src/tools/vppapigen/CMakeLists.txt index 04ebed548..bfabc3a67 100644 @@ -60,7 +60,7 @@ index 04ebed548..bfabc3a67 100644 @@ -11,22 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - + -find_package( - Python3 - REQUIRED @@ -97,7 +97,7 @@ index 2b0ce9999..f28a17302 100755 + import ply.lex as lex import ply.yacc as yacc - + diff --git src/vcl/CMakeLists.txt src/vcl/CMakeLists.txt index 610b422d1..c5e6f8ca8 100644 --- src/vcl/CMakeLists.txt @@ -105,7 +105,7 @@ index 610b422d1..c5e6f8ca8 100644 @@ -35,6 +35,8 @@ if (LDP_HAS_GNU_SOURCE) add_compile_definitions(HAVE_GNU_SOURCE) endif(LDP_HAS_GNU_SOURCE) - + +file(COPY vppcom.h DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) + add_vpp_library(vcl_ldpreload diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 7d52b7ac94e0..5c15f4e87db3 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -1503,13 +1503,13 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "VPP Comms Library", project_desc = "FD.io Vector Packet Processor (VPP) Comms Library", project_url = "https://fd.io/", - version = "493b8990d1185f818890560101e13e1b69f54b1d", - sha256 = "94a515b9396822911271a8f72ba8ffd9965992241754a88625f4cf2a3883a4ac", + version = "39e7f2e650a65bac596a6fc968c9860a1496a5bf", + sha256 = "63742d09ac223b30d71d9fe2da5afb75253fde31e3cf986a3f9ce89e264e801a", strip_prefix = "vpp-{version}", urls = ["https://github.com/FDio/vpp/archive/{version}.tar.gz"], use_category = ["other"], extensions = ["envoy.bootstrap.vcl"], - release_date = "2023-06-28", + release_date = "2024-03-13", cpe = "N/A", license = "Apache-2.0", license_url = "https://github.com/FDio/vpp/blob/{version}/LICENSE", From b2f1a847fd13c2dd7a3558deddd2338758cb0c01 Mon Sep 17 00:00:00 2001 From: Rama Chavali Date: Fri, 15 Mar 2024 06:25:34 +0530 Subject: [PATCH 044/124] minor variable rename (#32868) Signed-off-by: Rama Chavali --- .../envoy_default/http1_header_validator_test.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/extensions/http/header_validators/envoy_default/http1_header_validator_test.cc b/test/extensions/http/header_validators/envoy_default/http1_header_validator_test.cc index 8556ec7d1173..e8a6ad73950d 100644 --- a/test/extensions/http/header_validators/envoy_default/http1_header_validator_test.cc +++ b/test/extensions/http/header_validators/envoy_default/http1_header_validator_test.cc @@ -78,12 +78,12 @@ TEST_F(Http1HeaderValidatorTest, ValidateTransferEncodingInRequest) { TEST_F(Http1HeaderValidatorTest, ValidateTransferEncodingInResponse) { auto uhv = createH1(empty_config); - TestResponseHeaderMapImpl request_headers = makeGoodResponseHeaders(); - request_headers.setCopy(LowerCaseString("transfer-encoding"), "ChuNKeD"); - EXPECT_ACCEPT(uhv->validateResponseHeaders(request_headers)); + TestResponseHeaderMapImpl response_headers = makeGoodResponseHeaders(); + response_headers.setCopy(LowerCaseString("transfer-encoding"), "ChuNKeD"); + EXPECT_ACCEPT(uhv->validateResponseHeaders(response_headers)); - request_headers.setCopy(LowerCaseString("transfer-encoding"), "gzip"); - EXPECT_REJECT_WITH_DETAILS(uhv->validateResponseHeaders(request_headers), + response_headers.setCopy(LowerCaseString("transfer-encoding"), "gzip"); + EXPECT_REJECT_WITH_DETAILS(uhv->validateResponseHeaders(response_headers), "http1.invalid_transfer_encoding"); } From 8b293ba506611eb13da4064427235e2c457ca334 Mon Sep 17 00:00:00 2001 From: phlax Date: Fri, 15 Mar 2024 03:32:52 +0000 Subject: [PATCH 045/124] fips/build: Add `-fPIC` (#32901) Signed-off-by: Ryan Northey --- bazel/external/boringssl_fips.genrule_cmd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bazel/external/boringssl_fips.genrule_cmd b/bazel/external/boringssl_fips.genrule_cmd index 46526a9a84de..44f21fc9c515 100755 --- a/bazel/external/boringssl_fips.genrule_cmd +++ b/bazel/external/boringssl_fips.genrule_cmd @@ -119,7 +119,9 @@ rm -rf boringssl/build # Build BoringSSL. cd boringssl -mkdir build && cd build && cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=${HOME}/toolchain -DFIPS=1 -DCMAKE_BUILD_TYPE=Release .. +# Setting -fPIC only affects the compilation of the non-module code in libcrypto.a, +# because the FIPS module itself is already built with -fPIC. +mkdir build && cd build && cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=${HOME}/toolchain -DFIPS=1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_CXX_FLAGS="-fPIC" .. ninja ninja run_tests ./crypto/crypto_test From 49e8a5970fb0b942b7940895528e1168755b2929 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Fri, 15 Mar 2024 01:39:56 -0400 Subject: [PATCH 046/124] logs: removing a hard linkage on file based access logs (#32882) logs: removing a hard linkage on file based access logs Signed-off-by: Alyssa Wilk --- .github/workflows/mobile-perf.yml | 1 + source/server/BUILD | 2 +- source/server/configuration_impl.cc | 14 ++++++++------ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/mobile-perf.yml b/.github/workflows/mobile-perf.yml index 807f1eda861b..241199383f08 100644 --- a/.github/workflows/mobile-perf.yml +++ b/.github/workflows/mobile-perf.yml @@ -70,6 +70,7 @@ jobs: source/server/guarddog_impl.h source/server/watchdog_impl.h source/server/options_impl.cc + source/extensions/access_loggers/common/file_access_log_impl.h target: size-current - name: Main size args: >- diff --git a/source/server/BUILD b/source/server/BUILD index f656303df03e..c86be8637fc7 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -54,10 +54,10 @@ envoy_cc_library( "//source/common/network:socket_option_lib", "//source/common/network:utility_lib", "//source/common/protobuf:utility_lib", - "//source/extensions/access_loggers/common:file_access_log_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/config/metrics/v3:pkg_cc_proto", "@envoy_api//envoy/config/trace/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/access_loggers/file/v3:pkg_cc_proto", ], ) diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index b5c01e101646..7070baf2097d 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -10,6 +10,7 @@ #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/config/metrics/v3/stats.pb.h" #include "envoy/config/trace/v3/http_tracer.pb.h" +#include "envoy/extensions/access_loggers/file/v3/file.pb.h" #include "envoy/network/connection.h" #include "envoy/runtime/runtime.h" #include "envoy/server/instance.h" @@ -23,7 +24,6 @@ #include "source/common/config/utility.h" #include "source/common/network/socket_option_factory.h" #include "source/common/protobuf/utility.h" -#include "source/extensions/access_loggers/common/file_access_log_impl.h" namespace Envoy { namespace Server { @@ -276,11 +276,13 @@ void InitialImpl::initAdminAccessLog(const envoy::config::bootstrap::v3::Bootstr } if (!admin.access_log_path().empty()) { - Filesystem::FilePathAndType file_info{Filesystem::DestinationType::File, - admin.access_log_path()}; - admin_.access_logs_.emplace_back(new Extensions::AccessLoggers::File::FileAccessLog( - file_info, {}, Formatter::HttpSubstitutionFormatUtils::defaultSubstitutionFormatter(), - factory_context.serverFactoryContext().accessLogManager())); + envoy::extensions::access_loggers::file::v3::FileAccessLog config; + config.mutable_format(); + config.set_path(admin.access_log_path()); + + auto factory = Config::Utility::getFactoryByName( + "envoy.file_access_log"); + admin_.access_logs_.emplace_back(factory->createAccessLogInstance(config, {}, factory_context)); } } From 56ace997da32d1ccdc0f08868f3df8c1abb1f4c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 08:53:50 +0000 Subject: [PATCH 047/124] build(deps): bump node from `fbdb66d` to `54836e4` in /examples/shared/node (#32836) build(deps): bump node in /examples/shared/node Bumps node from `fbdb66d` to `54836e4`. --- updated-dependencies: - dependency-name: node dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/shared/node/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shared/node/Dockerfile b/examples/shared/node/Dockerfile index 3d497d08223a..4ef89898f771 100644 --- a/examples/shared/node/Dockerfile +++ b/examples/shared/node/Dockerfile @@ -1,4 +1,4 @@ -FROM node:21.7-bookworm-slim@sha256:fbdb66de323c6aa010c6afc04dda9a4031d113ebc623588f42633b124b3a5ac6 as node-base +FROM node:21.7-bookworm-slim@sha256:54836e4f9d2442d9dd01a220ba5f4d56d7afcfc86e7c129f9a79a5e4cdbb96b9 as node-base FROM node-base as node-http-auth From 136d7528a318aa85b770b4303c2eb2e7a37b35d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 08:54:06 +0000 Subject: [PATCH 048/124] build(deps): bump openzipkin/zipkin from `be6eedb` to `8448c6d` in /examples/zipkin (#32839) build(deps): bump openzipkin/zipkin in /examples/zipkin Bumps openzipkin/zipkin from `be6eedb` to `8448c6d`. --- updated-dependencies: - dependency-name: openzipkin/zipkin dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/zipkin/Dockerfile-zipkin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/zipkin/Dockerfile-zipkin b/examples/zipkin/Dockerfile-zipkin index 5e3813094aee..a3d2df5247fb 100644 --- a/examples/zipkin/Dockerfile-zipkin +++ b/examples/zipkin/Dockerfile-zipkin @@ -1 +1 @@ -FROM openzipkin/zipkin:latest@sha256:be6eedb41ecae348c1d61abe9b9f22e5af582604fec4ad12e4fa782e99746a6f +FROM openzipkin/zipkin:latest@sha256:8448c6d50247a413ea5d38393307b4a751896bb0fcfb31a21e95ecd194c0b4c7 From ab0be5c989158dd2b8dd0c5f225bba4db2c40163 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 08:54:43 +0000 Subject: [PATCH 049/124] build(deps): bump debian from `d02c76d` to `ccb33c3` in /examples/shared/websocket (#32840) build(deps): bump debian in /examples/shared/websocket Bumps debian from `d02c76d` to `ccb33c3`. --- updated-dependencies: - dependency-name: debian dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/shared/websocket/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shared/websocket/Dockerfile b/examples/shared/websocket/Dockerfile index 79ab5716f451..eec89e154fc0 100644 --- a/examples/shared/websocket/Dockerfile +++ b/examples/shared/websocket/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bookworm-slim@sha256:d02c76d82364cedca16ba3ed6f9102406fa9fa8833076a609cabf14270f43dfc as websocket-base +FROM debian:bookworm-slim@sha256:ccb33c3ac5b02588fc1d9e4fc09b952e433d0c54d8618d0ee1afadf1f3cf2455 as websocket-base ENV DEBIAN_FRONTEND=noninteractive RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ From d77ee908bc3c475efc017b831fc2d8e255c6ab15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 08:55:04 +0000 Subject: [PATCH 050/124] build(deps): bump debian from `d02c76d` to `ccb33c3` in /examples/shared/golang (#32841) build(deps): bump debian in /examples/shared/golang Bumps debian from `d02c76d` to `ccb33c3`. --- updated-dependencies: - dependency-name: debian dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/shared/golang/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shared/golang/Dockerfile b/examples/shared/golang/Dockerfile index 5a940c392906..3d06d98e72c2 100644 --- a/examples/shared/golang/Dockerfile +++ b/examples/shared/golang/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bookworm-slim@sha256:d02c76d82364cedca16ba3ed6f9102406fa9fa8833076a609cabf14270f43dfc as os-base +FROM debian:bookworm-slim@sha256:ccb33c3ac5b02588fc1d9e4fc09b952e433d0c54d8618d0ee1afadf1f3cf2455 as os-base RUN rm -f /etc/apt/apt.conf.d/docker-clean \ && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | tee /etc/apt/apt.conf.d/keep-cache From e698a76362eab523968e2eaffcc685bcbf1f9ed6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 08:56:08 +0000 Subject: [PATCH 051/124] build(deps): bump jaegertracing/all-in-one from `2c8e4a0` to `15b4256` in /examples/shared/jaeger (#32897) build(deps): bump jaegertracing/all-in-one in /examples/shared/jaeger Bumps jaegertracing/all-in-one from `2c8e4a0` to `15b4256`. --- updated-dependencies: - dependency-name: jaegertracing/all-in-one dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/shared/jaeger/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shared/jaeger/Dockerfile b/examples/shared/jaeger/Dockerfile index 13fb4ff4b21d..996f0b6c83dc 100644 --- a/examples/shared/jaeger/Dockerfile +++ b/examples/shared/jaeger/Dockerfile @@ -1,4 +1,4 @@ -FROM jaegertracing/all-in-one@sha256:2c8e4a0bec794046d92d0487f9abc423c60ebf0e970b832eec1bbb623f11134a +FROM jaegertracing/all-in-one@sha256:15b4256cc70141664c34c7aaa7e261588bbbfba7951393a82a49ff3db9f0f402 HEALTHCHECK \ --interval=1s \ --timeout=1s \ From 3121ef0ea334a8e7dc3e78575941a7ea6f569c05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 08:56:19 +0000 Subject: [PATCH 052/124] build(deps): bump the examples-local-ratelimit group in /examples/local_ratelimit with 1 update (#32895) build(deps): bump the examples-local-ratelimit group Bumps the examples-local-ratelimit group in /examples/local_ratelimit with 1 update: nginx. Updates `nginx` from `c26ae74` to `6db391d` --- updated-dependencies: - dependency-name: nginx dependency-type: direct:production dependency-group: examples-local-ratelimit ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/local_ratelimit/Dockerfile-nginx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/local_ratelimit/Dockerfile-nginx b/examples/local_ratelimit/Dockerfile-nginx index eff6acc5fa7d..2794de868137 100644 --- a/examples/local_ratelimit/Dockerfile-nginx +++ b/examples/local_ratelimit/Dockerfile-nginx @@ -1 +1 @@ -FROM nginx@sha256:c26ae7472d624ba1fafd296e73cecc4f93f853088e6a9c13c0d52f6ca5865107 +FROM nginx@sha256:6db391d1c0cfb30588ba0bf72ea999404f2764febf0f1f196acd5867ac7efa7e From e0b82a7c4a0c2f2022d1d7fea9f11952d82bb1c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 08:56:29 +0000 Subject: [PATCH 053/124] build(deps): bump postgres from `f58300a` to `4784d9b` in /examples/shared/postgres (#32866) build(deps): bump postgres in /examples/shared/postgres Bumps postgres from `f58300a` to `4784d9b`. --- updated-dependencies: - dependency-name: postgres dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/shared/postgres/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shared/postgres/Dockerfile b/examples/shared/postgres/Dockerfile index eb29c21895f4..b01031b6cadb 100644 --- a/examples/shared/postgres/Dockerfile +++ b/examples/shared/postgres/Dockerfile @@ -1,3 +1,3 @@ -FROM postgres:latest@sha256:f58300ac8d393b2e3b09d36ea12d7d24ee9440440e421472a300e929ddb63460 +FROM postgres:latest@sha256:4784d9bcd8e603179d03732c9d2cd145c3c90e42f29847a95b5317fb37ac4765 COPY docker-healthcheck.sh /usr/local/bin/ HEALTHCHECK CMD ["docker-healthcheck.sh"] From 9e65865e39b2a9a242c0434f3a2de61e73ba4453 Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Fri, 15 Mar 2024 08:41:02 -0700 Subject: [PATCH 054/124] Stringmatcher: factory context for http cache filter (#32923) Signed-off-by: Greg Greenway --- .../filters/http/cache/cache_filter.cc | 7 ++- .../filters/http/cache/cache_filter.h | 3 +- .../filters/http/cache/cache_headers_utils.cc | 8 ++- .../filters/http/cache/cache_headers_utils.h | 3 +- .../extensions/filters/http/cache/config.cc | 4 +- test/extensions/filters/http/cache/BUILD | 3 + .../filters/http/cache/cache_filter_test.cc | 18 +++--- .../http/cache/cache_headers_utils_test.cc | 56 ++++++++++++++----- .../http/cache/cacheability_utils_test.cc | 6 +- .../http_cache_implementation_test_common.cc | 6 +- .../http_cache_implementation_test_common.h | 2 + .../filters/http/cache/http_cache_test.cc | 4 +- .../file_system_http_cache_test.cc | 3 +- 13 files changed, 85 insertions(+), 38 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index a920ac3ddbb2..8628a68e23c9 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -42,10 +42,11 @@ struct CacheResponseCodeDetailValues { using CacheResponseCodeDetails = ConstSingleton; CacheFilter::CacheFilter(const envoy::extensions::filters::http::cache::v3::CacheConfig& config, - const std::string&, Stats::Scope&, TimeSource& time_source, + const std::string&, Stats::Scope&, + Server::Configuration::CommonFactoryContext& context, std::shared_ptr http_cache) - : time_source_(time_source), cache_(http_cache), - vary_allow_list_(config.allowed_vary_headers()) {} + : time_source_(context.timeSource()), cache_(http_cache), + vary_allow_list_(config.allowed_vary_headers(), context) {} void CacheFilter::onDestroy() { filter_state_ = FilterState::Destroyed; diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index cedf5a60ee28..5cac2781fa10 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -54,7 +54,8 @@ class CacheFilter : public Http::PassThroughFilter, public std::enable_shared_from_this { public: CacheFilter(const envoy::extensions::filters::http::cache::v3::CacheConfig& config, - const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source, + const std::string& stats_prefix, Stats::Scope& scope, + Server::Configuration::CommonFactoryContext& context, std::shared_ptr http_cache); // Http::StreamFilterBase void onDestroy() override; diff --git a/source/extensions/filters/http/cache/cache_headers_utils.cc b/source/extensions/filters/http/cache/cache_headers_utils.cc index 582746215dc2..5c622462c4e7 100644 --- a/source/extensions/filters/http/cache/cache_headers_utils.cc +++ b/source/extensions/filters/http/cache/cache_headers_utils.cc @@ -284,12 +284,14 @@ CacheHeadersUtils::parseCommaDelimitedHeader(const Http::HeaderMap::GetResult& e } VaryAllowList::VaryAllowList( - const Protobuf::RepeatedPtrField& allow_list) { + const Protobuf::RepeatedPtrField& allow_list, + Server::Configuration::CommonFactoryContext& context) { for (const auto& rule : allow_list) { allow_list_.emplace_back( - std::make_unique>( - rule)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + rule, context)); } } diff --git a/source/extensions/filters/http/cache/cache_headers_utils.h b/source/extensions/filters/http/cache/cache_headers_utils.h index 440b58843d1a..5f96d24c54e2 100644 --- a/source/extensions/filters/http/cache/cache_headers_utils.h +++ b/source/extensions/filters/http/cache/cache_headers_utils.h @@ -128,7 +128,8 @@ class VaryAllowList { public: // Parses the allow list from the Cache Config into the object's private allow_list_. VaryAllowList( - const Protobuf::RepeatedPtrField& allow_list); + const Protobuf::RepeatedPtrField& allow_list, + Server::Configuration::CommonFactoryContext& context); // Checks if the headers contain an allowed value in the Vary header. bool allowsHeaders(const Http::ResponseHeaderMap& headers) const; diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index bfb64ad4bfb7..e379b4c05d1a 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -28,8 +28,8 @@ Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( return [config, stats_prefix, &context, cache](Http::FilterChainFactoryCallbacks& callbacks) -> void { - callbacks.addStreamFilter(std::make_shared( - config, stats_prefix, context.scope(), context.serverFactoryContext().timeSource(), cache)); + callbacks.addStreamFilter(std::make_shared(config, stats_prefix, context.scope(), + context.serverFactoryContext(), cache)); }; } diff --git a/test/extensions/filters/http/cache/BUILD b/test/extensions/filters/http/cache/BUILD index f45131e56f5d..1d1f5f0ab270 100644 --- a/test/extensions/filters/http/cache/BUILD +++ b/test/extensions/filters/http/cache/BUILD @@ -25,6 +25,7 @@ envoy_extension_cc_test( "//envoy/http:header_map_interface", "//source/common/http:header_map_lib", "//source/extensions/filters/http/cache:cache_headers_utils_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:simulated_time_system_lib", "//test/test_common:utility_lib", ], @@ -56,6 +57,7 @@ envoy_extension_cc_test( "//source/extensions/filters/http/cache:http_cache_lib", "//source/extensions/http/cache/simple_http_cache:config", "//test/mocks/http:http_mocks", + "//test/mocks/server:factory_context_mocks", "//test/test_common:simulated_time_system_lib", "//test/test_common:test_runtime_lib", "//test/test_common:utility_lib", @@ -96,6 +98,7 @@ envoy_extension_cc_test( extension_names = ["envoy.filters.http.cache"], deps = [ "//source/extensions/filters/http/cache:cacheability_utils_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:utility_lib", ], ) diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index 9c4e1afb273d..9ddf067b4d58 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -30,15 +30,15 @@ class CacheFilterTest : public ::testing::Test { // The filter has to be created as a shared_ptr to enable shared_from_this() which is used in the // cache callbacks. CacheFilterSharedPtr makeFilter(std::shared_ptr cache, bool auto_destroy = true) { - std::shared_ptr filter( - new CacheFilter(config_, /*stats_prefix=*/"", context_.scope(), - context_.server_factory_context_.timeSource(), cache), - [auto_destroy](CacheFilter* f) { - if (auto_destroy) { - f->onDestroy(); - } - delete f; - }); + std::shared_ptr filter(new CacheFilter(config_, /*stats_prefix=*/"", + context_.scope(), + context_.server_factory_context_, cache), + [auto_destroy](CacheFilter* f) { + if (auto_destroy) { + f->onDestroy(); + } + delete f; + }); filter_state_ = std::make_shared( StreamInfo::FilterState::LifeSpan::FilterChain); filter->setDecoderFilterCallbacks(decoder_callbacks_); diff --git a/test/extensions/filters/http/cache/cache_headers_utils_test.cc b/test/extensions/filters/http/cache/cache_headers_utils_test.cc index 2b5a0de3304e..453ab301513a 100644 --- a/test/extensions/filters/http/cache/cache_headers_utils_test.cc +++ b/test/extensions/filters/http/cache/cache_headers_utils_test.cc @@ -10,6 +10,7 @@ #include "source/common/http/header_utility.h" #include "source/extensions/filters/http/cache/cache_headers_utils.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" @@ -652,8 +653,11 @@ TEST_P(ParseCommaDelimitedHeaderTest, ParseCommaDelimitedHeader) { } TEST(CreateVaryIdentifier, IsStableForAllowListOrder) { - VaryAllowList vary_allow_list1(toStringMatchers({"width", "accept", "accept-language"})); - VaryAllowList vary_allow_list2(toStringMatchers({"accept", "width", "accept-language"})); + NiceMock factory_context; + VaryAllowList vary_allow_list1(toStringMatchers({"width", "accept", "accept-language"}), + factory_context); + VaryAllowList vary_allow_list2(toStringMatchers({"accept", "width", "accept-language"}), + factory_context); Http::TestRequestHeaderMapImpl request_headers{ {"accept", "image/*"}, {"accept-language", "en-us"}, {"width", "640"}}; @@ -710,16 +714,20 @@ TEST(HasVary, NotEmpty) { } TEST(CreateVaryIdentifier, EmptyVaryEntry) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{{"accept", "image/*"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {}, request_headers), "vary-id\n"); } TEST(CreateVaryIdentifier, SingleHeaderExists) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{{"accept", "image/*"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"accept"}, request_headers), "vary-id\naccept\r" @@ -727,17 +735,21 @@ TEST(CreateVaryIdentifier, SingleHeaderExists) { } TEST(CreateVaryIdentifier, SingleHeaderMissing) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"accept"}, request_headers), "vary-id\naccept\r\n"); } TEST(CreateVaryIdentifier, MultipleHeadersAllExist) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{ {"accept", "image/*"}, {"accept-language", "en-us"}, {"width", "640"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier( vary_allow_list, {"accept", "accept-language", "width"}, request_headers), @@ -747,9 +759,11 @@ TEST(CreateVaryIdentifier, MultipleHeadersAllExist) { } TEST(CreateVaryIdentifier, MultipleHeadersSomeExist) { + NiceMock factory_context; Http::TestResponseHeaderMapImpl response_headers{{"vary", "accept, accept-language, width"}}; Http::TestRequestHeaderMapImpl request_headers{{"accept", "image/*"}, {"width", "640"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier( vary_allow_list, {"accept", "accept-language", "width"}, request_headers), @@ -758,9 +772,11 @@ TEST(CreateVaryIdentifier, MultipleHeadersSomeExist) { } TEST(CreateVaryIdentifier, ExtraRequestHeaders) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{ {"accept", "image/*"}, {"heigth", "1280"}, {"width", "640"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ( VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"accept", "width"}, request_headers), @@ -769,8 +785,10 @@ TEST(CreateVaryIdentifier, ExtraRequestHeaders) { } TEST(CreateVaryIdentifier, MultipleHeadersNoneExist) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier( vary_allow_list, {"accept", "accept-language", "width"}, request_headers), @@ -778,9 +796,12 @@ TEST(CreateVaryIdentifier, MultipleHeadersNoneExist) { } TEST(CreateVaryIdentifier, DifferentHeadersSameValue) { + NiceMock factory_context; + // Two requests with the same value for different headers must have different // vary-ids. - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); Http::TestRequestHeaderMapImpl request_headers1{{"accept", "foo"}}; absl::optional vary_identifier1 = VaryHeaderUtils::createVaryIdentifier( @@ -796,8 +817,10 @@ TEST(CreateVaryIdentifier, DifferentHeadersSameValue) { } TEST(CreateVaryIdentifier, MultiValueSameHeader) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{{"width", "foo"}, {"width", "bar"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"width"}, request_headers), "vary-id\nwidth\r" @@ -806,16 +829,20 @@ TEST(CreateVaryIdentifier, MultiValueSameHeader) { } TEST(CreateVaryIdentifier, DisallowedHeader) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{{"width", "foo"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"disallowed"}, request_headers), absl::nullopt); } TEST(CreateVaryIdentifier, DisallowedHeaderWithAllowedHeader) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{{"width", "foo"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ( VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"disallowed,width"}, request_headers), @@ -840,8 +867,9 @@ envoy::extensions::filters::http::cache::v3::CacheConfig getConfig() { class VaryAllowListTest : public testing::Test { protected: - VaryAllowListTest() : vary_allow_list_(getConfig().allowed_vary_headers()) {} + VaryAllowListTest() : vary_allow_list_(getConfig().allowed_vary_headers(), factory_context_) {} + NiceMock factory_context_; VaryAllowList vary_allow_list_; Http::TestRequestHeaderMapImpl request_headers_; Http::TestResponseHeaderMapImpl response_headers_; diff --git a/test/extensions/filters/http/cache/cacheability_utils_test.cc b/test/extensions/filters/http/cache/cacheability_utils_test.cc index e5bbf9061b8d..d43223f4075c 100644 --- a/test/extensions/filters/http/cache/cacheability_utils_test.cc +++ b/test/extensions/filters/http/cache/cacheability_utils_test.cc @@ -2,6 +2,7 @@ #include "source/extensions/filters/http/cache/cacheability_utils.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" @@ -35,13 +36,16 @@ envoy::extensions::filters::http::cache::v3::CacheConfig getConfig() { class IsCacheableResponseTest : public testing::Test { public: - IsCacheableResponseTest() : vary_allow_list_(getConfig().allowed_vary_headers()) {} + IsCacheableResponseTest() + : vary_allow_list_(getConfig().allowed_vary_headers(), factory_context_) {} protected: std::string cache_control_ = "max-age=3600"; Http::TestResponseHeaderMapImpl response_headers_ = {{":status", "200"}, {"date", "Sun, 06 Nov 1994 08:49:37 GMT"}, {"cache-control", cache_control_}}; + + NiceMock factory_context_; VaryAllowList vary_allow_list_; }; diff --git a/test/extensions/filters/http/cache/http_cache_implementation_test_common.cc b/test/extensions/filters/http/cache/http_cache_implementation_test_common.cc index 1101e451cabe..fdb4c3f240cf 100644 --- a/test/extensions/filters/http/cache/http_cache_implementation_test_common.cc +++ b/test/extensions/filters/http/cache/http_cache_implementation_test_common.cc @@ -41,7 +41,8 @@ MATCHER(IsOk, "") { return arg.ok(); } } // namespace HttpCacheImplementationTest::HttpCacheImplementationTest() - : delegate_(GetParam()()), vary_allow_list_(getConfig().allowed_vary_headers()) { + : delegate_(GetParam()()), + vary_allow_list_(getConfig().allowed_vary_headers(), factory_context_) { request_headers_.setMethod("GET"); request_headers_.setHost("example.com"); request_headers_.setScheme("https"); @@ -490,7 +491,8 @@ TEST_P(HttpCacheImplementationTest, VaryResponses) { Protobuf::RepeatedPtrField<::envoy::type::matcher::v3::StringMatcher> proto_allow_list; ::envoy::type::matcher::v3::StringMatcher* matcher = proto_allow_list.Add(); matcher->set_exact("width"); - vary_allow_list_ = VaryAllowList(proto_allow_list); + NiceMock factory_context; + vary_allow_list_ = VaryAllowList(proto_allow_list, factory_context); lookup(request_path); EXPECT_EQ(lookup_result_.cache_entry_status_, CacheEntryStatus::Unusable); } diff --git a/test/extensions/filters/http/cache/http_cache_implementation_test_common.h b/test/extensions/filters/http/cache/http_cache_implementation_test_common.h index 85c4ba8981fd..53553010fbaf 100644 --- a/test/extensions/filters/http/cache/http_cache_implementation_test_common.h +++ b/test/extensions/filters/http/cache/http_cache_implementation_test_common.h @@ -9,6 +9,7 @@ #include "test/mocks/event/mocks.h" #include "test/mocks/http/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" @@ -89,6 +90,7 @@ class HttpCacheImplementationTest expectLookupSuccessWithBodyAndTrailers(LookupContext* lookup, absl::string_view body, Http::TestResponseTrailerMapImpl trailers = {}); + NiceMock factory_context_; std::unique_ptr delegate_; VaryAllowList vary_allow_list_; LookupResult lookup_result_; diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 54c53fb5f2cf..fa22525396ca 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -5,6 +5,7 @@ #include "source/extensions/filters/http/cache/http_cache.h" #include "test/mocks/http/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" @@ -39,12 +40,13 @@ envoy::extensions::filters::http::cache::v3::CacheConfig getConfig() { class LookupRequestTest : public testing::TestWithParam { public: - LookupRequestTest() : vary_allow_list_(getConfig().allowed_vary_headers()) {} + LookupRequestTest() : vary_allow_list_(getConfig().allowed_vary_headers(), factory_context_) {} DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; Http::TestRequestHeaderMapImpl request_headers_{ {":path", "/"}, {":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}}; + NiceMock factory_context_; VaryAllowList vary_allow_list_; static const SystemTime& currentTime() { diff --git a/test/extensions/http/cache/file_system_http_cache/file_system_http_cache_test.cc b/test/extensions/http/cache/file_system_http_cache/file_system_http_cache_test.cc index 004eb996d33e..dad95f3f046c 100644 --- a/test/extensions/http/cache/file_system_http_cache/file_system_http_cache_test.cc +++ b/test/extensions/http/cache/file_system_http_cache/file_system_http_cache_test.cc @@ -370,7 +370,8 @@ class FileSystemHttpCacheTestWithMockFiles : public FileSystemHttpCacheTest { NiceMock encoder_callbacks_; Event::SimulatedTimeSystem time_system_; Http::TestRequestHeaderMapImpl request_headers_; - VaryAllowList vary_allow_list_{varyAllowListConfig().allowed_vary_headers()}; + NiceMock factory_context_; + VaryAllowList vary_allow_list_{varyAllowListConfig().allowed_vary_headers(), factory_context_}; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; Http::TestResponseHeaderMapImpl response_headers_{ {":status", "200"}, From 52fa026749bb080bacb1eb456f88061caddde473 Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Fri, 15 Mar 2024 08:47:56 -0700 Subject: [PATCH 055/124] Stringmatcher: factory context for rbac, dubbo_proxy (#32920) Signed-off-by: Greg Greenway --- .../filters/common/rbac/engine_impl.cc | 5 +- .../filters/common/rbac/engine_impl.h | 1 + .../filters/common/rbac/matchers.cc | 43 +++--- .../extensions/filters/common/rbac/matchers.h | 64 +++++---- .../extensions/filters/common/rbac/utility.h | 4 +- .../dubbo_proxy/router/route_matcher.cc | 9 +- .../dubbo_proxy/router/route_matcher.h | 6 +- test/extensions/filters/common/rbac/BUILD | 1 + .../filters/common/rbac/engine_impl_test.cc | 67 +++++---- .../filters/common/rbac/matchers_test.cc | 133 +++++++++++------- test/extensions/filters/common/rbac/mocks.h | 3 +- .../filters/http/rbac/rbac_filter_test.cc | 3 +- 12 files changed, 208 insertions(+), 131 deletions(-) diff --git a/source/extensions/filters/common/rbac/engine_impl.cc b/source/extensions/filters/common/rbac/engine_impl.cc index 76d010c562c7..f4b1f9e11bdd 100644 --- a/source/extensions/filters/common/rbac/engine_impl.cc +++ b/source/extensions/filters/common/rbac/engine_impl.cc @@ -42,7 +42,8 @@ void generateLog(StreamInfo::StreamInfo& info, EnforcementMode mode, bool log) { RoleBasedAccessControlEngineImpl::RoleBasedAccessControlEngineImpl( const envoy::config::rbac::v3::RBAC& rules, - ProtobufMessage::ValidationVisitor& validation_visitor, const EnforcementMode mode) + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context, const EnforcementMode mode) : action_(rules.action()), mode_(mode) { // guard expression builder by presence of a condition in policies for (const auto& policy : rules.policies()) { @@ -54,7 +55,7 @@ RoleBasedAccessControlEngineImpl::RoleBasedAccessControlEngineImpl( for (const auto& policy : rules.policies()) { policies_.emplace(policy.first, std::make_unique(policy.second, builder_.get(), - validation_visitor)); + validation_visitor, context)); } } diff --git a/source/extensions/filters/common/rbac/engine_impl.h b/source/extensions/filters/common/rbac/engine_impl.h index c748142c8a97..7f51648caa47 100644 --- a/source/extensions/filters/common/rbac/engine_impl.h +++ b/source/extensions/filters/common/rbac/engine_impl.h @@ -65,6 +65,7 @@ class RoleBasedAccessControlEngineImpl : public RoleBasedAccessControlEngine, No public: RoleBasedAccessControlEngineImpl(const envoy::config::rbac::v3::RBAC& rules, ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context, const EnforcementMode mode = EnforcementMode::Enforced); bool handleAction(const Network::Connection& connection, diff --git a/source/extensions/filters/common/rbac/matchers.cc b/source/extensions/filters/common/rbac/matchers.cc index 5df6db1bfe37..47ffb5ec57b7 100644 --- a/source/extensions/filters/common/rbac/matchers.cc +++ b/source/extensions/filters/common/rbac/matchers.cc @@ -13,12 +13,13 @@ namespace Common { namespace RBAC { MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Permission& permission, - ProtobufMessage::ValidationVisitor& validation_visitor) { + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) { switch (permission.rule_case()) { case envoy::config::rbac::v3::Permission::RuleCase::kAndRules: - return std::make_shared(permission.and_rules(), validation_visitor); + return std::make_shared(permission.and_rules(), validation_visitor, context); case envoy::config::rbac::v3::Permission::RuleCase::kOrRules: - return std::make_shared(permission.or_rules(), validation_visitor); + return std::make_shared(permission.or_rules(), validation_visitor, context); case envoy::config::rbac::v3::Permission::RuleCase::kHeader: return std::make_shared(permission.header()); case envoy::config::rbac::v3::Permission::RuleCase::kDestinationIp: @@ -33,9 +34,10 @@ MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Permission& case envoy::config::rbac::v3::Permission::RuleCase::kMetadata: return std::make_shared(permission.metadata()); case envoy::config::rbac::v3::Permission::RuleCase::kNotRule: - return std::make_shared(permission.not_rule(), validation_visitor); + return std::make_shared(permission.not_rule(), validation_visitor, context); case envoy::config::rbac::v3::Permission::RuleCase::kRequestedServerName: - return std::make_shared(permission.requested_server_name()); + return std::make_shared(permission.requested_server_name(), + context); case envoy::config::rbac::v3::Permission::RuleCase::kUrlPath: return std::make_shared(permission.url_path()); case envoy::config::rbac::v3::Permission::RuleCase::kUriTemplate: { @@ -56,14 +58,15 @@ MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Permission& PANIC_DUE_TO_CORRUPT_ENUM; } -MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Principal& principal) { +MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Principal& principal, + Server::Configuration::CommonFactoryContext& context) { switch (principal.identifier_case()) { case envoy::config::rbac::v3::Principal::IdentifierCase::kAndIds: - return std::make_shared(principal.and_ids()); + return std::make_shared(principal.and_ids(), context); case envoy::config::rbac::v3::Principal::IdentifierCase::kOrIds: - return std::make_shared(principal.or_ids()); + return std::make_shared(principal.or_ids(), context); case envoy::config::rbac::v3::Principal::IdentifierCase::kAuthenticated: - return std::make_shared(principal.authenticated()); + return std::make_shared(principal.authenticated(), context); case envoy::config::rbac::v3::Principal::IdentifierCase::kSourceIp: return std::make_shared(principal.source_ip(), IPMatcher::Type::ConnectionRemote); @@ -80,7 +83,7 @@ MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Principal& case envoy::config::rbac::v3::Principal::IdentifierCase::kMetadata: return std::make_shared(principal.metadata()); case envoy::config::rbac::v3::Principal::IdentifierCase::kNotId: - return std::make_shared(principal.not_id()); + return std::make_shared(principal.not_id(), context); case envoy::config::rbac::v3::Principal::IdentifierCase::kUrlPath: return std::make_shared(principal.url_path()); case envoy::config::rbac::v3::Principal::IdentifierCase::kFilterState: @@ -92,15 +95,17 @@ MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Principal& } AndMatcher::AndMatcher(const envoy::config::rbac::v3::Permission::Set& set, - ProtobufMessage::ValidationVisitor& validation_visitor) { + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) { for (const auto& rule : set.rules()) { - matchers_.push_back(Matcher::create(rule, validation_visitor)); + matchers_.push_back(Matcher::create(rule, validation_visitor, context)); } } -AndMatcher::AndMatcher(const envoy::config::rbac::v3::Principal::Set& set) { +AndMatcher::AndMatcher(const envoy::config::rbac::v3::Principal::Set& set, + Server::Configuration::CommonFactoryContext& context) { for (const auto& id : set.ids()) { - matchers_.push_back(Matcher::create(id)); + matchers_.push_back(Matcher::create(id, context)); } } @@ -117,15 +122,17 @@ bool AndMatcher::matches(const Network::Connection& connection, } OrMatcher::OrMatcher(const Protobuf::RepeatedPtrField& rules, - ProtobufMessage::ValidationVisitor& validation_visitor) { + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) { for (const auto& rule : rules) { - matchers_.push_back(Matcher::create(rule, validation_visitor)); + matchers_.push_back(Matcher::create(rule, validation_visitor, context)); } } -OrMatcher::OrMatcher(const Protobuf::RepeatedPtrField& ids) { +OrMatcher::OrMatcher(const Protobuf::RepeatedPtrField& ids, + Server::Configuration::CommonFactoryContext& context) { for (const auto& id : ids) { - matchers_.push_back(Matcher::create(id)); + matchers_.push_back(Matcher::create(id, context)); } } diff --git a/source/extensions/filters/common/rbac/matchers.h b/source/extensions/filters/common/rbac/matchers.h index b5914f397f3f..bc39bc94c97e 100644 --- a/source/extensions/filters/common/rbac/matchers.h +++ b/source/extensions/filters/common/rbac/matchers.h @@ -49,13 +49,15 @@ class Matcher { * proto message. */ static MatcherConstSharedPtr create(const envoy::config::rbac::v3::Permission& permission, - ProtobufMessage::ValidationVisitor& validation_visitor); + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context); /** * Creates a shared instance of a matcher based off the rules defined in the Principal config * proto message. */ - static MatcherConstSharedPtr create(const envoy::config::rbac::v3::Principal& principal); + static MatcherConstSharedPtr create(const envoy::config::rbac::v3::Principal& principal, + Server::Configuration::CommonFactoryContext& context); }; /** @@ -76,8 +78,10 @@ class AlwaysMatcher : public Matcher { class AndMatcher : public Matcher { public: AndMatcher(const envoy::config::rbac::v3::Permission::Set& rules, - ProtobufMessage::ValidationVisitor& validation_visitor); - AndMatcher(const envoy::config::rbac::v3::Principal::Set& ids); + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context); + AndMatcher(const envoy::config::rbac::v3::Principal::Set& ids, + Server::Configuration::CommonFactoryContext& context); bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; @@ -93,12 +97,17 @@ class AndMatcher : public Matcher { class OrMatcher : public Matcher { public: OrMatcher(const envoy::config::rbac::v3::Permission::Set& set, - ProtobufMessage::ValidationVisitor& validation_visitor) - : OrMatcher(set.rules(), validation_visitor) {} - OrMatcher(const envoy::config::rbac::v3::Principal::Set& set) : OrMatcher(set.ids()) {} + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) + : OrMatcher(set.rules(), validation_visitor, context) {} + OrMatcher(const envoy::config::rbac::v3::Principal::Set& set, + Server::Configuration::CommonFactoryContext& context) + : OrMatcher(set.ids(), context) {} OrMatcher(const Protobuf::RepeatedPtrField& rules, - ProtobufMessage::ValidationVisitor& validation_visitor); - OrMatcher(const Protobuf::RepeatedPtrField& ids); + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context); + OrMatcher(const Protobuf::RepeatedPtrField& ids, + Server::Configuration::CommonFactoryContext& context); bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; @@ -110,10 +119,12 @@ class OrMatcher : public Matcher { class NotMatcher : public Matcher { public: NotMatcher(const envoy::config::rbac::v3::Permission& permission, - ProtobufMessage::ValidationVisitor& validation_visitor) - : matcher_(Matcher::create(permission, validation_visitor)) {} - NotMatcher(const envoy::config::rbac::v3::Principal& principal) - : matcher_(Matcher::create(principal)) {} + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) + : matcher_(Matcher::create(permission, validation_visitor, context)) {} + NotMatcher(const envoy::config::rbac::v3::Principal& principal, + Server::Configuration::CommonFactoryContext& context) + : matcher_(Matcher::create(principal, context)) {} bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; @@ -188,18 +199,19 @@ class PortRangeMatcher : public Matcher { */ class AuthenticatedMatcher : public Matcher { public: - AuthenticatedMatcher(const envoy::config::rbac::v3::Principal::Authenticated& auth) + AuthenticatedMatcher(const envoy::config::rbac::v3::Principal::Authenticated& auth, + Server::Configuration::CommonFactoryContext& context) : matcher_(auth.has_principal_name() - ? absl::make_optional< - Matchers::StringMatcherImpl>( - auth.principal_name()) + ? absl::make_optional>(auth.principal_name(), context) : absl::nullopt) {} bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; private: - const absl::optional> + const absl::optional< + Matchers::StringMatcherImplWithContext> matcher_; }; @@ -211,9 +223,10 @@ class AuthenticatedMatcher : public Matcher { class PolicyMatcher : public Matcher, NonCopyable { public: PolicyMatcher(const envoy::config::rbac::v3::Policy& policy, Expr::Builder* builder, - ProtobufMessage::ValidationVisitor& validation_visitor) - : permissions_(policy.permissions(), validation_visitor), principals_(policy.principals()), - condition_(policy.condition()) { + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) + : permissions_(policy.permissions(), validation_visitor, context), + principals_(policy.principals(), context), condition_(policy.condition()) { if (policy.has_condition()) { expr_ = Expr::createExpression(*builder, condition_); } @@ -258,11 +271,12 @@ class FilterStateMatcher : public Matcher { */ class RequestedServerNameMatcher : public Matcher, - Envoy::Matchers::StringMatcherImpl { + Envoy::Matchers::StringMatcherImplWithContext { public: - RequestedServerNameMatcher(const envoy::type::matcher::v3::StringMatcher& requested_server_name) - : Envoy::Matchers::StringMatcherImpl( - requested_server_name) {} + RequestedServerNameMatcher(const envoy::type::matcher::v3::StringMatcher& requested_server_name, + Server::Configuration::CommonFactoryContext& context) + : Envoy::Matchers::StringMatcherImplWithContext( + requested_server_name, context) {} bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; diff --git a/source/extensions/filters/common/rbac/utility.h b/source/extensions/filters/common/rbac/utility.h index ac2a339226ca..79cd8be7d2bf 100644 --- a/source/extensions/filters/common/rbac/utility.h +++ b/source/extensions/filters/common/rbac/utility.h @@ -51,7 +51,7 @@ createEngine(const ConfigType& config, Server::Configuration::ServerFactoryConte } if (config.has_rules()) { return std::make_unique(config.rules(), validation_visitor, - EnforcementMode::Enforced); + context, EnforcementMode::Enforced); } return nullptr; @@ -71,7 +71,7 @@ createShadowEngine(const ConfigType& config, Server::Configuration::ServerFactor } if (config.has_shadow_rules()) { return std::make_unique( - config.shadow_rules(), validation_visitor, EnforcementMode::Shadow); + config.shadow_rules(), validation_visitor, context, EnforcementMode::Shadow); } return nullptr; diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc index 334236048f21..b58abc50dfc1 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc @@ -166,8 +166,9 @@ ParameterRouteEntryImpl::ParameterData::ParameterData(uint32_t index, } MethodRouteEntryImpl::MethodRouteEntryImpl( - const envoy::extensions::filters::network::dubbo_proxy::v3::Route& route) - : RouteEntryImplBase(route), method_name_(route.match().method().name()) { + const envoy::extensions::filters::network::dubbo_proxy::v3::Route& route, + Server::Configuration::CommonFactoryContext& context) + : RouteEntryImplBase(route), method_name_(route.match().method().name(), context) { if (route.match().method().params_match_size() != 0) { parameter_route_ = std::make_shared(route); } @@ -206,12 +207,12 @@ RouteConstSharedPtr MethodRouteEntryImpl::matches(const MessageMetadata& metadat } SingleRouteMatcherImpl::SingleRouteMatcherImpl(const RouteConfig& config, - Server::Configuration::ServerFactoryContext&) + Server::Configuration::ServerFactoryContext& context) : interface_matcher_(config.interface()), group_(config.group()), version_(config.version()) { using envoy::extensions::filters::network::dubbo_proxy::v3::RouteMatch; for (const auto& route : config.routes()) { - routes_.emplace_back(std::make_shared(route)); + routes_.emplace_back(std::make_shared(route, context)); } ENVOY_LOG(debug, "dubbo route matcher: routes list size {}", routes_.size()); } diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h index 8b37eb2a2f23..b5fbcb2c7004 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h @@ -118,7 +118,8 @@ class ParameterRouteEntryImpl : public RouteEntryImplBase { class MethodRouteEntryImpl : public RouteEntryImplBase { public: - MethodRouteEntryImpl(const envoy::extensions::filters::network::dubbo_proxy::v3::Route& route); + MethodRouteEntryImpl(const envoy::extensions::filters::network::dubbo_proxy::v3::Route& route, + Server::Configuration::CommonFactoryContext& context); ~MethodRouteEntryImpl() override; // RoutEntryImplBase @@ -126,7 +127,8 @@ class MethodRouteEntryImpl : public RouteEntryImplBase { uint64_t random_value) const override; private: - const Matchers::StringMatcherImpl method_name_; + const Matchers::StringMatcherImplWithContext + method_name_; std::shared_ptr parameter_route_; }; diff --git a/test/extensions/filters/common/rbac/BUILD b/test/extensions/filters/common/rbac/BUILD index 3cdc7f4eb9ff..ca59ee1f59d8 100644 --- a/test/extensions/filters/common/rbac/BUILD +++ b/test/extensions/filters/common/rbac/BUILD @@ -22,6 +22,7 @@ envoy_extension_cc_test( "//source/extensions/filters/common/expr:evaluator_lib", "//source/extensions/filters/common/rbac:matchers_lib", "//test/mocks/network:network_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/test_common:utility_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/test/extensions/filters/common/rbac/engine_impl_test.cc b/test/extensions/filters/common/rbac/engine_impl_test.cc index 872336d6744f..957229877617 100644 --- a/test/extensions/filters/common/rbac/engine_impl_test.cc +++ b/test/extensions/filters/common/rbac/engine_impl_test.cc @@ -100,15 +100,16 @@ void onMetadata(NiceMock& info) { } TEST(RoleBasedAccessControlEngineImpl, Disabled) { + Server::Configuration::MockServerFactoryContext factory_context; envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); RBAC::RoleBasedAccessControlEngineImpl engine_allow( - rbac, ProtobufMessage::getStrictValidationVisitor()); + rbac, ProtobufMessage::getStrictValidationVisitor(), factory_context); checkEngine(engine_allow, false, LogResult::Undecided); rbac.set_action(envoy::config::rbac::v3::RBAC::DENY); - RBAC::RoleBasedAccessControlEngineImpl engine_deny(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine_deny( + rbac, ProtobufMessage::getStrictValidationVisitor(), factory_context); checkEngine(engine_deny, true, LogResult::Undecided); } @@ -199,6 +200,7 @@ TEST(RoleBasedAccessControlEngineImpl, InvalidConfig) { } TEST(RoleBasedAccessControlEngineImpl, AllowedAllowlist) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_destination_port(123); policy.add_principals()->set_any(true); @@ -206,8 +208,8 @@ TEST(RoleBasedAccessControlEngineImpl, AllowedAllowlist) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; @@ -223,6 +225,7 @@ TEST(RoleBasedAccessControlEngineImpl, AllowedAllowlist) { } TEST(RoleBasedAccessControlEngineImpl, DeniedDenylist) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_destination_port(123); policy.add_principals()->set_any(true); @@ -230,8 +233,8 @@ TEST(RoleBasedAccessControlEngineImpl, DeniedDenylist) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::DENY); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; @@ -247,6 +250,7 @@ TEST(RoleBasedAccessControlEngineImpl, DeniedDenylist) { } TEST(RoleBasedAccessControlEngineImpl, BasicCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -259,12 +263,13 @@ TEST(RoleBasedAccessControlEngineImpl, BasicCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); checkEngine(engine, false, LogResult::Undecided); } TEST(RoleBasedAccessControlEngineImpl, MalformedCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -282,16 +287,17 @@ TEST(RoleBasedAccessControlEngineImpl, MalformedCondition) { (*rbac.mutable_policies())["foo"] = policy; EXPECT_THROW_WITH_REGEX(RBAC::RoleBasedAccessControlEngineImpl engine( - rbac, ProtobufMessage::getStrictValidationVisitor()), + rbac, ProtobufMessage::getStrictValidationVisitor(), factory_context), EnvoyException, "failed to create an expression: .*"); rbac.set_action(envoy::config::rbac::v3::RBAC::LOG); EXPECT_THROW_WITH_REGEX(RBAC::RoleBasedAccessControlEngineImpl engine_log( - rbac, ProtobufMessage::getStrictValidationVisitor()), + rbac, ProtobufMessage::getStrictValidationVisitor(), factory_context), EnvoyException, "failed to create an expression: .*"); } TEST(RoleBasedAccessControlEngineImpl, MistypedCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -304,12 +310,13 @@ TEST(RoleBasedAccessControlEngineImpl, MistypedCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); checkEngine(engine, false, LogResult::Undecided); } TEST(RoleBasedAccessControlEngineImpl, EvaluationFailure) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -325,12 +332,13 @@ TEST(RoleBasedAccessControlEngineImpl, EvaluationFailure) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); checkEngine(engine, false, LogResult::Undecided); } TEST(RoleBasedAccessControlEngineImpl, ErrorCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -351,12 +359,13 @@ TEST(RoleBasedAccessControlEngineImpl, ErrorCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); checkEngine(engine, false, LogResult::Undecided, Envoy::Network::MockConnection()); } TEST(RoleBasedAccessControlEngineImpl, HeaderCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -382,8 +391,8 @@ TEST(RoleBasedAccessControlEngineImpl, HeaderCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Http::TestRequestHeaderMapImpl headers; Envoy::Http::LowerCaseString key("foo"); @@ -394,6 +403,7 @@ TEST(RoleBasedAccessControlEngineImpl, HeaderCondition) { } TEST(RoleBasedAccessControlEngineImpl, MetadataCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -424,8 +434,8 @@ TEST(RoleBasedAccessControlEngineImpl, MetadataCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Http::TestRequestHeaderMapImpl headers; NiceMock info; @@ -440,6 +450,7 @@ TEST(RoleBasedAccessControlEngineImpl, MetadataCondition) { } TEST(RoleBasedAccessControlEngineImpl, ConjunctiveCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_destination_port(123); policy.add_principals()->set_any(true); @@ -452,8 +463,8 @@ TEST(RoleBasedAccessControlEngineImpl, ConjunctiveCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; @@ -572,17 +583,19 @@ TEST(RoleBasedAccessControlMatcherEngineImpl, DeniedDenylist) { // Log tests TEST(RoleBasedAccessControlEngineImpl, DisabledLog) { + NiceMock factory_context; NiceMock info; onMetadata(info); envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::LOG); - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); checkEngine(engine, true, RBAC::LogResult::No, info); } TEST(RoleBasedAccessControlEngineImpl, LogIfMatched) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_destination_port(123); policy.add_principals()->set_any(true); @@ -590,8 +603,8 @@ TEST(RoleBasedAccessControlEngineImpl, LogIfMatched) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::LOG); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; diff --git a/test/extensions/filters/common/rbac/matchers_test.cc b/test/extensions/filters/common/rbac/matchers_test.cc index 2ef2b577fcea..c64d6ec9818f 100644 --- a/test/extensions/filters/common/rbac/matchers_test.cc +++ b/test/extensions/filters/common/rbac/matchers_test.cc @@ -11,6 +11,7 @@ #include "source/extensions/filters/common/rbac/matchers.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "gmock/gmock.h" @@ -40,11 +41,13 @@ PortRangeMatcher createPortRangeMatcher(envoy::type::v3::Int32Range range) { ret TEST(AlwaysMatcher, AlwaysMatches) { checkMatcher(RBAC::AlwaysMatcher(), true); } TEST(AndMatcher, Permission_Set) { + NiceMock factory_context; envoy::config::rbac::v3::Permission::Set set; envoy::config::rbac::v3::Permission* perm = set.add_rules(); perm->set_any(true); - checkMatcher(RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor()), true); + checkMatcher( + RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), true); perm = set.add_rules(); perm->set_destination_port(123); @@ -56,22 +59,25 @@ TEST(AndMatcher, Permission_Set) { Envoy::Network::Utility::parseInternetAddress("1.2.3.4", 123, false); info.downstream_connection_info_provider_->setLocalAddress(addr); - checkMatcher(RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor()), true, conn, - headers, info); + checkMatcher( + RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), true, + conn, headers, info); addr = Envoy::Network::Utility::parseInternetAddress("1.2.3.4", 8080, false); info.downstream_connection_info_provider_->setLocalAddress(addr); - checkMatcher(RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor()), false, conn, - headers, info); + checkMatcher( + RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), false, + conn, headers, info); } TEST(AndMatcher, Principal_Set) { + NiceMock factory_context; envoy::config::rbac::v3::Principal::Set set; envoy::config::rbac::v3::Principal* principal = set.add_ids(); principal->set_any(true); - checkMatcher(RBAC::AndMatcher(set), true); + checkMatcher(RBAC::AndMatcher(set, factory_context), true); principal = set.add_ids(); auto* cidr = principal->mutable_direct_remote_ip(); @@ -85,15 +91,16 @@ TEST(AndMatcher, Principal_Set) { Envoy::Network::Utility::parseInternetAddress("1.2.3.4", 123, false); info.downstream_connection_info_provider_->setDirectRemoteAddressForTest(addr); - checkMatcher(RBAC::AndMatcher(set), true, conn, headers, info); + checkMatcher(RBAC::AndMatcher(set, factory_context), true, conn, headers, info); addr = Envoy::Network::Utility::parseInternetAddress("1.2.4.6", 123, false); info.downstream_connection_info_provider_->setDirectRemoteAddressForTest(addr); - checkMatcher(RBAC::AndMatcher(set), false, conn, headers, info); + checkMatcher(RBAC::AndMatcher(set, factory_context), false, conn, headers, info); } TEST(OrMatcher, Permission_Set) { + NiceMock factory_context; envoy::config::rbac::v3::Permission::Set set; envoy::config::rbac::v3::Permission* perm = set.add_rules(); perm->set_destination_port(123); @@ -105,21 +112,21 @@ TEST(OrMatcher, Permission_Set) { Envoy::Network::Utility::parseInternetAddress("1.2.3.4", 456, false); info.downstream_connection_info_provider_->setLocalAddress(addr); - checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor()), false, conn, - headers, info); + checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), + false, conn, headers, info); perm = set.add_rules(); perm->mutable_destination_port_range()->set_start(123); perm->mutable_destination_port_range()->set_end(456); - checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor()), false, conn, - headers, info); + checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), + false, conn, headers, info); perm = set.add_rules(); perm->set_any(true); - checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor()), true, conn, - headers, info); + checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), + true, conn, headers, info); } TEST(OrMatcher, Principal_Set) { @@ -129,6 +136,7 @@ TEST(OrMatcher, Principal_Set) { cidr->set_address_prefix("1.2.3.0"); cidr->mutable_prefix_len()->set_value(24); + NiceMock factory_context; Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; NiceMock info; @@ -136,27 +144,31 @@ TEST(OrMatcher, Principal_Set) { Envoy::Network::Utility::parseInternetAddress("1.2.4.6", 456, false); info.downstream_connection_info_provider_->setDirectRemoteAddressForTest(addr); - checkMatcher(RBAC::OrMatcher(set), false, conn, headers, info); + checkMatcher(RBAC::OrMatcher(set, factory_context), false, conn, headers, info); id = set.add_ids(); id->set_any(true); - checkMatcher(RBAC::OrMatcher(set), true, conn, headers, info); + checkMatcher(RBAC::OrMatcher(set, factory_context), true, conn, headers, info); } TEST(NotMatcher, Permission) { + NiceMock factory_context; envoy::config::rbac::v3::Permission perm; perm.set_any(true); - checkMatcher(RBAC::NotMatcher(perm, ProtobufMessage::getStrictValidationVisitor()), false, - Envoy::Network::MockConnection()); + checkMatcher( + RBAC::NotMatcher(perm, ProtobufMessage::getStrictValidationVisitor(), factory_context), false, + Envoy::Network::MockConnection()); } TEST(NotMatcher, Principal) { + NiceMock factory_context; envoy::config::rbac::v3::Principal principal; principal.set_any(true); - checkMatcher(RBAC::NotMatcher(principal), false, Envoy::Network::MockConnection()); + checkMatcher(RBAC::NotMatcher(principal, factory_context), false, + Envoy::Network::MockConnection()); } TEST(HeaderMatcher, HeaderMatcher) { @@ -315,15 +327,16 @@ TEST(AuthenticatedMatcher, uriSanPeerCertificate) { EXPECT_CALL(Const(conn), ssl()).WillRepeatedly(Return(ssl)); // We should check if any URI SAN matches. + NiceMock factory_context; envoy::config::rbac::v3::Principal::Authenticated auth; auth.mutable_principal_name()->set_exact("foo"); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->set_exact("baz"); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->set_exact("bar"); - checkMatcher(AuthenticatedMatcher(auth), false, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), false, conn); } TEST(AuthenticatedMatcher, dnsSanPeerCertificate) { @@ -344,14 +357,15 @@ TEST(AuthenticatedMatcher, dnsSanPeerCertificate) { // We should get check if any DNS SAN matches as URI SAN is not available. envoy::config::rbac::v3::Principal::Authenticated auth; + NiceMock factory_context; auth.mutable_principal_name()->set_exact("foo"); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->set_exact("baz"); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->set_exact("bar"); - checkMatcher(AuthenticatedMatcher(auth), false, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), false, conn); } TEST(AuthenticatedMatcher, subjectPeerCertificate) { @@ -366,11 +380,12 @@ TEST(AuthenticatedMatcher, subjectPeerCertificate) { EXPECT_CALL(Const(conn), ssl()).WillRepeatedly(Return(ssl)); envoy::config::rbac::v3::Principal::Authenticated auth; + NiceMock factory_context; auth.mutable_principal_name()->set_exact("bar"); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->set_exact("foo"); - checkMatcher(AuthenticatedMatcher(auth), false, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), false, conn); } TEST(AuthenticatedMatcher, AnySSLSubject) { @@ -381,16 +396,18 @@ TEST(AuthenticatedMatcher, AnySSLSubject) { EXPECT_CALL(Const(conn), ssl()).WillRepeatedly(Return(ssl)); envoy::config::rbac::v3::Principal::Authenticated auth; - checkMatcher(AuthenticatedMatcher(auth), true, conn); + NiceMock factory_context; + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->MergeFrom(TestUtility::createRegexMatcher(".*")); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); } TEST(AuthenticatedMatcher, NoSSL) { + NiceMock factory_context; Envoy::Network::MockConnection conn; EXPECT_CALL(Const(conn), ssl()).WillOnce(Return(nullptr)); - checkMatcher(AuthenticatedMatcher({}), false, conn); + checkMatcher(AuthenticatedMatcher({}, factory_context), false, conn); } TEST(MetadataMatcher, MetadataMatcher) { @@ -417,6 +434,7 @@ TEST(MetadataMatcher, MetadataMatcher) { } TEST(PolicyMatcher, PolicyMatcher) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_destination_port(123); policy.add_permissions()->set_destination_port(456); @@ -424,7 +442,8 @@ TEST(PolicyMatcher, PolicyMatcher) { policy.add_principals()->mutable_authenticated()->mutable_principal_name()->set_exact("bar"); Expr::BuilderPtr builder = Expr::createBuilder(nullptr); - RBAC::PolicyMatcher matcher(policy, builder.get(), ProtobufMessage::getStrictValidationVisitor()); + RBAC::PolicyMatcher matcher(policy, builder.get(), ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; @@ -457,36 +476,52 @@ TEST(PolicyMatcher, PolicyMatcher) { } TEST(RequestedServerNameMatcher, ValidRequestedServerName) { + NiceMock factory_context; Envoy::Network::MockConnection conn; EXPECT_CALL(conn, requestedServerName()) .Times(9) .WillRepeatedly(Return(absl::string_view("www.cncf.io"))); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*cncf.io")), true, - conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*cncf.*")), true, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher("www.*")), true, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*io")), true, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*")), true, conn); - - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("")), false, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("www.cncf.io")), true, - conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("xyz.cncf.io")), false, - conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("example.com")), false, - conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*cncf.io"), factory_context), + true, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*cncf.*"), factory_context), + true, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createRegexMatcher("www.*"), factory_context), true, + conn); + checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*io"), factory_context), + true, conn); + checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*"), factory_context), + true, conn); + + checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher(""), factory_context), + false, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createExactMatcher("www.cncf.io"), factory_context), + true, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createExactMatcher("xyz.cncf.io"), factory_context), + false, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createExactMatcher("example.com"), factory_context), + false, conn); } TEST(RequestedServerNameMatcher, EmptyRequestedServerName) { + NiceMock factory_context; Envoy::Network::MockConnection conn; EXPECT_CALL(conn, requestedServerName()).Times(3).WillRepeatedly(Return(absl::string_view(""))); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*")), true, conn); + checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*"), factory_context), + true, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("")), true, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("example.com")), false, - conn); + checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher(""), factory_context), + true, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createExactMatcher("example.com"), factory_context), + false, conn); } TEST(PathMatcher, NoPathInHeader) { diff --git a/test/extensions/filters/common/rbac/mocks.h b/test/extensions/filters/common/rbac/mocks.h index 83088d8ab6ef..c1f9ac0916e9 100644 --- a/test/extensions/filters/common/rbac/mocks.h +++ b/test/extensions/filters/common/rbac/mocks.h @@ -17,9 +17,10 @@ namespace RBAC { class MockEngine : public RoleBasedAccessControlEngineImpl { public: MockEngine(const envoy::config::rbac::v3::RBAC& rules, + Server::Configuration::CommonFactoryContext& context, const EnforcementMode mode = EnforcementMode::Enforced) : RoleBasedAccessControlEngineImpl(rules, ProtobufMessage::getStrictValidationVisitor(), - mode){}; + context, mode){}; MOCK_METHOD(bool, handleAction, (const Envoy::Network::Connection&, const Envoy::Http::RequestHeaderMap&, diff --git a/test/extensions/filters/http/rbac/rbac_filter_test.cc b/test/extensions/filters/http/rbac/rbac_filter_test.cc index 29e638179176..c8578f2e2622 100644 --- a/test/extensions/filters/http/rbac/rbac_filter_test.cc +++ b/test/extensions/filters/http/rbac/rbac_filter_test.cc @@ -422,7 +422,8 @@ TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverride) { envoy::extensions::filters::http::rbac::v3::RBACPerRoute route_config; route_config.mutable_rbac()->mutable_rules()->set_action(envoy::config::rbac::v3::RBAC::DENY); - NiceMock engine{route_config.rbac().rules()}; + NiceMock factory_context; + NiceMock engine{route_config.rbac().rules(), factory_context}; NiceMock per_route_config_{route_config, context_}; From 9f6bc78508005f25ef7d7c0d3a24dc85bd4ce004 Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Fri, 15 Mar 2024 08:48:16 -0700 Subject: [PATCH 056/124] Stringmatcher: factory context for ext_authz (#32922) Signed-off-by: Greg Greenway --- .../common/ext_authz/check_request_utils.cc | 18 ++++--- .../common/ext_authz/check_request_utils.h | 6 ++- .../common/ext_authz/ext_authz_http_impl.cc | 48 ++++++++++++------- .../common/ext_authz/ext_authz_http_impl.h | 15 ++++-- .../filters/http/ext_authz/config.cc | 7 ++- .../filters/http/ext_authz/ext_authz.h | 13 ++--- .../extensions/filters/common/ext_authz/BUILD | 2 + .../ext_authz/check_request_utils_test.cc | 5 +- .../ext_authz/ext_authz_http_impl_test.cc | 4 +- test/extensions/filters/http/ext_authz/BUILD | 2 + .../http/ext_authz/ext_authz_fuzz_test.cc | 9 ++-- .../filters/http/ext_authz/ext_authz_test.cc | 31 ++++++------ test/mocks/server/instance.cc | 1 + test/mocks/server/instance.h | 1 + 14 files changed, 98 insertions(+), 64 deletions(-) diff --git a/source/extensions/filters/common/ext_authz/check_request_utils.cc b/source/extensions/filters/common/ext_authz/check_request_utils.cc index 8a62aa1cdc98..0e24523eccb8 100644 --- a/source/extensions/filters/common/ext_authz/check_request_utils.cc +++ b/source/extensions/filters/common/ext_authz/check_request_utils.cc @@ -247,8 +247,9 @@ void CheckRequestUtils::createTcpCheck( MatcherSharedPtr CheckRequestUtils::toRequestMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, - bool add_http_headers) { - std::vector matchers(createStringMatchers(list)); + bool add_http_headers, + Server::Configuration::CommonFactoryContext& context) { + std::vector matchers(createStringMatchers(list, context)); if (add_http_headers) { const std::vector keys{ @@ -259,8 +260,9 @@ CheckRequestUtils::toRequestMatchers(const envoy::type::matcher::v3::ListStringM envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact(key.get()); matchers.push_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); } } @@ -268,12 +270,14 @@ CheckRequestUtils::toRequestMatchers(const envoy::type::matcher::v3::ListStringM } std::vector -CheckRequestUtils::createStringMatchers(const envoy::type::matcher::v3::ListStringMatcher& list) { +CheckRequestUtils::createStringMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context) { std::vector matchers; for (const auto& matcher : list.patterns()) { matchers.push_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); } return matchers; } diff --git a/source/extensions/filters/common/ext_authz/check_request_utils.h b/source/extensions/filters/common/ext_authz/check_request_utils.h index 1390485c0ae0..9f436d2e3857 100644 --- a/source/extensions/filters/common/ext_authz/check_request_utils.h +++ b/source/extensions/filters/common/ext_authz/check_request_utils.h @@ -113,9 +113,11 @@ class CheckRequestUtils { const Protobuf::Map& destination_labels); static MatcherSharedPtr toRequestMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, - bool add_http_headers); + bool add_http_headers, + Server::Configuration::CommonFactoryContext& context); static std::vector - createStringMatchers(const envoy::type::matcher::v3::ListStringMatcher& list); + createStringMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context); private: static void setAttrContextPeer(envoy::service::auth::v3::AttributeContext::Peer& peer, diff --git a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc index bd6bfa0e26bd..101a991dc100 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc +++ b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc @@ -103,17 +103,20 @@ struct SuccessResponse { // Config ClientConfig::ClientConfig(const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& config, - uint32_t timeout, absl::string_view path_prefix) + uint32_t timeout, absl::string_view path_prefix, + Server::Configuration::CommonFactoryContext& context) : client_header_matchers_(toClientMatchers( - config.http_service().authorization_response().allowed_client_headers())), + config.http_service().authorization_response().allowed_client_headers(), context)), client_header_on_success_matchers_(toClientMatchersOnSuccess( - config.http_service().authorization_response().allowed_client_headers_on_success())), + config.http_service().authorization_response().allowed_client_headers_on_success(), + context)), to_dynamic_metadata_matchers_(toDynamicMetadataMatchers( - config.http_service().authorization_response().dynamic_metadata_from_headers())), + config.http_service().authorization_response().dynamic_metadata_from_headers(), context)), upstream_header_matchers_(toUpstreamMatchers( - config.http_service().authorization_response().allowed_upstream_headers())), + config.http_service().authorization_response().allowed_upstream_headers(), context)), upstream_header_to_append_matchers_(toUpstreamMatchers( - config.http_service().authorization_response().allowed_upstream_headers_to_append())), + config.http_service().authorization_response().allowed_upstream_headers_to_append(), + context)), cluster_name_(config.http_service().server_uri().cluster()), timeout_(timeout), path_prefix_(path_prefix), tracing_name_(fmt::format("async {} egress", config.http_service().server_uri().cluster())), @@ -122,20 +125,26 @@ ClientConfig::ClientConfig(const envoy::extensions::filters::http::ext_authz::v3 envoy::config::core::v3::HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD)) {} MatcherSharedPtr -ClientConfig::toClientMatchersOnSuccess(const envoy::type::matcher::v3::ListStringMatcher& list) { - std::vector matchers(CheckRequestUtils::createStringMatchers(list)); +ClientConfig::toClientMatchersOnSuccess(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context) { + std::vector matchers( + CheckRequestUtils::createStringMatchers(list, context)); return std::make_shared(std::move(matchers)); } MatcherSharedPtr -ClientConfig::toDynamicMetadataMatchers(const envoy::type::matcher::v3::ListStringMatcher& list) { - std::vector matchers(CheckRequestUtils::createStringMatchers(list)); +ClientConfig::toDynamicMetadataMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context) { + std::vector matchers( + CheckRequestUtils::createStringMatchers(list, context)); return std::make_shared(std::move(matchers)); } MatcherSharedPtr -ClientConfig::toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher& list) { - std::vector matchers(CheckRequestUtils::createStringMatchers(list)); +ClientConfig::toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context) { + std::vector matchers( + CheckRequestUtils::createStringMatchers(list, context)); // If list is empty, all authorization response headers, except Host, should be added to // the client response. @@ -143,8 +152,9 @@ ClientConfig::toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact(Http::Headers::get().Host.get()); matchers.push_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); return std::make_shared(std::move(matchers)); } @@ -159,16 +169,18 @@ ClientConfig::toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact(key.get()); matchers.push_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); } return std::make_shared(std::move(matchers)); } MatcherSharedPtr -ClientConfig::toUpstreamMatchers(const envoy::type::matcher::v3::ListStringMatcher& list) { - return std::make_unique(CheckRequestUtils::createStringMatchers(list)); +ClientConfig::toUpstreamMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context) { + return std::make_unique(CheckRequestUtils::createStringMatchers(list, context)); } RawHttpClientImpl::RawHttpClientImpl(Upstream::ClusterManager& cm, ClientConfigSharedPtr config) diff --git a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h index db611f4fad73..4211fc9f4ae4 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h +++ b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h @@ -25,7 +25,8 @@ namespace ExtAuthz { class ClientConfig { public: ClientConfig(const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& config, - uint32_t timeout, absl::string_view path_prefix); + uint32_t timeout, absl::string_view path_prefix, + Server::Configuration::CommonFactoryContext& context); /** * Returns the name of the authorization cluster. @@ -87,13 +88,17 @@ class ClientConfig { const Router::HeaderParser& requestHeaderParser() const { return *request_headers_parser_; } private: - static MatcherSharedPtr toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher& list); + static MatcherSharedPtr toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context); static MatcherSharedPtr - toClientMatchersOnSuccess(const envoy::type::matcher::v3::ListStringMatcher& list); + toClientMatchersOnSuccess(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context); static MatcherSharedPtr - toDynamicMetadataMatchers(const envoy::type::matcher::v3::ListStringMatcher& list); + toDynamicMetadataMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context); static MatcherSharedPtr - toUpstreamMatchers(const envoy::type::matcher::v3::ListStringMatcher& list); + toUpstreamMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context); const MatcherSharedPtr request_header_matchers_; const MatcherSharedPtr client_header_matchers_; diff --git a/source/extensions/filters/http/ext_authz/config.cc b/source/extensions/filters/http/ext_authz/config.cc index 881609481e96..87a20cbd12e9 100644 --- a/source/extensions/filters/http/ext_authz/config.cc +++ b/source/extensions/filters/http/ext_authz/config.cc @@ -25,9 +25,8 @@ Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoTyped( const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { auto& server_context = context.serverFactoryContext(); - const auto filter_config = std::make_shared( - proto_config, context.scope(), server_context.runtime(), server_context.httpContext(), - stats_prefix, server_context.bootstrap()); + const auto filter_config = + std::make_shared(proto_config, context.scope(), stats_prefix, server_context); // The callback is created in main thread and executed in worker thread, variables except factory // context must be captured by value into the callback. Http::FilterFactoryCb callback; @@ -38,7 +37,7 @@ Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoTyped( timeout, DefaultTimeout); const auto client_config = std::make_shared( - proto_config, timeout_ms, proto_config.http_service().path_prefix()); + proto_config, timeout_ms, proto_config.http_service().path_prefix(), server_context); callback = [filter_config, client_config, &server_context](Http::FilterChainFactoryCallbacks& callbacks) { auto client = std::make_unique( diff --git a/source/extensions/filters/http/ext_authz/ext_authz.h b/source/extensions/filters/http/ext_authz/ext_authz.h index 7a24e2d84deb..a3d78bd2787c 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.h +++ b/source/extensions/filters/http/ext_authz/ext_authz.h @@ -56,8 +56,8 @@ class FilterConfig { public: FilterConfig(const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& config, - Stats::Scope& scope, Runtime::Loader& runtime, Http::Context& http_context, - const std::string& stats_prefix, envoy::config::bootstrap::v3::Bootstrap& bootstrap) + Stats::Scope& scope, const std::string& stats_prefix, + Server::Configuration::ServerFactoryContext& factory_context) : allow_partial_message_(config.with_request_body().allow_partial_message()), failure_mode_allow_(config.failure_mode_allow()), failure_mode_allow_header_add_(config.failure_mode_allow_header_add()), @@ -70,7 +70,7 @@ class FilterConfig { pack_as_bytes_(config.has_http_service() || config.with_request_body().pack_as_bytes()), status_on_error_(toErrorCode(config.status_on_error().code())), scope_(scope), - runtime_(runtime), http_context_(http_context), + runtime_(factory_context.runtime()), http_context_(factory_context.httpContext()), filter_enabled_(config.has_filter_enabled() ? absl::optional( Runtime::FractionalPercent(config.filter_enabled(), runtime_)) @@ -103,6 +103,7 @@ class FilterConfig { ext_authz_error_(pool_.add(createPoolStatName(config.stat_prefix(), "error"))), ext_authz_failure_mode_allowed_( pool_.add(createPoolStatName(config.stat_prefix(), "failure_mode_allowed"))) { + auto bootstrap = factory_context.bootstrap(); auto labels_key_it = bootstrap.node().metadata().fields().find(config.bootstrap_metadata_labels_key()); if (labels_key_it != bootstrap.node().metadata().fields().end()) { @@ -123,14 +124,14 @@ class FilterConfig { // Method, Path and Host). if (config.has_grpc_service() && config.has_allowed_headers()) { request_header_matchers_ = Filters::Common::ExtAuthz::CheckRequestUtils::toRequestMatchers( - config.allowed_headers(), false); + config.allowed_headers(), false, factory_context); } else if (config.has_http_service()) { if (config.http_service().authorization_request().has_allowed_headers()) { request_header_matchers_ = Filters::Common::ExtAuthz::CheckRequestUtils::toRequestMatchers( - config.http_service().authorization_request().allowed_headers(), true); + config.http_service().authorization_request().allowed_headers(), true, factory_context); } else { request_header_matchers_ = Filters::Common::ExtAuthz::CheckRequestUtils::toRequestMatchers( - config.allowed_headers(), true); + config.allowed_headers(), true, factory_context); } } } diff --git a/test/extensions/filters/common/ext_authz/BUILD b/test/extensions/filters/common/ext_authz/BUILD index 5c87d0b63ad4..2802d9397a4f 100644 --- a/test/extensions/filters/common/ext_authz/BUILD +++ b/test/extensions/filters/common/ext_authz/BUILD @@ -19,6 +19,7 @@ envoy_cc_test( "//source/extensions/filters/common/ext_authz:ext_authz_interface", "//test/mocks/http:http_mocks", "//test/mocks/network:network_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/mocks/stream_info:stream_info_mocks", "//test/test_common:utility_lib", @@ -45,6 +46,7 @@ envoy_cc_test( deps = [ "//source/extensions/filters/common/ext_authz:ext_authz_http_lib", "//test/extensions/filters/common/ext_authz:ext_authz_test_common", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/stream_info:stream_info_mocks", "//test/mocks/upstream:cluster_manager_mocks", "@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg_cc_proto", diff --git a/test/extensions/filters/common/ext_authz/check_request_utils_test.cc b/test/extensions/filters/common/ext_authz/check_request_utils_test.cc index 9486088ba523..e87ede32c7ae 100644 --- a/test/extensions/filters/common/ext_authz/check_request_utils_test.cc +++ b/test/extensions/filters/common/ext_authz/check_request_utils_test.cc @@ -10,6 +10,7 @@ #include "test/mocks/http/mocks.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stream_info/mocks.h" @@ -110,11 +111,13 @@ class CheckRequestUtilsTest : public testing::Test { } MatcherSharedPtr createRequestHeaderMatchers() { + NiceMock factory_context; envoy::extensions::filters::http::ext_authz::v3::ExtAuthz ext_autz_proto_; ext_autz_proto_.mutable_allowed_headers()->add_patterns()->set_exact("foo"); ext_autz_proto_.mutable_allowed_headers()->add_patterns()->set_exact("hello"); ext_autz_proto_.mutable_allowed_headers()->add_patterns()->set_exact("duplicate"); - return CheckRequestUtils::toRequestMatchers(ext_autz_proto_.allowed_headers(), false); + return CheckRequestUtils::toRequestMatchers(ext_autz_proto_.allowed_headers(), false, + factory_context); } Network::Address::InstanceConstSharedPtr addr_; diff --git a/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc b/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc index e7ca63a9a97b..6322721b6729 100644 --- a/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc +++ b/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc @@ -10,6 +10,7 @@ #include "test/extensions/filters/common/ext_authz/mocks.h" #include "test/extensions/filters/common/ext_authz/test_common.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -90,7 +91,7 @@ class ExtAuthzHttpClientTest : public testing::Test { } cm_.initializeThreadLocalClusters({"ext_authz"}); - return std::make_shared(proto_config, timeout, path_prefix); + return std::make_shared(proto_config, timeout, path_prefix, factory_context_); } void dynamicMetadataTest(CheckStatus status, const std::string& http_status) { @@ -178,6 +179,7 @@ class ExtAuthzHttpClientTest : public testing::Test { return message_ptr; } + NiceMock factory_context_; NiceMock cm_; NiceMock async_client_; NiceMock async_request_; diff --git a/test/extensions/filters/http/ext_authz/BUILD b/test/extensions/filters/http/ext_authz/BUILD index 6aaad4beb600..3ba58efb6997 100644 --- a/test/extensions/filters/http/ext_authz/BUILD +++ b/test/extensions/filters/http/ext_authz/BUILD @@ -32,6 +32,7 @@ envoy_extension_cc_test( "//test/mocks/http:http_mocks", "//test/mocks/network:network_mocks", "//test/mocks/runtime:runtime_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/tracing:tracing_mocks", "//test/mocks/upstream:cluster_manager_mocks", "//test/proto:helloworld_proto_cc_proto", @@ -110,6 +111,7 @@ envoy_cc_fuzz_test( "//test/mocks/http:http_mocks", "//test/mocks/network:network_mocks", "//test/mocks/runtime:runtime_mocks", + "//test/mocks/server:server_factory_context_mocks", "@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg_cc_proto", ], ) diff --git a/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc index d8723088ef81..7745a37cb4d5 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc @@ -11,6 +11,7 @@ #include "test/mocks/http/mocks.h" #include "test/mocks/network/mocks.h" #include "test/mocks/runtime/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "gmock/gmock.h" @@ -74,7 +75,7 @@ class FuzzerMocks { filter_metadata_.CopyFrom(metadata); } - NiceMock runtime_; + NiceMock factory_context_; NiceMock decoder_callbacks_; NiceMock encoder_callbacks_; Network::Address::InstanceConstSharedPtr addr_; @@ -95,16 +96,14 @@ DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzT static FuzzerMocks mocks; NiceMock stats_store; static ScopedInjectableLoader engine(std::make_unique()); - envoy::config::bootstrap::v3::Bootstrap bootstrap; - Http::ContextImpl http_context(stats_store.symbolTable()); // Prepare filter. const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz proto_config = input.config(); FilterConfigSharedPtr config; try { - config = std::make_shared(proto_config, *stats_store.rootScope(), mocks.runtime_, - http_context, "ext_authz_prefix", bootstrap); + config = std::make_shared(proto_config, *stats_store.rootScope(), + "ext_authz_prefix", mocks.factory_context_); } catch (const EnvoyException& e) { ENVOY_LOG_MISC(debug, "EnvoyException during filter config validation: {}", e.what()); return; 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 b7164fa12778..6b66f26bda40 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -24,6 +24,7 @@ #include "test/mocks/network/mocks.h" #include "test/mocks/router/mocks.h" #include "test/mocks/runtime/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/tracing/mocks.h" #include "test/mocks/upstream/cluster_manager.h" #include "test/proto/helloworld.pb.h" @@ -54,15 +55,15 @@ namespace { template class HttpFilterTestBase : public T { public: - HttpFilterTestBase() : http_context_(stats_store_.symbolTable()) {} + HttpFilterTestBase() {} void initialize(std::string&& yaml) { envoy::extensions::filters::http::ext_authz::v3::ExtAuthz proto_config{}; if (!yaml.empty()) { TestUtility::loadFromYaml(yaml, proto_config); } - config_ = std::make_shared(proto_config, *stats_store_.rootScope(), runtime_, - http_context_, "ext_authz_prefix", bootstrap_); + config_ = std::make_shared(proto_config, *stats_store_.rootScope(), + "ext_authz_prefix", factory_context_); client_ = new Filters::Common::ExtAuthz::MockClient(); filter_ = std::make_unique(config_, Filters::Common::ExtAuthz::ClientPtr{client_}); filter_->setDecoderFilterCallbacks(decoder_filter_callbacks_); @@ -122,7 +123,6 @@ template class HttpFilterTestBase : public T { NiceMock stats_store_; Stats::Scope& stats_scope_{*stats_store_.rootScope()}; - envoy::config::bootstrap::v3::Bootstrap bootstrap_; FilterConfigSharedPtr config_; Filters::Common::ExtAuthz::MockClient* client_; std::unique_ptr filter_; @@ -132,11 +132,10 @@ template class HttpFilterTestBase : public T { Http::TestRequestHeaderMapImpl request_headers_; Http::TestRequestTrailerMapImpl request_trailers_; Buffer::OwnedImpl data_; - NiceMock runtime_; + NiceMock factory_context_; NiceMock cm_; Network::Address::InstanceConstSharedPtr addr_; NiceMock connection_; - Http::ContextImpl http_context_; }; class HttpFilterTest : public HttpFilterTestBase { @@ -1693,7 +1692,7 @@ TEST_F(HttpFilterTest, FilterDisabled) { denominator: HUNDRED )EOF"); - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(0)))) .WillByDefault(Return(false)); @@ -1720,7 +1719,7 @@ TEST_F(HttpFilterTest, FilterEnabled) { prepareCheck(); - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(100)))) .WillByDefault(Return(true)); @@ -1824,7 +1823,7 @@ TEST_F(HttpFilterTest, FilterEnabledButMetadataDisabled) { )EOF"); // Enable in filter_enabled. - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(100)))) .WillByDefault(Return(true)); @@ -1869,7 +1868,7 @@ TEST_F(HttpFilterTest, FilterDisabledButMetadataEnabled) { )EOF"); // Disable in filter_enabled. - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(0)))) .WillByDefault(Return(false)); @@ -1914,7 +1913,7 @@ TEST_F(HttpFilterTest, FilterEnabledAndMetadataEnabled) { )EOF"); // Enable in filter_enabled. - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(100)))) .WillByDefault(Return(true)); @@ -1957,12 +1956,13 @@ TEST_F(HttpFilterTest, FilterDenyAtDisable) { value: true )EOF"); - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(0)))) .WillByDefault(Return(false)); - ON_CALL(runtime_.snapshot_, featureEnabled("http.ext_authz.enabled", false)) + ON_CALL(factory_context_.runtime_loader_.snapshot_, + featureEnabled("http.ext_authz.enabled", false)) .WillByDefault(Return(true)); // Make sure check is not called. @@ -1990,12 +1990,13 @@ TEST_F(HttpFilterTest, FilterAllowAtDisable) { value: false )EOF"); - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(0)))) .WillByDefault(Return(false)); - ON_CALL(runtime_.snapshot_, featureEnabled("http.ext_authz.enabled", false)) + ON_CALL(factory_context_.runtime_loader_.snapshot_, + featureEnabled("http.ext_authz.enabled", false)) .WillByDefault(Return(false)); // Make sure check is not called. diff --git a/test/mocks/server/instance.cc b/test/mocks/server/instance.cc index eca664bd89a8..ebee702e5dbb 100644 --- a/test/mocks/server/instance.cc +++ b/test/mocks/server/instance.cc @@ -48,6 +48,7 @@ MockInstance::MockInstance() ON_CALL(*this, overloadManager()).WillByDefault(ReturnRef(overload_manager_)); ON_CALL(*this, messageValidationContext()).WillByDefault(ReturnRef(validation_context_)); ON_CALL(*this, statsConfig()).WillByDefault(ReturnRef(*stats_config_)); + ON_CALL(*this, regexEngine()).WillByDefault(ReturnRef(regex_engine_)); ON_CALL(*this, serverFactoryContext()).WillByDefault(ReturnRef(*server_factory_context_)); ON_CALL(*this, transportSocketFactoryContext()) .WillByDefault(ReturnRef(*transport_socket_factory_context_)); diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index de109c9c5a97..b5418fd5f6ab 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -102,6 +102,7 @@ class MockInstance : public Instance { std::shared_ptr> transport_socket_factory_context_; Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_; + Regex::GoogleReEngine regex_engine_; }; } // namespace Server From fa21d1148cc6531e0c0203fbcae55b18d47f802b Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Fri, 15 Mar 2024 09:09:40 -0700 Subject: [PATCH 057/124] stringmatcher: import updated xds types and support custom extension (#32856) Signed-off-by: Greg Greenway --- api/bazel/repository_locations.bzl | 6 +-- source/common/common/matchers.h | 78 ++++++++++-------------------- 2 files changed, 28 insertions(+), 56 deletions(-) diff --git a/api/bazel/repository_locations.bzl b/api/bazel/repository_locations.bzl index 4ba96d3a72f0..902923662e0d 100644 --- a/api/bazel/repository_locations.bzl +++ b/api/bazel/repository_locations.bzl @@ -39,9 +39,9 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_desc = "xDS API Working Group (xDS-WG)", project_url = "https://github.com/cncf/xds", # During the UDPA -> xDS migration, we aren't working with releases. - version = "3a472e524827f72d1ad621c4983dd5af54c46776", - sha256 = "dc305e20c9fa80822322271b50aa2ffa917bf4fd3973bcec52bfc28dc32c5927", - release_date = "2023-11-16", + version = "ee0267137e252710af66562e0d54bcf8669b74b1", + sha256 = "0adcd7a74d5158f710612f38e5b9ec8bd4aabd2f53ff7905e0c198028ca68dfc", + release_date = "2024-03-12", strip_prefix = "xds-{version}", urls = ["https://github.com/cncf/xds/archive/{version}.tar.gz"], use_category = ["api"], diff --git a/source/common/common/matchers.h b/source/common/common/matchers.h index 49b68c705a42..1ee93a032295 100644 --- a/source/common/common/matchers.h +++ b/source/common/common/matchers.h @@ -114,13 +114,35 @@ class PrivateStringMatcherImpl : public ValueMatcher, public StringMatcher { // Cache the lowercase conversion of the Contains matcher for future use lowercase_contains_match_ = absl::AsciiStrToLower(matcher_.contains()); } - } else { - initialize(matcher, tls, api); + } else if (matcher.has_custom()) { + custom_ = getExtensionStringMatcher(matcher.custom(), *tls, *api); } } // StringMatcher - bool match(const absl::string_view value) const override { return match(value, matcher_); } + bool match(const absl::string_view value) const override { + switch (matcher_.match_pattern_case()) { + case StringMatcherType::MatchPatternCase::kExact: + return matcher_.ignore_case() ? absl::EqualsIgnoreCase(value, matcher_.exact()) + : value == matcher_.exact(); + case StringMatcherType::MatchPatternCase::kPrefix: + return matcher_.ignore_case() ? absl::StartsWithIgnoreCase(value, matcher_.prefix()) + : absl::StartsWith(value, matcher_.prefix()); + case StringMatcherType::MatchPatternCase::kSuffix: + return matcher_.ignore_case() ? absl::EndsWithIgnoreCase(value, matcher_.suffix()) + : absl::EndsWith(value, matcher_.suffix()); + case StringMatcherType::MatchPatternCase::kContains: + return matcher_.ignore_case() + ? absl::StrContains(absl::AsciiStrToLower(value), lowercase_contains_match_) + : absl::StrContains(value, matcher_.contains()); + case StringMatcherType::MatchPatternCase::kSafeRegex: + return regex_->match(value); + case StringMatcherType::MatchPatternCase::kCustom: + return custom_->match(value); + default: + PANIC("unexpected"); + } + } bool match(const ProtobufWkt::Value& value) const override { @@ -151,56 +173,6 @@ class PrivateStringMatcherImpl : public ValueMatcher, public StringMatcher { } private: - // Type `xds::type::matcher::v3::StringMatcher` doesn't have an extension type, so use function - // overloading to only handle that case for type `envoy::type::matcher::v3::StringMatcher` to - // prevent compilation errors on use of `kCustom`. - - void initialize(const xds::type::matcher::v3::StringMatcher&, ThreadLocal::SlotAllocator*, - Api::Api*) {} - - void initialize(const envoy::type::matcher::v3::StringMatcher& matcher, - ThreadLocal::SlotAllocator* tls, Api::Api* api) { - if (matcher.has_custom()) { - custom_ = getExtensionStringMatcher(matcher.custom(), *tls, *api); - } - } - - bool match(const absl::string_view value, const xds::type::matcher::v3::StringMatcher&) const { - return matchCommon(value); - } - - bool match(const absl::string_view value, - const envoy::type::matcher::v3::StringMatcher& matcher) const { - if (matcher.match_pattern_case() == - envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kCustom) { - return custom_->match(value); - } - return matchCommon(value); - } - - // StringMatcher - bool matchCommon(const absl::string_view value) const { - switch (matcher_.match_pattern_case()) { - case StringMatcherType::MatchPatternCase::kExact: - return matcher_.ignore_case() ? absl::EqualsIgnoreCase(value, matcher_.exact()) - : value == matcher_.exact(); - case StringMatcherType::MatchPatternCase::kPrefix: - return matcher_.ignore_case() ? absl::StartsWithIgnoreCase(value, matcher_.prefix()) - : absl::StartsWith(value, matcher_.prefix()); - case StringMatcherType::MatchPatternCase::kSuffix: - return matcher_.ignore_case() ? absl::EndsWithIgnoreCase(value, matcher_.suffix()) - : absl::EndsWith(value, matcher_.suffix()); - case StringMatcherType::MatchPatternCase::kContains: - return matcher_.ignore_case() - ? absl::StrContains(absl::AsciiStrToLower(value), lowercase_contains_match_) - : absl::StrContains(value, matcher_.contains()); - case StringMatcherType::MatchPatternCase::kSafeRegex: - return regex_->match(value); - default: - PANIC("unexpected"); - } - } - const StringMatcherType matcher_; Regex::CompiledMatcherPtr regex_; std::string lowercase_contains_match_; From e8ea33a86a0271ff410659ae3755dc513222322a Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Sat, 16 Mar 2024 01:14:10 +0800 Subject: [PATCH 058/124] io_uring: add io_uring socket handle (#32265) * io_uring: add io_uring socket io handle Signed-off-by: Xie Zhihao --- source/common/io/io_uring_worker_impl.h | 2 + source/common/network/BUILD | 24 +- .../network/io_uring_socket_handle_impl.cc | 415 ++++++++ .../network/io_uring_socket_handle_impl.h | 94 ++ .../common/network/socket_interface_impl.cc | 28 +- source/common/network/socket_interface_impl.h | 6 +- test/common/network/BUILD | 31 + ...ing_socket_handle_impl_integration_test.cc | 973 ++++++++++++++++++ .../io_uring_socket_handle_impl_test.cc | 133 +++ tools/spelling/spelling_dictionary.txt | 1 + 10 files changed, 1700 insertions(+), 7 deletions(-) create mode 100644 source/common/network/io_uring_socket_handle_impl.cc create mode 100644 source/common/network/io_uring_socket_handle_impl.h create mode 100644 test/common/network/io_uring_socket_handle_impl_integration_test.cc create mode 100644 test/common/network/io_uring_socket_handle_impl_test.cc diff --git a/source/common/io/io_uring_worker_impl.h b/source/common/io/io_uring_worker_impl.h index 1aa2222592d7..b2ebd1d38049 100644 --- a/source/common/io/io_uring_worker_impl.h +++ b/source/common/io/io_uring_worker_impl.h @@ -201,6 +201,8 @@ class IoUringServerSocket : public IoUringSocketEntry { void onShutdown(Request* req, int32_t result, bool injected) override; void onCancel(Request* req, int32_t result, bool injected) override; + Buffer::OwnedImpl& getReadBuffer() { return read_buf_; } + protected: // Since the write of IoUringSocket is async, there may have write request is on the fly when // close the socket. This timeout is setting for a time to wait the write request done. diff --git a/source/common/network/BUILD b/source/common/network/BUILD index 53ca953779a4..4268d592f29c 100644 --- a/source/common/network/BUILD +++ b/source/common/network/BUILD @@ -250,25 +250,43 @@ envoy_cc_library( "io_socket_handle_impl.cc", "socket_interface_impl.cc", "win32_socket_handle_impl.cc", - ], + ] + select({ + "//bazel:linux": [ + "io_uring_socket_handle_impl.cc", + ], + "//conditions:default": [], + }), hdrs = [ "io_socket_handle_base_impl.h", "io_socket_handle_impl.h", "socket_interface_impl.h", "win32_socket_handle_impl.h", - ], + ] + select({ + "//bazel:linux": [ + "io_uring_socket_handle_impl.h", + ], + "//conditions:default": [], + }), deps = [ ":address_lib", ":io_socket_error_lib", ":socket_interface_lib", ":socket_lib", + "//envoy/common/io:io_uring_interface", "//envoy/event:dispatcher_interface", "//envoy/network:io_handle_interface", "//source/common/api:os_sys_calls_lib", "//source/common/buffer:buffer_lib", "//source/common/event:dispatcher_includes", "@envoy_api//envoy/extensions/network/socket_interface/v3:pkg_cc_proto", - ], + ] + select({ + "//bazel:linux": [ + "//source/common/io:io_uring_impl_lib", + "//source/common/io:io_uring_worker_factory_impl_lib", + "//source/common/io:io_uring_worker_lib", + ], + "//conditions:default": [], + }), alwayslink = LEGACY_ALWAYSLINK, ) diff --git a/source/common/network/io_uring_socket_handle_impl.cc b/source/common/network/io_uring_socket_handle_impl.cc new file mode 100644 index 000000000000..330a250edd61 --- /dev/null +++ b/source/common/network/io_uring_socket_handle_impl.cc @@ -0,0 +1,415 @@ +#include "source/common/network/io_uring_socket_handle_impl.h" + +#include "envoy/buffer/buffer.h" +#include "envoy/common/exception.h" +#include "envoy/event/dispatcher.h" + +#include "source/common/api/os_sys_calls_impl.h" +#include "source/common/common/assert.h" +#include "source/common/common/utility.h" +#include "source/common/io/io_uring_worker_impl.h" +#include "source/common/network/address_impl.h" +#include "source/common/network/io_socket_error_impl.h" +#include "source/common/network/io_socket_handle_impl.h" +#include "source/common/network/socket_interface_impl.h" + +namespace Envoy { +namespace Network { + +IoUringSocketHandleImpl::IoUringSocketHandleImpl(Io::IoUringWorkerFactory& io_uring_worker_factory, + os_fd_t fd, bool socket_v6only, + absl::optional domain, bool is_server_socket) + : IoSocketHandleBaseImpl(fd, socket_v6only, domain), + io_uring_worker_factory_(io_uring_worker_factory), + io_uring_socket_type_(is_server_socket ? IoUringSocketType::Server + : IoUringSocketType::Unknown) { + ENVOY_LOG(trace, "construct io uring socket handle, fd = {}, type = {}", fd_, + ioUringSocketTypeStr()); +} + +IoUringSocketHandleImpl::~IoUringSocketHandleImpl() { + ENVOY_LOG(trace, "~IoUringSocketHandleImpl, type = {}", ioUringSocketTypeStr()); + if (SOCKET_VALID(fd_)) { + // If the socket is owned by the main thread like a listener, it may outlive the IoUringWorker. + // We have to ensure that the current thread has been registered and the io_uring in the thread + // is still available. + // TODO(zhxie): for current usage of server socket and client socket, the check may be + // redundant. + if (io_uring_socket_type_ != IoUringSocketType::Unknown && + io_uring_socket_type_ != IoUringSocketType::Accept && + io_uring_worker_factory_.currentThreadRegistered() && io_uring_socket_.has_value()) { + if (io_uring_socket_->getStatus() != Io::IoUringSocketStatus::Closed) { + io_uring_socket_.ref().close(false); + } + } else { + // The TLS slot has been shut down by this moment with io_uring wiped out, thus better use the + // POSIX system call instead of IoUringSocketHandleImpl::close(). + ::close(fd_); + } + } +} + +Api::IoCallUint64Result IoUringSocketHandleImpl::close() { + ENVOY_LOG(trace, "close, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + ASSERT(SOCKET_VALID(fd_)); + + if (io_uring_socket_type_ == IoUringSocketType::Unknown || + io_uring_socket_type_ == IoUringSocketType::Accept || !io_uring_socket_.has_value()) { + if (file_event_) { + file_event_.reset(); + } + ::close(fd_); + } else { + io_uring_socket_.ref().close(false); + io_uring_socket_.reset(); + } + SET_SOCKET_INVALID(fd_); + return Api::ioCallUint64ResultNoError(); +} + +Api::IoCallUint64Result +IoUringSocketHandleImpl::readv(uint64_t max_length, Buffer::RawSlice* slices, uint64_t num_slice) { + ENVOY_LOG(debug, "readv, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + Api::IoCallUint64Result result = copyOut(max_length, slices, num_slice); + if (result.ok()) { + // If the return value is 0, there should be a remote close. Return the value directly. + if (result.return_value_ != 0) { + io_uring_socket_->getReadParam()->buf_.drain(result.return_value_); + } + } + return result; +} + +Api::IoCallUint64Result IoUringSocketHandleImpl::read(Buffer::Instance& buffer, + absl::optional max_length_opt) { + ENVOY_LOG(trace, "read, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + auto read_result = checkReadResult(); + if (read_result.has_value()) { + return std::move(*read_result); + } + + const OptRef& read_param = io_uring_socket_->getReadParam(); + uint64_t max_read_length = + std::min(max_length_opt.value_or(UINT64_MAX), read_param->buf_.length()); + buffer.move(read_param->buf_, max_read_length); + return {max_read_length, IoSocketError::none()}; +} + +Api::IoCallUint64Result IoUringSocketHandleImpl::writev(const Buffer::RawSlice* slices, + uint64_t num_slice) { + ENVOY_LOG(trace, "writev, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + auto write_result = checkWriteResult(); + if (write_result.has_value()) { + return std::move(*write_result); + } + + uint64_t ret = io_uring_socket_->write(slices, num_slice); + return {ret, IoSocketError::none()}; +} + +Api::IoCallUint64Result IoUringSocketHandleImpl::write(Buffer::Instance& buffer) { + ENVOY_LOG(trace, "write {}, fd = {}, type = {}", buffer.length(), fd_, ioUringSocketTypeStr()); + + auto write_result = checkWriteResult(); + if (write_result.has_value()) { + return std::move(*write_result); + } + + uint64_t buffer_size = buffer.length(); + io_uring_socket_->write(buffer); + return {buffer_size, IoSocketError::none()}; +} + +Api::IoCallUint64Result IoUringSocketHandleImpl::sendmsg(const Buffer::RawSlice*, uint64_t, int, + const Address::Ip*, + const Address::Instance&) { + ENVOY_LOG(trace, "sendmsg, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + return Network::IoSocketError::ioResultSocketInvalidAddress(); +} + +Api::IoCallUint64Result IoUringSocketHandleImpl::recvmsg(Buffer::RawSlice*, const uint64_t, + uint32_t, RecvMsgOutput&) { + ENVOY_LOG(trace, "recvmsg, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + return Network::IoSocketError::ioResultSocketInvalidAddress(); +} + +Api::IoCallUint64Result IoUringSocketHandleImpl::recvmmsg(RawSliceArrays&, uint32_t, + RecvMsgOutput&) { + ENVOY_LOG(trace, "recvmmsg, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + return Network::IoSocketError::ioResultSocketInvalidAddress(); +} + +Api::IoCallUint64Result IoUringSocketHandleImpl::recv(void* buffer, size_t length, int flags) { + ASSERT(io_uring_socket_.has_value()); + ENVOY_LOG(trace, "recv, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + // The only used flag in Envoy is MSG_PEEK for listener filters, including TLS inspectors. + ASSERT(flags == 0 || flags == MSG_PEEK); + Buffer::RawSlice slice; + slice.mem_ = buffer; + slice.len_ = length; + if (flags == 0) { + return readv(length, &slice, 1); + } + + return copyOut(length, &slice, 1); +} + +Api::SysCallIntResult IoUringSocketHandleImpl::bind(Address::InstanceConstSharedPtr address) { + ENVOY_LOG(trace, "bind {}, fd = {}, io_uring_socket_type = {}", address->asString(), fd_, + ioUringSocketTypeStr()); + return Api::OsSysCallsSingleton::get().bind(fd_, address->sockAddr(), address->sockAddrLen()); +} + +Api::SysCallIntResult IoUringSocketHandleImpl::listen(int backlog) { + ENVOY_LOG(trace, "listen, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + ASSERT(io_uring_socket_type_ == IoUringSocketType::Unknown); + + io_uring_socket_type_ = IoUringSocketType::Accept; + setBlocking(false); + return Api::OsSysCallsSingleton::get().listen(fd_, backlog); +} + +IoHandlePtr IoUringSocketHandleImpl::accept(struct sockaddr* addr, socklen_t* addrlen) { + ENVOY_LOG(trace, "accept, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + ASSERT(io_uring_socket_type_ == IoUringSocketType::Accept); + + auto result = Api::OsSysCallsSingleton::get().accept(fd_, addr, addrlen); + if (SOCKET_INVALID(result.return_value_)) { + return nullptr; + } + return std::make_unique(io_uring_worker_factory_, result.return_value_, + socket_v6only_, domain_, true); +} + +Api::SysCallIntResult IoUringSocketHandleImpl::connect(Address::InstanceConstSharedPtr address) { + ENVOY_LOG(trace, "connect, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + ASSERT(io_uring_socket_type_ == IoUringSocketType::Client); + + io_uring_socket_->connect(address); + return Api::SysCallIntResult{-1, EINPROGRESS}; +} + +Api::SysCallIntResult IoUringSocketHandleImpl::getOption(int level, int optname, void* optval, + socklen_t* optlen) { + // io_uring socket does not populate connect error in getsockopt. Instead, the connect error is + // returned in onConnect() handling. We will imitate the default socket behavior here for client + // socket with optname SO_ERROR, which is only used to check connect error. + if (io_uring_socket_type_ == IoUringSocketType::Client && optname == SO_ERROR && + io_uring_socket_.has_value()) { + int* intval = static_cast(optval); + *intval = -io_uring_socket_->getWriteParam()->result_; + *optlen = sizeof(int); + return {0, 0}; + } + + return IoSocketHandleBaseImpl::getOption(level, optname, optval, optlen); +} + +IoHandlePtr IoUringSocketHandleImpl::duplicate() { + ENVOY_LOG(trace, "duplicate, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + Api::SysCallSocketResult result = Api::OsSysCallsSingleton::get().duplicate(fd_); + RELEASE_ASSERT(result.return_value_ != -1, + fmt::format("duplicate failed for '{}': ({}) {}", fd_, result.errno_, + errorDetails(result.errno_))); + return SocketInterfaceImpl::makePlatformSpecificSocket(result.return_value_, socket_v6only_, + domain_, &io_uring_worker_factory_); +} + +void IoUringSocketHandleImpl::initializeFileEvent(Event::Dispatcher& dispatcher, + Event::FileReadyCb cb, + Event::FileTriggerType trigger, uint32_t events) { + ENVOY_LOG(trace, "initialize file event, fd = {}, type = {}, has socket = {}", fd_, + ioUringSocketTypeStr(), io_uring_socket_.has_value()); + + // The IoUringSocket has already been created. It usually happened after a resetFileEvents. + if (io_uring_socket_.has_value()) { + if (&io_uring_socket_->getIoUringWorker().dispatcher() == + &io_uring_worker_factory_.getIoUringWorker()->dispatcher()) { + io_uring_socket_->setFileReadyCb(std::move(cb)); + io_uring_socket_->enableRead(); + io_uring_socket_->enableCloseEvent(events & Event::FileReadyType::Closed); + } else { + ENVOY_LOG(trace, "initialize file event from another thread, fd = {}, type = {}", fd_, + ioUringSocketTypeStr()); + Thread::CondVar wait_cv; + Thread::MutexBasicLockable mutex; + Buffer::OwnedImpl buf; + os_fd_t fd = io_uring_socket_->fd(); + + { + Thread::LockGuard lock(mutex); + // Close the original socket in its running thread. + io_uring_socket_->getIoUringWorker().dispatcher().post( + [&origin_socket = io_uring_socket_, &wait_cv, &mutex, &buf]() { + // Move the data of original socket's read buffer to the temporary buf. + origin_socket->close(true, [&wait_cv, &mutex, &buf](Buffer::Instance& buffer) { + Thread::LockGuard lock(mutex); + buf.move(buffer); + wait_cv.notifyOne(); + }); + }); + wait_cv.wait(mutex); + } + + // Move the temporary buf to the newly created one. + io_uring_socket_ = io_uring_worker_factory_.getIoUringWorker()->addServerSocket( + fd, buf, std::move(cb), events & Event::FileReadyType::Closed); + } + return; + } + + switch (io_uring_socket_type_) { + case IoUringSocketType::Accept: + file_event_ = dispatcher.createFileEvent(fd_, cb, trigger, events); + break; + case IoUringSocketType::Server: + io_uring_socket_ = io_uring_worker_factory_.getIoUringWorker()->addServerSocket( + fd_, std::move(cb), events & Event::FileReadyType::Closed); + break; + case IoUringSocketType::Unknown: + case IoUringSocketType::Client: + io_uring_socket_type_ = IoUringSocketType::Client; + io_uring_socket_ = io_uring_worker_factory_.getIoUringWorker()->addClientSocket( + fd_, std::move(cb), events & Event::FileReadyType::Closed); + break; + } +} + +void IoUringSocketHandleImpl::activateFileEvents(uint32_t events) { + ENVOY_LOG(trace, "activate file events {}, fd = {}, type = {}", events, fd_, + ioUringSocketTypeStr()); + + if (io_uring_socket_type_ == IoUringSocketType::Accept) { + ASSERT(file_event_ != nullptr); + file_event_->activate(events); + return; + } + + if (events & Event::FileReadyType::Read) { + io_uring_socket_->injectCompletion(Io::Request::RequestType::Read); + } + if (events & Event::FileReadyType::Write) { + io_uring_socket_->injectCompletion(Io::Request::RequestType::Write); + } +} + +void IoUringSocketHandleImpl::enableFileEvents(uint32_t events) { + ENVOY_LOG(trace, "enable file events {}, fd = {}, type = {}", events, fd_, + ioUringSocketTypeStr()); + + if (io_uring_socket_type_ == IoUringSocketType::Accept) { + ASSERT(file_event_ != nullptr); + file_event_->setEnabled(events); + return; + } + + if (events & Event::FileReadyType::Read) { + io_uring_socket_->enableRead(); + } else { + io_uring_socket_->disableRead(); + } + io_uring_socket_->enableCloseEvent(events & Event::FileReadyType::Closed); +} + +void IoUringSocketHandleImpl::resetFileEvents() { + ENVOY_LOG(trace, "reset file events, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + if (io_uring_socket_type_ == IoUringSocketType::Accept) { + file_event_.reset(); + return; + } + + io_uring_socket_->disableRead(); + io_uring_socket_->enableCloseEvent(false); +} + +Api::SysCallIntResult IoUringSocketHandleImpl::shutdown(int how) { + ENVOY_LOG(trace, "shutdown, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + + ASSERT(io_uring_socket_type_ == IoUringSocketType::Server || + io_uring_socket_type_ == IoUringSocketType::Client); + + io_uring_socket_->shutdown(how); + return Api::SysCallIntResult{0, 0}; +} + +absl::optional IoUringSocketHandleImpl::checkReadResult() const { + ASSERT(io_uring_socket_.has_value()); + ASSERT(io_uring_socket_type_ == IoUringSocketType::Server || + io_uring_socket_type_ == IoUringSocketType::Client); + + const OptRef& read_param = io_uring_socket_->getReadParam(); + // A absl::nullopt read param means that there is no io_uring request which has been done. + if (read_param == absl::nullopt) { + if (io_uring_socket_->getStatus() != Io::IoUringSocketStatus::RemoteClosed) { + return Api::IoCallUint64Result{0, IoSocketError::getIoSocketEagainError()}; + } else { + ENVOY_LOG(trace, "read, fd = {}, type = {}, remote close", fd_, ioUringSocketTypeStr()); + return Api::ioCallUint64ResultNoError(); + } + } + + if (read_param->result_ == 0) { + ENVOY_LOG(trace, "read remote close, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); + return Api::ioCallUint64ResultNoError(); + } + + if (read_param->result_ < 0) { + ASSERT(read_param->buf_.length() == 0); + ENVOY_LOG(trace, "read error = {}, fd = {}, type = {}", -read_param->result_, fd_, + ioUringSocketTypeStr()); + if (read_param->result_ == -EAGAIN) { + return Api::IoCallUint64Result{0, IoSocketError::getIoSocketEagainError()}; + } + return Api::IoCallUint64Result{0, IoSocketError::create(-read_param->result_)}; + } + + // The buffer has been read in the previous call, return EAGAIN to tell the caller to wait for + // the next read event. + if (read_param->buf_.length() == 0) { + return Api::IoCallUint64Result{0, IoSocketError::getIoSocketEagainError()}; + } + return absl::nullopt; +} + +absl::optional IoUringSocketHandleImpl::checkWriteResult() const { + ASSERT(io_uring_socket_.has_value()); + ASSERT(io_uring_socket_type_ == IoUringSocketType::Server || + io_uring_socket_type_ == IoUringSocketType::Client); + + const OptRef& write_param = io_uring_socket_->getWriteParam(); + if (write_param != absl::nullopt) { + // EAGAIN indicates an injected write event to trigger IO handle write. Submit the new write to + // the io_uring. + if (write_param->result_ < 0 && write_param->result_ != -EAGAIN) { + return Api::IoCallUint64Result{0, IoSocketError::create(-write_param->result_)}; + } + } + return absl::nullopt; +} + +Api::IoCallUint64Result IoUringSocketHandleImpl::copyOut(uint64_t max_length, + Buffer::RawSlice* slices, + uint64_t num_slice) { + auto read_result = checkReadResult(); + if (read_result.has_value()) { + return std::move(*read_result); + } + + const OptRef& read_param = io_uring_socket_->getReadParam(); + const uint64_t max_read_length = std::min(max_length, static_cast(read_param->result_)); + uint64_t num_bytes_to_read = read_param->buf_.copyOutToSlices(max_read_length, slices, num_slice); + return {num_bytes_to_read, IoSocketError::none()}; +} + +} // namespace Network +} // namespace Envoy diff --git a/source/common/network/io_uring_socket_handle_impl.h b/source/common/network/io_uring_socket_handle_impl.h new file mode 100644 index 000000000000..85e8469b11d1 --- /dev/null +++ b/source/common/network/io_uring_socket_handle_impl.h @@ -0,0 +1,94 @@ +#pragma once + +#include "envoy/buffer/buffer.h" +#include "envoy/common/io/io_uring.h" +#include "envoy/network/io_handle.h" + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/common/logger.h" +#include "source/common/network/io_socket_handle_base_impl.h" + +namespace Envoy { + +namespace Network { + +class IoUringSocketHandleImpl; + +using IoUringSocketHandleImplOptRef = + absl::optional>; + +enum class IoUringSocketType { + Unknown, + Accept, + Server, + Client, +}; + +/** + * IoHandle derivative for sockets. + */ +class IoUringSocketHandleImpl : public IoSocketHandleBaseImpl { +public: + IoUringSocketHandleImpl(Io::IoUringWorkerFactory& io_uring_worker_factory, + os_fd_t fd = INVALID_SOCKET, bool socket_v6only = false, + absl::optional domain = absl::nullopt, + bool is_server_socket = false); + ~IoUringSocketHandleImpl() override; + + Api::IoCallUint64Result close() override; + Api::IoCallUint64Result readv(uint64_t max_length, Buffer::RawSlice* slices, + uint64_t num_slice) override; + Api::IoCallUint64Result read(Buffer::Instance& buffer, + absl::optional max_length_opt) override; + Api::IoCallUint64Result writev(const Buffer::RawSlice* slices, uint64_t num_slice) override; + Api::IoCallUint64Result write(Buffer::Instance& buffer) override; + Api::IoCallUint64Result sendmsg(const Buffer::RawSlice* slices, uint64_t num_slice, int flags, + const Address::Ip* self_ip, + const Address::Instance& peer_address) override; + Api::IoCallUint64Result recvmsg(Buffer::RawSlice* slices, const uint64_t num_slice, + uint32_t self_port, RecvMsgOutput& output) override; + Api::IoCallUint64Result recvmmsg(RawSliceArrays& slices, uint32_t self_port, + RecvMsgOutput& output) override; + Api::IoCallUint64Result recv(void* buffer, size_t length, int flags) override; + Api::SysCallIntResult bind(Address::InstanceConstSharedPtr address) override; + Api::SysCallIntResult listen(int backlog) override; + IoHandlePtr accept(struct sockaddr* addr, socklen_t* addrlen) override; + Api::SysCallIntResult connect(Address::InstanceConstSharedPtr address) override; + Api::SysCallIntResult getOption(int level, int optname, void* optval, socklen_t* optlen) override; + IoHandlePtr duplicate() override; + void initializeFileEvent(Event::Dispatcher& dispatcher, Event::FileReadyCb cb, + Event::FileTriggerType trigger, uint32_t events) override; + void activateFileEvents(uint32_t events) override; + void enableFileEvents(uint32_t events) override; + void resetFileEvents() override; + Api::SysCallIntResult shutdown(int how) override; + +protected: + std::string ioUringSocketTypeStr() const { + switch (io_uring_socket_type_) { + case IoUringSocketType::Unknown: + return "unknown"; + case IoUringSocketType::Accept: + return "accept"; + case IoUringSocketType::Client: + return "client"; + case IoUringSocketType::Server: + return "server"; + } + PANIC_DUE_TO_CORRUPT_ENUM; + } + + Io::IoUringWorkerFactory& io_uring_worker_factory_; + IoUringSocketType io_uring_socket_type_; + OptRef io_uring_socket_{absl::nullopt}; + + Event::FileEventPtr file_event_{nullptr}; + + absl::optional checkReadResult() const; + absl::optional checkWriteResult() const; + Api::IoCallUint64Result copyOut(uint64_t max_length, Buffer::RawSlice* slices, + uint64_t num_slice); +}; + +} // namespace Network +} // namespace Envoy diff --git a/source/common/network/socket_interface_impl.cc b/source/common/network/socket_interface_impl.cc index aead17ecc3ab..a3dbec704417 100644 --- a/source/common/network/socket_interface_impl.cc +++ b/source/common/network/socket_interface_impl.cc @@ -10,15 +10,39 @@ #include "source/common/network/io_socket_handle_impl.h" #include "source/common/network/win32_socket_handle_impl.h" +#ifdef __linux__ +#include "source/common/network/io_uring_socket_handle_impl.h" +#endif + namespace Envoy { namespace Network { -IoHandlePtr SocketInterfaceImpl::makePlatformSpecificSocket(int socket_fd, bool socket_v6only, - absl::optional domain) { +namespace { +[[maybe_unused]] bool hasIoUringWorkerFactory(Io::IoUringWorkerFactory* io_uring_worker_factory) { + return io_uring_worker_factory != nullptr && io_uring_worker_factory->currentThreadRegistered() && + io_uring_worker_factory->getIoUringWorker() != absl::nullopt; +} +} // namespace + +IoHandlePtr SocketInterfaceImpl::makePlatformSpecificSocket( + int socket_fd, bool socket_v6only, absl::optional domain, + [[maybe_unused]] Io::IoUringWorkerFactory* io_uring_worker_factory) { if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { return std::make_unique(socket_fd, socket_v6only, domain); } +#ifdef __linux__ + // Only create IoUringSocketHandleImpl when the IoUringWorkerFactory has been created and it has + // been registered in the TLS, initialized. There are cases that test may create threads before + // IoUringWorkerFactory has been added to the TLS and got initialized. + if (hasIoUringWorkerFactory(io_uring_worker_factory)) { + return std::make_unique(*io_uring_worker_factory, socket_fd, + socket_v6only, domain); + } else { + return std::make_unique(socket_fd, socket_v6only, domain); + } +#else return std::make_unique(socket_fd, socket_v6only, domain); +#endif } IoHandlePtr SocketInterfaceImpl::makeSocket(int socket_fd, bool socket_v6only, diff --git a/source/common/network/socket_interface_impl.h b/source/common/network/socket_interface_impl.h index 780c0dc97b71..6c9d8b1ac6b2 100644 --- a/source/common/network/socket_interface_impl.h +++ b/source/common/network/socket_interface_impl.h @@ -1,5 +1,6 @@ #pragma once +#include "envoy/common/io/io_uring.h" #include "envoy/network/socket.h" #include "source/common/network/socket_interface.h" @@ -26,8 +27,9 @@ class SocketInterfaceImpl : public SocketInterfaceBase { return "envoy.extensions.network.socket_interface.default_socket_interface"; }; - static IoHandlePtr makePlatformSpecificSocket(int socket_fd, bool socket_v6only, - absl::optional domain); + static IoHandlePtr + makePlatformSpecificSocket(int socket_fd, bool socket_v6only, absl::optional domain, + Io::IoUringWorkerFactory* io_uring_worker_factory = nullptr); protected: virtual IoHandlePtr makeSocket(int socket_fd, bool socket_v6only, diff --git a/test/common/network/BUILD b/test/common/network/BUILD index 9e4c1c2e8205..ca7317da451c 100644 --- a/test/common/network/BUILD +++ b/test/common/network/BUILD @@ -433,6 +433,37 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "io_uring_socket_handle_impl_test", + srcs = select({ + "//bazel:linux": ["io_uring_socket_handle_impl_test.cc"], + "//conditions:default": [], + }), + deps = [ + "//test/mocks/api:api_mocks", + "//test/mocks/event:event_mocks", + "//test/mocks/io:io_mocks", + "//test/test_common:threadsafe_singleton_injector_lib", + ], +) + +envoy_cc_test( + name = "io_uring_socket_handle_impl_integration_test", + srcs = select({ + "//bazel:linux": ["io_uring_socket_handle_impl_integration_test.cc"], + "//conditions:default": [], + }), + deps = [ + "//source/common/network:default_socket_interface_lib", + "//source/common/thread_local:thread_local_lib", + "//test/test_common:environment_lib", + "//test/test_common:utility_lib", + ] + select({ + "//bazel:linux": ["//source/common/io:io_uring_worker_factory_impl_lib"], + "//conditions:default": [], + }), +) + envoy_cc_test( name = "win32_socket_handle_impl_test", srcs = ["win32_socket_handle_impl_test.cc"], diff --git a/test/common/network/io_uring_socket_handle_impl_integration_test.cc b/test/common/network/io_uring_socket_handle_impl_integration_test.cc new file mode 100644 index 000000000000..50422910c848 --- /dev/null +++ b/test/common/network/io_uring_socket_handle_impl_integration_test.cc @@ -0,0 +1,973 @@ +#include + +#include "source/common/api/os_sys_calls_impl.h" +#include "source/common/io/io_uring_impl.h" +#include "source/common/io/io_uring_worker_factory_impl.h" +#include "source/common/network/address_impl.h" +#include "source/common/network/io_socket_handle_impl.h" +#include "source/common/network/io_uring_socket_handle_impl.h" +#include "source/common/thread_local/thread_local_impl.h" + +#include "test/test_common/test_time.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Network { +namespace { + +class IoUringSocketHandleImplIntegrationTest : public testing::Test { +public: + IoUringSocketHandleImplIntegrationTest() : should_skip_(!Io::isIoUringSupported()) {} + + void SetUp() override { + if (should_skip_) { + GTEST_SKIP(); + } + } + + void TearDown() override { + if (!thread_is_shutdown_) { + instance_.shutdownGlobalThreading(); + instance_.shutdownThread(); + } + } + + void initialize(bool create_second_thread = false) { + api_ = Api::createApiForTest(time_system_); + dispatcher_ = api_->allocateDispatcher("test_thread"); + instance_.registerThread(*dispatcher_, true); + + if (create_second_thread) { + second_dispatcher_ = api_->allocateDispatcher("test_second_thread"); + instance_.registerThread(*second_dispatcher_, false); + } + + io_uring_worker_factory_ = + std::make_unique(10, false, 8192, 1000, instance_); + io_uring_worker_factory_->onWorkerThreadInitialized(); + + // Create the thread after the io_uring worker has been initialized, otherwise the dispatcher + // will quit when there is no any remaining registered event. + if (create_second_thread) { + second_thread_ = api_->threadFactory().createThread( + [this]() -> void { second_dispatcher_->run(Event::Dispatcher::RunType::Block); }); + } + } + + void createAcceptConnection() { + // Create an io_uring handle with accept socket. + fd_ = Api::OsSysCallsSingleton::get().socket(AF_INET, SOCK_STREAM, IPPROTO_TCP).return_value_; + EXPECT_GE(fd_, 0); + io_uring_socket_handle_ = std::make_unique( + *io_uring_worker_factory_, fd_, false, absl::nullopt, false); + + // Listen within the io_uring handle. + auto local_addr = std::make_shared("127.0.0.1", 0); + io_uring_socket_handle_->bind(local_addr); + io_uring_socket_handle_->listen(1); + + // Create a socket handle. + os_fd_t fd = Api::OsSysCallsSingleton::get() + .socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP) + .return_value_; + EXPECT_GE(fd, 0); + io_socket_handle_ = std::make_unique(fd); + } + + void createServerConnection() { + // Create an io_uring handle with server socket. + fd_ = Api::OsSysCallsSingleton::get().socket(AF_INET, SOCK_STREAM, IPPROTO_TCP).return_value_; + EXPECT_GE(fd_, 0); + io_uring_socket_handle_ = std::make_unique( + *io_uring_worker_factory_, fd_, false, absl::nullopt, true); + } + + void createClientConnection() { + // Prepare the listener. + os_fd_t fd = Api::OsSysCallsSingleton::get() + .socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP) + .return_value_; + EXPECT_GE(fd, 0); + IoHandlePtr listener = std::make_unique(fd); + + // Listen within the listener. + auto local_addr = std::make_shared("127.0.0.1", 0); + listener->bind(local_addr); + listener->listen(1); + listener->initializeFileEvent( + *dispatcher_, + [this, &listener](uint32_t) { + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + io_socket_handle_ = listener->accept(&addr, &addrlen); + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Create an io_uring handle with client socket. + fd_ = Api::OsSysCallsSingleton::get().socket(AF_INET, SOCK_STREAM, IPPROTO_TCP).return_value_; + EXPECT_GE(fd_, 0); + io_uring_socket_handle_ = std::make_unique( + *io_uring_worker_factory_, fd_, false, absl::nullopt, false); + + int error = -1; + socklen_t error_size = sizeof(error); + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &error, &error_size](uint32_t events) { + if (events & Event::FileReadyType::Write) { + io_uring_socket_handle_->getOption(SOL_SOCKET, SO_ERROR, &error, &error_size); + } + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Connect from io_uring handle. + io_uring_socket_handle_->connect(listener->localAddress()); + while (error == -1) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(error, 0); + } + + bool should_skip_{false}; + Api::ApiPtr api_; + Event::DispatcherPtr dispatcher_; + Event::GlobalTimeSystem time_system_; + ThreadLocal::InstanceImpl instance_; + std::unique_ptr io_uring_worker_factory_; + os_fd_t fd_; + IoHandlePtr io_uring_socket_handle_; + IoHandlePtr io_socket_handle_; + Thread::ThreadPtr second_thread_; + Event::DispatcherPtr second_dispatcher_; + bool thread_is_shutdown_{false}; +}; + +TEST_F(IoUringSocketHandleImplIntegrationTest, Close) { + initialize(); + createServerConnection(); + + io_uring_socket_handle_->close(); + + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(errno, EBADF); +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, CancelAndClose) { + initialize(); + createServerConnection(); + + // Submit the read request. + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + io_uring_socket_handle_->close(); + + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(errno, EBADF); +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, Accept) { + initialize(); + createAcceptConnection(); + + bool accepted = false; + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &accepted](uint32_t) { + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + auto handle = io_uring_socket_handle_->accept(&addr, &addrlen); + EXPECT_NE(handle, nullptr); + accepted = true; + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Connect from the socket handle. + io_socket_handle_->connect(io_uring_socket_handle_->localAddress()); + while (!accepted) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_TRUE(accepted); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(errno, EBADF); +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, AcceptError) { + initialize(); + createAcceptConnection(); + + bool accepted = false; + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &accepted](uint32_t) { + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + auto handle = io_uring_socket_handle_->accept(&addr, &addrlen); + EXPECT_EQ(handle, nullptr); + accepted = true; + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Accept nothing. + io_uring_socket_handle_->enableFileEvents(Event::FileReadyType::Read); + io_uring_socket_handle_->activateFileEvents(Event::FileReadyType::Read); + while (!accepted) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_TRUE(accepted); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(errno, EBADF); +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, Connect) { + initialize(); + createClientConnection(); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, ConnectError) { + initialize(); + + // Create an io_uring handle with client socket. + fd_ = Api::OsSysCallsSingleton::get().socket(AF_INET, SOCK_STREAM, IPPROTO_TCP).return_value_; + EXPECT_GE(fd_, 0); + io_uring_socket_handle_ = std::make_unique( + *io_uring_worker_factory_, fd_, false, absl::nullopt, false); + + int original_error = -1; + socklen_t original_error_size = sizeof(original_error); + int error = -1; + socklen_t error_size = sizeof(error); + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &error, &error_size, &original_error, &original_error_size](uint32_t events) { + if (events & Event::FileReadyType::Write) { + // We cannot get error with getsockopt since the error has been read within io_uring. + getsockopt(io_uring_socket_handle_->fdDoNotUse(), SOL_SOCKET, SO_ERROR, &original_error, + &original_error_size); + // The read error will be transferred to the io_uring handle. + io_uring_socket_handle_->getOption(SOL_SOCKET, SO_ERROR, &error, &error_size); + } + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Connect from io_uring handle. + auto local_addr = std::make_shared("127.0.0.1", 9999); + io_uring_socket_handle_->connect(local_addr); + while (error == -1) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(original_error, 0); + EXPECT_EQ(error, ECONNREFUSED); + + // Close safely. + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, Read) { + initialize(); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl read_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer, &data](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Read); + auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_EQ(ret.return_value_, data.size()); + + // Read again would expect the EAGAIN returned. + ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_TRUE(ret.wouldBlock()); + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Write from the peer handle. + io_socket_handle_->write(write_buffer); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, ReadContinuity) { + initialize(); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl read_buffer; + + bool first_read = true; + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer, &first_read](uint32_t event) { + if (first_read) { + EXPECT_EQ(event, Event::FileReadyType::Read); + auto ret = io_uring_socket_handle_->read(read_buffer, 5); + EXPECT_EQ(ret.return_value_, 5); + } else { + EXPECT_EQ(event, Event::FileReadyType::Read); + auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + } + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Write from the peer handle. + io_socket_handle_->write(write_buffer); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data.substr(0, 5)); + + // Cleanup previous read. + first_read = false; + read_buffer.drain(read_buffer.length()); + EXPECT_EQ(write_buffer.length(), 0); + + // Write from the peer handle again to trigger the read event. + std::string data2 = " again"; + write_buffer.add(data2); + io_socket_handle_->write(write_buffer); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data.substr(5) + data2); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, ReadActively) { + initialize(); + createClientConnection(); + + Buffer::OwnedImpl read_buffer; + + // Read actively. + auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_EQ(ret.wouldBlock(), true); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, Readv) { + initialize(); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl read_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer, &data](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Read); + Buffer::Reservation reservation = read_buffer.reserveForRead(); + auto ret = + io_uring_socket_handle_->readv(11, reservation.slices(), reservation.numSlices()); + EXPECT_EQ(ret.return_value_, data.size()); + reservation.commit(ret.return_value_); + + // Read again would expect the EAGAIN returned. + Buffer::Reservation reservation2 = read_buffer.reserveForRead(); + ret = io_uring_socket_handle_->readv(11, reservation2.slices(), reservation2.numSlices()); + EXPECT_TRUE(ret.wouldBlock()); + reservation2.commit(0); + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Write from the peer handle. + io_socket_handle_->write(write_buffer); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, ReadvContinuity) { + initialize(); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl read_buffer; + + bool first_read = true; + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer, &first_read](uint32_t event) { + if (first_read) { + Buffer::Reservation reservation = read_buffer.reserveForRead(); + auto ret = + io_uring_socket_handle_->readv(5, reservation.slices(), reservation.numSlices()); + EXPECT_EQ(ret.return_value_, 5); + reservation.commit(ret.return_value_); + } else { + EXPECT_EQ(event, Event::FileReadyType::Read); + Buffer::Reservation reservation = read_buffer.reserveForRead(); + auto ret = + io_uring_socket_handle_->readv(1024, reservation.slices(), reservation.numSlices()); + reservation.commit(ret.return_value_); + } + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Write from the peer handle. + io_socket_handle_->write(write_buffer); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data.substr(0, 5)); + + // Cleanup previous read. + first_read = false; + read_buffer.drain(read_buffer.length()); + EXPECT_EQ(write_buffer.length(), 0); + + // Write from the peer handle again to trigger the read event. + std::string data2 = " again"; + write_buffer.add(data2); + io_socket_handle_->write(write_buffer); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data.substr(5) + data2); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, Write) { + initialize(); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl read_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, + Event::FileReadyType::Write); + + io_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer, &data](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Read); + auto ret = io_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_EQ(ret.return_value_, data.size()); + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Write with the io_uring handle. + auto ret = io_uring_socket_handle_->write(write_buffer).return_value_; + EXPECT_EQ(ret, data.size()); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, Writev) { + initialize(); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl read_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, + Event::FileReadyType::Write); + + io_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer, &data](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Read); + auto ret = io_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_EQ(ret.return_value_, data.size()); + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Write with the io_uring handle. + auto ret = io_uring_socket_handle_->writev(&write_buffer.getRawSlices()[0], 1).return_value_; + EXPECT_EQ(ret, data.size()); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, Recv) { + initialize(); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl peek_buffer; + Buffer::OwnedImpl recv_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &peek_buffer, &recv_buffer, &data](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Read); + // Recv with MSG_PEEK will not drain the buffer. + Buffer::Reservation reservation = peek_buffer.reserveForRead(); + auto ret = io_uring_socket_handle_->recv(reservation.slices()->mem_, 5, MSG_PEEK); + EXPECT_EQ(ret.return_value_, 5); + reservation.commit(ret.return_value_); + + // Recv without flags behaves the same as readv. + Buffer::Reservation reservation2 = recv_buffer.reserveForRead(); + auto ret2 = + io_uring_socket_handle_->recv(reservation2.slices()->mem_, reservation2.length(), 0); + EXPECT_EQ(ret2.return_value_, data.size()); + reservation2.commit(ret2.return_value_); + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Write from the peer handle. + io_socket_handle_->write(write_buffer); + while (recv_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(peek_buffer.toString(), "Hello"); + EXPECT_EQ(recv_buffer.toString(), data); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, Bind) { + initialize(); + createClientConnection(); + // Create an io_uring handle with client socket. + fd_ = Api::OsSysCallsSingleton::get().socket(AF_INET, SOCK_STREAM, IPPROTO_TCP).return_value_; + EXPECT_GE(fd_, 0); + io_uring_socket_handle_ = std::make_unique( + *io_uring_worker_factory_, fd_, false, absl::nullopt, false); + auto local_addr = std::make_shared("127.0.0.1", 0); + io_uring_socket_handle_->bind(local_addr); + + // Close safely. + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, GetOption) { + initialize(); + createServerConnection(); + + int optval = -1; + socklen_t optlen = sizeof(optval); + auto ret = io_uring_socket_handle_->getOption(SOL_SOCKET, SO_REUSEADDR, &optval, &optlen); + EXPECT_EQ(ret.return_value_, 0); + EXPECT_EQ(optval, 0); + + // Close safely. + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, Duplicate) { + initialize(); + createClientConnection(); + + IoHandlePtr io_uring_socket_handle_2 = io_uring_socket_handle_->duplicate(); + + // Close safely. + io_uring_socket_handle_2->close(); + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, ActivateReadEvent) { + initialize(); + createServerConnection(); + + // Submit the read request. + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Read); + Buffer::OwnedImpl read_buffer; + auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_TRUE(ret.wouldBlock()); + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + io_uring_socket_handle_->activateFileEvents(Event::FileReadyType::Read); + + // Close safely. + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, ActivateWriteEvent) { + initialize(); + createClientConnection(); + + Buffer::OwnedImpl write_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &write_buffer](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Write); + auto ret = io_uring_socket_handle_->write(write_buffer); + EXPECT_TRUE(ret.wouldBlock()); + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Write); + + io_uring_socket_handle_->activateFileEvents(Event::FileReadyType::Write); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, Shutdown) { + initialize(); + createClientConnection(); + + Buffer::OwnedImpl read_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + io_uring_socket_handle_->shutdown(SHUT_WR); + auto ret = io_socket_handle_->read(read_buffer, absl::nullopt); + while (ret.wouldBlock()) { + ret = io_socket_handle_->read(read_buffer, absl::nullopt); + } + EXPECT_EQ(ret.return_value_, 0); + + // Close safely. + io_socket_handle_->close(); + io_uring_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +// Tests the case of a write event will be emitted after remote closed when the read is disabled and +// the write is listened only. +TEST_F(IoUringSocketHandleImplIntegrationTest, + RemoteCloseWithCloseEventDisabledAndReadEventDisabled) { + initialize(); + createClientConnection(); + + Buffer::OwnedImpl read_buffer; + bool got_write_event = false; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer, &got_write_event](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Write); + auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_TRUE(ret.ok()); + EXPECT_EQ(0, ret.return_value_); + io_uring_socket_handle_->close(); + got_write_event = true; + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + io_uring_socket_handle_->enableFileEvents(Event::FileReadyType::Write); + + io_socket_handle_->close(); + while (!got_write_event) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.length(), 0); + + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, RemoteCloseWithCloseEventDisabled) { + initialize(); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl read_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer, &data](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Read); + auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + if (ret.return_value_ > 0) { + EXPECT_EQ(ret.return_value_, data.size()); + + // Read again would expect the EAGAIN returned. + ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_TRUE(ret.wouldBlock()); + } else if (ret.return_value_ == 0) { + io_uring_socket_handle_->close(); + } + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + io_uring_socket_handle_->enableFileEvents(Event::FileReadyType::Read); + + // Write from the peer handle. + io_socket_handle_->write(write_buffer); + io_socket_handle_->close(); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data); + + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, RemoteCloseWithCloseEventEnabled) { + initialize(); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl read_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer, &data](uint32_t event) { + if (event & Event::FileReadyType::Read) { + auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_EQ(ret.return_value_, data.size()); + + // Read again would expect the EAGAIN returned. + ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_TRUE(ret.wouldBlock()); + } else if (event & Event::FileReadyType::Closed) { + auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_EQ(0, ret.return_value_); + io_uring_socket_handle_->close(); + } + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read | Event::FileReadyType::Closed); + + // Write from the peer handle. + io_socket_handle_->write(write_buffer); + io_socket_handle_->close(); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data); + + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +// Ensures IoUringHandleImpl will close the socket on destruction. +TEST_F(IoUringSocketHandleImplIntegrationTest, CloseIoUringSocketOnDestruction) { + initialize(); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl read_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer, &data](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Read); + auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_EQ(ret.return_value_, data.size()); + + // Read again would expect the EAGAIN returned. + ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + EXPECT_TRUE(ret.wouldBlock()); + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Write from the peer handle. + io_socket_handle_->write(write_buffer); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data); + io_uring_socket_handle_.reset(); + while (io_socket_handle_->read(read_buffer, absl::nullopt).return_value_ != 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + + // Close safely. + io_socket_handle_->close(); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } +} + +// Ensures IoUringHandleImpl can be released correctly when the IoUringWorker is released earlier. +TEST_F(IoUringSocketHandleImplIntegrationTest, IoUringWorkerEarlyRelease) { + initialize(); + createClientConnection(); + + thread_is_shutdown_ = true; + instance_.shutdownGlobalThreading(); + instance_.shutdownThread(); +} + +TEST_F(IoUringSocketHandleImplIntegrationTest, MigrateServerSocketBetweenThreads) { + initialize(true); + createClientConnection(); + + std::string data = "Hello world"; + Buffer::OwnedImpl write_buffer(data); + Buffer::OwnedImpl read_buffer; + + io_uring_socket_handle_->initializeFileEvent( + *dispatcher_, + [this, &read_buffer](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Read); + auto ret = io_uring_socket_handle_->read(read_buffer, 5); + EXPECT_EQ(ret.return_value_, 5); + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // Write from the peer handle. + io_socket_handle_->write(write_buffer); + while (read_buffer.length() == 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data.substr(0, 5)); + + io_uring_socket_handle_->resetFileEvents(); + read_buffer.drain(read_buffer.length()); + + // Migrate io_uring between threads. + std::atomic initialized_in_new_thread = false; + std::atomic read_in_new_thread = false; + second_dispatcher_->post([this, &second_dispatcher = second_dispatcher_, &read_buffer, &data, + &initialized_in_new_thread, &read_in_new_thread]() { + io_uring_socket_handle_->initializeFileEvent( + *second_dispatcher, + [this, &read_buffer, &data, &read_in_new_thread](uint32_t event) { + EXPECT_EQ(event, Event::FileReadyType::Read); + auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); + // For the next read. + if (read_buffer.length() > data.substr(5).length()) { + read_in_new_thread = true; + } + }, + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + initialized_in_new_thread = true; + }); + while (!initialized_in_new_thread) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + + // Write from the peer handle again to trigger the read event. + std::string data2 = " again"; + write_buffer.add(data2); + io_socket_handle_->write(write_buffer); + while (!read_in_new_thread) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + EXPECT_EQ(read_buffer.toString(), data.substr(5) + data2); + + // Close safely. + io_socket_handle_->close(); + second_dispatcher_->post([this]() { io_uring_socket_handle_->close(); }); + while (fcntl(fd_, F_GETFD, 0) >= 0) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + second_dispatcher_->exit(); + second_thread_->join(); +} + +} // namespace +} // namespace Network +} // namespace Envoy diff --git a/test/common/network/io_uring_socket_handle_impl_test.cc b/test/common/network/io_uring_socket_handle_impl_test.cc new file mode 100644 index 000000000000..80031b200e8a --- /dev/null +++ b/test/common/network/io_uring_socket_handle_impl_test.cc @@ -0,0 +1,133 @@ +#include "source/common/network/address_impl.h" +#include "source/common/network/io_uring_socket_handle_impl.h" + +#include "test/mocks/api/mocks.h" +#include "test/mocks/event/mocks.h" +#include "test/mocks/io/mocks.h" +#include "test/test_common/threadsafe_singleton_injector.h" + +namespace Envoy { +namespace Network { +namespace { + +class IoUringSocketHandleTestImpl : public IoUringSocketHandleImpl { +public: + IoUringSocketHandleTestImpl(Io::IoUringWorkerFactory& factory, bool is_server_socket) + : IoUringSocketHandleImpl(factory, INVALID_SOCKET, false, absl::nullopt, is_server_socket) {} + IoUringSocketType ioUringSocketType() const { return io_uring_socket_type_; } +}; + +class IoUringSocketHandleTest : public ::testing::Test { +public: + Io::MockIoUringSocket socket_; + Io::MockIoUringWorker worker_; + Io::MockIoUringWorkerFactory factory_; + Event::MockDispatcher dispatcher_; +}; + +TEST_F(IoUringSocketHandleTest, CreateServerSocket) { + IoUringSocketHandleTestImpl impl(factory_, true); + EXPECT_EQ(IoUringSocketType::Server, impl.ioUringSocketType()); +} + +TEST_F(IoUringSocketHandleTest, CreateClientSocket) { + IoUringSocketHandleTestImpl impl(factory_, false); + EXPECT_EQ(IoUringSocketType::Unknown, impl.ioUringSocketType()); + EXPECT_CALL(worker_, addClientSocket(_, _, _)).WillOnce(testing::ReturnRef(socket_)); + EXPECT_CALL(factory_, getIoUringWorker()) + .WillOnce(testing::Return(OptRef(worker_))); + impl.initializeFileEvent( + dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + EXPECT_EQ(IoUringSocketType::Client, impl.ioUringSocketType()); +} + +TEST_F(IoUringSocketHandleTest, ReadError) { + IoUringSocketHandleTestImpl impl(factory_, false); + EXPECT_CALL(worker_, addClientSocket(_, _, _)).WillOnce(testing::ReturnRef(socket_)); + EXPECT_CALL(factory_, getIoUringWorker()) + .WillOnce(testing::Return(OptRef(worker_))); + impl.initializeFileEvent( + dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + // EAGAIN error. + Buffer::OwnedImpl read_buffer; + Io::ReadParam read_param{read_buffer, -EAGAIN}; + auto read_param_ref = OptRef(read_param); + EXPECT_CALL(socket_, getReadParam()).WillOnce(testing::ReturnRef(read_param_ref)); + auto ret = impl.read(read_buffer, absl::nullopt); + EXPECT_EQ(ret.err_->getErrorCode(), Api::IoError::IoErrorCode::Again); + + // Non-EAGAIN error. + Io::ReadParam read_param_2{read_buffer, -EBADF}; + auto read_param_ref_2 = OptRef(read_param_2); + EXPECT_CALL(socket_, getReadParam()).WillOnce(testing::ReturnRef(read_param_ref_2)); + ret = impl.read(read_buffer, absl::nullopt); + EXPECT_EQ(ret.err_->getErrorCode(), Api::IoError::IoErrorCode::BadFd); +} + +TEST_F(IoUringSocketHandleTest, WriteError) { + IoUringSocketHandleTestImpl impl(factory_, false); + EXPECT_CALL(worker_, addClientSocket(_, _, _)).WillOnce(testing::ReturnRef(socket_)); + EXPECT_CALL(factory_, getIoUringWorker()) + .WillOnce(testing::Return(OptRef(worker_))); + impl.initializeFileEvent( + dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + Buffer::OwnedImpl write_buffer; + Io::WriteParam write_param{-EBADF}; + auto write_param_ref = OptRef(write_param); + EXPECT_CALL(socket_, getWriteParam()).WillOnce(testing::ReturnRef(write_param_ref)); + auto ret = impl.write(write_buffer); + EXPECT_EQ(ret.err_->getErrorCode(), Api::IoError::IoErrorCode::BadFd); +} + +TEST_F(IoUringSocketHandleTest, WritevError) { + IoUringSocketHandleTestImpl impl(factory_, false); + EXPECT_CALL(worker_, addClientSocket(_, _, _)).WillOnce(testing::ReturnRef(socket_)); + EXPECT_CALL(factory_, getIoUringWorker()) + .WillOnce(testing::Return(OptRef(worker_))); + impl.initializeFileEvent( + dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); + + Buffer::OwnedImpl write_buffer; + Io::WriteParam write_param{-EBADF}; + auto write_param_ref = OptRef(write_param); + EXPECT_CALL(socket_, getWriteParam()).WillOnce(testing::ReturnRef(write_param_ref)); + auto slice = write_buffer.frontSlice(); + auto ret = impl.writev(&slice, 1); + EXPECT_EQ(ret.err_->getErrorCode(), Api::IoError::IoErrorCode::BadFd); +} + +TEST_F(IoUringSocketHandleTest, SendmsgNotSupported) { + IoUringSocketHandleTestImpl impl(factory_, true); + + Buffer::OwnedImpl write_buffer; + auto slice = write_buffer.frontSlice(); + auto local_addr = std::make_shared("127.0.0.1", 0); + EXPECT_THAT(impl.sendmsg(&slice, 0, 0, nullptr, *local_addr).err_->getErrorCode(), + Api::IoError::IoErrorCode::NoSupport); +} + +TEST_F(IoUringSocketHandleTest, RecvmsgNotSupported) { + IoUringSocketHandleTestImpl impl(factory_, true); + + Buffer::OwnedImpl write_buffer; + auto slice = write_buffer.frontSlice(); + IoHandle::RecvMsgOutput output(0, nullptr); + EXPECT_THAT(impl.recvmsg(&slice, 0, 0, output).err_->getErrorCode(), + Api::IoError::IoErrorCode::NoSupport); +} + +TEST_F(IoUringSocketHandleTest, RecvmmsgNotSupported) { + IoUringSocketHandleTestImpl impl(factory_, true); + + Buffer::OwnedImpl write_buffer; + RawSliceArrays array(0, absl::FixedArray(0)); + IoHandle::RecvMsgOutput output(0, nullptr); + EXPECT_THAT(impl.recvmmsg(array, 0, output).err_->getErrorCode(), + Api::IoError::IoErrorCode::NoSupport); +} + +} // namespace +} // namespace Network +} // namespace Envoy diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 1a1eae3faa56..de722d444565 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -1411,6 +1411,7 @@ upcasts upstreams uptime upvalue +uring urlencoded urls userdata From 89eeeceef1cfcc4f3b43f26847b9aca0ad809de1 Mon Sep 17 00:00:00 2001 From: "dependency-envoy[bot]" <148525496+dependency-envoy[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:14:37 +0000 Subject: [PATCH 059/124] deps/api: Bump `com_github_bufbuild_buf` -> 1.30.0 (#32933) Signed-off-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> Co-authored-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> --- api/bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/bazel/repository_locations.bzl b/api/bazel/repository_locations.bzl index 902923662e0d..d7853884cac6 100644 --- a/api/bazel/repository_locations.bzl +++ b/api/bazel/repository_locations.bzl @@ -131,11 +131,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "buf", project_desc = "A new way of working with Protocol Buffers.", # Used for breaking change detection in API protobufs project_url = "https://buf.build", - version = "1.29.0", - sha256 = "1033f26361e6fc30ffcfab9d4e4274ffd4af88d9c97de63d2e1721c4a07c1380", + version = "1.30.0", + sha256 = "219f48fb1bb190e0f761e35cac0821dfd9c1b0dfda80d7aaf522347755d829ab", strip_prefix = "buf", urls = ["https://github.com/bufbuild/buf/releases/download/v{version}/buf-Linux-x86_64.tar.gz"], - release_date = "2024-01-24", + release_date = "2024-03-07", use_category = ["api"], license = "Apache-2.0", license_url = "https://github.com/bufbuild/buf/blob/v{version}/LICENSE", From 213b757639b2b0c911250e73c6c57ea59cd30ead Mon Sep 17 00:00:00 2001 From: Tiago Quelhas Date: Fri, 15 Mar 2024 19:28:52 +0100 Subject: [PATCH 060/124] bazel: remove `output_to_genfiles = True` (#32931) Remove `output_to_genfiles = True`. This is a no-op, as Bazel already enables `--incompatible_merge_genfiles_directory` by default, which makes the distinction between genfiles and bin moot. Signed-off-by: Tiago Quelhas --- api/bazel/cc_proto_descriptor_library/builddefs.bzl | 1 - 1 file changed, 1 deletion(-) diff --git a/api/bazel/cc_proto_descriptor_library/builddefs.bzl b/api/bazel/cc_proto_descriptor_library/builddefs.bzl index 2da95d00a063..c38c20bd8a1a 100644 --- a/api/bazel/cc_proto_descriptor_library/builddefs.bzl +++ b/api/bazel/cc_proto_descriptor_library/builddefs.bzl @@ -335,7 +335,6 @@ cc_proto_descriptor_library_aspect = aspect( ) cc_proto_descriptor_library = rule( - output_to_genfiles = True, implementation = _cc_proto_descriptor_rule_impl, attrs = { "deps": attr.label_list( From 3a8fadb08e00568f7962f72193ea8c58b52fc15f Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Fri, 15 Mar 2024 11:54:03 -0700 Subject: [PATCH 061/124] Stringmatcher: factory context for jwt_authn, ext_proc, csrf filters (#32924) Signed-off-by: Greg Greenway --- source/extensions/filters/http/csrf/config.cc | 4 ++-- source/extensions/filters/http/csrf/csrf_filter.cc | 9 +++++---- source/extensions/filters/http/csrf/csrf_filter.h | 12 +++++++----- source/extensions/filters/http/ext_proc/config.cc | 5 ++--- source/extensions/filters/http/ext_proc/ext_proc.h | 9 +++++---- .../filters/http/ext_proc/matching_utils.cc | 8 +++++--- .../filters/http/ext_proc/matching_utils.h | 3 ++- .../extensions/filters/http/jwt_authn/jwks_cache.cc | 5 +++-- test/extensions/filters/http/csrf/BUILD | 1 + .../extensions/filters/http/csrf/csrf_filter_test.cc | 10 ++++++---- test/extensions/filters/http/ext_proc/BUILD | 5 ++--- test/extensions/filters/http/ext_proc/filter_test.cc | 6 +++--- .../filters/http/ext_proc/ordering_test.cc | 6 +++--- .../filters/http/ext_proc/unit_test_fuzz/BUILD | 2 +- .../unit_test_fuzz/ext_proc_unit_test_fuzz.cc | 6 +++--- 15 files changed, 50 insertions(+), 41 deletions(-) diff --git a/source/extensions/filters/http/csrf/config.cc b/source/extensions/filters/http/csrf/config.cc index a36139e17bb7..425bf3812693 100644 --- a/source/extensions/filters/http/csrf/config.cc +++ b/source/extensions/filters/http/csrf/config.cc @@ -15,7 +15,7 @@ Http::FilterFactoryCb CsrfFilterFactory::createFilterFactoryFromProtoTyped( const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { CsrfFilterConfigSharedPtr config = std::make_shared( - policy, stats_prefix, context.scope(), context.serverFactoryContext().runtime()); + policy, stats_prefix, context.scope(), context.serverFactoryContext()); return [config](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamDecoderFilter(std::make_shared(config)); }; @@ -25,7 +25,7 @@ Router::RouteSpecificFilterConfigConstSharedPtr CsrfFilterFactory::createRouteSpecificFilterConfigTyped( const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, Server::Configuration::ServerFactoryContext& context, ProtobufMessage::ValidationVisitor&) { - return std::make_shared(policy, context.runtime()); + return std::make_shared(policy, context); } /** diff --git a/source/extensions/filters/http/csrf/csrf_filter.cc b/source/extensions/filters/http/csrf/csrf_filter.cc index ea8a521fd85e..c36c1ceb2816 100644 --- a/source/extensions/filters/http/csrf/csrf_filter.cc +++ b/source/extensions/filters/http/csrf/csrf_filter.cc @@ -76,15 +76,16 @@ static CsrfStats generateStats(const std::string& prefix, Stats::Scope& scope) { static CsrfPolicyPtr generatePolicy(const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, - Runtime::Loader& runtime) { - return std::make_unique(policy, runtime); + Server::Configuration::CommonFactoryContext& context) { + return std::make_unique(policy, context); } } // namespace CsrfFilterConfig::CsrfFilterConfig( const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) - : stats_(generateStats(stats_prefix, scope)), policy_(generatePolicy(policy, runtime)) {} + const std::string& stats_prefix, Stats::Scope& scope, + Server::Configuration::CommonFactoryContext& context) + : stats_(generateStats(stats_prefix, scope)), policy_(generatePolicy(policy, context)) {} CsrfFilter::CsrfFilter(const CsrfFilterConfigSharedPtr config) : config_(config) {} diff --git a/source/extensions/filters/http/csrf/csrf_filter.h b/source/extensions/filters/http/csrf/csrf_filter.h index b52b9473a884..fe7bc78a1a29 100644 --- a/source/extensions/filters/http/csrf/csrf_filter.h +++ b/source/extensions/filters/http/csrf/csrf_filter.h @@ -35,12 +35,13 @@ struct CsrfStats { class CsrfPolicy : public Router::RouteSpecificFilterConfig { public: CsrfPolicy(const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, - Runtime::Loader& runtime) - : policy_(policy), runtime_(runtime) { + Server::Configuration::CommonFactoryContext& context) + : policy_(policy), runtime_(context.runtime()) { for (const auto& additional_origin : policy.additional_origins()) { additional_origins_.emplace_back( - std::make_unique>( - additional_origin)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + additional_origin, context)); } } @@ -78,7 +79,8 @@ using CsrfPolicyPtr = std::unique_ptr; class CsrfFilterConfig { public: CsrfFilterConfig(const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); + const std::string& stats_prefix, Stats::Scope& scope, + Server::Configuration::CommonFactoryContext& context); CsrfStats& stats() { return stats_; } const CsrfPolicy* policy() { return policy_.get(); } diff --git a/source/extensions/filters/http/ext_proc/config.cc b/source/extensions/filters/http/ext_proc/config.cc index ee45fc6a73c4..cfbba66b4181 100644 --- a/source/extensions/filters/http/ext_proc/config.cc +++ b/source/extensions/filters/http/ext_proc/config.cc @@ -20,7 +20,7 @@ Http::FilterFactoryCb ExternalProcessingFilterConfig::createFilterFactoryFromPro proto_config, std::chrono::milliseconds(message_timeout_ms), max_message_timeout_ms, context.scope(), stats_prefix, Envoy::Extensions::Filters::Common::Expr::getBuilder(context.serverFactoryContext()), - context.serverFactoryContext().localInfo()); + context.serverFactoryContext()); return [filter_config, grpc_service = proto_config.grpc_service(), &context](Http::FilterChainFactoryCallbacks& callbacks) { @@ -50,8 +50,7 @@ ExternalProcessingFilterConfig::createFilterFactoryFromProtoWithServerContextTyp const auto filter_config = std::make_shared( proto_config, std::chrono::milliseconds(message_timeout_ms), max_message_timeout_ms, server_context.scope(), stats_prefix, - Envoy::Extensions::Filters::Common::Expr::getBuilder(server_context), - server_context.localInfo()); + Envoy::Extensions::Filters::Common::Expr::getBuilder(server_context), server_context); return [filter_config, grpc_service = proto_config.grpc_service(), &server_context](Http::FilterChainFactoryCallbacks& callbacks) { diff --git a/source/extensions/filters/http/ext_proc/ext_proc.h b/source/extensions/filters/http/ext_proc/ext_proc.h index 689b1e322872..45fb789b6d7b 100644 --- a/source/extensions/filters/http/ext_proc/ext_proc.h +++ b/source/extensions/filters/http/ext_proc/ext_proc.h @@ -129,7 +129,7 @@ class FilterConfig { const uint32_t max_message_timeout_ms, Stats::Scope& scope, const std::string& stats_prefix, Extensions::Filters::Common::Expr::BuilderInstanceSharedPtr builder, - const LocalInfo::LocalInfo& local_info) + Server::Configuration::CommonFactoryContext& context) : failure_mode_allow_(config.failure_mode_allow()), disable_clear_route_cache_(config.disable_clear_route_cache()), message_timeout_(message_timeout), max_message_timeout_ms_(max_message_timeout_ms), @@ -138,8 +138,9 @@ class FilterConfig { filter_metadata_(config.filter_metadata()), allow_mode_override_(config.allow_mode_override()), disable_immediate_response_(config.disable_immediate_response()), - allowed_headers_(initHeaderMatchers(config.forward_rules().allowed_headers())), - disallowed_headers_(initHeaderMatchers(config.forward_rules().disallowed_headers())), + allowed_headers_(initHeaderMatchers(config.forward_rules().allowed_headers(), context)), + disallowed_headers_( + initHeaderMatchers(config.forward_rules().disallowed_headers(), context)), untyped_forwarding_namespaces_( config.metadata_options().forwarding_namespaces().untyped().begin(), config.metadata_options().forwarding_namespaces().untyped().end()), @@ -149,7 +150,7 @@ class FilterConfig { untyped_receiving_namespaces_( config.metadata_options().receiving_namespaces().untyped().begin(), config.metadata_options().receiving_namespaces().untyped().end()), - expression_manager_(builder, local_info, config.request_attributes(), + expression_manager_(builder, context.localInfo(), config.request_attributes(), config.response_attributes()) {} bool failureModeAllow() const { return failure_mode_allow_; } diff --git a/source/extensions/filters/http/ext_proc/matching_utils.cc b/source/extensions/filters/http/ext_proc/matching_utils.cc index e778be5ab945..e17c600da533 100644 --- a/source/extensions/filters/http/ext_proc/matching_utils.cc +++ b/source/extensions/filters/http/ext_proc/matching_utils.cc @@ -86,12 +86,14 @@ ExpressionManager::evaluateAttributes(const Filters::Common::Expr::Activation& a } std::vector -initHeaderMatchers(const envoy::type::matcher::v3::ListStringMatcher& header_list) { +initHeaderMatchers(const envoy::type::matcher::v3::ListStringMatcher& header_list, + Server::Configuration::CommonFactoryContext& context) { std::vector header_matchers; for (const auto& matcher : header_list.patterns()) { header_matchers.push_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); } return header_matchers; } diff --git a/source/extensions/filters/http/ext_proc/matching_utils.h b/source/extensions/filters/http/ext_proc/matching_utils.h index f02c51ac2728..efd3a1f522f7 100644 --- a/source/extensions/filters/http/ext_proc/matching_utils.h +++ b/source/extensions/filters/http/ext_proc/matching_utils.h @@ -57,7 +57,8 @@ class ExpressionManager : public Logger::Loggable { }; std::vector -initHeaderMatchers(const envoy::type::matcher::v3::ListStringMatcher& header_list); +initHeaderMatchers(const envoy::type::matcher::v3::ListStringMatcher& header_list, + Server::Configuration::CommonFactoryContext& context); } // namespace ExternalProcessing } // namespace HttpFilters diff --git a/source/extensions/filters/http/jwt_authn/jwks_cache.cc b/source/extensions/filters/http/jwt_authn/jwks_cache.cc index 37bad44e10e9..b5109b639ef6 100644 --- a/source/extensions/filters/http/jwt_authn/jwks_cache.cc +++ b/source/extensions/filters/http/jwt_authn/jwks_cache.cc @@ -63,7 +63,7 @@ class JwksDataImpl : public JwksCache::JwksData, public Logger::Loggable(audiences); if (jwt_provider_.has_subjects()) { - sub_matcher_.emplace(jwt_provider_.subjects()); + sub_matcher_.emplace(jwt_provider_.subjects(), context.serverFactoryContext()); } if (jwt_provider_.require_expiration()) { @@ -189,7 +189,8 @@ class JwksDataImpl : public JwksCache::JwksData, public Logger::Loggable tls_; // async fetcher JwksAsyncFetcherPtr async_fetcher_; - absl::optional> sub_matcher_; + absl::optional> + sub_matcher_; absl::optional max_exp_; }; diff --git a/test/extensions/filters/http/csrf/BUILD b/test/extensions/filters/http/csrf/BUILD index 913a61c7c447..5a958bce60e8 100644 --- a/test/extensions/filters/http/csrf/BUILD +++ b/test/extensions/filters/http/csrf/BUILD @@ -21,6 +21,7 @@ envoy_extension_cc_test( "//source/extensions/filters/http/csrf:csrf_filter_lib", "//test/mocks/buffer:buffer_mocks", "//test/mocks/http:http_mocks", + "//test/mocks/server:factory_context_mocks", "//test/mocks/upstream:upstream_mocks", "@envoy_api//envoy/extensions/filters/http/csrf/v3:pkg_cc_proto", "@envoy_api//envoy/type/v3:pkg_cc_proto", diff --git a/test/extensions/filters/http/csrf/csrf_filter_test.cc b/test/extensions/filters/http/csrf/csrf_filter_test.cc index 85e2bb18a3bf..ba3b3bbe27ba 100644 --- a/test/extensions/filters/http/csrf/csrf_filter_test.cc +++ b/test/extensions/filters/http/csrf/csrf_filter_test.cc @@ -6,6 +6,7 @@ #include "test/mocks/buffer/mocks.h" #include "test/mocks/http/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/printers.h" @@ -43,7 +44,8 @@ class CsrfFilterTest : public testing::Test { const auto& add_regex_origin = policy.mutable_additional_origins()->Add(); add_regex_origin->MergeFrom(TestUtility::createRegexMatcher(R"(www\-[0-9]\.allow\.com)")); - return std::make_shared(policy, "test", *stats_.rootScope(), runtime_); + return std::make_shared(policy, "test", *stats_.rootScope(), + factory_context_); } CsrfFilterTest() : config_(setupConfig()), filter_(config_) {} @@ -69,14 +71,14 @@ class CsrfFilterTest : public testing::Test { } void setFilterEnabled(bool enabled) { - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("csrf.enabled", testing::Matcher(_))) .WillByDefault(Return(enabled)); } void setShadowEnabled(bool enabled) { - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("csrf.shadow_enabled", testing::Matcher(_))) .WillByDefault(Return(enabled)); @@ -87,7 +89,7 @@ class CsrfFilterTest : public testing::Test { Buffer::OwnedImpl data_; Router::MockDirectResponseEntry direct_response_entry_; Stats::IsolatedStoreImpl stats_; - NiceMock runtime_; + NiceMock factory_context_; CsrfFilterConfigSharedPtr config_; CsrfFilter filter_; diff --git a/test/extensions/filters/http/ext_proc/BUILD b/test/extensions/filters/http/ext_proc/BUILD index 13abe5f9969d..46ec34379f44 100644 --- a/test/extensions/filters/http/ext_proc/BUILD +++ b/test/extensions/filters/http/ext_proc/BUILD @@ -54,10 +54,9 @@ envoy_extension_cc_test( "//test/mocks/event:event_mocks", "//test/mocks/http:stream_encoder_mock", "//test/mocks/http:stream_mock", - "//test/mocks/local_info:local_info_mocks", "//test/mocks/runtime:runtime_mocks", - "//test/mocks/server:factory_context_mocks", "//test/mocks/server:overload_manager_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/proto:helloworld_proto_cc_proto", "//test/test_common:test_runtime_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", @@ -88,7 +87,7 @@ envoy_extension_cc_test( "//test/common/http:common_lib", "//test/mocks/event:event_mocks", "//test/mocks/local_info:local_info_mocks", - "//test/mocks/server:factory_context_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:test_runtime_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], diff --git a/test/extensions/filters/http/ext_proc/filter_test.cc b/test/extensions/filters/http/ext_proc/filter_test.cc index 85c742435fe7..b10e28c20ceb 100644 --- a/test/extensions/filters/http/ext_proc/filter_test.cc +++ b/test/extensions/filters/http/ext_proc/filter_test.cc @@ -25,7 +25,7 @@ #include "test/mocks/network/mocks.h" #include "test/mocks/router/mocks.h" #include "test/mocks/runtime/mocks.h" -#include "test/mocks/server/factory_context.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/tracing/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -136,7 +136,7 @@ class HttpFilterTest : public testing::Test { proto_config, 200ms, 10000, *stats_store_.rootScope(), "", std::make_shared( Envoy::Extensions::Filters::Common::Expr::createBuilder(nullptr)), - local_info_); + factory_context_); filter_ = std::make_unique(config_, std::move(client_), proto_config.grpc_service()); filter_->setEncoderFilterCallbacks(encoder_callbacks_); EXPECT_CALL(encoder_callbacks_, encoderBufferLimit()).WillRepeatedly(Return(BufferSize)); @@ -579,7 +579,7 @@ class HttpFilterTest : public testing::Test { Envoy::Event::SimulatedTimeSystem* test_time_; envoy::config::core::v3::Metadata dynamic_metadata_; testing::NiceMock connection_; - testing::NiceMock local_info_; + NiceMock factory_context_; }; // Using the default configuration, test the filter with a processor that diff --git a/test/extensions/filters/http/ext_proc/ordering_test.cc b/test/extensions/filters/http/ext_proc/ordering_test.cc index 06d1e89e4aba..c6ecc82ea31a 100644 --- a/test/extensions/filters/http/ext_proc/ordering_test.cc +++ b/test/extensions/filters/http/ext_proc/ordering_test.cc @@ -7,9 +7,9 @@ #include "test/extensions/filters/http/ext_proc/mock_server.h" #include "test/mocks/event/mocks.h" #include "test/mocks/http/mocks.h" -#include "test/mocks/local_info/mocks.h" #include "test/mocks/network/mocks.h" #include "test/mocks/router/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/tracing/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -75,7 +75,7 @@ class OrderingTest : public testing::Test { proto_config, kMessageTimeout, kMaxMessageTimeoutMs, *stats_store_.rootScope(), "", std::make_shared( Envoy::Extensions::Filters::Common::Expr::createBuilder(nullptr)), - local_info_); + factory_context_); filter_ = std::make_unique(config_, std::move(client_), proto_config.grpc_service()); filter_->setEncoderFilterCallbacks(encoder_callbacks_); filter_->setDecoderFilterCallbacks(decoder_callbacks_); @@ -216,7 +216,7 @@ class OrderingTest : public testing::Test { Http::TestResponseHeaderMapImpl response_headers_; Http::TestRequestTrailerMapImpl request_trailers_; Http::TestResponseTrailerMapImpl response_trailers_; - NiceMock local_info_; + NiceMock factory_context_; }; // A base class for tests that will check that gRPC streams fail while being created diff --git a/test/extensions/filters/http/ext_proc/unit_test_fuzz/BUILD b/test/extensions/filters/http/ext_proc/unit_test_fuzz/BUILD index 630a47ea12df..13649e730933 100644 --- a/test/extensions/filters/http/ext_proc/unit_test_fuzz/BUILD +++ b/test/extensions/filters/http/ext_proc/unit_test_fuzz/BUILD @@ -41,7 +41,7 @@ envoy_cc_fuzz_test( "//source/extensions/filters/http/ext_proc:config", "//test/extensions/filters/http/common/fuzz:http_filter_fuzzer_lib", "//test/mocks/http:http_mocks", - "//test/mocks/local_info:local_info_mocks", "//test/mocks/network:network_mocks", + "//test/mocks/server:server_factory_context_mocks", ], ) diff --git a/test/extensions/filters/http/ext_proc/unit_test_fuzz/ext_proc_unit_test_fuzz.cc b/test/extensions/filters/http/ext_proc/unit_test_fuzz/ext_proc_unit_test_fuzz.cc index 184c4be32365..2675fda20eea 100644 --- a/test/extensions/filters/http/ext_proc/unit_test_fuzz/ext_proc_unit_test_fuzz.cc +++ b/test/extensions/filters/http/ext_proc/unit_test_fuzz/ext_proc_unit_test_fuzz.cc @@ -5,8 +5,8 @@ #include "test/extensions/filters/http/ext_proc/unit_test_fuzz/mocks.h" #include "test/fuzz/fuzz_runner.h" #include "test/mocks/http/mocks.h" -#include "test/mocks/local_info/mocks.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/server/server_factory_context.h" using testing::Return; using testing::ReturnRef; @@ -47,7 +47,7 @@ class FuzzerMocks { NiceMock response_trailers_; NiceMock buffer_; NiceMock async_client_stream_info_; - NiceMock local_info_; + NiceMock factory_context_; }; DEFINE_PROTO_FUZZER( @@ -87,7 +87,7 @@ DEFINE_PROTO_FUZZER( proto_config, std::chrono::milliseconds(200), 200, *stats_store.rootScope(), "", std::make_shared( Envoy::Extensions::Filters::Common::Expr::createBuilder(nullptr)), - mocks.local_info_); + mocks.factory_context_); } catch (const EnvoyException& e) { ENVOY_LOG_MISC(debug, "EnvoyException during ext_proc filter config validation: {}", e.what()); return; From fa482d5efcdbd63a3ada92617a32f60dde8e23d2 Mon Sep 17 00:00:00 2001 From: phlax Date: Fri, 15 Mar 2024 18:57:02 +0000 Subject: [PATCH 062/124] Revert "io_uring: add io_uring socket handle (#32265)" (#32936) This reverts commit e8ea33a86a0271ff410659ae3755dc513222322a. Signed-off-by: Ryan Northey --- source/common/io/io_uring_worker_impl.h | 2 - source/common/network/BUILD | 24 +- .../network/io_uring_socket_handle_impl.cc | 415 -------- .../network/io_uring_socket_handle_impl.h | 94 -- .../common/network/socket_interface_impl.cc | 28 +- source/common/network/socket_interface_impl.h | 6 +- test/common/network/BUILD | 31 - ...ing_socket_handle_impl_integration_test.cc | 973 ------------------ .../io_uring_socket_handle_impl_test.cc | 133 --- tools/spelling/spelling_dictionary.txt | 1 - 10 files changed, 7 insertions(+), 1700 deletions(-) delete mode 100644 source/common/network/io_uring_socket_handle_impl.cc delete mode 100644 source/common/network/io_uring_socket_handle_impl.h delete mode 100644 test/common/network/io_uring_socket_handle_impl_integration_test.cc delete mode 100644 test/common/network/io_uring_socket_handle_impl_test.cc diff --git a/source/common/io/io_uring_worker_impl.h b/source/common/io/io_uring_worker_impl.h index b2ebd1d38049..1aa2222592d7 100644 --- a/source/common/io/io_uring_worker_impl.h +++ b/source/common/io/io_uring_worker_impl.h @@ -201,8 +201,6 @@ class IoUringServerSocket : public IoUringSocketEntry { void onShutdown(Request* req, int32_t result, bool injected) override; void onCancel(Request* req, int32_t result, bool injected) override; - Buffer::OwnedImpl& getReadBuffer() { return read_buf_; } - protected: // Since the write of IoUringSocket is async, there may have write request is on the fly when // close the socket. This timeout is setting for a time to wait the write request done. diff --git a/source/common/network/BUILD b/source/common/network/BUILD index 4268d592f29c..53ca953779a4 100644 --- a/source/common/network/BUILD +++ b/source/common/network/BUILD @@ -250,43 +250,25 @@ envoy_cc_library( "io_socket_handle_impl.cc", "socket_interface_impl.cc", "win32_socket_handle_impl.cc", - ] + select({ - "//bazel:linux": [ - "io_uring_socket_handle_impl.cc", - ], - "//conditions:default": [], - }), + ], hdrs = [ "io_socket_handle_base_impl.h", "io_socket_handle_impl.h", "socket_interface_impl.h", "win32_socket_handle_impl.h", - ] + select({ - "//bazel:linux": [ - "io_uring_socket_handle_impl.h", - ], - "//conditions:default": [], - }), + ], deps = [ ":address_lib", ":io_socket_error_lib", ":socket_interface_lib", ":socket_lib", - "//envoy/common/io:io_uring_interface", "//envoy/event:dispatcher_interface", "//envoy/network:io_handle_interface", "//source/common/api:os_sys_calls_lib", "//source/common/buffer:buffer_lib", "//source/common/event:dispatcher_includes", "@envoy_api//envoy/extensions/network/socket_interface/v3:pkg_cc_proto", - ] + select({ - "//bazel:linux": [ - "//source/common/io:io_uring_impl_lib", - "//source/common/io:io_uring_worker_factory_impl_lib", - "//source/common/io:io_uring_worker_lib", - ], - "//conditions:default": [], - }), + ], alwayslink = LEGACY_ALWAYSLINK, ) diff --git a/source/common/network/io_uring_socket_handle_impl.cc b/source/common/network/io_uring_socket_handle_impl.cc deleted file mode 100644 index 330a250edd61..000000000000 --- a/source/common/network/io_uring_socket_handle_impl.cc +++ /dev/null @@ -1,415 +0,0 @@ -#include "source/common/network/io_uring_socket_handle_impl.h" - -#include "envoy/buffer/buffer.h" -#include "envoy/common/exception.h" -#include "envoy/event/dispatcher.h" - -#include "source/common/api/os_sys_calls_impl.h" -#include "source/common/common/assert.h" -#include "source/common/common/utility.h" -#include "source/common/io/io_uring_worker_impl.h" -#include "source/common/network/address_impl.h" -#include "source/common/network/io_socket_error_impl.h" -#include "source/common/network/io_socket_handle_impl.h" -#include "source/common/network/socket_interface_impl.h" - -namespace Envoy { -namespace Network { - -IoUringSocketHandleImpl::IoUringSocketHandleImpl(Io::IoUringWorkerFactory& io_uring_worker_factory, - os_fd_t fd, bool socket_v6only, - absl::optional domain, bool is_server_socket) - : IoSocketHandleBaseImpl(fd, socket_v6only, domain), - io_uring_worker_factory_(io_uring_worker_factory), - io_uring_socket_type_(is_server_socket ? IoUringSocketType::Server - : IoUringSocketType::Unknown) { - ENVOY_LOG(trace, "construct io uring socket handle, fd = {}, type = {}", fd_, - ioUringSocketTypeStr()); -} - -IoUringSocketHandleImpl::~IoUringSocketHandleImpl() { - ENVOY_LOG(trace, "~IoUringSocketHandleImpl, type = {}", ioUringSocketTypeStr()); - if (SOCKET_VALID(fd_)) { - // If the socket is owned by the main thread like a listener, it may outlive the IoUringWorker. - // We have to ensure that the current thread has been registered and the io_uring in the thread - // is still available. - // TODO(zhxie): for current usage of server socket and client socket, the check may be - // redundant. - if (io_uring_socket_type_ != IoUringSocketType::Unknown && - io_uring_socket_type_ != IoUringSocketType::Accept && - io_uring_worker_factory_.currentThreadRegistered() && io_uring_socket_.has_value()) { - if (io_uring_socket_->getStatus() != Io::IoUringSocketStatus::Closed) { - io_uring_socket_.ref().close(false); - } - } else { - // The TLS slot has been shut down by this moment with io_uring wiped out, thus better use the - // POSIX system call instead of IoUringSocketHandleImpl::close(). - ::close(fd_); - } - } -} - -Api::IoCallUint64Result IoUringSocketHandleImpl::close() { - ENVOY_LOG(trace, "close, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - ASSERT(SOCKET_VALID(fd_)); - - if (io_uring_socket_type_ == IoUringSocketType::Unknown || - io_uring_socket_type_ == IoUringSocketType::Accept || !io_uring_socket_.has_value()) { - if (file_event_) { - file_event_.reset(); - } - ::close(fd_); - } else { - io_uring_socket_.ref().close(false); - io_uring_socket_.reset(); - } - SET_SOCKET_INVALID(fd_); - return Api::ioCallUint64ResultNoError(); -} - -Api::IoCallUint64Result -IoUringSocketHandleImpl::readv(uint64_t max_length, Buffer::RawSlice* slices, uint64_t num_slice) { - ENVOY_LOG(debug, "readv, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - Api::IoCallUint64Result result = copyOut(max_length, slices, num_slice); - if (result.ok()) { - // If the return value is 0, there should be a remote close. Return the value directly. - if (result.return_value_ != 0) { - io_uring_socket_->getReadParam()->buf_.drain(result.return_value_); - } - } - return result; -} - -Api::IoCallUint64Result IoUringSocketHandleImpl::read(Buffer::Instance& buffer, - absl::optional max_length_opt) { - ENVOY_LOG(trace, "read, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - auto read_result = checkReadResult(); - if (read_result.has_value()) { - return std::move(*read_result); - } - - const OptRef& read_param = io_uring_socket_->getReadParam(); - uint64_t max_read_length = - std::min(max_length_opt.value_or(UINT64_MAX), read_param->buf_.length()); - buffer.move(read_param->buf_, max_read_length); - return {max_read_length, IoSocketError::none()}; -} - -Api::IoCallUint64Result IoUringSocketHandleImpl::writev(const Buffer::RawSlice* slices, - uint64_t num_slice) { - ENVOY_LOG(trace, "writev, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - auto write_result = checkWriteResult(); - if (write_result.has_value()) { - return std::move(*write_result); - } - - uint64_t ret = io_uring_socket_->write(slices, num_slice); - return {ret, IoSocketError::none()}; -} - -Api::IoCallUint64Result IoUringSocketHandleImpl::write(Buffer::Instance& buffer) { - ENVOY_LOG(trace, "write {}, fd = {}, type = {}", buffer.length(), fd_, ioUringSocketTypeStr()); - - auto write_result = checkWriteResult(); - if (write_result.has_value()) { - return std::move(*write_result); - } - - uint64_t buffer_size = buffer.length(); - io_uring_socket_->write(buffer); - return {buffer_size, IoSocketError::none()}; -} - -Api::IoCallUint64Result IoUringSocketHandleImpl::sendmsg(const Buffer::RawSlice*, uint64_t, int, - const Address::Ip*, - const Address::Instance&) { - ENVOY_LOG(trace, "sendmsg, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - return Network::IoSocketError::ioResultSocketInvalidAddress(); -} - -Api::IoCallUint64Result IoUringSocketHandleImpl::recvmsg(Buffer::RawSlice*, const uint64_t, - uint32_t, RecvMsgOutput&) { - ENVOY_LOG(trace, "recvmsg, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - return Network::IoSocketError::ioResultSocketInvalidAddress(); -} - -Api::IoCallUint64Result IoUringSocketHandleImpl::recvmmsg(RawSliceArrays&, uint32_t, - RecvMsgOutput&) { - ENVOY_LOG(trace, "recvmmsg, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - return Network::IoSocketError::ioResultSocketInvalidAddress(); -} - -Api::IoCallUint64Result IoUringSocketHandleImpl::recv(void* buffer, size_t length, int flags) { - ASSERT(io_uring_socket_.has_value()); - ENVOY_LOG(trace, "recv, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - // The only used flag in Envoy is MSG_PEEK for listener filters, including TLS inspectors. - ASSERT(flags == 0 || flags == MSG_PEEK); - Buffer::RawSlice slice; - slice.mem_ = buffer; - slice.len_ = length; - if (flags == 0) { - return readv(length, &slice, 1); - } - - return copyOut(length, &slice, 1); -} - -Api::SysCallIntResult IoUringSocketHandleImpl::bind(Address::InstanceConstSharedPtr address) { - ENVOY_LOG(trace, "bind {}, fd = {}, io_uring_socket_type = {}", address->asString(), fd_, - ioUringSocketTypeStr()); - return Api::OsSysCallsSingleton::get().bind(fd_, address->sockAddr(), address->sockAddrLen()); -} - -Api::SysCallIntResult IoUringSocketHandleImpl::listen(int backlog) { - ENVOY_LOG(trace, "listen, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - ASSERT(io_uring_socket_type_ == IoUringSocketType::Unknown); - - io_uring_socket_type_ = IoUringSocketType::Accept; - setBlocking(false); - return Api::OsSysCallsSingleton::get().listen(fd_, backlog); -} - -IoHandlePtr IoUringSocketHandleImpl::accept(struct sockaddr* addr, socklen_t* addrlen) { - ENVOY_LOG(trace, "accept, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - ASSERT(io_uring_socket_type_ == IoUringSocketType::Accept); - - auto result = Api::OsSysCallsSingleton::get().accept(fd_, addr, addrlen); - if (SOCKET_INVALID(result.return_value_)) { - return nullptr; - } - return std::make_unique(io_uring_worker_factory_, result.return_value_, - socket_v6only_, domain_, true); -} - -Api::SysCallIntResult IoUringSocketHandleImpl::connect(Address::InstanceConstSharedPtr address) { - ENVOY_LOG(trace, "connect, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - ASSERT(io_uring_socket_type_ == IoUringSocketType::Client); - - io_uring_socket_->connect(address); - return Api::SysCallIntResult{-1, EINPROGRESS}; -} - -Api::SysCallIntResult IoUringSocketHandleImpl::getOption(int level, int optname, void* optval, - socklen_t* optlen) { - // io_uring socket does not populate connect error in getsockopt. Instead, the connect error is - // returned in onConnect() handling. We will imitate the default socket behavior here for client - // socket with optname SO_ERROR, which is only used to check connect error. - if (io_uring_socket_type_ == IoUringSocketType::Client && optname == SO_ERROR && - io_uring_socket_.has_value()) { - int* intval = static_cast(optval); - *intval = -io_uring_socket_->getWriteParam()->result_; - *optlen = sizeof(int); - return {0, 0}; - } - - return IoSocketHandleBaseImpl::getOption(level, optname, optval, optlen); -} - -IoHandlePtr IoUringSocketHandleImpl::duplicate() { - ENVOY_LOG(trace, "duplicate, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - Api::SysCallSocketResult result = Api::OsSysCallsSingleton::get().duplicate(fd_); - RELEASE_ASSERT(result.return_value_ != -1, - fmt::format("duplicate failed for '{}': ({}) {}", fd_, result.errno_, - errorDetails(result.errno_))); - return SocketInterfaceImpl::makePlatformSpecificSocket(result.return_value_, socket_v6only_, - domain_, &io_uring_worker_factory_); -} - -void IoUringSocketHandleImpl::initializeFileEvent(Event::Dispatcher& dispatcher, - Event::FileReadyCb cb, - Event::FileTriggerType trigger, uint32_t events) { - ENVOY_LOG(trace, "initialize file event, fd = {}, type = {}, has socket = {}", fd_, - ioUringSocketTypeStr(), io_uring_socket_.has_value()); - - // The IoUringSocket has already been created. It usually happened after a resetFileEvents. - if (io_uring_socket_.has_value()) { - if (&io_uring_socket_->getIoUringWorker().dispatcher() == - &io_uring_worker_factory_.getIoUringWorker()->dispatcher()) { - io_uring_socket_->setFileReadyCb(std::move(cb)); - io_uring_socket_->enableRead(); - io_uring_socket_->enableCloseEvent(events & Event::FileReadyType::Closed); - } else { - ENVOY_LOG(trace, "initialize file event from another thread, fd = {}, type = {}", fd_, - ioUringSocketTypeStr()); - Thread::CondVar wait_cv; - Thread::MutexBasicLockable mutex; - Buffer::OwnedImpl buf; - os_fd_t fd = io_uring_socket_->fd(); - - { - Thread::LockGuard lock(mutex); - // Close the original socket in its running thread. - io_uring_socket_->getIoUringWorker().dispatcher().post( - [&origin_socket = io_uring_socket_, &wait_cv, &mutex, &buf]() { - // Move the data of original socket's read buffer to the temporary buf. - origin_socket->close(true, [&wait_cv, &mutex, &buf](Buffer::Instance& buffer) { - Thread::LockGuard lock(mutex); - buf.move(buffer); - wait_cv.notifyOne(); - }); - }); - wait_cv.wait(mutex); - } - - // Move the temporary buf to the newly created one. - io_uring_socket_ = io_uring_worker_factory_.getIoUringWorker()->addServerSocket( - fd, buf, std::move(cb), events & Event::FileReadyType::Closed); - } - return; - } - - switch (io_uring_socket_type_) { - case IoUringSocketType::Accept: - file_event_ = dispatcher.createFileEvent(fd_, cb, trigger, events); - break; - case IoUringSocketType::Server: - io_uring_socket_ = io_uring_worker_factory_.getIoUringWorker()->addServerSocket( - fd_, std::move(cb), events & Event::FileReadyType::Closed); - break; - case IoUringSocketType::Unknown: - case IoUringSocketType::Client: - io_uring_socket_type_ = IoUringSocketType::Client; - io_uring_socket_ = io_uring_worker_factory_.getIoUringWorker()->addClientSocket( - fd_, std::move(cb), events & Event::FileReadyType::Closed); - break; - } -} - -void IoUringSocketHandleImpl::activateFileEvents(uint32_t events) { - ENVOY_LOG(trace, "activate file events {}, fd = {}, type = {}", events, fd_, - ioUringSocketTypeStr()); - - if (io_uring_socket_type_ == IoUringSocketType::Accept) { - ASSERT(file_event_ != nullptr); - file_event_->activate(events); - return; - } - - if (events & Event::FileReadyType::Read) { - io_uring_socket_->injectCompletion(Io::Request::RequestType::Read); - } - if (events & Event::FileReadyType::Write) { - io_uring_socket_->injectCompletion(Io::Request::RequestType::Write); - } -} - -void IoUringSocketHandleImpl::enableFileEvents(uint32_t events) { - ENVOY_LOG(trace, "enable file events {}, fd = {}, type = {}", events, fd_, - ioUringSocketTypeStr()); - - if (io_uring_socket_type_ == IoUringSocketType::Accept) { - ASSERT(file_event_ != nullptr); - file_event_->setEnabled(events); - return; - } - - if (events & Event::FileReadyType::Read) { - io_uring_socket_->enableRead(); - } else { - io_uring_socket_->disableRead(); - } - io_uring_socket_->enableCloseEvent(events & Event::FileReadyType::Closed); -} - -void IoUringSocketHandleImpl::resetFileEvents() { - ENVOY_LOG(trace, "reset file events, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - if (io_uring_socket_type_ == IoUringSocketType::Accept) { - file_event_.reset(); - return; - } - - io_uring_socket_->disableRead(); - io_uring_socket_->enableCloseEvent(false); -} - -Api::SysCallIntResult IoUringSocketHandleImpl::shutdown(int how) { - ENVOY_LOG(trace, "shutdown, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - - ASSERT(io_uring_socket_type_ == IoUringSocketType::Server || - io_uring_socket_type_ == IoUringSocketType::Client); - - io_uring_socket_->shutdown(how); - return Api::SysCallIntResult{0, 0}; -} - -absl::optional IoUringSocketHandleImpl::checkReadResult() const { - ASSERT(io_uring_socket_.has_value()); - ASSERT(io_uring_socket_type_ == IoUringSocketType::Server || - io_uring_socket_type_ == IoUringSocketType::Client); - - const OptRef& read_param = io_uring_socket_->getReadParam(); - // A absl::nullopt read param means that there is no io_uring request which has been done. - if (read_param == absl::nullopt) { - if (io_uring_socket_->getStatus() != Io::IoUringSocketStatus::RemoteClosed) { - return Api::IoCallUint64Result{0, IoSocketError::getIoSocketEagainError()}; - } else { - ENVOY_LOG(trace, "read, fd = {}, type = {}, remote close", fd_, ioUringSocketTypeStr()); - return Api::ioCallUint64ResultNoError(); - } - } - - if (read_param->result_ == 0) { - ENVOY_LOG(trace, "read remote close, fd = {}, type = {}", fd_, ioUringSocketTypeStr()); - return Api::ioCallUint64ResultNoError(); - } - - if (read_param->result_ < 0) { - ASSERT(read_param->buf_.length() == 0); - ENVOY_LOG(trace, "read error = {}, fd = {}, type = {}", -read_param->result_, fd_, - ioUringSocketTypeStr()); - if (read_param->result_ == -EAGAIN) { - return Api::IoCallUint64Result{0, IoSocketError::getIoSocketEagainError()}; - } - return Api::IoCallUint64Result{0, IoSocketError::create(-read_param->result_)}; - } - - // The buffer has been read in the previous call, return EAGAIN to tell the caller to wait for - // the next read event. - if (read_param->buf_.length() == 0) { - return Api::IoCallUint64Result{0, IoSocketError::getIoSocketEagainError()}; - } - return absl::nullopt; -} - -absl::optional IoUringSocketHandleImpl::checkWriteResult() const { - ASSERT(io_uring_socket_.has_value()); - ASSERT(io_uring_socket_type_ == IoUringSocketType::Server || - io_uring_socket_type_ == IoUringSocketType::Client); - - const OptRef& write_param = io_uring_socket_->getWriteParam(); - if (write_param != absl::nullopt) { - // EAGAIN indicates an injected write event to trigger IO handle write. Submit the new write to - // the io_uring. - if (write_param->result_ < 0 && write_param->result_ != -EAGAIN) { - return Api::IoCallUint64Result{0, IoSocketError::create(-write_param->result_)}; - } - } - return absl::nullopt; -} - -Api::IoCallUint64Result IoUringSocketHandleImpl::copyOut(uint64_t max_length, - Buffer::RawSlice* slices, - uint64_t num_slice) { - auto read_result = checkReadResult(); - if (read_result.has_value()) { - return std::move(*read_result); - } - - const OptRef& read_param = io_uring_socket_->getReadParam(); - const uint64_t max_read_length = std::min(max_length, static_cast(read_param->result_)); - uint64_t num_bytes_to_read = read_param->buf_.copyOutToSlices(max_read_length, slices, num_slice); - return {num_bytes_to_read, IoSocketError::none()}; -} - -} // namespace Network -} // namespace Envoy diff --git a/source/common/network/io_uring_socket_handle_impl.h b/source/common/network/io_uring_socket_handle_impl.h deleted file mode 100644 index 85e8469b11d1..000000000000 --- a/source/common/network/io_uring_socket_handle_impl.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include "envoy/buffer/buffer.h" -#include "envoy/common/io/io_uring.h" -#include "envoy/network/io_handle.h" - -#include "source/common/buffer/buffer_impl.h" -#include "source/common/common/logger.h" -#include "source/common/network/io_socket_handle_base_impl.h" - -namespace Envoy { - -namespace Network { - -class IoUringSocketHandleImpl; - -using IoUringSocketHandleImplOptRef = - absl::optional>; - -enum class IoUringSocketType { - Unknown, - Accept, - Server, - Client, -}; - -/** - * IoHandle derivative for sockets. - */ -class IoUringSocketHandleImpl : public IoSocketHandleBaseImpl { -public: - IoUringSocketHandleImpl(Io::IoUringWorkerFactory& io_uring_worker_factory, - os_fd_t fd = INVALID_SOCKET, bool socket_v6only = false, - absl::optional domain = absl::nullopt, - bool is_server_socket = false); - ~IoUringSocketHandleImpl() override; - - Api::IoCallUint64Result close() override; - Api::IoCallUint64Result readv(uint64_t max_length, Buffer::RawSlice* slices, - uint64_t num_slice) override; - Api::IoCallUint64Result read(Buffer::Instance& buffer, - absl::optional max_length_opt) override; - Api::IoCallUint64Result writev(const Buffer::RawSlice* slices, uint64_t num_slice) override; - Api::IoCallUint64Result write(Buffer::Instance& buffer) override; - Api::IoCallUint64Result sendmsg(const Buffer::RawSlice* slices, uint64_t num_slice, int flags, - const Address::Ip* self_ip, - const Address::Instance& peer_address) override; - Api::IoCallUint64Result recvmsg(Buffer::RawSlice* slices, const uint64_t num_slice, - uint32_t self_port, RecvMsgOutput& output) override; - Api::IoCallUint64Result recvmmsg(RawSliceArrays& slices, uint32_t self_port, - RecvMsgOutput& output) override; - Api::IoCallUint64Result recv(void* buffer, size_t length, int flags) override; - Api::SysCallIntResult bind(Address::InstanceConstSharedPtr address) override; - Api::SysCallIntResult listen(int backlog) override; - IoHandlePtr accept(struct sockaddr* addr, socklen_t* addrlen) override; - Api::SysCallIntResult connect(Address::InstanceConstSharedPtr address) override; - Api::SysCallIntResult getOption(int level, int optname, void* optval, socklen_t* optlen) override; - IoHandlePtr duplicate() override; - void initializeFileEvent(Event::Dispatcher& dispatcher, Event::FileReadyCb cb, - Event::FileTriggerType trigger, uint32_t events) override; - void activateFileEvents(uint32_t events) override; - void enableFileEvents(uint32_t events) override; - void resetFileEvents() override; - Api::SysCallIntResult shutdown(int how) override; - -protected: - std::string ioUringSocketTypeStr() const { - switch (io_uring_socket_type_) { - case IoUringSocketType::Unknown: - return "unknown"; - case IoUringSocketType::Accept: - return "accept"; - case IoUringSocketType::Client: - return "client"; - case IoUringSocketType::Server: - return "server"; - } - PANIC_DUE_TO_CORRUPT_ENUM; - } - - Io::IoUringWorkerFactory& io_uring_worker_factory_; - IoUringSocketType io_uring_socket_type_; - OptRef io_uring_socket_{absl::nullopt}; - - Event::FileEventPtr file_event_{nullptr}; - - absl::optional checkReadResult() const; - absl::optional checkWriteResult() const; - Api::IoCallUint64Result copyOut(uint64_t max_length, Buffer::RawSlice* slices, - uint64_t num_slice); -}; - -} // namespace Network -} // namespace Envoy diff --git a/source/common/network/socket_interface_impl.cc b/source/common/network/socket_interface_impl.cc index a3dbec704417..aead17ecc3ab 100644 --- a/source/common/network/socket_interface_impl.cc +++ b/source/common/network/socket_interface_impl.cc @@ -10,39 +10,15 @@ #include "source/common/network/io_socket_handle_impl.h" #include "source/common/network/win32_socket_handle_impl.h" -#ifdef __linux__ -#include "source/common/network/io_uring_socket_handle_impl.h" -#endif - namespace Envoy { namespace Network { -namespace { -[[maybe_unused]] bool hasIoUringWorkerFactory(Io::IoUringWorkerFactory* io_uring_worker_factory) { - return io_uring_worker_factory != nullptr && io_uring_worker_factory->currentThreadRegistered() && - io_uring_worker_factory->getIoUringWorker() != absl::nullopt; -} -} // namespace - -IoHandlePtr SocketInterfaceImpl::makePlatformSpecificSocket( - int socket_fd, bool socket_v6only, absl::optional domain, - [[maybe_unused]] Io::IoUringWorkerFactory* io_uring_worker_factory) { +IoHandlePtr SocketInterfaceImpl::makePlatformSpecificSocket(int socket_fd, bool socket_v6only, + absl::optional domain) { if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { return std::make_unique(socket_fd, socket_v6only, domain); } -#ifdef __linux__ - // Only create IoUringSocketHandleImpl when the IoUringWorkerFactory has been created and it has - // been registered in the TLS, initialized. There are cases that test may create threads before - // IoUringWorkerFactory has been added to the TLS and got initialized. - if (hasIoUringWorkerFactory(io_uring_worker_factory)) { - return std::make_unique(*io_uring_worker_factory, socket_fd, - socket_v6only, domain); - } else { - return std::make_unique(socket_fd, socket_v6only, domain); - } -#else return std::make_unique(socket_fd, socket_v6only, domain); -#endif } IoHandlePtr SocketInterfaceImpl::makeSocket(int socket_fd, bool socket_v6only, diff --git a/source/common/network/socket_interface_impl.h b/source/common/network/socket_interface_impl.h index 6c9d8b1ac6b2..780c0dc97b71 100644 --- a/source/common/network/socket_interface_impl.h +++ b/source/common/network/socket_interface_impl.h @@ -1,6 +1,5 @@ #pragma once -#include "envoy/common/io/io_uring.h" #include "envoy/network/socket.h" #include "source/common/network/socket_interface.h" @@ -27,9 +26,8 @@ class SocketInterfaceImpl : public SocketInterfaceBase { return "envoy.extensions.network.socket_interface.default_socket_interface"; }; - static IoHandlePtr - makePlatformSpecificSocket(int socket_fd, bool socket_v6only, absl::optional domain, - Io::IoUringWorkerFactory* io_uring_worker_factory = nullptr); + static IoHandlePtr makePlatformSpecificSocket(int socket_fd, bool socket_v6only, + absl::optional domain); protected: virtual IoHandlePtr makeSocket(int socket_fd, bool socket_v6only, diff --git a/test/common/network/BUILD b/test/common/network/BUILD index ca7317da451c..9e4c1c2e8205 100644 --- a/test/common/network/BUILD +++ b/test/common/network/BUILD @@ -433,37 +433,6 @@ envoy_cc_test( ], ) -envoy_cc_test( - name = "io_uring_socket_handle_impl_test", - srcs = select({ - "//bazel:linux": ["io_uring_socket_handle_impl_test.cc"], - "//conditions:default": [], - }), - deps = [ - "//test/mocks/api:api_mocks", - "//test/mocks/event:event_mocks", - "//test/mocks/io:io_mocks", - "//test/test_common:threadsafe_singleton_injector_lib", - ], -) - -envoy_cc_test( - name = "io_uring_socket_handle_impl_integration_test", - srcs = select({ - "//bazel:linux": ["io_uring_socket_handle_impl_integration_test.cc"], - "//conditions:default": [], - }), - deps = [ - "//source/common/network:default_socket_interface_lib", - "//source/common/thread_local:thread_local_lib", - "//test/test_common:environment_lib", - "//test/test_common:utility_lib", - ] + select({ - "//bazel:linux": ["//source/common/io:io_uring_worker_factory_impl_lib"], - "//conditions:default": [], - }), -) - envoy_cc_test( name = "win32_socket_handle_impl_test", srcs = ["win32_socket_handle_impl_test.cc"], diff --git a/test/common/network/io_uring_socket_handle_impl_integration_test.cc b/test/common/network/io_uring_socket_handle_impl_integration_test.cc deleted file mode 100644 index 50422910c848..000000000000 --- a/test/common/network/io_uring_socket_handle_impl_integration_test.cc +++ /dev/null @@ -1,973 +0,0 @@ -#include - -#include "source/common/api/os_sys_calls_impl.h" -#include "source/common/io/io_uring_impl.h" -#include "source/common/io/io_uring_worker_factory_impl.h" -#include "source/common/network/address_impl.h" -#include "source/common/network/io_socket_handle_impl.h" -#include "source/common/network/io_uring_socket_handle_impl.h" -#include "source/common/thread_local/thread_local_impl.h" - -#include "test/test_common/test_time.h" -#include "test/test_common/utility.h" - -#include "gtest/gtest.h" - -namespace Envoy { -namespace Network { -namespace { - -class IoUringSocketHandleImplIntegrationTest : public testing::Test { -public: - IoUringSocketHandleImplIntegrationTest() : should_skip_(!Io::isIoUringSupported()) {} - - void SetUp() override { - if (should_skip_) { - GTEST_SKIP(); - } - } - - void TearDown() override { - if (!thread_is_shutdown_) { - instance_.shutdownGlobalThreading(); - instance_.shutdownThread(); - } - } - - void initialize(bool create_second_thread = false) { - api_ = Api::createApiForTest(time_system_); - dispatcher_ = api_->allocateDispatcher("test_thread"); - instance_.registerThread(*dispatcher_, true); - - if (create_second_thread) { - second_dispatcher_ = api_->allocateDispatcher("test_second_thread"); - instance_.registerThread(*second_dispatcher_, false); - } - - io_uring_worker_factory_ = - std::make_unique(10, false, 8192, 1000, instance_); - io_uring_worker_factory_->onWorkerThreadInitialized(); - - // Create the thread after the io_uring worker has been initialized, otherwise the dispatcher - // will quit when there is no any remaining registered event. - if (create_second_thread) { - second_thread_ = api_->threadFactory().createThread( - [this]() -> void { second_dispatcher_->run(Event::Dispatcher::RunType::Block); }); - } - } - - void createAcceptConnection() { - // Create an io_uring handle with accept socket. - fd_ = Api::OsSysCallsSingleton::get().socket(AF_INET, SOCK_STREAM, IPPROTO_TCP).return_value_; - EXPECT_GE(fd_, 0); - io_uring_socket_handle_ = std::make_unique( - *io_uring_worker_factory_, fd_, false, absl::nullopt, false); - - // Listen within the io_uring handle. - auto local_addr = std::make_shared("127.0.0.1", 0); - io_uring_socket_handle_->bind(local_addr); - io_uring_socket_handle_->listen(1); - - // Create a socket handle. - os_fd_t fd = Api::OsSysCallsSingleton::get() - .socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP) - .return_value_; - EXPECT_GE(fd, 0); - io_socket_handle_ = std::make_unique(fd); - } - - void createServerConnection() { - // Create an io_uring handle with server socket. - fd_ = Api::OsSysCallsSingleton::get().socket(AF_INET, SOCK_STREAM, IPPROTO_TCP).return_value_; - EXPECT_GE(fd_, 0); - io_uring_socket_handle_ = std::make_unique( - *io_uring_worker_factory_, fd_, false, absl::nullopt, true); - } - - void createClientConnection() { - // Prepare the listener. - os_fd_t fd = Api::OsSysCallsSingleton::get() - .socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP) - .return_value_; - EXPECT_GE(fd, 0); - IoHandlePtr listener = std::make_unique(fd); - - // Listen within the listener. - auto local_addr = std::make_shared("127.0.0.1", 0); - listener->bind(local_addr); - listener->listen(1); - listener->initializeFileEvent( - *dispatcher_, - [this, &listener](uint32_t) { - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); - io_socket_handle_ = listener->accept(&addr, &addrlen); - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Create an io_uring handle with client socket. - fd_ = Api::OsSysCallsSingleton::get().socket(AF_INET, SOCK_STREAM, IPPROTO_TCP).return_value_; - EXPECT_GE(fd_, 0); - io_uring_socket_handle_ = std::make_unique( - *io_uring_worker_factory_, fd_, false, absl::nullopt, false); - - int error = -1; - socklen_t error_size = sizeof(error); - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &error, &error_size](uint32_t events) { - if (events & Event::FileReadyType::Write) { - io_uring_socket_handle_->getOption(SOL_SOCKET, SO_ERROR, &error, &error_size); - } - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Connect from io_uring handle. - io_uring_socket_handle_->connect(listener->localAddress()); - while (error == -1) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(error, 0); - } - - bool should_skip_{false}; - Api::ApiPtr api_; - Event::DispatcherPtr dispatcher_; - Event::GlobalTimeSystem time_system_; - ThreadLocal::InstanceImpl instance_; - std::unique_ptr io_uring_worker_factory_; - os_fd_t fd_; - IoHandlePtr io_uring_socket_handle_; - IoHandlePtr io_socket_handle_; - Thread::ThreadPtr second_thread_; - Event::DispatcherPtr second_dispatcher_; - bool thread_is_shutdown_{false}; -}; - -TEST_F(IoUringSocketHandleImplIntegrationTest, Close) { - initialize(); - createServerConnection(); - - io_uring_socket_handle_->close(); - - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(errno, EBADF); -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, CancelAndClose) { - initialize(); - createServerConnection(); - - // Submit the read request. - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - io_uring_socket_handle_->close(); - - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(errno, EBADF); -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, Accept) { - initialize(); - createAcceptConnection(); - - bool accepted = false; - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &accepted](uint32_t) { - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); - auto handle = io_uring_socket_handle_->accept(&addr, &addrlen); - EXPECT_NE(handle, nullptr); - accepted = true; - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Connect from the socket handle. - io_socket_handle_->connect(io_uring_socket_handle_->localAddress()); - while (!accepted) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_TRUE(accepted); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(errno, EBADF); -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, AcceptError) { - initialize(); - createAcceptConnection(); - - bool accepted = false; - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &accepted](uint32_t) { - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); - auto handle = io_uring_socket_handle_->accept(&addr, &addrlen); - EXPECT_EQ(handle, nullptr); - accepted = true; - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Accept nothing. - io_uring_socket_handle_->enableFileEvents(Event::FileReadyType::Read); - io_uring_socket_handle_->activateFileEvents(Event::FileReadyType::Read); - while (!accepted) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_TRUE(accepted); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(errno, EBADF); -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, Connect) { - initialize(); - createClientConnection(); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, ConnectError) { - initialize(); - - // Create an io_uring handle with client socket. - fd_ = Api::OsSysCallsSingleton::get().socket(AF_INET, SOCK_STREAM, IPPROTO_TCP).return_value_; - EXPECT_GE(fd_, 0); - io_uring_socket_handle_ = std::make_unique( - *io_uring_worker_factory_, fd_, false, absl::nullopt, false); - - int original_error = -1; - socklen_t original_error_size = sizeof(original_error); - int error = -1; - socklen_t error_size = sizeof(error); - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &error, &error_size, &original_error, &original_error_size](uint32_t events) { - if (events & Event::FileReadyType::Write) { - // We cannot get error with getsockopt since the error has been read within io_uring. - getsockopt(io_uring_socket_handle_->fdDoNotUse(), SOL_SOCKET, SO_ERROR, &original_error, - &original_error_size); - // The read error will be transferred to the io_uring handle. - io_uring_socket_handle_->getOption(SOL_SOCKET, SO_ERROR, &error, &error_size); - } - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Connect from io_uring handle. - auto local_addr = std::make_shared("127.0.0.1", 9999); - io_uring_socket_handle_->connect(local_addr); - while (error == -1) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(original_error, 0); - EXPECT_EQ(error, ECONNREFUSED); - - // Close safely. - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, Read) { - initialize(); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl read_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer, &data](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Read); - auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_EQ(ret.return_value_, data.size()); - - // Read again would expect the EAGAIN returned. - ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_TRUE(ret.wouldBlock()); - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Write from the peer handle. - io_socket_handle_->write(write_buffer); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, ReadContinuity) { - initialize(); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl read_buffer; - - bool first_read = true; - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer, &first_read](uint32_t event) { - if (first_read) { - EXPECT_EQ(event, Event::FileReadyType::Read); - auto ret = io_uring_socket_handle_->read(read_buffer, 5); - EXPECT_EQ(ret.return_value_, 5); - } else { - EXPECT_EQ(event, Event::FileReadyType::Read); - auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - } - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Write from the peer handle. - io_socket_handle_->write(write_buffer); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data.substr(0, 5)); - - // Cleanup previous read. - first_read = false; - read_buffer.drain(read_buffer.length()); - EXPECT_EQ(write_buffer.length(), 0); - - // Write from the peer handle again to trigger the read event. - std::string data2 = " again"; - write_buffer.add(data2); - io_socket_handle_->write(write_buffer); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data.substr(5) + data2); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, ReadActively) { - initialize(); - createClientConnection(); - - Buffer::OwnedImpl read_buffer; - - // Read actively. - auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_EQ(ret.wouldBlock(), true); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, Readv) { - initialize(); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl read_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer, &data](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Read); - Buffer::Reservation reservation = read_buffer.reserveForRead(); - auto ret = - io_uring_socket_handle_->readv(11, reservation.slices(), reservation.numSlices()); - EXPECT_EQ(ret.return_value_, data.size()); - reservation.commit(ret.return_value_); - - // Read again would expect the EAGAIN returned. - Buffer::Reservation reservation2 = read_buffer.reserveForRead(); - ret = io_uring_socket_handle_->readv(11, reservation2.slices(), reservation2.numSlices()); - EXPECT_TRUE(ret.wouldBlock()); - reservation2.commit(0); - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Write from the peer handle. - io_socket_handle_->write(write_buffer); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, ReadvContinuity) { - initialize(); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl read_buffer; - - bool first_read = true; - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer, &first_read](uint32_t event) { - if (first_read) { - Buffer::Reservation reservation = read_buffer.reserveForRead(); - auto ret = - io_uring_socket_handle_->readv(5, reservation.slices(), reservation.numSlices()); - EXPECT_EQ(ret.return_value_, 5); - reservation.commit(ret.return_value_); - } else { - EXPECT_EQ(event, Event::FileReadyType::Read); - Buffer::Reservation reservation = read_buffer.reserveForRead(); - auto ret = - io_uring_socket_handle_->readv(1024, reservation.slices(), reservation.numSlices()); - reservation.commit(ret.return_value_); - } - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Write from the peer handle. - io_socket_handle_->write(write_buffer); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data.substr(0, 5)); - - // Cleanup previous read. - first_read = false; - read_buffer.drain(read_buffer.length()); - EXPECT_EQ(write_buffer.length(), 0); - - // Write from the peer handle again to trigger the read event. - std::string data2 = " again"; - write_buffer.add(data2); - io_socket_handle_->write(write_buffer); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data.substr(5) + data2); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, Write) { - initialize(); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl read_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, - Event::FileReadyType::Write); - - io_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer, &data](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Read); - auto ret = io_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_EQ(ret.return_value_, data.size()); - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Write with the io_uring handle. - auto ret = io_uring_socket_handle_->write(write_buffer).return_value_; - EXPECT_EQ(ret, data.size()); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, Writev) { - initialize(); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl read_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, - Event::FileReadyType::Write); - - io_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer, &data](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Read); - auto ret = io_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_EQ(ret.return_value_, data.size()); - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Write with the io_uring handle. - auto ret = io_uring_socket_handle_->writev(&write_buffer.getRawSlices()[0], 1).return_value_; - EXPECT_EQ(ret, data.size()); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, Recv) { - initialize(); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl peek_buffer; - Buffer::OwnedImpl recv_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &peek_buffer, &recv_buffer, &data](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Read); - // Recv with MSG_PEEK will not drain the buffer. - Buffer::Reservation reservation = peek_buffer.reserveForRead(); - auto ret = io_uring_socket_handle_->recv(reservation.slices()->mem_, 5, MSG_PEEK); - EXPECT_EQ(ret.return_value_, 5); - reservation.commit(ret.return_value_); - - // Recv without flags behaves the same as readv. - Buffer::Reservation reservation2 = recv_buffer.reserveForRead(); - auto ret2 = - io_uring_socket_handle_->recv(reservation2.slices()->mem_, reservation2.length(), 0); - EXPECT_EQ(ret2.return_value_, data.size()); - reservation2.commit(ret2.return_value_); - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Write from the peer handle. - io_socket_handle_->write(write_buffer); - while (recv_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(peek_buffer.toString(), "Hello"); - EXPECT_EQ(recv_buffer.toString(), data); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, Bind) { - initialize(); - createClientConnection(); - // Create an io_uring handle with client socket. - fd_ = Api::OsSysCallsSingleton::get().socket(AF_INET, SOCK_STREAM, IPPROTO_TCP).return_value_; - EXPECT_GE(fd_, 0); - io_uring_socket_handle_ = std::make_unique( - *io_uring_worker_factory_, fd_, false, absl::nullopt, false); - auto local_addr = std::make_shared("127.0.0.1", 0); - io_uring_socket_handle_->bind(local_addr); - - // Close safely. - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, GetOption) { - initialize(); - createServerConnection(); - - int optval = -1; - socklen_t optlen = sizeof(optval); - auto ret = io_uring_socket_handle_->getOption(SOL_SOCKET, SO_REUSEADDR, &optval, &optlen); - EXPECT_EQ(ret.return_value_, 0); - EXPECT_EQ(optval, 0); - - // Close safely. - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, Duplicate) { - initialize(); - createClientConnection(); - - IoHandlePtr io_uring_socket_handle_2 = io_uring_socket_handle_->duplicate(); - - // Close safely. - io_uring_socket_handle_2->close(); - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, ActivateReadEvent) { - initialize(); - createServerConnection(); - - // Submit the read request. - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Read); - Buffer::OwnedImpl read_buffer; - auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_TRUE(ret.wouldBlock()); - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - io_uring_socket_handle_->activateFileEvents(Event::FileReadyType::Read); - - // Close safely. - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, ActivateWriteEvent) { - initialize(); - createClientConnection(); - - Buffer::OwnedImpl write_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &write_buffer](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Write); - auto ret = io_uring_socket_handle_->write(write_buffer); - EXPECT_TRUE(ret.wouldBlock()); - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Write); - - io_uring_socket_handle_->activateFileEvents(Event::FileReadyType::Write); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, Shutdown) { - initialize(); - createClientConnection(); - - Buffer::OwnedImpl read_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - io_uring_socket_handle_->shutdown(SHUT_WR); - auto ret = io_socket_handle_->read(read_buffer, absl::nullopt); - while (ret.wouldBlock()) { - ret = io_socket_handle_->read(read_buffer, absl::nullopt); - } - EXPECT_EQ(ret.return_value_, 0); - - // Close safely. - io_socket_handle_->close(); - io_uring_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -// Tests the case of a write event will be emitted after remote closed when the read is disabled and -// the write is listened only. -TEST_F(IoUringSocketHandleImplIntegrationTest, - RemoteCloseWithCloseEventDisabledAndReadEventDisabled) { - initialize(); - createClientConnection(); - - Buffer::OwnedImpl read_buffer; - bool got_write_event = false; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer, &got_write_event](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Write); - auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_TRUE(ret.ok()); - EXPECT_EQ(0, ret.return_value_); - io_uring_socket_handle_->close(); - got_write_event = true; - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - io_uring_socket_handle_->enableFileEvents(Event::FileReadyType::Write); - - io_socket_handle_->close(); - while (!got_write_event) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.length(), 0); - - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, RemoteCloseWithCloseEventDisabled) { - initialize(); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl read_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer, &data](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Read); - auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - if (ret.return_value_ > 0) { - EXPECT_EQ(ret.return_value_, data.size()); - - // Read again would expect the EAGAIN returned. - ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_TRUE(ret.wouldBlock()); - } else if (ret.return_value_ == 0) { - io_uring_socket_handle_->close(); - } - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - io_uring_socket_handle_->enableFileEvents(Event::FileReadyType::Read); - - // Write from the peer handle. - io_socket_handle_->write(write_buffer); - io_socket_handle_->close(); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data); - - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, RemoteCloseWithCloseEventEnabled) { - initialize(); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl read_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer, &data](uint32_t event) { - if (event & Event::FileReadyType::Read) { - auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_EQ(ret.return_value_, data.size()); - - // Read again would expect the EAGAIN returned. - ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_TRUE(ret.wouldBlock()); - } else if (event & Event::FileReadyType::Closed) { - auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_EQ(0, ret.return_value_); - io_uring_socket_handle_->close(); - } - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read | Event::FileReadyType::Closed); - - // Write from the peer handle. - io_socket_handle_->write(write_buffer); - io_socket_handle_->close(); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data); - - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -// Ensures IoUringHandleImpl will close the socket on destruction. -TEST_F(IoUringSocketHandleImplIntegrationTest, CloseIoUringSocketOnDestruction) { - initialize(); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl read_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer, &data](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Read); - auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_EQ(ret.return_value_, data.size()); - - // Read again would expect the EAGAIN returned. - ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - EXPECT_TRUE(ret.wouldBlock()); - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Write from the peer handle. - io_socket_handle_->write(write_buffer); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data); - io_uring_socket_handle_.reset(); - while (io_socket_handle_->read(read_buffer, absl::nullopt).return_value_ != 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - - // Close safely. - io_socket_handle_->close(); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } -} - -// Ensures IoUringHandleImpl can be released correctly when the IoUringWorker is released earlier. -TEST_F(IoUringSocketHandleImplIntegrationTest, IoUringWorkerEarlyRelease) { - initialize(); - createClientConnection(); - - thread_is_shutdown_ = true; - instance_.shutdownGlobalThreading(); - instance_.shutdownThread(); -} - -TEST_F(IoUringSocketHandleImplIntegrationTest, MigrateServerSocketBetweenThreads) { - initialize(true); - createClientConnection(); - - std::string data = "Hello world"; - Buffer::OwnedImpl write_buffer(data); - Buffer::OwnedImpl read_buffer; - - io_uring_socket_handle_->initializeFileEvent( - *dispatcher_, - [this, &read_buffer](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Read); - auto ret = io_uring_socket_handle_->read(read_buffer, 5); - EXPECT_EQ(ret.return_value_, 5); - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // Write from the peer handle. - io_socket_handle_->write(write_buffer); - while (read_buffer.length() == 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data.substr(0, 5)); - - io_uring_socket_handle_->resetFileEvents(); - read_buffer.drain(read_buffer.length()); - - // Migrate io_uring between threads. - std::atomic initialized_in_new_thread = false; - std::atomic read_in_new_thread = false; - second_dispatcher_->post([this, &second_dispatcher = second_dispatcher_, &read_buffer, &data, - &initialized_in_new_thread, &read_in_new_thread]() { - io_uring_socket_handle_->initializeFileEvent( - *second_dispatcher, - [this, &read_buffer, &data, &read_in_new_thread](uint32_t event) { - EXPECT_EQ(event, Event::FileReadyType::Read); - auto ret = io_uring_socket_handle_->read(read_buffer, absl::nullopt); - // For the next read. - if (read_buffer.length() > data.substr(5).length()) { - read_in_new_thread = true; - } - }, - Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - initialized_in_new_thread = true; - }); - while (!initialized_in_new_thread) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - - // Write from the peer handle again to trigger the read event. - std::string data2 = " again"; - write_buffer.add(data2); - io_socket_handle_->write(write_buffer); - while (!read_in_new_thread) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - EXPECT_EQ(read_buffer.toString(), data.substr(5) + data2); - - // Close safely. - io_socket_handle_->close(); - second_dispatcher_->post([this]() { io_uring_socket_handle_->close(); }); - while (fcntl(fd_, F_GETFD, 0) >= 0) { - dispatcher_->run(Event::Dispatcher::RunType::NonBlock); - } - second_dispatcher_->exit(); - second_thread_->join(); -} - -} // namespace -} // namespace Network -} // namespace Envoy diff --git a/test/common/network/io_uring_socket_handle_impl_test.cc b/test/common/network/io_uring_socket_handle_impl_test.cc deleted file mode 100644 index 80031b200e8a..000000000000 --- a/test/common/network/io_uring_socket_handle_impl_test.cc +++ /dev/null @@ -1,133 +0,0 @@ -#include "source/common/network/address_impl.h" -#include "source/common/network/io_uring_socket_handle_impl.h" - -#include "test/mocks/api/mocks.h" -#include "test/mocks/event/mocks.h" -#include "test/mocks/io/mocks.h" -#include "test/test_common/threadsafe_singleton_injector.h" - -namespace Envoy { -namespace Network { -namespace { - -class IoUringSocketHandleTestImpl : public IoUringSocketHandleImpl { -public: - IoUringSocketHandleTestImpl(Io::IoUringWorkerFactory& factory, bool is_server_socket) - : IoUringSocketHandleImpl(factory, INVALID_SOCKET, false, absl::nullopt, is_server_socket) {} - IoUringSocketType ioUringSocketType() const { return io_uring_socket_type_; } -}; - -class IoUringSocketHandleTest : public ::testing::Test { -public: - Io::MockIoUringSocket socket_; - Io::MockIoUringWorker worker_; - Io::MockIoUringWorkerFactory factory_; - Event::MockDispatcher dispatcher_; -}; - -TEST_F(IoUringSocketHandleTest, CreateServerSocket) { - IoUringSocketHandleTestImpl impl(factory_, true); - EXPECT_EQ(IoUringSocketType::Server, impl.ioUringSocketType()); -} - -TEST_F(IoUringSocketHandleTest, CreateClientSocket) { - IoUringSocketHandleTestImpl impl(factory_, false); - EXPECT_EQ(IoUringSocketType::Unknown, impl.ioUringSocketType()); - EXPECT_CALL(worker_, addClientSocket(_, _, _)).WillOnce(testing::ReturnRef(socket_)); - EXPECT_CALL(factory_, getIoUringWorker()) - .WillOnce(testing::Return(OptRef(worker_))); - impl.initializeFileEvent( - dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - EXPECT_EQ(IoUringSocketType::Client, impl.ioUringSocketType()); -} - -TEST_F(IoUringSocketHandleTest, ReadError) { - IoUringSocketHandleTestImpl impl(factory_, false); - EXPECT_CALL(worker_, addClientSocket(_, _, _)).WillOnce(testing::ReturnRef(socket_)); - EXPECT_CALL(factory_, getIoUringWorker()) - .WillOnce(testing::Return(OptRef(worker_))); - impl.initializeFileEvent( - dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - // EAGAIN error. - Buffer::OwnedImpl read_buffer; - Io::ReadParam read_param{read_buffer, -EAGAIN}; - auto read_param_ref = OptRef(read_param); - EXPECT_CALL(socket_, getReadParam()).WillOnce(testing::ReturnRef(read_param_ref)); - auto ret = impl.read(read_buffer, absl::nullopt); - EXPECT_EQ(ret.err_->getErrorCode(), Api::IoError::IoErrorCode::Again); - - // Non-EAGAIN error. - Io::ReadParam read_param_2{read_buffer, -EBADF}; - auto read_param_ref_2 = OptRef(read_param_2); - EXPECT_CALL(socket_, getReadParam()).WillOnce(testing::ReturnRef(read_param_ref_2)); - ret = impl.read(read_buffer, absl::nullopt); - EXPECT_EQ(ret.err_->getErrorCode(), Api::IoError::IoErrorCode::BadFd); -} - -TEST_F(IoUringSocketHandleTest, WriteError) { - IoUringSocketHandleTestImpl impl(factory_, false); - EXPECT_CALL(worker_, addClientSocket(_, _, _)).WillOnce(testing::ReturnRef(socket_)); - EXPECT_CALL(factory_, getIoUringWorker()) - .WillOnce(testing::Return(OptRef(worker_))); - impl.initializeFileEvent( - dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - Buffer::OwnedImpl write_buffer; - Io::WriteParam write_param{-EBADF}; - auto write_param_ref = OptRef(write_param); - EXPECT_CALL(socket_, getWriteParam()).WillOnce(testing::ReturnRef(write_param_ref)); - auto ret = impl.write(write_buffer); - EXPECT_EQ(ret.err_->getErrorCode(), Api::IoError::IoErrorCode::BadFd); -} - -TEST_F(IoUringSocketHandleTest, WritevError) { - IoUringSocketHandleTestImpl impl(factory_, false); - EXPECT_CALL(worker_, addClientSocket(_, _, _)).WillOnce(testing::ReturnRef(socket_)); - EXPECT_CALL(factory_, getIoUringWorker()) - .WillOnce(testing::Return(OptRef(worker_))); - impl.initializeFileEvent( - dispatcher_, [](uint32_t) {}, Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); - - Buffer::OwnedImpl write_buffer; - Io::WriteParam write_param{-EBADF}; - auto write_param_ref = OptRef(write_param); - EXPECT_CALL(socket_, getWriteParam()).WillOnce(testing::ReturnRef(write_param_ref)); - auto slice = write_buffer.frontSlice(); - auto ret = impl.writev(&slice, 1); - EXPECT_EQ(ret.err_->getErrorCode(), Api::IoError::IoErrorCode::BadFd); -} - -TEST_F(IoUringSocketHandleTest, SendmsgNotSupported) { - IoUringSocketHandleTestImpl impl(factory_, true); - - Buffer::OwnedImpl write_buffer; - auto slice = write_buffer.frontSlice(); - auto local_addr = std::make_shared("127.0.0.1", 0); - EXPECT_THAT(impl.sendmsg(&slice, 0, 0, nullptr, *local_addr).err_->getErrorCode(), - Api::IoError::IoErrorCode::NoSupport); -} - -TEST_F(IoUringSocketHandleTest, RecvmsgNotSupported) { - IoUringSocketHandleTestImpl impl(factory_, true); - - Buffer::OwnedImpl write_buffer; - auto slice = write_buffer.frontSlice(); - IoHandle::RecvMsgOutput output(0, nullptr); - EXPECT_THAT(impl.recvmsg(&slice, 0, 0, output).err_->getErrorCode(), - Api::IoError::IoErrorCode::NoSupport); -} - -TEST_F(IoUringSocketHandleTest, RecvmmsgNotSupported) { - IoUringSocketHandleTestImpl impl(factory_, true); - - Buffer::OwnedImpl write_buffer; - RawSliceArrays array(0, absl::FixedArray(0)); - IoHandle::RecvMsgOutput output(0, nullptr); - EXPECT_THAT(impl.recvmmsg(array, 0, output).err_->getErrorCode(), - Api::IoError::IoErrorCode::NoSupport); -} - -} // namespace -} // namespace Network -} // namespace Envoy diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index de722d444565..1a1eae3faa56 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -1411,7 +1411,6 @@ upcasts upstreams uptime upvalue -uring urlencoded urls userdata From 7991d71d663108561e6e33fb46348a09d7a3b8ac Mon Sep 17 00:00:00 2001 From: "dependency-envoy[bot]" <148525496+dependency-envoy[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:06:40 +0000 Subject: [PATCH 063/124] deps: Bump `com_github_luajit_luajit` -> d06beb0 (#32930) * deps: Bump `com_github_luajit_luajit` -> d06beb0 Signed-off-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> * rm-win-patch Signed-off-by: Ryan Northey --------- Signed-off-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> Signed-off-by: Ryan Northey Co-authored-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> Co-authored-by: Ryan Northey --- bazel/foreign_cc/luajit.patch | 84 ++-------------------------------- bazel/repository_locations.bzl | 6 +-- 2 files changed, 6 insertions(+), 84 deletions(-) diff --git a/bazel/foreign_cc/luajit.patch b/bazel/foreign_cc/luajit.patch index f7781067b4ab..ab525a950f69 100644 --- a/bazel/foreign_cc/luajit.patch +++ b/bazel/foreign_cc/luajit.patch @@ -39,96 +39,18 @@ index 30d64be2..ae7ec875 100644 TARGET_AR= $(CROSS)ar rcus -TARGET_STRIP= $(CROSS)strip +TARGET_STRIP?= $(CROSS)strip - + TARGET_LIBPATH= $(or $(PREFIX),/usr/local)/$(or $(MULTILIB),lib) TARGET_SONAME= libluajit-$(ABIVER).so.$(MAJVER) @@ -598,7 +598,7 @@ endif - + Q= @ E= @echo -#Q= +Q= #E= @: - + ############################################################################## -diff --git a/src/msvcbuild.bat b/src/msvcbuild.bat -index d323d8d4..2e08a3a1 100644 ---- a/src/msvcbuild.bat -+++ b/src/msvcbuild.bat -@@ -13,9 +13,7 @@ - @if not defined INCLUDE goto :FAIL - - @setlocal --@rem Add more debug flags here, e.g. DEBUGCFLAGS=/DLUA_USE_APICHECK --@set DEBUGCFLAGS= --@set LJCOMPILE=cl /nologo /c /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline -+@set LJCOMPILE=cl /nologo /c /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline /DLUAJIT_ENABLE_LUA52COMPAT - @set LJLINK=link /nologo - @set LJMT=mt /nologo - @set LJLIB=lib /nologo /nodefaultlib -@@ -24,10 +22,9 @@ - @set DASC=vm_x64.dasc - @set LJDLLNAME=lua51.dll - @set LJLIBNAME=lua51.lib --@set BUILDTYPE=release - @set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c lib_buffer.c - --%LJCOMPILE% host\minilua.c -+%LJCOMPILE% /O2 host\minilua.c - @if errorlevel 1 goto :BAD - %LJLINK% /out:minilua.exe minilua.obj - @if errorlevel 1 goto :BAD -@@ -51,7 +48,7 @@ if exist minilua.exe.manifest^ - minilua %DASM% -LN %DASMFLAGS% -o host\buildvm_arch.h %DASC% - @if errorlevel 1 goto :BAD - --%LJCOMPILE% /I "." /I %DASMDIR% host\buildvm*.c -+%LJCOMPILE% /O2 /I "." /I %DASMDIR% host\buildvm*.c - @if errorlevel 1 goto :BAD - %LJLINK% /out:buildvm.exe buildvm*.obj - @if errorlevel 1 goto :BAD -@@ -75,26 +72,35 @@ buildvm -m folddef -o lj_folddef.h lj_opt_fold.c - - @if "%1" neq "debug" goto :NODEBUG - @shift --@set BUILDTYPE=debug --@set LJCOMPILE=%LJCOMPILE% /Zi %DEBUGCFLAGS% --@set LJLINK=%LJLINK% /opt:ref /opt:icf /incremental:no -+@set LJCOMPILE=%LJCOMPILE% /O0 /Z7 -+@set LJLINK=%LJLINK% /debug /opt:ref /opt:icf /incremental:no -+@set LJCRTDBG=d -+@goto :ENDDEBUG - :NODEBUG --@set LJLINK=%LJLINK% /%BUILDTYPE% -+@set LJCOMPILE=%LJCOMPILE% /O2 /Z7 -+@set LJLINK=%LJLINK% /release /incremental:no -+@set LJCRTDBG= -+:ENDDEBUG - @if "%1"=="amalg" goto :AMALGDLL - @if "%1"=="static" goto :STATIC --%LJCOMPILE% /MD /DLUA_BUILD_AS_DLL lj_*.c lib_*.c -+@set LJCOMPILE=%LJCOMPILE% /MD%LJCRTDBG% -+%LJCOMPILE% /DLUA_BUILD_AS_DLL lj_*.c lib_*.c - @if errorlevel 1 goto :BAD - %LJLINK% /DLL /out:%LJDLLNAME% lj_*.obj lib_*.obj - @if errorlevel 1 goto :BAD - @goto :MTDLL - :STATIC -+@shift -+@set LJCOMPILE=%LJCOMPILE% /MT%LJCRTDBG% - %LJCOMPILE% lj_*.c lib_*.c - @if errorlevel 1 goto :BAD - %LJLIB% /OUT:%LJLIBNAME% lj_*.obj lib_*.obj - @if errorlevel 1 goto :BAD - @goto :MTDLL - :AMALGDLL --%LJCOMPILE% /MD /DLUA_BUILD_AS_DLL ljamalg.c -+@shift -+@set LJCOMPILE=%LJCOMPILE% /MD%LJCRTDBG% -+%LJCOMPILE% /DLUA_BUILD_AS_DLL ljamalg.c - @if errorlevel 1 goto :BAD - %LJLINK% /DLL /out:%LJDLLNAME% ljamalg.obj lj_vm.obj - @if errorlevel 1 goto :BAD diff --git a/build.py b/build.py new file mode 100755 index 00000000..1201542c diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 5c15f4e87db3..b81d31b33a9d 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -471,11 +471,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_desc = "Just-In-Time compiler for Lua", project_url = "https://luajit.org", # LuaJIT only provides rolling releases - version = "1c279127050e86e99970100e9c42e0f09cd54ab7", - sha256 = "c62f6e6d5bff89e4718709841cd6be71ad419ac9fa780c91abf1635cda923f8f", + version = "d06beb0480c5d1eb53b3343e78063950275aa281", + sha256 = "6abd146a1dfa240a965748f63221633446affa2a715e3eb03879136e3efb95f4", strip_prefix = "LuaJIT-{version}", urls = ["https://github.com/LuaJIT/LuaJIT/archive/{version}.tar.gz"], - release_date = "2023-04-16", + release_date = "2024-03-10", use_category = ["dataplane_ext"], extensions = [ "envoy.filters.http.lua", From 0dede366537171607da9e5a0ff40586e6017e827 Mon Sep 17 00:00:00 2001 From: "Antonio V. Leonti" <53806445+antoniovleonti@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:48:20 -0400 Subject: [PATCH 064/124] fuzz: Extend ext_authz fuzzer coverage to grpc client (#32859) Signed-off-by: Antonio Leonti Signed-off-by: Antonio V. Leonti <53806445+antoniovleonti@users.noreply.github.com> --- test/extensions/filters/http/ext_authz/BUILD | 1 + .../http/ext_authz/ext_authz_fuzz_test.cc | 100 ++++++++++-------- 2 files changed, 56 insertions(+), 45 deletions(-) diff --git a/test/extensions/filters/http/ext_authz/BUILD b/test/extensions/filters/http/ext_authz/BUILD index 3ba58efb6997..dc83e80e281d 100644 --- a/test/extensions/filters/http/ext_authz/BUILD +++ b/test/extensions/filters/http/ext_authz/BUILD @@ -108,6 +108,7 @@ envoy_cc_fuzz_test( "//source/extensions/filters/http/ext_authz", "//test/extensions/filters/common/ext_authz:ext_authz_mocks", "//test/extensions/filters/http/common/fuzz:http_filter_fuzzer_lib", + "//test/mocks/grpc:grpc_mocks", "//test/mocks/http:http_mocks", "//test/mocks/network:network_mocks", "//test/mocks/runtime:runtime_mocks", diff --git a/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc index 7745a37cb4d5..9b121b4e6626 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc @@ -8,6 +8,7 @@ #include "test/extensions/filters/http/common/fuzz/http_filter_fuzzer.h" #include "test/extensions/filters/http/ext_authz/ext_authz_fuzz.pb.validate.h" #include "test/fuzz/fuzz_runner.h" +#include "test/mocks/grpc/mocks.h" #include "test/mocks/http/mocks.h" #include "test/mocks/network/mocks.h" #include "test/mocks/runtime/mocks.h" @@ -23,30 +24,29 @@ namespace HttpFilters { namespace ExtAuthz { namespace { -Filters::Common::ExtAuthz::ResponsePtr -makeAuthzResponse(const Filters::Common::ExtAuthz::CheckStatus status) { - Filters::Common::ExtAuthz::ResponsePtr response = - std::make_unique(); - response->status = status; +std::unique_ptr +makeGrpcCheckResponse(const Grpc::Status::WellKnownGrpcStatus status) { + auto response = std::make_unique(); + response->mutable_status()->set_code(status); // TODO: We only add the response status. // Add fuzzed inputs for headers_to_(set/append/add), body, status_code to the Response. return response; } -Filters::Common::ExtAuthz::CheckStatus resultCaseToCheckStatus( +Grpc::Status::WellKnownGrpcStatus resultCaseToGrpcStatus( const envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::AuthResult result) { - Filters::Common::ExtAuthz::CheckStatus check_status; + Grpc::Status::WellKnownGrpcStatus check_status; switch (result) { case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::OK: { - check_status = Filters::Common::ExtAuthz::CheckStatus::OK; + check_status = Grpc::Status::WellKnownGrpcStatus::Ok; break; } case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::ERROR: { - check_status = Filters::Common::ExtAuthz::CheckStatus::Error; + check_status = Grpc::Status::WellKnownGrpcStatus::Internal; break; } case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::DENIED: { - check_status = Filters::Common::ExtAuthz::CheckStatus::Denied; + check_status = Grpc::Status::WellKnownGrpcStatus::PermissionDenied; break; } default: { @@ -57,32 +57,22 @@ Filters::Common::ExtAuthz::CheckStatus resultCaseToCheckStatus( return check_status; } -class FuzzerMocks { +class StatelessFuzzerMocks { public: - FuzzerMocks() : addr_(std::make_shared("/test/test.sock")) { - - ON_CALL(decoder_callbacks_, connection()) - .WillByDefault(Return(OptRef{connection_})); + StatelessFuzzerMocks() + : addr_(std::make_shared("/test/test.sock")) { connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress(addr_); connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress(addr_); - - ON_CALL(decoder_callbacks_.stream_info_, dynamicMetadata()) - .WillByDefault( - Invoke([&]() -> envoy::config::core::v3::Metadata& { return filter_metadata_; })); - } - - void setFilterMetadata(const envoy::config::core::v3::Metadata& metadata) { - filter_metadata_.CopyFrom(metadata); } + // Only add mocks here that are stateless. I.e. if you need to call ON_CALL on a mock each fuzzer + // run, do not add the mock here, because it will leak memory. NiceMock factory_context_; - NiceMock decoder_callbacks_; NiceMock encoder_callbacks_; Network::Address::InstanceConstSharedPtr addr_; NiceMock connection_; - - // Returned by mock decoder_callbacks_.stream_info_.dynamicMetadata() - envoy::config::core::v3::Metadata filter_metadata_; + NiceMock async_request_; + Envoy::Tracing::MockSpan mock_span_; }; DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase& input) { @@ -93,7 +83,7 @@ DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzT return; } - static FuzzerMocks mocks; + static StatelessFuzzerMocks mocks; NiceMock stats_store; static ScopedInjectableLoader engine(std::make_unique()); @@ -109,32 +99,52 @@ DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzT return; } - Filters::Common::ExtAuthz::MockClient* client = new Filters::Common::ExtAuthz::MockClient(); - std::unique_ptr filter = - std::make_unique(config, Filters::Common::ExtAuthz::ClientPtr{client}); - filter->setDecoderFilterCallbacks(mocks.decoder_callbacks_); - filter->setEncoderFilterCallbacks(mocks.encoder_callbacks_); + auto internal_mock_client = std::make_shared>(); + auto grpc_client = new Filters::Common::ExtAuthz::GrpcClientImpl(internal_mock_client, + std::chrono::milliseconds(1000)); + auto filter = std::make_unique(config, Filters::Common::ExtAuthz::ClientPtr{grpc_client}); // Set metadata context. - mocks.setFilterMetadata(input.filter_metadata()); + NiceMock decoder_callbacks; + ON_CALL(decoder_callbacks, connection()) + .WillByDefault(Return(OptRef{mocks.connection_})); + envoy::config::core::v3::Metadata metadata = input.filter_metadata(); + ON_CALL(decoder_callbacks.stream_info_, dynamicMetadata()) + .WillByDefault(testing::ReturnRef(metadata)); + + filter->setDecoderFilterCallbacks(decoder_callbacks); + filter->setEncoderFilterCallbacks(mocks.encoder_callbacks_); // Set check result default action. - envoy::service::auth::v3::CheckRequest check_request; - ON_CALL(*client, check(_, _, _, _)) - .WillByDefault(Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks, - const envoy::service::auth::v3::CheckRequest& check_param, - Tracing::Span&, const StreamInfo::StreamInfo&) -> void { - check_request = check_param; - callbacks.onComplete(makeAuthzResponse(resultCaseToCheckStatus(input.result()))); - })); + ON_CALL(*internal_mock_client, sendRaw(_, _, _, _, _, _)) + .WillByDefault( + Invoke([&](absl::string_view, absl::string_view, Buffer::InstancePtr&& serialized_req, + Grpc::RawAsyncRequestCallbacks&, Tracing::Span&, + const Http::AsyncClient::RequestOptions&) -> Grpc::AsyncRequest* { + envoy::service::auth::v3::CheckRequest check_request; + EXPECT_TRUE(check_request.ParseFromString(serialized_req->toString())) + << "Could not parse serialized check request"; + + // TODO: Query the request header map in HttpFilterFuzzer to test + // headers_to_(add/remove/append). + // TODO: Test check request attributes against config + // and filter metadata. + ENVOY_LOG_MISC(trace, "Check Request attributes {}", + check_request.attributes().DebugString()); + + const Grpc::Status::WellKnownGrpcStatus status = resultCaseToGrpcStatus(input.result()); + if (status == Grpc::Status::WellKnownGrpcStatus::Ok) { + grpc_client->onSuccess(makeGrpcCheckResponse(status), mocks.mock_span_); + } else { + grpc_client->onFailure(status, "Fuzz input status was not ok!", mocks.mock_span_); + } + return &mocks.async_request_; + })); // TODO: Add response headers. Envoy::Extensions::HttpFilters::HttpFilterFuzzer fuzzer; fuzzer.runData(static_cast(filter.get()), input.request_data()); - // TODO: Query the request header map in HttpFilterFuzzer to test headers_to_(add/remove/append). - // TODO: Test check request attributes against config and filter metadata. - ENVOY_LOG_MISC(trace, "Check Request attributes {}", check_request.attributes().DebugString()); } } // namespace From 9e447bfaa2eaf29925bfb279b83e1ad202d1b514 Mon Sep 17 00:00:00 2001 From: GiantCroc Date: Sun, 17 Mar 2024 21:53:10 +0800 Subject: [PATCH 065/124] contrib: add qatzstd compressor (#32166) Signed-off-by: giantcroc --- api/BUILD | 1 + .../qatzstd/compressor/v3alpha/BUILD | 9 ++ .../qatzstd/compressor/v3alpha/qatzstd.proto | 69 +++++++++++ api/versioning/BUILD | 1 + bazel/foreign_cc/qatzstd.patch | 13 +++ bazel/repositories.bzl | 9 ++ bazel/repository_locations.bzl | 17 ++- changelogs/current.yaml | 3 + contrib/all_contrib_extensions.bzl | 2 + contrib/contrib_build_config.bzl | 1 + contrib/extensions_metadata.yaml | 5 + contrib/qat/BUILD | 6 +- .../qatzstd/compressor/source/BUILD | 69 +++++++++++ .../qatzstd/compressor/source/config.cc | 81 +++++++++++++ .../qatzstd/compressor/source/config.h | 83 +++++++++++++ .../source/qatzstd_compressor_impl.cc | 85 ++++++++++++++ .../source/qatzstd_compressor_impl.h | 51 ++++++++ .../compression/qatzstd/compressor/test/BUILD | 20 ++++ .../test/qatzstd_compressor_impl_test.cc | 110 ++++++++++++++++++ docs/BUILD | 1 + docs/root/api-v3/config/contrib/qat/qat.rst | 1 + .../other_features/_include/qatzstd.yaml | 63 ++++++++++ .../other_features/other_features.rst | 1 + .../configuration/other_features/qatzstd.rst | 34 ++++++ source/common/common/logger.h | 1 + source/common/compression/zstd/common/BUILD | 19 +++ .../compression/zstd/common/base.cc | 4 +- .../compression/zstd/common/base.h | 2 - .../common/compression/zstd/compressor/BUILD | 20 ++++ .../compressor/zstd_compressor_impl_base.cc | 59 ++++++++++ .../compressor/zstd_compressor_impl_base.h | 43 +++++++ .../extensions/compression/zstd/common/BUILD | 10 -- .../compression/zstd/compressor/BUILD | 3 +- .../zstd/compressor/zstd_compressor_impl.cc | 47 ++------ .../zstd/compressor/zstd_compressor_impl.h | 20 ++-- .../compression/zstd/decompressor/BUILD | 2 +- .../decompressor/zstd_decompressor_impl.cc | 2 +- .../decompressor/zstd_decompressor_impl.h | 4 +- test/test_common/utility.cc | 6 +- test/test_common/utility.h | 3 +- tools/spelling/spelling_dictionary.txt | 1 + 41 files changed, 909 insertions(+), 72 deletions(-) create mode 100644 api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/BUILD create mode 100644 api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.proto create mode 100644 bazel/foreign_cc/qatzstd.patch create mode 100644 contrib/qat/compression/qatzstd/compressor/source/BUILD create mode 100644 contrib/qat/compression/qatzstd/compressor/source/config.cc create mode 100644 contrib/qat/compression/qatzstd/compressor/source/config.h create mode 100644 contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.cc create mode 100644 contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h create mode 100644 contrib/qat/compression/qatzstd/compressor/test/BUILD create mode 100644 contrib/qat/compression/qatzstd/compressor/test/qatzstd_compressor_impl_test.cc create mode 100644 docs/root/configuration/other_features/_include/qatzstd.yaml create mode 100644 docs/root/configuration/other_features/qatzstd.rst create mode 100644 source/common/compression/zstd/common/BUILD rename source/{extensions => common}/compression/zstd/common/base.cc (86%) rename source/{extensions => common}/compression/zstd/common/base.h (92%) create mode 100644 source/common/compression/zstd/compressor/BUILD create mode 100644 source/common/compression/zstd/compressor/zstd_compressor_impl_base.cc create mode 100644 source/common/compression/zstd/compressor/zstd_compressor_impl_base.h diff --git a/api/BUILD b/api/BUILD index e0efeebce4f7..829715418693 100644 --- a/api/BUILD +++ b/api/BUILD @@ -73,6 +73,7 @@ proto_library( visibility = ["//visibility:public"], deps = [ "//contrib/envoy/extensions/compression/qatzip/compressor/v3alpha:pkg", + "//contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha:pkg", "//contrib/envoy/extensions/filters/http/checksum/v3alpha:pkg", "//contrib/envoy/extensions/filters/http/dynamo/v3:pkg", "//contrib/envoy/extensions/filters/http/golang/v3alpha:pkg", diff --git a/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/BUILD b/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/BUILD new file mode 100644 index 000000000000..29ebf0741406 --- /dev/null +++ b/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/BUILD @@ -0,0 +1,9 @@ +# 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 = ["@com_github_cncf_xds//udpa/annotations:pkg"], +) diff --git a/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.proto b/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.proto new file mode 100644 index 000000000000..182722d01e4f --- /dev/null +++ b/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.proto @@ -0,0 +1,69 @@ +syntax = "proto3"; + +package envoy.extensions.compression.qatzstd.compressor.v3alpha; + +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.compression.qatzstd.compressor.v3alpha"; +option java_outer_classname = "QatzstdProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Qatzstd Compressor] +// Qatzstd :ref:`configuration overview `. +// [#extension: envoy.compression.qatzstd.compressor] + +// [#next-free-field: 8] +message Qatzstd { + // Reference to http://facebook.github.io/zstd/zstd_manual.html + enum Strategy { + DEFAULT = 0; + FAST = 1; + DFAST = 2; + GREEDY = 3; + LAZY = 4; + LAZY2 = 5; + BTLAZY2 = 6; + BTOPT = 7; + BTULTRA = 8; + BTULTRA2 = 9; + } + + // Set compression parameters according to pre-defined compression level table. + // Note that exact compression parameters are dynamically determined, + // depending on both compression level and source content size (when known). + // Value 0 means default, and default level is 3. + // + // Setting a level does not automatically set all other compression parameters + // to default. Setting this will however eventually dynamically impact the compression + // parameters which have not been manually set. The manually set + // ones will 'stick'. + google.protobuf.UInt32Value compression_level = 1 [(validate.rules).uint32 = {lte: 22 gte: 1}]; + + // A 32-bits checksum of content is written at end of frame. If not set, defaults to false. + bool enable_checksum = 2; + + // The higher the value of selected strategy, the more complex it is, + // resulting in stronger and slower compression. + // + // Special: value 0 means "use default strategy". + Strategy strategy = 3 [(validate.rules).enum = {defined_only: true}]; + + // Value for compressor's next output buffer. If not set, defaults to 4096. + google.protobuf.UInt32Value chunk_size = 5 [(validate.rules).uint32 = {lte: 65536 gte: 4096}]; + + // Enable QAT to accelerate Zstd compression or not. If not set, defaults to false. + // + // This is useful in the case that users want to enable QAT for a period of time and disable QAT for another period of time, + // they don't have to change the config too much or prepare for another config that has software zstd compressor and just changing the value of this filed. + bool enable_qat_zstd = 6; + + // Fallback to software for Qatzstd when input size is less than this value. + // Valid only ``enable_qat_zstd`` is ``true``. 0 means no fallback at all. If not set, defaults to 4000. + google.protobuf.UInt32Value qat_zstd_fallback_threshold = 7 + [(validate.rules).uint32 = {lte: 65536 gte: 0}]; +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index eb4569353709..b74e3df2a867 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -10,6 +10,7 @@ proto_library( visibility = ["//visibility:public"], deps = [ "//contrib/envoy/extensions/compression/qatzip/compressor/v3alpha:pkg", + "//contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha:pkg", "//contrib/envoy/extensions/config/v3alpha:pkg", "//contrib/envoy/extensions/filters/http/checksum/v3alpha:pkg", "//contrib/envoy/extensions/filters/http/dynamo/v3:pkg", diff --git a/bazel/foreign_cc/qatzstd.patch b/bazel/foreign_cc/qatzstd.patch new file mode 100644 index 000000000000..84bd231db5e9 --- /dev/null +++ b/bazel/foreign_cc/qatzstd.patch @@ -0,0 +1,13 @@ +diff --git a/src/Makefile b/src/Makefile +index 1abf10d..c5fa3a6 100644 +--- a/src/Makefile ++++ b/src/Makefile +@@ -54,7 +54,7 @@ ifneq ($(ICP_ROOT), ) + endif + else + QATFLAGS = -DINTREE +- LDFLAGS = -lqat ++ LDFLAGS += -lqat + ifneq ($(ENABLE_USDM_DRV), 0) + QATFLAGS += -DENABLE_USDM_DRV + LDFLAGS += -lusdm diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index bec5940badf3..d231f276c7ab 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -313,6 +313,7 @@ def envoy_dependencies(skip_targets = []): _com_github_intel_ipp_crypto_crypto_mb_fips() _com_github_intel_qatlib() _com_github_intel_qatzip() + _com_github_qat_zstd() _com_github_lz4_lz4() _com_github_jbeder_yaml_cpp() _com_github_libevent_libevent() @@ -585,6 +586,14 @@ def _com_github_intel_qatzip(): build_file_content = BUILD_ALL_CONTENT, ) +def _com_github_qat_zstd(): + external_http_archive( + name = "com_github_qat_zstd", + build_file_content = BUILD_ALL_CONTENT, + patch_args = ["-p1"], + patches = ["@envoy//bazel/foreign_cc:qatzstd.patch"], + ) + def _com_github_lz4_lz4(): external_http_archive( name = "com_github_lz4_lz4", diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index b81d31b33a9d..6cddbf98ad60 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -446,7 +446,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( urls = ["https://github.com/intel/qatlib/archive/refs/tags/{version}.tar.gz"], use_category = ["dataplane_ext"], release_date = "2024-02-20", - extensions = ["envoy.tls.key_providers.qat", "envoy.compression.qatzip.compressor"], + extensions = ["envoy.tls.key_providers.qat", "envoy.compression.qatzip.compressor", "envoy.compression.qatzstd.compressor"], cpe = "N/A", license = "BSD-3-Clause", license_url = "https://github.com/intel/qatlib/blob/{version}/LICENSE", @@ -466,6 +466,21 @@ REPOSITORY_LOCATIONS_SPEC = dict( license = "BSD-3-Clause", license_url = "https://github.com/intel/QATzip/blob/v{version}/LICENSE", ), + com_github_qat_zstd = dict( + project_name = "QAT-ZSTD-Plugin", + project_desc = "Intel® QuickAssist Technology ZSTD Plugin (QAT ZSTD Plugin)", + project_url = "https://github.com/intel/QAT-ZSTD-Plugin/", + version = "0.1.0", + sha256 = "74c5bfbb3b0c6f1334e128ee0b43958d1d34751a4762e54e8f970c443e445f33", + strip_prefix = "QAT-ZSTD-Plugin-{version}", + urls = ["https://github.com/intel/QAT-ZSTD-Plugin/archive/refs/tags/v{version}.tar.gz"], + use_category = ["dataplane_ext"], + extensions = [ + "envoy.compression.qatzstd.compressor", + ], + release_date = "2023-09-08", + cpe = "N/A", + ), com_github_luajit_luajit = dict( project_name = "LuaJIT", project_desc = "Just-In-Time compiler for Lua", diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 4bb71b22796c..092048320fae 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -246,6 +246,9 @@ new_features: change: | added support for :ref:`%UPSTREAM_CONNECTION_ID% ` for the upstream connection identifier. +- area: compression + change: | + Added Qatzstd :ref:`compressor `. - area: aws_lambda change: | Added :ref:`host_rewrite ` config to be used diff --git a/contrib/all_contrib_extensions.bzl b/contrib/all_contrib_extensions.bzl index 6b7994a2623b..7caf371f6032 100644 --- a/contrib/all_contrib_extensions.bzl +++ b/contrib/all_contrib_extensions.bzl @@ -18,6 +18,7 @@ ARM64_SKIP_CONTRIB_TARGETS = [ "envoy.tls.key_providers.qat", "envoy.network.connection_balance.dlb", "envoy.compression.qatzip.compressor", + "envoy.compression.qatzstd.compressor", ] PPC_SKIP_CONTRIB_TARGETS = [ "envoy.tls.key_providers.cryptomb", @@ -26,6 +27,7 @@ PPC_SKIP_CONTRIB_TARGETS = [ "envoy.network.connection_balance.dlb", "envoy.regex_engines.hyperscan", "envoy.compression.qatzip.compressor", + "envoy.compression.qatzstd.compressor", ] FIPS_LINUX_X86_SKIP_CONTRIB_TARGETS = [ diff --git a/contrib/contrib_build_config.bzl b/contrib/contrib_build_config.bzl index b8e7811a168c..49b1f7afde79 100644 --- a/contrib/contrib_build_config.bzl +++ b/contrib/contrib_build_config.bzl @@ -5,6 +5,7 @@ CONTRIB_EXTENSIONS = { # "envoy.compression.qatzip.compressor": "//contrib/qat/compression/qatzip/compressor/source:config", + "envoy.compression.qatzstd.compressor": "//contrib/qat/compression/qatzstd/compressor/source:config", # # HTTP filters diff --git a/contrib/extensions_metadata.yaml b/contrib/extensions_metadata.yaml index 8168b063da58..d8aee9d710fb 100644 --- a/contrib/extensions_metadata.yaml +++ b/contrib/extensions_metadata.yaml @@ -18,6 +18,11 @@ envoy.compression.qatzip.compressor: - envoy.compression.compressor security_posture: robust_to_untrusted_downstream_and_upstream status: alpha +envoy.compression.qatzstd.compressor: + categories: + - envoy.compression.compressor + security_posture: robust_to_untrusted_downstream_and_upstream + status: alpha envoy.filters.http.squash: categories: - envoy.filters.http diff --git a/contrib/qat/BUILD b/contrib/qat/BUILD index d435976e4953..08ddf04136c3 100644 --- a/contrib/qat/BUILD +++ b/contrib/qat/BUILD @@ -20,12 +20,16 @@ configure_make( configure_options = [ "--disable-fast-crc-in-assembler", "--disable-systemd", - "--disable-shared", "--with-pic", + "--enable-shared", "--enable-static", "--enable-samples=no", ], lib_source = "@com_github_intel_qatlib//:all", + out_shared_libs = [ + "libqat.so", + "libusdm.so", + ], out_static_libs = [ "libqat.a", "libusdm.a", diff --git a/contrib/qat/compression/qatzstd/compressor/source/BUILD b/contrib/qat/compression/qatzstd/compressor/source/BUILD new file mode 100644 index 000000000000..960fa2b9b55e --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/source/BUILD @@ -0,0 +1,69 @@ +load("@rules_foreign_cc//foreign_cc:defs.bzl", "make") +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_contrib_extension", + "envoy_cc_library", + "envoy_contrib_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_contrib_package() + +make( + name = "qat-zstd", + build_data = ["@com_github_qat_zstd//:all"], + env = select({ + "//bazel:clang_build": { + "CFLAGS": "-Wno-error=unused-parameter -Wno-error=unused-command-line-argument -I$$EXT_BUILD_DEPS/qatlib/include -I$$EXT_BUILD_DEPS/zstd/include", + }, + "//conditions:default": { + "CFLAGS": "-I$$EXT_BUILD_DEPS/qatlib/include -I$$EXT_BUILD_DEPS/zstd/include", + }, + }), + includes = [], + lib_source = "@com_github_qat_zstd//:all", + out_static_libs = ["libqatseqprod.a"], + tags = ["skip_on_windows"], + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + targets = [ + "ENABLE_USDM_DRV=1", + "install", + ], + deps = [ + "//contrib/qat:qatlib", + "//external:zstd", + ], +) + +envoy_cc_library( + name = "compressor_lib", + srcs = ["qatzstd_compressor_impl.cc"], + hdrs = ["qatzstd_compressor_impl.h"], + deps = [ + ":qat-zstd", + "//envoy/compression/compressor:compressor_interface", + "//envoy/server:factory_context_interface", + "//source/common/buffer:buffer_lib", + "//source/common/compression/zstd/common:zstd_base_lib", + "//source/common/compression/zstd/compressor:compressor_base", + ], +) + +envoy_cc_contrib_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + ":compressor_lib", + ":qat-zstd", + "//envoy/event:dispatcher_interface", + "//envoy/thread_local:thread_local_interface", + "//source/common/http:headers_lib", + "//source/extensions/compression/common/compressor:compressor_factory_base_lib", + "@envoy_api//contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha:pkg_cc_proto", + ], +) diff --git a/contrib/qat/compression/qatzstd/compressor/source/config.cc b/contrib/qat/compression/qatzstd/compressor/source/config.cc new file mode 100644 index 000000000000..67a24d530d1b --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/source/config.cc @@ -0,0 +1,81 @@ +#include "contrib/qat/compression/qatzstd/compressor/source/config.h" + +namespace Envoy { +namespace Extensions { +namespace Compression { +namespace Qatzstd { +namespace Compressor { + +QatzstdCompressorFactory::QatzstdCompressorFactory( + const envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd& qatzstd, + ThreadLocal::SlotAllocator& tls) + : compression_level_( + PROTOBUF_GET_WRAPPED_OR_DEFAULT(qatzstd, compression_level, ZSTD_CLEVEL_DEFAULT)), + enable_checksum_(qatzstd.enable_checksum()), strategy_(qatzstd.strategy()), + chunk_size_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(qatzstd, chunk_size, ZSTD_CStreamOutSize())), + enable_qat_zstd_(qatzstd.enable_qat_zstd()), + qat_zstd_fallback_threshold_(PROTOBUF_GET_WRAPPED_OR_DEFAULT( + qatzstd, qat_zstd_fallback_threshold, DefaultQatZstdFallbackThreshold)), + tls_slot_(nullptr) { + if (enable_qat_zstd_) { + tls_slot_ = ThreadLocal::TypedSlot::makeUnique(tls); + tls_slot_->set([](Event::Dispatcher&) { return std::make_shared(); }); + } +} + +QatzstdCompressorFactory::QatzstdThreadLocal::QatzstdThreadLocal() + : initialized_(false), sequenceProducerState_(nullptr) {} + +QatzstdCompressorFactory::QatzstdThreadLocal::~QatzstdThreadLocal() { + if (initialized_) { + /* Free sequence producer state */ + QZSTD_freeSeqProdState(sequenceProducerState_); + /* Stop QAT device, please call this function when + you won't use QAT anymore or before the process exits */ + QZSTD_stopQatDevice(); + } +} + +void* QatzstdCompressorFactory::QatzstdThreadLocal::GetQATSession() { + // The session must be initialized only once in every worker thread. + if (!initialized_) { + + int status = QZSTD_startQatDevice(); + // RELEASE_ASSERT(status == QZSTD_OK, "failed to initialize hardware"); + if (status != QZSTD_OK) { + ENVOY_LOG(warn, "Failed to initialize qat hardware"); + } else { + ENVOY_LOG(debug, "Initialize qat hardware successful"); + } + sequenceProducerState_ = QZSTD_createSeqProdState(); + initialized_ = true; + } + + return sequenceProducerState_; +} + +Envoy::Compression::Compressor::CompressorPtr QatzstdCompressorFactory::createCompressor() { + return std::make_unique( + compression_level_, enable_checksum_, strategy_, chunk_size_, enable_qat_zstd_, + qat_zstd_fallback_threshold_, enable_qat_zstd_ ? tls_slot_->get()->GetQATSession() : nullptr); +} + +Envoy::Compression::Compressor::CompressorFactoryPtr +QatzstdCompressorLibraryFactory::createCompressorFactoryFromProtoTyped( + const envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd& proto_config, + Server::Configuration::FactoryContext& context) { + return std::make_unique(proto_config, + context.serverFactoryContext().threadLocal()); +} + +/** + * Static registration for the zstd compressor library. @see NamedCompressorLibraryConfigFactory. + */ +REGISTER_FACTORY(QatzstdCompressorLibraryFactory, + Envoy::Compression::Compressor::NamedCompressorLibraryConfigFactory); + +} // namespace Compressor +} // namespace Qatzstd +} // namespace Compression +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/qat/compression/qatzstd/compressor/source/config.h b/contrib/qat/compression/qatzstd/compressor/source/config.h new file mode 100644 index 000000000000..8e78cd8123be --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/source/config.h @@ -0,0 +1,83 @@ +#pragma once + +#include "envoy/compression/compressor/factory.h" +#include "envoy/event/dispatcher.h" +#include "envoy/thread_local/thread_local.h" + +#include "source/common/common/logger.h" +#include "source/common/http/headers.h" +#include "source/extensions/compression/common/compressor/factory_base.h" + +#include "contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.pb.h" +#include "contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.pb.validate.h" +#include "contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h" +#include "qatseqprod.h" + +namespace Envoy { +namespace Extensions { +namespace Compression { +namespace Qatzstd { +namespace Compressor { + +// Default threshold for qat_zstd fallback to software. +const uint32_t DefaultQatZstdFallbackThreshold = 4000; + +namespace { + +const std::string& qatzstdStatsPrefix() { CONSTRUCT_ON_FIRST_USE(std::string, "qatzstd."); } +const std::string& qatzstdExtensionName() { + CONSTRUCT_ON_FIRST_USE(std::string, "envoy.compression.qatzstd.compressor"); +} + +} // namespace + +class QatzstdCompressorFactory : public Envoy::Compression::Compressor::CompressorFactory { +public: + QatzstdCompressorFactory( + const envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd& qatzstd, + ThreadLocal::SlotAllocator& tls); + + // Envoy::Compression::Compressor::CompressorFactory + Envoy::Compression::Compressor::CompressorPtr createCompressor() override; + const std::string& statsPrefix() const override { return qatzstdStatsPrefix(); } + const std::string& contentEncoding() const override { + return Http::CustomHeaders::get().ContentEncodingValues.Zstd; + } + +private: + struct QatzstdThreadLocal : public ThreadLocal::ThreadLocalObject, + public Logger::Loggable { + QatzstdThreadLocal(); + ~QatzstdThreadLocal() override; + void* GetQATSession(); + bool initialized_; + void* sequenceProducerState_; + }; + const uint32_t compression_level_; + const bool enable_checksum_; + const uint32_t strategy_; + const uint32_t chunk_size_; + const bool enable_qat_zstd_; + const uint32_t qat_zstd_fallback_threshold_; + ThreadLocal::TypedSlotPtr tls_slot_; +}; + +class QatzstdCompressorLibraryFactory + : public Compression::Common::Compressor::CompressorLibraryFactoryBase< + envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd> { +public: + QatzstdCompressorLibraryFactory() : CompressorLibraryFactoryBase(qatzstdExtensionName()) {} + +private: + Envoy::Compression::Compressor::CompressorFactoryPtr createCompressorFactoryFromProtoTyped( + const envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd& config, + Server::Configuration::FactoryContext& context) override; +}; + +DECLARE_FACTORY(QatzstdCompressorLibraryFactory); + +} // namespace Compressor +} // namespace Qatzstd +} // namespace Compression +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.cc b/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.cc new file mode 100644 index 000000000000..7b1cb85a8aae --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.cc @@ -0,0 +1,85 @@ +#include "contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h" + +namespace Envoy { +namespace Extensions { +namespace Compression { +namespace Qatzstd { +namespace Compressor { + +QatzstdCompressorImpl::QatzstdCompressorImpl(uint32_t compression_level, bool enable_checksum, + uint32_t strategy, uint32_t chunk_size, + bool enable_qat_zstd, + uint32_t qat_zstd_fallback_threshold, + void* sequenceProducerState) + : ZstdCompressorImplBase(compression_level, enable_checksum, strategy, chunk_size), + enable_qat_zstd_(enable_qat_zstd), qat_zstd_fallback_threshold_(qat_zstd_fallback_threshold), + sequenceProducerState_(sequenceProducerState), input_ptr_{std::make_unique( + chunk_size)}, + input_len_(0), chunk_size_(chunk_size) { + size_t result; + result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_compressionLevel, compression_level_); + RELEASE_ASSERT(!ZSTD_isError(result), ""); + + ENVOY_LOG(debug, + "zstd new ZstdCompressorImpl, compression_level: {}, strategy: {}, chunk_size: " + "{}, enable_qat_zstd: {}, qat_zstd_fallback_threshold: {}", + compression_level, strategy, chunk_size, enable_qat_zstd, qat_zstd_fallback_threshold); + if (enable_qat_zstd_) { + /* register qatSequenceProducer */ + ZSTD_registerSequenceProducer(cctx_.get(), sequenceProducerState_, qatSequenceProducer); + result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_enableSeqProducerFallback, 1); + RELEASE_ASSERT(!ZSTD_isError(result), ""); + } +} + +void QatzstdCompressorImpl::compressPreprocess(Buffer::Instance& buffer, + Envoy::Compression::Compressor::State state) { + ENVOY_LOG(debug, "zstd compress input size {}", buffer.length()); + if (enable_qat_zstd_ && state == Envoy::Compression::Compressor::State::Flush) { + // Fall back to software if input size less than threshold to achieve better performance. + if (buffer.length() < qat_zstd_fallback_threshold_) { + ENVOY_LOG(debug, "zstd compress fall back to software"); + ZSTD_registerSequenceProducer(cctx_.get(), nullptr, nullptr); + } + } +} + +void QatzstdCompressorImpl::setInput(const uint8_t* input, size_t size) { + input_.src = input; + input_.pos = 0; + input_.size = size; + input_len_ = 0; +} + +void QatzstdCompressorImpl::compressProcess(const Buffer::Instance& buffer, + const Buffer::RawSlice& slice, + Buffer::Instance& accumulation_buffer) { + if (slice.len_ == buffer.length() || slice.len_ > chunk_size_) { + if (input_len_ > 0) { + setInput(input_ptr_.get(), input_len_); + process(accumulation_buffer, ZSTD_e_continue); + } + setInput(static_cast(slice.mem_), slice.len_); + process(accumulation_buffer, ZSTD_e_continue); + } else { + if (input_len_ + slice.len_ > chunk_size_) { + setInput(input_ptr_.get(), input_len_); + process(accumulation_buffer, ZSTD_e_continue); + } + memcpy(input_ptr_.get() + input_len_, slice.mem_, slice.len_); // NOLINT(safe-memcpy) + input_len_ += slice.len_; + } +} + +void QatzstdCompressorImpl::compressPostprocess(Buffer::Instance& accumulation_buffer) { + if (input_len_ > 0) { + setInput(input_ptr_.get(), input_len_); + process(accumulation_buffer, ZSTD_e_continue); + } +} + +} // namespace Compressor +} // namespace Qatzstd +} // namespace Compression +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h b/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h new file mode 100644 index 000000000000..1960b13b7671 --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h @@ -0,0 +1,51 @@ +#pragma once + +#include "envoy/compression/compressor/compressor.h" +#include "envoy/server/factory_context.h" + +#include "source/common/common/logger.h" +#include "source/common/compression/zstd/common/base.h" +#include "source/common/compression/zstd/compressor/zstd_compressor_impl_base.h" + +#include "qatseqprod.h" + +namespace Envoy { +namespace Extensions { +namespace Compression { +namespace Qatzstd { +namespace Compressor { + +/** + * Implementation of compressor's interface. + */ +class QatzstdCompressorImpl : public Envoy::Compression::Zstd::Compressor::ZstdCompressorImplBase, + public Logger::Loggable { +public: + QatzstdCompressorImpl(uint32_t compression_level, bool enable_checksum, uint32_t strategy, + uint32_t chunk_size, bool enable_qat_zstd, + uint32_t qat_zstd_fallback_threshold, void* sequenceProducerState); + +private: + void compressPreprocess(Buffer::Instance& buffer, + Envoy::Compression::Compressor::State state) override; + + void compressProcess(const Buffer::Instance& buffer, const Buffer::RawSlice& slice, + Buffer::Instance& accumulation_buffer) override; + + void compressPostprocess(Buffer::Instance& accumulation_buffer) override; + + void setInput(const uint8_t* input, size_t size); + + bool enable_qat_zstd_; + const uint32_t qat_zstd_fallback_threshold_; + void* sequenceProducerState_; + std::unique_ptr input_ptr_; + uint64_t input_len_; + uint64_t chunk_size_; +}; + +} // namespace Compressor +} // namespace Qatzstd +} // namespace Compression +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/qat/compression/qatzstd/compressor/test/BUILD b/contrib/qat/compression/qatzstd/compressor/test/BUILD new file mode 100644 index 000000000000..85297caa141a --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/test/BUILD @@ -0,0 +1,20 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_contrib_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_contrib_package() + +envoy_cc_test( + name = "compressor_test", + srcs = ["qatzstd_compressor_impl_test.cc"], + deps = [ + "//contrib/qat/compression/qatzstd/compressor/source:config", + "//source/extensions/compression/zstd/decompressor:decompressor_lib", + "//test/mocks/server:factory_context_mocks", + "//test/test_common:utility_lib", + ], +) diff --git a/contrib/qat/compression/qatzstd/compressor/test/qatzstd_compressor_impl_test.cc b/contrib/qat/compression/qatzstd/compressor/test/qatzstd_compressor_impl_test.cc new file mode 100644 index 000000000000..257a23061ed2 --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/test/qatzstd_compressor_impl_test.cc @@ -0,0 +1,110 @@ +#include "source/common/buffer/buffer_impl.h" +#include "source/common/stats/isolated_store_impl.h" +#include "source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.h" + +#include "test/mocks/server/factory_context.h" +#include "test/test_common/utility.h" + +#include "contrib/qat/compression/qatzstd/compressor/source/config.h" +#include "gtest/gtest.h" +#include "qatseqprod.h" + +namespace Envoy { +namespace Extensions { +namespace Compression { +namespace Qatzstd { +namespace Compressor { +namespace { + +class QatzstdCompressorImplTest : public testing::Test { +protected: + void drainBuffer(Buffer::OwnedImpl& buffer) { + buffer.drain(buffer.length()); + ASSERT_EQ(0, buffer.length()); + } + + void verifyWithDecompressor(Envoy::Compression::Compressor::CompressorPtr compressor) { + Buffer::OwnedImpl buffer; + Buffer::OwnedImpl accumulation_buffer; + std::string original_text{}; + for (uint64_t i = 0; i < 10; i++) { + TestUtility::feedBufferWithRandomCharacters(buffer, default_input_size_ * i, i, i); + original_text.append(buffer.toString()); + ASSERT_EQ(default_input_size_ * i * i, buffer.length()); + compressor->compress(buffer, Envoy::Compression::Compressor::State::Flush); + accumulation_buffer.add(buffer); + drainBuffer(buffer); + } + + compressor->compress(buffer, Envoy::Compression::Compressor::State::Finish); + accumulation_buffer.add(buffer); + drainBuffer(buffer); + + Stats::IsolatedStoreImpl stats_store{}; + Zstd::Decompressor::ZstdDecompressorImpl decompressor{*stats_store.rootScope(), "test.", + default_ddict_manager_, 4096}; + + decompressor.decompress(accumulation_buffer, buffer); + std::string decompressed_text{buffer.toString()}; + + ASSERT_EQ(original_text.length(), decompressed_text.length()); + EXPECT_EQ(original_text, decompressed_text); + } + + Envoy::Compression::Compressor::CompressorFactoryPtr + createQatzstdCompressorFactoryFromConfig(const std::string& json) { + envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd qatzstd_config; + TestUtility::loadFromJson(json, qatzstd_config); + + return qatzstd_compressor_library_factory_.createCompressorFactoryFromProto(qatzstd_config, + context_); + } + + uint32_t default_input_size_{796}; + Zstd::Decompressor::ZstdDDictManagerPtr default_ddict_manager_{nullptr}; + QatzstdCompressorLibraryFactory qatzstd_compressor_library_factory_; + NiceMock context_; +}; + +class QatzstdConfigTest : public QatzstdCompressorImplTest, + public ::testing::WithParamInterface> {}; + +// These tests should pass even if required hardware or setup steps required for qatzstd are +// missing. Qatzstd uses a sofware fallback in this case. +INSTANTIATE_TEST_SUITE_P( + QatzstdConfigTestInstantiation, QatzstdConfigTest, + // First tuple has all default values. + ::testing::Values(std::make_tuple(1, 4096, true, 4096), std::make_tuple(2, 4096, true, 4096), + std::make_tuple(3, 65536, true, 4096), std::make_tuple(4, 4096, true, 4096), + std::make_tuple(5, 8192, true, 1024), std::make_tuple(6, 4096, false, 1024), + std::make_tuple(7, 4096, true, 1024), std::make_tuple(8, 8192, true, 4096), + std::make_tuple(9, 8192, true, 1024), std::make_tuple(10, 16384, true, 1024), + std::make_tuple(11, 8192, true, 8192), + std::make_tuple(12, 4096, true, 1024))); + +TEST_P(QatzstdConfigTest, LoadConfigAndVerifyWithDecompressor) { + std::tuple config_value_tuple = GetParam(); + std::string json{fmt::format(R"EOF({{ + "compression_level": {}, + "chunk_size": {}, + "enable_qat_zstd": {}, + "qat_zstd_fallback_threshold": {}, +}})EOF", + std::get<0>(config_value_tuple), std::get<1>(config_value_tuple), + std::get<2>(config_value_tuple), std::get<3>(config_value_tuple))}; + + Envoy::Compression::Compressor::CompressorFactoryPtr qatzstd_compressor_factory = + createQatzstdCompressorFactoryFromConfig(json); + + EXPECT_EQ("zstd", qatzstd_compressor_factory->contentEncoding()); + EXPECT_EQ("qatzstd.", qatzstd_compressor_factory->statsPrefix()); + + verifyWithDecompressor(qatzstd_compressor_factory->createCompressor()); +} + +} // namespace +} // namespace Compressor +} // namespace Qatzstd +} // namespace Compression +} // namespace Extensions +} // namespace Envoy diff --git a/docs/BUILD b/docs/BUILD index 16df02c3e5a6..3497d8d9f186 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -35,6 +35,7 @@ filegroup( "root/configuration/other_features/_include/hyperscan_matcher_multiple.yaml", "root/configuration/other_features/_include/hyperscan_regex_engine.yaml", "root/configuration/other_features/_include/qatzip.yaml", + "root/configuration/other_features/_include/qatzstd.yaml", "root/intro/arch_overview/security/_include/ssl.yaml", "root/configuration/listeners/network_filters/_include/generic_proxy_filter.yaml", "root/configuration/overview/_include/xds_api/oauth-sds-example.yaml", diff --git a/docs/root/api-v3/config/contrib/qat/qat.rst b/docs/root/api-v3/config/contrib/qat/qat.rst index 3e524d700b13..c03e01dd4397 100644 --- a/docs/root/api-v3/config/contrib/qat/qat.rst +++ b/docs/root/api-v3/config/contrib/qat/qat.rst @@ -4,3 +4,4 @@ ../../../extensions/private_key_providers/qat/v3alpha/* ../../../extensions/compression/qatzip/compressor/v3alpha/* + ../../../extensions/compression/qatzstd/compressor/v3alpha/* diff --git a/docs/root/configuration/other_features/_include/qatzstd.yaml b/docs/root/configuration/other_features/_include/qatzstd.yaml new file mode 100644 index 000000000000..4a33cfe65094 --- /dev/null +++ b/docs/root/configuration/other_features/_include/qatzstd.yaml @@ -0,0 +1,63 @@ +static_resources: + listeners: + - address: + socket_address: + address: 0.0.0.0 + port_value: 10000 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: backend + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: service + http_filters: + - name: envoy.filters.http.compressor + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor + response_direction_config: + common_config: + min_content_length: 100 + content_type: + - application/octet-stream + disable_on_etag_header: false + compressor_library: + name: text_optimized + typed_config: + "@type": type.googleapis.com/envoy.extensions.compression.qatzstd.compressor.v3alpha.Qatzstd + compression_level: 9 + enable_qat_zstd: true + chunk_size: 65536 + qat_zstd_fallback_threshold: 0 + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: service + connect_timeout: 0.25s + type: STRICT_DNS + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 1234 +admin: + address: + socket_address: + address: 0.0.0.0 + port_value: 9901 diff --git a/docs/root/configuration/other_features/other_features.rst b/docs/root/configuration/other_features/other_features.rst index bd54f4e7a129..81c5ca3046cc 100644 --- a/docs/root/configuration/other_features/other_features.rst +++ b/docs/root/configuration/other_features/other_features.rst @@ -12,4 +12,5 @@ Other features wasm wasm_service qatzip + qatzstd string_matcher diff --git a/docs/root/configuration/other_features/qatzstd.rst b/docs/root/configuration/other_features/qatzstd.rst new file mode 100644 index 000000000000..e963c862e6d7 --- /dev/null +++ b/docs/root/configuration/other_features/qatzstd.rst @@ -0,0 +1,34 @@ +.. _config_qatzstd: + +Qatzstd Compressor +======================= + +* :ref:`v3 API reference ` + + +Qatzstd compressor provides Envoy with faster hardware-accelerated zstd compression by integrating with `Intel® QuickAssist Technology (Intel® QAT) `_ through the qatlib and QAT-ZSTD-Plugin libraries. + +Example configuration +--------------------- + +An example for Qatzstd compressor configuration is: + +.. literalinclude:: _include/qatzstd.yaml + :language: yaml + + +How it works +------------ + +If enabled, the Qatzstd compressor will: + +- attach Qat hardware +- create Threadlocal Qatzstd context for each worker thread + +When new http package come, one worker thread will process it using its Qatzstd context and send the data needed to be compressed to +Qat hardware using standard zstd api. + +Installing and using QAT-ZSTD-Plugin +------------------------------------ + +For information on how to build/install and use QAT-ZSTD-Plugin see `introduction `_. diff --git a/source/common/common/logger.h b/source/common/common/logger.h index a90e43628e1e..4f0e7375f459 100644 --- a/source/common/common/logger.h +++ b/source/common/common/logger.h @@ -45,6 +45,7 @@ const static bool should_log = true; FUNCTION(config) \ FUNCTION(connection) \ FUNCTION(conn_handler) \ + FUNCTION(compression) \ FUNCTION(decompression) \ FUNCTION(dns) \ FUNCTION(dubbo) \ diff --git a/source/common/compression/zstd/common/BUILD b/source/common/compression/zstd/common/BUILD new file mode 100644 index 000000000000..e9ecaa5338b2 --- /dev/null +++ b/source/common/compression/zstd/common/BUILD @@ -0,0 +1,19 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_library( + name = "zstd_base_lib", + srcs = ["base.cc"], + hdrs = ["base.h"], + external_deps = ["zstd"], + deps = [ + "//source/common/buffer:buffer_lib", + ], +) diff --git a/source/extensions/compression/zstd/common/base.cc b/source/common/compression/zstd/common/base.cc similarity index 86% rename from source/extensions/compression/zstd/common/base.cc rename to source/common/compression/zstd/common/base.cc index 3682f3626934..89dc2941cb2d 100644 --- a/source/extensions/compression/zstd/common/base.cc +++ b/source/common/compression/zstd/common/base.cc @@ -1,7 +1,6 @@ -#include "source/extensions/compression/zstd/common/base.h" +#include "source/common/compression/zstd/common/base.h" namespace Envoy { -namespace Extensions { namespace Compression { namespace Zstd { namespace Common { @@ -27,5 +26,4 @@ void Base::getOutput(Buffer::Instance& output_buffer) { } // namespace Common } // namespace Zstd } // namespace Compression -} // namespace Extensions } // namespace Envoy diff --git a/source/extensions/compression/zstd/common/base.h b/source/common/compression/zstd/common/base.h similarity index 92% rename from source/extensions/compression/zstd/common/base.h rename to source/common/compression/zstd/common/base.h index 6a3c626a3701..8582fe3f86f3 100644 --- a/source/extensions/compression/zstd/common/base.h +++ b/source/common/compression/zstd/common/base.h @@ -7,7 +7,6 @@ #include "zstd.h" namespace Envoy { -namespace Extensions { namespace Compression { namespace Zstd { namespace Common { @@ -29,5 +28,4 @@ struct Base { } // namespace Common } // namespace Zstd } // namespace Compression -} // namespace Extensions } // namespace Envoy diff --git a/source/common/compression/zstd/compressor/BUILD b/source/common/compression/zstd/compressor/BUILD new file mode 100644 index 000000000000..16a3fbe97f1e --- /dev/null +++ b/source/common/compression/zstd/compressor/BUILD @@ -0,0 +1,20 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_library( + name = "compressor_base", + srcs = ["zstd_compressor_impl_base.cc"], + hdrs = ["zstd_compressor_impl_base.h"], + deps = [ + "//envoy/compression/compressor:compressor_interface", + "//source/common/buffer:buffer_lib", + "//source/common/compression/zstd/common:zstd_base_lib", + ], +) diff --git a/source/common/compression/zstd/compressor/zstd_compressor_impl_base.cc b/source/common/compression/zstd/compressor/zstd_compressor_impl_base.cc new file mode 100644 index 000000000000..2a04f72b83c0 --- /dev/null +++ b/source/common/compression/zstd/compressor/zstd_compressor_impl_base.cc @@ -0,0 +1,59 @@ +#include "source/common/compression/zstd/compressor/zstd_compressor_impl_base.h" + +#include "source/common/buffer/buffer_impl.h" + +namespace Envoy { +namespace Compression { +namespace Zstd { +namespace Compressor { + +ZstdCompressorImplBase::ZstdCompressorImplBase(uint32_t compression_level, bool enable_checksum, + uint32_t strategy, uint32_t chunk_size) + : Common::Base(chunk_size), cctx_(ZSTD_createCCtx(), &ZSTD_freeCCtx), + compression_level_(compression_level) { + size_t result; + result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_checksumFlag, enable_checksum); + RELEASE_ASSERT(!ZSTD_isError(result), ""); + + result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_strategy, strategy); + RELEASE_ASSERT(!ZSTD_isError(result), ""); +} + +void ZstdCompressorImplBase::compress(Buffer::Instance& buffer, + Envoy::Compression::Compressor::State state) { + compressPreprocess(buffer, state); + + Buffer::OwnedImpl accumulation_buffer; + for (const Buffer::RawSlice& input_slice : buffer.getRawSlices()) { + if (input_slice.len_ > 0) { + compressProcess(buffer, input_slice, accumulation_buffer); + buffer.drain(input_slice.len_); + } + } + + compressPostprocess(accumulation_buffer); + + ASSERT(buffer.length() == 0); + buffer.move(accumulation_buffer); + + if (state == Envoy::Compression::Compressor::State::Finish) { + process(buffer, ZSTD_e_end); + } +} + +void ZstdCompressorImplBase::process(Buffer::Instance& output_buffer, ZSTD_EndDirective mode) { + bool finished; + do { + const size_t remaining = ZSTD_compressStream2(cctx_.get(), &output_, &input_, mode); + getOutput(output_buffer); + // If we're on the last chunk we're finished when zstd returns 0, + // which means its consumed all the input AND finished the frame. + // Otherwise, we're finished when we've consumed all the input. + finished = (ZSTD_e_end == mode) ? (remaining == 0) : (input_.pos == input_.size); + } while (!finished); +} + +} // namespace Compressor +} // namespace Zstd +} // namespace Compression +} // namespace Envoy diff --git a/source/common/compression/zstd/compressor/zstd_compressor_impl_base.h b/source/common/compression/zstd/compressor/zstd_compressor_impl_base.h new file mode 100644 index 000000000000..954b3aa17f9a --- /dev/null +++ b/source/common/compression/zstd/compressor/zstd_compressor_impl_base.h @@ -0,0 +1,43 @@ +#pragma once + +#include "envoy/compression/compressor/compressor.h" + +#include "source/common/compression/zstd/common/base.h" + +namespace Envoy { +namespace Compression { +namespace Zstd { +namespace Compressor { + +/** + * Implementation of compressor's interface. + */ +class ZstdCompressorImplBase : public Common::Base, + public Envoy::Compression::Compressor::Compressor, + NonCopyable { +public: + ZstdCompressorImplBase(uint32_t compression_level, bool enable_checksum, uint32_t strategy, + uint32_t chunk_size); + + // Compression::Compressor::Compressor + void compress(Buffer::Instance& buffer, Envoy::Compression::Compressor::State state) override; + + void process(Buffer::Instance& output_buffer, ZSTD_EndDirective mode); + +protected: + virtual void compressPreprocess(Buffer::Instance& buffer, + Envoy::Compression::Compressor::State state) PURE; + + virtual void compressProcess(const Buffer::Instance& buffer, const Buffer::RawSlice& input_slice, + Buffer::Instance& accumulation_buffer) PURE; + + virtual void compressPostprocess(Buffer::Instance& accumulation_buffer) PURE; + + std::unique_ptr cctx_; + const uint32_t compression_level_; +}; + +} // namespace Compressor +} // namespace Zstd +} // namespace Compression +} // namespace Envoy diff --git a/source/extensions/compression/zstd/common/BUILD b/source/extensions/compression/zstd/common/BUILD index 98b6a3abb62f..a0aa94556434 100644 --- a/source/extensions/compression/zstd/common/BUILD +++ b/source/extensions/compression/zstd/common/BUILD @@ -8,16 +8,6 @@ licenses(["notice"]) # Apache 2 envoy_extension_package() -envoy_cc_library( - name = "zstd_base_lib", - srcs = ["base.cc"], - hdrs = ["base.h"], - external_deps = ["zstd"], - deps = [ - "//source/common/buffer:buffer_lib", - ], -) - envoy_cc_library( name = "zstd_dictionary_manager_lib", hdrs = ["dictionary_manager.h"], diff --git a/source/extensions/compression/zstd/compressor/BUILD b/source/extensions/compression/zstd/compressor/BUILD index e6167ff1a1cb..76c3343a3202 100644 --- a/source/extensions/compression/zstd/compressor/BUILD +++ b/source/extensions/compression/zstd/compressor/BUILD @@ -16,7 +16,8 @@ envoy_cc_library( deps = [ "//envoy/compression/compressor:compressor_interface", "//source/common/buffer:buffer_lib", - "//source/extensions/compression/zstd/common:zstd_base_lib", + "//source/common/compression/zstd/common:zstd_base_lib", + "//source/common/compression/zstd/compressor:compressor_base", "//source/extensions/compression/zstd/common:zstd_dictionary_manager_lib", ], ) diff --git a/source/extensions/compression/zstd/compressor/zstd_compressor_impl.cc b/source/extensions/compression/zstd/compressor/zstd_compressor_impl.cc index 89d3ed754e65..55ece012d086 100644 --- a/source/extensions/compression/zstd/compressor/zstd_compressor_impl.cc +++ b/source/extensions/compression/zstd/compressor/zstd_compressor_impl.cc @@ -1,7 +1,5 @@ #include "source/extensions/compression/zstd/compressor/zstd_compressor_impl.h" -#include "source/common/buffer/buffer_impl.h" - namespace Envoy { namespace Extensions { namespace Compression { @@ -11,15 +9,9 @@ namespace Compressor { ZstdCompressorImpl::ZstdCompressorImpl(uint32_t compression_level, bool enable_checksum, uint32_t strategy, const ZstdCDictManagerPtr& cdict_manager, uint32_t chunk_size) - : Common::Base(chunk_size), cctx_(ZSTD_createCCtx(), &ZSTD_freeCCtx), - cdict_manager_(cdict_manager), compression_level_(compression_level) { + : ZstdCompressorImplBase(compression_level, enable_checksum, strategy, chunk_size), + cdict_manager_(cdict_manager) { size_t result; - result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_checksumFlag, enable_checksum); - RELEASE_ASSERT(!ZSTD_isError(result), ""); - - result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_strategy, strategy); - RELEASE_ASSERT(!ZSTD_isError(result), ""); - if (cdict_manager_) { ZSTD_CDict* cdict = cdict_manager_->getFirstDictionary(); result = ZSTD_CCtx_refCDict(cctx_.get(), cdict); @@ -29,36 +21,17 @@ ZstdCompressorImpl::ZstdCompressorImpl(uint32_t compression_level, bool enable_c RELEASE_ASSERT(!ZSTD_isError(result), ""); } -void ZstdCompressorImpl::compress(Buffer::Instance& buffer, - Envoy::Compression::Compressor::State state) { - Buffer::OwnedImpl accumulation_buffer; - for (const Buffer::RawSlice& input_slice : buffer.getRawSlices()) { - if (input_slice.len_ > 0) { - setInput(input_slice); - process(accumulation_buffer, ZSTD_e_continue); - buffer.drain(input_slice.len_); - } - } - - ASSERT(buffer.length() == 0); - buffer.move(accumulation_buffer); +void ZstdCompressorImpl::compressPreprocess(Buffer::Instance&, + Envoy::Compression::Compressor::State) {} - if (state == Envoy::Compression::Compressor::State::Finish) { - process(buffer, ZSTD_e_end); - } +void ZstdCompressorImpl::compressProcess(const Buffer::Instance&, + const Buffer::RawSlice& input_slice, + Buffer::Instance& accumulation_buffer) { + setInput(input_slice); + process(accumulation_buffer, ZSTD_e_continue); } -void ZstdCompressorImpl::process(Buffer::Instance& output_buffer, ZSTD_EndDirective mode) { - bool finished; - do { - const size_t remaining = ZSTD_compressStream2(cctx_.get(), &output_, &input_, mode); - getOutput(output_buffer); - // If we're on the last chunk we're finished when zstd returns 0, - // which means its consumed all the input AND finished the frame. - // Otherwise, we're finished when we've consumed all the input. - finished = (ZSTD_e_end == mode) ? (remaining == 0) : (input_.pos == input_.size); - } while (!finished); -} +void ZstdCompressorImpl::compressPostprocess(Buffer::Instance&) {} } // namespace Compressor } // namespace Zstd diff --git a/source/extensions/compression/zstd/compressor/zstd_compressor_impl.h b/source/extensions/compression/zstd/compressor/zstd_compressor_impl.h index e0adaec7caaf..95da3d1a8e12 100644 --- a/source/extensions/compression/zstd/compressor/zstd_compressor_impl.h +++ b/source/extensions/compression/zstd/compressor/zstd_compressor_impl.h @@ -2,7 +2,8 @@ #include "envoy/compression/compressor/compressor.h" -#include "source/extensions/compression/zstd/common/base.h" +#include "source/common/compression/zstd/common/base.h" +#include "source/common/compression/zstd/compressor/zstd_compressor_impl_base.h" #include "source/extensions/compression/zstd/common/dictionary_manager.h" namespace Envoy { @@ -18,22 +19,21 @@ using ZstdCDictManagerPtr = std::unique_ptr; /** * Implementation of compressor's interface. */ -class ZstdCompressorImpl : public Common::Base, - public Envoy::Compression::Compressor::Compressor, - NonCopyable { +class ZstdCompressorImpl : public Envoy::Compression::Zstd::Compressor::ZstdCompressorImplBase { public: ZstdCompressorImpl(uint32_t compression_level, bool enable_checksum, uint32_t strategy, const ZstdCDictManagerPtr& cdict_manager, uint32_t chunk_size); - // Compression::Compressor::Compressor - void compress(Buffer::Instance& buffer, Envoy::Compression::Compressor::State state) override; - private: - void process(Buffer::Instance& output_buffer, ZSTD_EndDirective mode); + void compressPreprocess(Buffer::Instance& buffer, + Envoy::Compression::Compressor::State state) override; + + void compressProcess(const Buffer::Instance& buffer, const Buffer::RawSlice& input_slice, + Buffer::Instance& accumulation_buffer) override; + + void compressPostprocess(Buffer::Instance& accumulation_buffer) override; - std::unique_ptr cctx_; const ZstdCDictManagerPtr& cdict_manager_; - const uint32_t compression_level_; }; } // namespace Compressor diff --git a/source/extensions/compression/zstd/decompressor/BUILD b/source/extensions/compression/zstd/decompressor/BUILD index 9fc4383b939a..361a3b866f23 100644 --- a/source/extensions/compression/zstd/decompressor/BUILD +++ b/source/extensions/compression/zstd/decompressor/BUILD @@ -18,7 +18,7 @@ envoy_cc_library( "//envoy/stats:stats_interface", "//envoy/stats:stats_macros", "//source/common/buffer:buffer_lib", - "//source/extensions/compression/zstd/common:zstd_base_lib", + "//source/common/compression/zstd/common:zstd_base_lib", "//source/extensions/compression/zstd/common:zstd_dictionary_manager_lib", ], ) diff --git a/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.cc b/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.cc index 7a165da1973c..e2a00232c120 100644 --- a/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.cc +++ b/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.cc @@ -21,7 +21,7 @@ constexpr uint64_t MaxInflateRatio = 100; ZstdDecompressorImpl::ZstdDecompressorImpl(Stats::Scope& scope, const std::string& stats_prefix, const ZstdDDictManagerPtr& ddict_manager, uint32_t chunk_size) - : Common::Base(chunk_size), dctx_(ZSTD_createDCtx(), &ZSTD_freeDCtx), + : Envoy::Compression::Zstd::Common::Base(chunk_size), dctx_(ZSTD_createDCtx(), &ZSTD_freeDCtx), ddict_manager_(ddict_manager), stats_(generateStats(stats_prefix, scope)) {} void ZstdDecompressorImpl::decompress(const Buffer::Instance& input_buffer, diff --git a/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.h b/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.h index 597a8c72ad0e..5f7ed470e3e4 100644 --- a/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.h +++ b/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.h @@ -5,7 +5,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/common/logger.h" -#include "source/extensions/compression/zstd/common/base.h" +#include "source/common/compression/zstd/common/base.h" #include "source/extensions/compression/zstd/common/dictionary_manager.h" #include "zstd_errors.h" @@ -39,7 +39,7 @@ struct ZstdDecompressorStats { /** * Implementation of decompressor's interface. */ -class ZstdDecompressorImpl : public Common::Base, +class ZstdDecompressorImpl : public Envoy::Compression::Zstd::Common::Base, public Envoy::Compression::Decompressor::Decompressor, public Logger::Loggable, NonCopyable { diff --git a/test/test_common/utility.cc b/test/test_common/utility.cc index 7f40c6e3efd3..8b1843373671 100644 --- a/test/test_common/utility.cc +++ b/test/test_common/utility.cc @@ -130,7 +130,7 @@ bool TestUtility::rawSlicesEqual(const Buffer::RawSlice* lhs, const Buffer::RawS } void TestUtility::feedBufferWithRandomCharacters(Buffer::Instance& buffer, uint64_t n_char, - uint64_t seed) { + uint64_t seed, uint64_t n_slice) { const std::string sample = "Neque porro quisquam est qui dolorem ipsum.."; std::mt19937 generate(seed); std::uniform_int_distribution<> distribute(1, sample.length() - 1); @@ -138,7 +138,9 @@ void TestUtility::feedBufferWithRandomCharacters(Buffer::Instance& buffer, uint6 for (uint64_t n = 0; n < n_char; ++n) { str += sample.at(distribute(generate)); } - buffer.add(str); + for (uint64_t n = 0; n < n_slice; ++n) { + buffer.add(str); + } } Stats::CounterSharedPtr TestUtility::findCounter(Stats::Store& store, const std::string& name) { diff --git a/test/test_common/utility.h b/test/test_common/utility.h index c365450e83a4..77e0785ea210 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -178,9 +178,10 @@ class TestUtility { * @param buffer supplies the buffer to be fed. * @param n_char number of characters that should be added to the supplied buffer. * @param seed seeds pseudo-random number generator (default = 0). + * @param n_slice number of slices (default = 1). */ static void feedBufferWithRandomCharacters(Buffer::Instance& buffer, uint64_t n_char, - uint64_t seed = 0); + uint64_t seed = 0, uint64_t n_slice = 1); /** * Finds a stat in a vector with the given name. diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 1a1eae3faa56..1edc5139573f 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -354,6 +354,7 @@ QUIC QoS qat qatzip +qatzstd RAII RANLUX RBAC From 3f248d591fcc499386d037b2f5ad562b0c829196 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:08:26 +0000 Subject: [PATCH 066/124] build(deps): bump redis from `e647cfe` to `3134997` in /examples/redis (#32926) Bumps redis from `e647cfe` to `3134997`. --- updated-dependencies: - dependency-name: redis dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/redis/Dockerfile-redis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/redis/Dockerfile-redis b/examples/redis/Dockerfile-redis index c0098b611cf2..825ed0e86758 100644 --- a/examples/redis/Dockerfile-redis +++ b/examples/redis/Dockerfile-redis @@ -1 +1 @@ -FROM redis@sha256:e647cfe134bf5e8e74e620f66346f93418acfc240b71dd85640325cb7cd01402 +FROM redis@sha256:3134997edb04277814aa51a4175a588d45eb4299272f8eff2307bbf8b39e4d43 From f4fda5aef258ae6ca53cfb60f7b39d47355b932e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:09:11 +0000 Subject: [PATCH 067/124] build(deps): bump setuptools from 69.1.1 to 69.2.0 in /tools/base (#32898) Bumps [setuptools](https://github.com/pypa/setuptools) from 69.1.1 to 69.2.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v69.1.1...v69.2.0) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 019da779c73f..542b6bd88dcd 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -1604,7 +1604,7 @@ zstandard==0.22.0 \ # via envoy-base-utils # The following packages are considered to be unsafe in a requirements file: -setuptools==69.1.1 \ - --hash=sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56 \ - --hash=sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8 +setuptools==69.2.0 \ + --hash=sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e \ + --hash=sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c # via -r requirements.in From b9d8dec81d5b04e850b57bbb28df6187279898a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:09:44 +0000 Subject: [PATCH 068/124] build(deps): bump actions/checkout from 4.1.1 to 4.1.2 (#32865) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/b4ffde65f46336ab88eb53be808477a3936bae11...9bb56186c3b09b4f86b1c65136769dd318469633) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/_precheck_deps.yml | 2 +- .github/workflows/codeql-daily.yml | 2 +- .github/workflows/codeql-push.yml | 2 +- .github/workflows/envoy-dependency.yml | 4 ++-- .github/workflows/mobile-release.yml | 2 +- .github/workflows/mobile-traffic_director.yml | 2 +- .github/workflows/pr_notifier.yml | 2 +- .github/workflows/scorecard.yml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/_precheck_deps.yml b/.github/workflows/_precheck_deps.yml index 8864b7beada4..241fdce64090 100644 --- a/.github/workflows/_precheck_deps.yml +++ b/.github/workflows/_precheck_deps.yml @@ -50,7 +50,7 @@ jobs: if: ${{ inputs.dependency-review }} steps: - name: Checkout Repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 with: ref: ${{ fromJSON(inputs.request).request.sha }} persist-credentials: false diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index 0217ac0c4462..6f2a31997585 100644 --- a/.github/workflows/codeql-daily.yml +++ b/.github/workflows/codeql-daily.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Free disk space uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.27 diff --git a/.github/workflows/codeql-push.yml b/.github/workflows/codeql-push.yml index 91cfb8b2df1f..06e1f29a7260 100644 --- a/.github/workflows/codeql-push.yml +++ b/.github/workflows/codeql-push.yml @@ -32,7 +32,7 @@ jobs: if: github.repository == 'envoyproxy/envoy' steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 with: fetch-depth: 2 diff --git a/.github/workflows/envoy-dependency.yml b/.github/workflows/envoy-dependency.yml index 1aae9d60d58d..286b9f248ea1 100644 --- a/.github/workflows/envoy-dependency.yml +++ b/.github/workflows/envoy-dependency.yml @@ -143,7 +143,7 @@ jobs: path: envoy fetch-depth: 0 token: ${{ steps.appauth.outputs.token }} - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 name: Checkout Envoy build tools repository with: repository: envoyproxy/envoy-build-tools @@ -235,7 +235,7 @@ jobs: issues: write steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Run dependency checker run: | TODAY_DATE=$(date -u -I"date") diff --git a/.github/workflows/mobile-release.yml b/.github/workflows/mobile-release.yml index d051821814f0..c0a0e3981b97 100644 --- a/.github/workflows/mobile-release.yml +++ b/.github/workflows/mobile-release.yml @@ -95,7 +95,7 @@ jobs: - output: envoy - output: envoy_xds steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 with: fetch-depth: 0 - name: Add safe directory diff --git a/.github/workflows/mobile-traffic_director.yml b/.github/workflows/mobile-traffic_director.yml index 4458469f0636..b4fa71cf30bd 100644 --- a/.github/workflows/mobile-traffic_director.yml +++ b/.github/workflows/mobile-traffic_director.yml @@ -30,7 +30,7 @@ jobs: timeout-minutes: 120 steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Add safe directory run: git config --global --add safe.directory /__w/envoy/envoy - name: 'Run GcpTrafficDirectorIntegrationTest' diff --git a/.github/workflows/pr_notifier.yml b/.github/workflows/pr_notifier.yml index 95083e527889..06e8ff695cc7 100644 --- a/.github/workflows/pr_notifier.yml +++ b/.github/workflows/pr_notifier.yml @@ -22,7 +22,7 @@ jobs: || !contains(github.actor, '[bot]')) }} steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Notify about PRs run: | ARGS=() diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 8c4bb04b2ba0..589daf48cffe 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -21,7 +21,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 with: persist-credentials: false From dc21640dff119e1365aa8286b7c34a1f0585e147 Mon Sep 17 00:00:00 2001 From: "dependency-envoy[bot]" <148525496+dependency-envoy[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:11:43 +0000 Subject: [PATCH 069/124] deps: Bump `aspect_bazel_lib` -> 2.5.3 (#32932) Signed-off-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> Co-authored-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> --- bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 6cddbf98ad60..ae267e507a16 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -148,12 +148,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Aspect Bazel helpers", project_desc = "Base Starlark libraries and basic Bazel rules which are useful for constructing rulesets and BUILD files", project_url = "https://github.com/aspect-build/bazel-lib", - version = "2.4.2", - sha256 = "f75d03783588e054899eb0729a97fb5b8973c1a26f30373fafd485c90bf207d1", + version = "2.5.3", + sha256 = "6c25c59581041ede31e117693047f972cc4700c89acf913658dc89d04c338f8d", strip_prefix = "bazel-lib-{version}", urls = ["https://github.com/aspect-build/bazel-lib/archive/v{version}.tar.gz"], use_category = ["build"], - release_date = "2024-02-21", + release_date = "2024-03-08", cpe = "N/A", license = "Apache-2.0", license_url = "https://github.com/aspect-build/bazel-lib/blob/v{version}/LICENSE", From 50e9108c22c2e46d4ad04a977d24d89201aa0c62 Mon Sep 17 00:00:00 2001 From: "Vikas Choudhary (vikasc)" Date: Mon, 18 Mar 2024 19:20:20 +0530 Subject: [PATCH 070/124] Support upstream http filters with tcp tunneling (#27183) As suggested in this comment, Router::UpstreamRequest is being initialized and owned by TcpProxy::CombinedUpstream, which is replacement for TcpProxy::HttpUpstream. Eventually TcpProxy::HttpUpstream will be removed. "Combined" in CombinedUpstream signifies that logic of this class,unlike current TcpProxy::HttpUpstream , does not depend on H1/H2/H3 Signed-off-by: Vikas Choudhary --- envoy/router/router.h | 11 + envoy/tcp/BUILD | 1 + envoy/tcp/upstream.h | 15 +- source/common/http/conn_manager_impl.h | 1 + source/common/http/filter_manager.cc | 12 +- source/common/http/filter_manager.h | 5 + source/common/router/router.cc | 10 +- source/common/router/upstream_request.cc | 12 +- source/common/router/upstream_request.h | 23 +- source/common/runtime/runtime_features.cc | 3 + source/common/runtime/runtime_features.h | 2 + source/common/tcp_proxy/BUILD | 7 + source/common/tcp_proxy/tcp_proxy.cc | 122 ++++++-- source/common/tcp_proxy/tcp_proxy.h | 118 +++++++- source/common/tcp_proxy/upstream.cc | 223 ++++++++++++++- source/common/tcp_proxy/upstream.h | 184 +++++++++++- .../upstreams/http/http/upstream_request.h | 1 + .../upstreams/http/tcp/upstream_request.h | 1 + .../upstreams/http/udp/upstream_request.h | 1 + source/extensions/upstreams/tcp/generic/BUILD | 1 + .../upstreams/tcp/generic/config.cc | 4 +- .../extensions/upstreams/tcp/generic/config.h | 2 + test/common/router/upstream_request_test.cc | 5 +- test/common/tcp_proxy/BUILD | 4 + test/common/tcp_proxy/tcp_proxy_test.cc | 193 +++++++++---- test/common/tcp_proxy/tcp_proxy_test_base.h | 5 +- test/common/tcp_proxy/upstream_test.cc | 264 ++++++++++++++++-- test/extensions/filters/http/cache/BUILD | 1 + test/extensions/filters/http/csrf/BUILD | 1 + .../filters/http/custom_response/BUILD | 2 +- test/extensions/filters/http/fault/BUILD | 1 + .../filters/http/health_check/BUILD | 1 + test/extensions/filters/http/jwt_authn/BUILD | 2 +- test/extensions/filters/http/rbac/BUILD | 2 +- .../http/tcp/upstream_request_test.cc | 2 +- .../http/udp/upstream_request_test.cc | 1 + test/extensions/upstreams/tcp/generic/BUILD | 1 + .../upstreams/tcp/generic/config_test.cc | 46 +-- test/integration/BUILD | 13 +- test/integration/base_integration_test.cc | 1 + test/integration/http_protocol_integration.cc | 24 +- test/integration/http_protocol_integration.h | 4 + test/mocks/http/mocks.cc | 1 + test/mocks/http/mocks.h | 2 + test/mocks/router/BUILD | 9 + test/mocks/router/upstream_request.cc | 13 + test/mocks/router/upstream_request.h | 21 ++ 47 files changed, 1185 insertions(+), 193 deletions(-) create mode 100644 test/mocks/router/upstream_request.cc create mode 100644 test/mocks/router/upstream_request.h diff --git a/envoy/router/router.h b/envoy/router/router.h index 299cb25c2960..8d559c280d33 100644 --- a/envoy/router/router.h +++ b/envoy/router/router.h @@ -1526,6 +1526,17 @@ class GenericUpstream { * @param trailers supplies the trailers to encode. */ virtual void encodeTrailers(const Http::RequestTrailerMap& trailers) PURE; + + // TODO(vikaschoudhary16): Remove this api. + // This api is only used to enable half-close semantics on the upstream connection. + // This ideally should be done via calling connection.enableHalfClose() but since TcpProxy + // does not have access to the upstream connection, it is done via this api for now. + /** + * Enable half-close semantics on the upstream connection. Reading a remote half-close + * will not fully close the connection. This is off by default. + * @param enabled Whether to set half-close semantics as enabled or disabled. + */ + virtual void enableHalfClose() PURE; /** * Enable/disable further data from this stream. */ diff --git a/envoy/tcp/BUILD b/envoy/tcp/BUILD index 86d70b1774c6..5c3bdca0d9ef 100644 --- a/envoy/tcp/BUILD +++ b/envoy/tcp/BUILD @@ -26,6 +26,7 @@ envoy_cc_library( "//envoy/http:header_evaluator", "//envoy/tcp:conn_pool_interface", "//envoy/upstream:upstream_interface", + "//source/common/router:router_lib", "@envoy_api//envoy/extensions/filters/network/tcp_proxy/v3:pkg_cc_proto", ], ) diff --git a/envoy/tcp/upstream.h b/envoy/tcp/upstream.h index 200ec7fc9ea7..f6191a27513b 100644 --- a/envoy/tcp/upstream.h +++ b/envoy/tcp/upstream.h @@ -2,11 +2,14 @@ #include "envoy/buffer/buffer.h" #include "envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.pb.h" +#include "envoy/http/filter.h" #include "envoy/http/header_evaluator.h" #include "envoy/stream_info/stream_info.h" #include "envoy/tcp/conn_pool.h" #include "envoy/upstream/upstream.h" +#include "source/common/router/router.h" + namespace Envoy { namespace Upstream { @@ -48,14 +51,17 @@ class TunnelingConfigHelper { virtual void propagateResponseTrailers(Http::ResponseTrailerMapPtr&& trailers, const StreamInfo::FilterStateSharedPtr& filter_state) const PURE; + virtual const Envoy::Router::FilterConfig& routerFilterConfig() const PURE; + virtual Server::Configuration::ServerFactoryContext& serverFactoryContext() const PURE; }; using TunnelingConfigHelperOptConstRef = OptRef; // An API for wrapping either a TCP or an HTTP connection pool. -class GenericConnPool : public Logger::Loggable { +class GenericConnPool : public Event::DeferredDeletable, + public Logger::Loggable { public: - virtual ~GenericConnPool() = default; + ~GenericConnPool() override = default; /** * Called to create a TCP connection or HTTP stream for "CONNECT" streams. @@ -105,9 +111,9 @@ class GenericConnectionPoolCallbacks { // Interface for a generic Upstream, which can communicate with a TCP or HTTP // upstream. -class GenericUpstream { +class GenericUpstream : public Event::DeferredDeletable { public: - virtual ~GenericUpstream() = default; + ~GenericUpstream() override = default; /** * Enable/disable further data from this stream. @@ -175,6 +181,7 @@ class GenericConnPoolFactory : public Envoy::Config::TypedFactory { TunnelingConfigHelperOptConstRef config, Upstream::LoadBalancerContext* context, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, + Http::StreamDecoderFilterCallbacks& stream_decoder_callbacks, StreamInfo::StreamInfo& downstream_info) const PURE; }; diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index 11151b157bf5..c8d731afdb3d 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -314,6 +314,7 @@ class ConnectionManagerImpl : Logger::Loggable, OptRef tracingConfig() const override; const ScopeTrackedObject& scope() override; OptRef downstreamCallbacks() override { return *this; } + bool isHalfCloseEnabled() override { return false; } // DownstreamStreamFilterCallbacks void setRoute(Router::RouteConstSharedPtr route) override; diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index 187bb7b58561..070440572660 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -901,9 +901,17 @@ FilterManager::commonEncodePrefix(ActiveStreamEncoderFilter* filter, bool end_st FilterIterationStartState filter_iteration_start_state) { // Only do base state setting on the initial call. Subsequent calls for filtering do not touch // the base state. + ENVOY_STREAM_LOG(trace, "commonEncodePrefix end_stream: {}, isHalfCloseEnabled: {}", *this, + end_stream, filter_manager_callbacks_.isHalfCloseEnabled()); if (filter == nullptr) { - ASSERT(!state_.local_complete_); - state_.local_complete_ = end_stream; + // half close is enabled in case tcp proxying is done with http1 encoder. In this case, we + // should not set the local_complete_ flag to true when end_stream is true. + // setting local_complete_ to true will cause any data sent in the upstream direction to be + // dropped. + if (end_stream && !filter_manager_callbacks_.isHalfCloseEnabled()) { + ASSERT(!state_.local_complete_); + state_.local_complete_ = true; + } return encoder_filters_.begin(); } diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index 6a671ab99e9b..da5ba20033bd 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -545,6 +545,11 @@ class FilterManagerCallbacks { * Returns the DownstreamStreamFilterCallbacks for downstream HTTP filters. */ virtual OptRef downstreamCallbacks() { return {}; } + /** + * Returns if close from the upstream is to be handled with half-close semantics. + * This is used for HTTP/1.1 codec. + */ + virtual bool isHalfCloseEnabled() PURE; }; /** diff --git a/source/common/router/router.cc b/source/common/router/router.cc index a41e1c3374dc..c3baed7b839e 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -744,8 +744,9 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, // will never transition from false to true. bool can_use_http3 = !transport_socket_options_ || !transport_socket_options_->http11ProxyInfo().has_value(); - UpstreamRequestPtr upstream_request = std::make_unique( - *this, std::move(generic_conn_pool), can_send_early_data, can_use_http3); + UpstreamRequestPtr upstream_request = + std::make_unique(*this, std::move(generic_conn_pool), can_send_early_data, + can_use_http3, false /*enable_half_close*/); LinkedList::moveIntoList(std::move(upstream_request), upstream_requests_); upstream_requests_.front()->acceptHeadersFromRouter(end_stream); if (streaming_shadows_) { @@ -1987,8 +1988,9 @@ void Filter::doRetry(bool can_send_early_data, bool can_use_http3, TimeoutRetry cleanup(); return; } - UpstreamRequestPtr upstream_request = std::make_unique( - *this, std::move(generic_conn_pool), can_send_early_data, can_use_http3); + UpstreamRequestPtr upstream_request = + std::make_unique(*this, std::move(generic_conn_pool), can_send_early_data, + can_use_http3, false /*enable_tcp_tunneling*/); if (include_attempt_count_in_request_) { downstream_headers_->setEnvoyAttemptCount(attempt_count_); diff --git a/source/common/router/upstream_request.cc b/source/common/router/upstream_request.cc index 7c9079e58a44..bb0803df3be5 100644 --- a/source/common/router/upstream_request.cc +++ b/source/common/router/upstream_request.cc @@ -80,7 +80,8 @@ class UpstreamFilterManager : public Http::FilterManager { UpstreamRequest::UpstreamRequest(RouterFilterInterface& parent, std::unique_ptr&& conn_pool, - bool can_send_early_data, bool can_use_http3) + bool can_send_early_data, bool can_use_http3, + bool enable_half_close) : parent_(parent), conn_pool_(std::move(conn_pool)), stream_info_(parent_.callbacks()->dispatcher().timeSource(), nullptr), start_time_(parent_.callbacks()->dispatcher().timeSource().monotonicTime()), @@ -93,7 +94,8 @@ UpstreamRequest::UpstreamRequest(RouterFilterInterface& parent, cleaned_up_(false), had_upstream_(false), stream_options_({can_send_early_data, can_use_http3}), grpc_rq_success_deferred_(false), upstream_wait_for_response_headers_before_disabling_read_(Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.upstream_wait_for_response_headers_before_disabling_read")) { + "envoy.reloadable_features.upstream_wait_for_response_headers_before_disabling_read")), + enable_half_close_(enable_half_close) { if (auto tracing_config = parent_.callbacks()->tracingConfig(); tracing_config.has_value()) { if (tracing_config->spawnUpstreamSpan() || parent_.config().start_child_span_) { span_ = parent_.callbacks()->activeSpan().spawnChild( @@ -258,7 +260,8 @@ void UpstreamRequest::decode1xxHeaders(Http::ResponseHeaderMapPtr&& headers) { // on to the router. void UpstreamRequest::decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) { ASSERT(headers.get()); - ENVOY_STREAM_LOG(trace, "upstream response headers:\n{}", *parent_.callbacks(), *headers); + ENVOY_STREAM_LOG(trace, "end_stream: {}, upstream response headers:\n{}", *parent_.callbacks(), + end_stream, *headers); ScopeTrackerScopeState scope(&parent_.callbacks()->scope(), parent_.callbacks()->dispatcher()); resetPerTryIdleTimer(); @@ -581,6 +584,9 @@ void UpstreamRequest::onPoolReady(std::unique_ptr&& upstream, had_upstream_ = true; // Have the upstream use the account of the downstream. upstream_->setAccount(parent_.callbacks()->account()); + if (enable_half_close_) { + upstream_->enableHalfClose(); + } host->outlierDetector().putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess); diff --git a/source/common/router/upstream_request.h b/source/common/router/upstream_request.h index 9e88bfefda31..c6e7a4ec4c94 100644 --- a/source/common/router/upstream_request.h +++ b/source/common/router/upstream_request.h @@ -62,29 +62,27 @@ class UpstreamCodecFilter; * UpstreamCodecFilter. This is accomplished via the UpstreamStreamFilterCallbacks * interface, with the UpstreamFilterManager acting as intermediary. * - * UpstreamRequest is marked as final because no subclasses are expected. - * This class is intended to be used as-is without any specialized inheritance. */ -class UpstreamRequest final : public Logger::Loggable, - public UpstreamToDownstream, - public LinkedObject, - public GenericConnectionPoolCallbacks, - public Event::DeferredDeletable { +class UpstreamRequest : public Logger::Loggable, + public UpstreamToDownstream, + public LinkedObject, + public GenericConnectionPoolCallbacks, + public Event::DeferredDeletable { public: UpstreamRequest(RouterFilterInterface& parent, std::unique_ptr&& conn_pool, - bool can_send_early_data, bool can_use_http3); + bool can_send_early_data, bool can_use_http3, bool enable_half_close); ~UpstreamRequest() override; void deleteIsPending() override { cleanUp(); } // To be called from the destructor, or prior to deferred delete. void cleanUp(); - void acceptHeadersFromRouter(bool end_stream); - void acceptDataFromRouter(Buffer::Instance& data, bool end_stream); + virtual void acceptHeadersFromRouter(bool end_stream); + virtual void acceptDataFromRouter(Buffer::Instance& data, bool end_stream); void acceptTrailersFromRouter(Http::RequestTrailerMap& trailers); void acceptMetadataFromRouter(Http::MetadataMapPtr&& metadata_map_ptr); - void resetStream(); + virtual void resetStream(); void setupPerTryTimeout(); void maybeEndDecode(bool end_stream); void onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host, bool pool_success); @@ -258,6 +256,7 @@ class UpstreamRequest final : public Logger::Loggable, Http::ConnectionPool::Instance::StreamOptions stream_options_; bool grpc_rq_success_deferred_ : 1; bool upstream_wait_for_response_headers_before_disabling_read_ : 1; + bool enable_half_close_ : 1; }; class UpstreamRequestFilterManagerCallbacks : public Http::FilterManagerCallbacks, @@ -371,7 +370,7 @@ class UpstreamRequestFilterManagerCallbacks : public Http::FilterManagerCallback void setUpstreamToDownstream(UpstreamToDownstream& upstream_to_downstream_interface) override { upstream_request_.upstream_interface_ = upstream_to_downstream_interface; } - + bool isHalfCloseEnabled() override { return upstream_request_.enable_half_close_; } Http::RequestTrailerMapPtr trailers_; Http::ResponseHeaderMapPtr informational_headers_; Http::ResponseHeaderMapPtr response_headers_; diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 40e526641425..ec9e7f6a40e3 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -130,6 +130,9 @@ FALSE_RUNTIME_GUARD(envoy_reloadable_features_runtime_initialized); FALSE_RUNTIME_GUARD(envoy_reloadable_features_always_use_v6); // TODO(wbpcode) complete remove this feature is no one use it. FALSE_RUNTIME_GUARD(envoy_reloadable_features_refresh_rtt_after_request); +// TODO(vikaschoudhary16) flip this to true only after all the +// TcpProxy::Filter::HttpStreamDecoderFilterCallbacks are implemented or commented as unnecessary +FALSE_RUNTIME_GUARD(envoy_restart_features_upstream_http_filters_with_tcp_proxy); // TODO(danzh) false deprecate it once QUICHE has its own enable/disable flag. FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_reject_all); // TODO(suniltheta): Once the newly added http async technique is stabilized move it under diff --git a/source/common/runtime/runtime_features.h b/source/common/runtime/runtime_features.h index 0c2fb2d9b9e7..e698064a5971 100644 --- a/source/common/runtime/runtime_features.h +++ b/source/common/runtime/runtime_features.h @@ -26,6 +26,8 @@ void maybeSetRuntimeGuard(absl::string_view name, bool value); void maybeSetDeprecatedInts(absl::string_view name, uint32_t value); constexpr absl::string_view defer_processing_backedup_streams = "envoy.reloadable_features.defer_processing_backedup_streams"; +constexpr absl::string_view upstream_http_filters_with_tcp_proxy = + "envoy.restart_features.upstream_http_filters_with_tcp_proxy"; } // namespace Runtime } // namespace Envoy diff --git a/source/common/tcp_proxy/BUILD b/source/common/tcp_proxy/BUILD index 966088ed4a08..c6883b43aec3 100644 --- a/source/common/tcp_proxy/BUILD +++ b/source/common/tcp_proxy/BUILD @@ -17,15 +17,22 @@ envoy_cc_library( "upstream.h", ], deps = [ + "//envoy/http:header_map_interface", + "//envoy/router:router_ratelimit_interface", "//envoy/tcp:conn_pool_interface", "//envoy/tcp:upstream_interface", "//envoy/upstream:cluster_manager_interface", "//envoy/upstream:load_balancer_interface", + "//source/common/http:async_client_lib", "//source/common/http:codec_client_lib", + "//source/common/http:hash_policy_lib", "//source/common/http:header_map_lib", "//source/common/http:headers_lib", "//source/common/http:utility_lib", + "//source/common/network:utility_lib", "//source/common/router:header_parser_lib", + "//source/common/router:router_lib", + "//source/common/router:shadow_writer_lib", ], ) diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index f60f31ec44db..21483aca5463 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -32,8 +32,10 @@ #include "source/common/network/upstream_server_name.h" #include "source/common/network/upstream_socket_options_filter_state.h" #include "source/common/router/metadatamatchcriteria_impl.h" +#include "source/common/router/shadow_writer_impl.h" #include "source/common/stream_info/stream_id_provider_impl.h" #include "source/common/stream_info/uint64_accessor_impl.h" +#include "source/common/tracing/http_tracer_impl.h" namespace Envoy { namespace TcpProxy { @@ -110,7 +112,7 @@ Config::SharedConfig::SharedConfig( } if (config.has_tunneling_config()) { tunneling_config_helper_ = - std::make_unique(config.tunneling_config(), context); + std::make_unique(*stats_scope_.get(), config, context); } if (config.has_max_downstream_connection_duration()) { const uint64_t connection_duration = @@ -230,8 +232,10 @@ UpstreamDrainManager& Config::drainManager() { } Filter::Filter(ConfigSharedPtr config, Upstream::ClusterManager& cluster_manager) - : config_(config), cluster_manager_(cluster_manager), downstream_callbacks_(*this), - upstream_callbacks_(new UpstreamCallbacks(this)) { + : tracing_config_(Tracing::EgressConfig::get()), config_(config), + cluster_manager_(cluster_manager), downstream_callbacks_(*this), + upstream_callbacks_(new UpstreamCallbacks(this)), + upstream_decoder_filter_callbacks_(HttpStreamDecoderFilterCallbacks(this)) { ASSERT(config != nullptr); } @@ -289,9 +293,11 @@ void Filter::onInitFailure(UpstreamFailureReason reason) { // not have started attempting to connect to an upstream and there is no // connection pool callback latency to record. if (initial_upstream_connection_start_time_.has_value()) { - getStreamInfo().upstreamInfo()->upstreamTiming().recordConnectionPoolCallbackLatency( - initial_upstream_connection_start_time_.value(), - read_callbacks_->connection().dispatcher().timeSource()); + if (!getStreamInfo().upstreamInfo()->upstreamTiming().connectionPoolCallbackLatency()) { + getStreamInfo().upstreamInfo()->upstreamTiming().recordConnectionPoolCallbackLatency( + initial_upstream_connection_start_time_.value(), + read_callbacks_->connection().dispatcher().timeSource()); + } } read_callbacks_->connection().close( Network::ConnectionCloseType::NoFlush, @@ -552,9 +558,16 @@ bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster) { if (!factory) { return false; } - - generic_conn_pool_ = factory->createGenericConnPool(cluster, config_->tunnelingConfigHelper(), - this, *upstream_callbacks_, getStreamInfo()); + if (Runtime::runtimeFeatureEnabled( + "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { + // TODO(vikaschoudhary16): Initialize route_ once per cluster. + upstream_decoder_filter_callbacks_.route_ = std::make_shared( + cluster.info()->name(), + *std::unique_ptr{new Router::RetryPolicyImpl()}); + } + generic_conn_pool_ = factory->createGenericConnPool( + cluster, config_->tunnelingConfigHelper(), this, *upstream_callbacks_, + upstream_decoder_filter_callbacks_, getStreamInfo()); if (generic_conn_pool_) { connecting_ = true; connect_attempts_++; @@ -570,7 +583,19 @@ bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster) { void Filter::onGenericPoolFailure(ConnectionPool::PoolFailureReason reason, absl::string_view failure_reason, Upstream::HostDescriptionConstSharedPtr host) { - generic_conn_pool_.reset(); + if (Runtime::runtimeFeatureEnabled( + "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { + // generic_conn_pool_ is an instance of TcpProxy::HttpConnPool. + // generic_conn_pool_->newStream() is called in maybeTunnel() which initializes an instance of + // Router::UpstreamRequest. If Router::UpstreamRequest receives headers from the upstream which + // results in end_stream=true, then via callbacks passed to Router::UpstreamRequest, + // TcpProxy::Filter::onGenericPoolFailure() gets invoked. If we do not do deferredDelete here, + // then the same instance of UpstreamRequest which is under execution will go out of scope. + read_callbacks_->connection().dispatcher().deferredDelete(std::move(generic_conn_pool_)); + } else { + generic_conn_pool_.reset(); + } + read_callbacks_->upstreamHost(host); getStreamInfo().upstreamInfo()->setUpstreamHost(host); getStreamInfo().upstreamInfo()->setUpstreamTransportFailureReason(failure_reason); @@ -594,23 +619,30 @@ void Filter::onGenericPoolReady(StreamInfo::StreamInfo* info, Upstream::HostDescriptionConstSharedPtr& host, const Network::ConnectionInfoProvider& address_provider, Ssl::ConnectionInfoConstSharedPtr ssl_info) { + StreamInfo::UpstreamInfo& upstream_info = *getStreamInfo().upstreamInfo(); + if (!upstream_info.upstreamTiming().connectionPoolCallbackLatency()) { + upstream_info.upstreamTiming().recordConnectionPoolCallbackLatency( + initial_upstream_connection_start_time_.value(), + read_callbacks_->connection().dispatcher().timeSource()); + } upstream_ = std::move(upstream); generic_conn_pool_.reset(); read_callbacks_->upstreamHost(host); - StreamInfo::UpstreamInfo& upstream_info = *getStreamInfo().upstreamInfo(); - upstream_info.upstreamTiming().recordConnectionPoolCallbackLatency( - initial_upstream_connection_start_time_.value(), - read_callbacks_->connection().dispatcher().timeSource()); + // No need to set information using address_provider in case routing via Router::UpstreamRequest + // because in that case, information is already set by the + // Router::UpstreamRequest::onPoolReady() method before reaching here. + if (upstream_info.upstreamLocalAddress() == nullptr) { + upstream_info.setUpstreamLocalAddress(address_provider.localAddress()); + upstream_info.setUpstreamRemoteAddress(address_provider.remoteAddress()); + } upstream_info.setUpstreamHost(host); - upstream_info.setUpstreamLocalAddress(address_provider.localAddress()); - upstream_info.setUpstreamRemoteAddress(address_provider.remoteAddress()); upstream_info.setUpstreamSslConnection(ssl_info); onUpstreamConnection(); read_callbacks_->continueReading(); if (info) { upstream_info.setUpstreamFilterState(info->filterState()); } -} +} // namespace TcpProxy const Router::MetadataMatchCriteria* Filter::metadataMatchCriteria() { const Router::MetadataMatchCriteria* route_criteria = @@ -640,14 +672,30 @@ const std::string& TunnelResponseTrailers::key() { } TunnelingConfigHelperImpl::TunnelingConfigHelperImpl( - const envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig& - config_message, + Stats::Scope& stats_scope, + const envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy& config_message, Server::Configuration::FactoryContext& context) - : use_post_(config_message.use_post()), - header_parser_(Envoy::Router::HeaderParser::configure(config_message.headers_to_add())), - propagate_response_headers_(config_message.propagate_response_headers()), - propagate_response_trailers_(config_message.propagate_response_trailers()), - post_path_(config_message.post_path()) { + : use_post_(config_message.tunneling_config().use_post()), + header_parser_(Envoy::Router::HeaderParser::configure( + config_message.tunneling_config().headers_to_add())), + propagate_response_headers_(config_message.tunneling_config().propagate_response_headers()), + propagate_response_trailers_(config_message.tunneling_config().propagate_response_trailers()), + post_path_(config_message.tunneling_config().post_path()), + route_stat_name_storage_("tcpproxy_tunneling", context.scope().symbolTable()), + // TODO(vikaschoudhary16): figure out which of the following router_config_ members are + // not required by tcp_proxy and move them to a different class + router_config_(route_stat_name_storage_.statName(), + context.serverFactoryContext().localInfo(), stats_scope, + context.serverFactoryContext().clusterManager(), + context.serverFactoryContext().runtime(), + context.serverFactoryContext().api().randomGenerator(), + std::make_unique( + context.serverFactoryContext().clusterManager()), + true, false, false, false, false, false, {}, + context.serverFactoryContext().api().timeSource(), + context.serverFactoryContext().httpContext(), + context.serverFactoryContext().routerContext()), + server_factory_context_(context.serverFactoryContext()) { if (!post_path_.empty() && !use_post_) { throw EnvoyException("Can't set a post path when POST method isn't used"); } @@ -655,7 +703,7 @@ TunnelingConfigHelperImpl::TunnelingConfigHelperImpl( envoy::config::core::v3::SubstitutionFormatString substitution_format_config; substitution_format_config.mutable_text_format_source()->set_inline_string( - config_message.hostname()); + config_message.tunneling_config().hostname()); hostname_fmt_ = Formatter::SubstitutionFormatStringUtils::fromProtoConfig( substitution_format_config, context); } @@ -697,8 +745,8 @@ void Filter::onConnectTimeout() { } Network::FilterStatus Filter::onData(Buffer::Instance& data, bool end_stream) { - ENVOY_CONN_LOG(trace, "downstream connection received {} bytes, end_stream={}", - read_callbacks_->connection(), data.length(), end_stream); + ENVOY_CONN_LOG(trace, "downstream connection received {} bytes, end_stream={}, has upstream {}", + read_callbacks_->connection(), data.length(), end_stream, upstream_ != nullptr); getStreamInfo().getDownstreamBytesMeter()->addWireBytesReceived(data.length()); if (upstream_) { getStreamInfo().getUpstreamBytesMeter()->addWireBytesSent(data.length()); @@ -799,14 +847,23 @@ void Filter::onUpstreamEvent(Network::ConnectionEvent event) { if (event == Network::ConnectionEvent::RemoteClose || event == Network::ConnectionEvent::LocalClose) { - upstream_.reset(); + if (Runtime::runtimeFeatureEnabled( + "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { + read_callbacks_->connection().dispatcher().deferredDelete(std::move(upstream_)); + } else { + upstream_.reset(); + } disableIdleTimer(); if (connecting) { if (event == Network::ConnectionEvent::RemoteClose) { - getStreamInfo().setResponseFlag(StreamInfo::CoreResponseFlag::UpstreamConnectionFailure); - read_callbacks_->upstreamHost()->outlierDetector().putResult( - Upstream::Outlier::Result::LocalOriginConnectFailed); + getStreamInfo().setResponseFlag(StreamInfo::UpstreamConnectionFailure); + // upstreamHost can be nullptr if we received a disconnect from the upstream before + // receiving any response + if (read_callbacks_->upstreamHost() != nullptr) { + read_callbacks_->upstreamHost()->outlierDetector().putResult( + Upstream::Outlier::Result::LocalOriginConnectFailed); + } } if (!downstream_closed_) { route_ = pickRoute(); @@ -930,6 +987,9 @@ void Filter::disableIdleTimer() { } } +Filter::HttpStreamDecoderFilterCallbacks::HttpStreamDecoderFilterCallbacks(Filter* parent) + : parent_(parent), request_trailer_map_(Http::RequestTrailerMapImpl::create()) {} + UpstreamDrainManager::~UpstreamDrainManager() { // If connections aren't closed before they are destructed an ASSERT fires, // so cancel all pending drains, which causes the connections to be closed. diff --git a/source/common/tcp_proxy/tcp_proxy.h b/source/common/tcp_proxy/tcp_proxy.h index 45f34b901405..4019ea1211ef 100644 --- a/source/common/tcp_proxy/tcp_proxy.h +++ b/source/common/tcp_proxy/tcp_proxy.h @@ -10,6 +10,7 @@ #include "envoy/common/random_generator.h" #include "envoy/event/timer.h" #include "envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.pb.h" +#include "envoy/http/codec.h" #include "envoy/http/header_evaluator.h" #include "envoy/network/connection.h" #include "envoy/network/filter.h" @@ -22,6 +23,7 @@ #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/upstream.h" +#include "source/common/common/assert.h" #include "source/common/common/logger.h" #include "source/common/formatter/substitution_format_string.h" #include "source/common/http/header_map_impl.h" @@ -143,24 +145,29 @@ class TunnelResponseTrailers : public Http::TunnelResponseHeadersOrTrailersImpl private: const Http::ResponseTrailerMapPtr response_trailers_; }; - +class Config; class TunnelingConfigHelperImpl : public TunnelingConfigHelper, protected Logger::Loggable { public: TunnelingConfigHelperImpl( - const envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig& - config_message, + Stats::Scope& scope, + const envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy& config_message, Server::Configuration::FactoryContext& context); std::string host(const StreamInfo::StreamInfo& stream_info) const override; bool usePost() const override { return use_post_; } const std::string& postPath() const override { return post_path_; } Envoy::Http::HeaderEvaluator& headerEvaluator() const override { return *header_parser_; } + + const Envoy::Router::FilterConfig& routerFilterConfig() const override { return router_config_; } void propagateResponseHeaders(Http::ResponseHeaderMapPtr&& headers, const StreamInfo::FilterStateSharedPtr& filter_state) const override; void propagateResponseTrailers(Http::ResponseTrailerMapPtr&& trailers, const StreamInfo::FilterStateSharedPtr& filter_state) const override; + Server::Configuration::ServerFactoryContext& serverFactoryContext() const override { + return server_factory_context_; + } private: const bool use_post_; @@ -169,6 +176,9 @@ class TunnelingConfigHelperImpl : public TunnelingConfigHelper, const bool propagate_response_headers_; const bool propagate_response_trailers_; std::string post_path_; + Stats::StatNameManagedStorage route_stat_name_storage_; + const Router::FilterConfig router_config_; + Server::Configuration::ServerFactoryContext& server_factory_context_; }; /** @@ -461,6 +471,107 @@ class Filter : public Network::ReadFilter, }; StreamInfo::StreamInfo& getStreamInfo(); + class HttpStreamDecoderFilterCallbacks : public Http::StreamDecoderFilterCallbacks, + public ScopeTrackedObject { + public: + HttpStreamDecoderFilterCallbacks(Filter* parent); + // Http::StreamDecoderFilterCallbacks + OptRef connection() override { + return parent_->read_callbacks_->connection(); + } + StreamInfo::StreamInfo& streamInfo() override { return parent_->getStreamInfo(); } + const ScopeTrackedObject& scope() override { return *this; } + Event::Dispatcher& dispatcher() override { + return parent_->read_callbacks_->connection().dispatcher(); + } + void resetStream(Http::StreamResetReason, absl::string_view) override { + IS_ENVOY_BUG("Not implemented. Unexpected call to resetStream()"); + }; + Router::RouteConstSharedPtr route() override { return route_; } + Upstream::ClusterInfoConstSharedPtr clusterInfo() override { + return parent_->cluster_manager_.getThreadLocalCluster(parent_->route_->clusterName()) + ->info(); + } + uint64_t streamId() const override { + auto sip = parent_->getStreamInfo().getStreamIdProvider(); + if (sip) { + return sip->toInteger().value(); + } + return 0; + } + Tracing::Span& activeSpan() override { return parent_->active_span_; } + OptRef tracingConfig() const override { + return makeOptRef(parent_->tracing_config_); + } + void continueDecoding() override {} + void addDecodedData(Buffer::Instance&, bool) override {} + void injectDecodedDataToFilterChain(Buffer::Instance&, bool) override {} + Http::RequestTrailerMap& addDecodedTrailers() override { return *request_trailer_map_; } + Http::MetadataMapVector& addDecodedMetadata() override { + static Http::MetadataMapVector metadata_map_vector; + return metadata_map_vector; + } + const Buffer::Instance* decodingBuffer() override { return nullptr; } + void modifyDecodingBuffer(std::function) override {} + void sendLocalReply(Http::Code, absl::string_view, + std::function, + const absl::optional, + absl::string_view) override {} + void encode1xxHeaders(Http::ResponseHeaderMapPtr&&) override {} + Http::ResponseHeaderMapOptRef informationalHeaders() override { return {}; } + void encodeHeaders(Http::ResponseHeaderMapPtr&&, bool, absl::string_view) override {} + Http::ResponseHeaderMapOptRef responseHeaders() override { return {}; } + void encodeData(Buffer::Instance&, bool) override {} + Http::RequestHeaderMapOptRef requestHeaders() override { return {}; } + Http::RequestTrailerMapOptRef requestTrailers() override { return {}; } + void encodeTrailers(Http::ResponseTrailerMapPtr&&) override {} + Http::ResponseTrailerMapOptRef responseTrailers() override { return {}; } + void encodeMetadata(Http::MetadataMapPtr&&) override {} + // TODO(vikaschoudhary16): Implement watermark callbacks and test through flow control e2es. + void onDecoderFilterAboveWriteBufferHighWatermark() override {} + void onDecoderFilterBelowWriteBufferLowWatermark() override {} + void addDownstreamWatermarkCallbacks(Http::DownstreamWatermarkCallbacks&) override {} + void removeDownstreamWatermarkCallbacks(Http::DownstreamWatermarkCallbacks&) override {} + void setDecoderBufferLimit(uint32_t) override {} + uint32_t decoderBufferLimit() override { return 0; } + bool recreateStream(const Http::ResponseHeaderMap*) override { return false; } + void addUpstreamSocketOptions(const Network::Socket::OptionsSharedPtr&) override {} + Network::Socket::OptionsSharedPtr getUpstreamSocketOptions() const override { return nullptr; } + const Router::RouteSpecificFilterConfig* mostSpecificPerFilterConfig() const override { + return nullptr; + } + Buffer::BufferMemoryAccountSharedPtr account() const override { return nullptr; } + void setUpstreamOverrideHost(Upstream::LoadBalancerContext::OverrideHost) override {} + absl::optional + upstreamOverrideHost() const override { + return absl::nullopt; + } + void restoreContextOnContinue(ScopeTrackedObjectStack& tracked_object_stack) override { + tracked_object_stack.add(*this); + } + void traversePerFilterConfig( + std::function) const override {} + Http::Http1StreamEncoderOptionsOptRef http1StreamEncoderOptions() override { return {}; } + OptRef downstreamCallbacks() override { return {}; } + OptRef upstreamCallbacks() override { return {}; } + void resetIdleTimer() override {} + // absl::optional upstreamOverrideHost() const override { + // return absl::nullopt; + // } + absl::string_view filterConfigName() const override { return ""; } + + // ScopeTrackedObject + void dumpState(std::ostream& os, int indent_level) const override { + const char* spaces = spacesForLevel(indent_level); + os << spaces << "TcpProxy " << this << DUMP_MEMBER(streamId()) << "\n"; + DUMP_DETAILS(parent_->getStreamInfo().upstreamInfo()); + } + Filter* parent_{}; + Http::RequestTrailerMapPtr request_trailer_map_; + std::shared_ptr route_; + }; + Tracing::NullSpan active_span_; + const Tracing::Config& tracing_config_; protected: struct DownstreamCallbacks : public Network::ConnectionCallbacks { @@ -545,6 +656,7 @@ class Filter : public Network::ReadFilter, uint32_t connect_attempts_{}; bool connecting_{}; bool downstream_closed_{}; + HttpStreamDecoderFilterCallbacks upstream_decoder_filter_callbacks_; }; // This class deals with an upstream connection that needs to finish flushing, when the downstream diff --git a/source/common/tcp_proxy/upstream.cc b/source/common/tcp_proxy/upstream.cc index 5e4eaa35338d..01e99426b9f6 100644 --- a/source/common/tcp_proxy/upstream.cc +++ b/source/common/tcp_proxy/upstream.cc @@ -1,16 +1,19 @@ #include "source/common/tcp_proxy/upstream.h" +#include "envoy/http/header_map.h" #include "envoy/upstream/cluster_manager.h" #include "source/common/http/codec_client.h" #include "source/common/http/codes.h" #include "source/common/http/header_map_impl.h" #include "source/common/http/headers.h" +#include "source/common/http/null_route_impl.h" #include "source/common/http/utility.h" #include "source/common/runtime/runtime_features.h" namespace Envoy { namespace TcpProxy { + using TunnelingConfig = envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig; @@ -194,6 +197,7 @@ void HttpUpstream::resetEncoder(Network::ConnectionEvent event, bool inform_down conn_pool_callbacks_->onFailure(); return; } + if (inform_downstream) { upstream_callbacks_.onEvent(event); } @@ -229,7 +233,7 @@ TcpConnPool::~TcpConnPool() { void TcpConnPool::newStream(GenericConnectionPoolCallbacks& callbacks) { callbacks_ = &callbacks; - // Given this function is reentrant, make sure we only reset the upstream_handle_ if given a + // Given this function is re-entrant, make sure we only reset the upstream_handle_ if given a // valid connection handle. If newConnection fails inline it may result in attempting to // select a new host, and a recursive call to establishUpstreamConnection. In this case the // first call to newConnection will return null and the inner call will persist. @@ -270,29 +274,67 @@ HttpConnPool::HttpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, Upstream::LoadBalancerContext* context, const TunnelingConfigHelper& config, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, + Http::StreamDecoderFilterCallbacks& stream_decoder_callbacks, Http::CodecType type, StreamInfo::StreamInfo& downstream_info) - : config_(config), type_(type), upstream_callbacks_(upstream_callbacks), - downstream_info_(downstream_info) { + : config_(config), type_(type), decoder_filter_callbacks_(&stream_decoder_callbacks), + upstream_callbacks_(upstream_callbacks), downstream_info_(downstream_info) { absl::optional protocol; if (type_ == Http::CodecType::HTTP3) { protocol = Http::Protocol::Http3; } else if (type_ == Http::CodecType::HTTP2) { protocol = Http::Protocol::Http2; } + if (Runtime::runtimeFeatureEnabled( + "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { + absl::optional upstream_protocol = protocol; + generic_conn_pool_ = createConnPool(thread_local_cluster, context, upstream_protocol); + return; + } conn_pool_data_ = thread_local_cluster.httpConnPool(Upstream::ResourcePriority::Default, protocol, context); } +std::unique_ptr +HttpConnPool::createConnPool(Upstream::ThreadLocalCluster& cluster, + Upstream::LoadBalancerContext* context, + absl::optional protocol) { + Router::GenericConnPoolFactory* factory = nullptr; + factory = Envoy::Config::Utility::getFactoryByName( + "envoy.filters.connection_pools.http.generic"); + if (!factory) { + return nullptr; + } + + return factory->createGenericConnPool( + cluster, Envoy::Router::GenericConnPoolFactory::UpstreamProtocol::HTTP, + decoder_filter_callbacks_->route()->routeEntry()->priority(), protocol, context); +} + HttpConnPool::~HttpConnPool() { if (upstream_handle_ != nullptr) { // Because HTTP connections are generally shorter lived and have a higher probability of use // before going idle, they are closed with Default rather than CloseExcess. upstream_handle_->cancel(ConnectionPool::CancelPolicy::Default); } + if (combined_upstream_ != nullptr) { + combined_upstream_->onDownstreamEvent(Network::ConnectionEvent::LocalClose); + } } void HttpConnPool::newStream(GenericConnectionPoolCallbacks& callbacks) { callbacks_ = &callbacks; + if (Runtime::runtimeFeatureEnabled( + "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { + combined_upstream_ = std::make_unique( + *this, upstream_callbacks_, *decoder_filter_callbacks_, config_, downstream_info_); + RouterUpstreamRequestPtr upstream_request = std::make_unique( + *combined_upstream_, std::move(generic_conn_pool_), /*can_send_early_data_=*/false, + /*can_use_http3_=*/true, true /*enable_tcp_tunneling*/); + combined_upstream_->setRouterUpstreamRequest(std::move(upstream_request)); + combined_upstream_->newStream(callbacks); + return; + } + upstream_ = std::make_unique(upstream_callbacks_, config_, downstream_info_, type_); Tcp::ConnectionPool::Cancellable* handle = conn_pool_data_.value().newStream(upstream_->responseDecoder(), *this, @@ -310,6 +352,15 @@ void HttpConnPool::onPoolFailure(ConnectionPool::PoolFailureReason reason, callbacks_->onGenericPoolFailure(reason, failure_reason, host); } +void HttpConnPool::onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host, + bool pool_success) { + if (!pool_success) { + return; + } + combined_upstream_->setConnPoolCallbacks(std::make_unique( + *this, host, downstream_info_.downstreamAddressProvider().sslConnection())); +} + void HttpConnPool::onPoolReady(Http::RequestEncoder& request_encoder, Upstream::HostDescriptionConstSharedPtr host, StreamInfo::StreamInfo& info, absl::optional) { @@ -332,8 +383,174 @@ void HttpConnPool::onPoolReady(Http::RequestEncoder& request_encoder, void HttpConnPool::onGenericPoolReady(Upstream::HostDescriptionConstSharedPtr& host, const Network::ConnectionInfoProvider& address_provider, Ssl::ConnectionInfoConstSharedPtr ssl_info) { + if (Runtime::runtimeFeatureEnabled( + "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { + + callbacks_->onGenericPoolReady(nullptr, std::move(combined_upstream_), host, address_provider, + ssl_info); + return; + } callbacks_->onGenericPoolReady(nullptr, std::move(upstream_), host, address_provider, ssl_info); } +CombinedUpstream::CombinedUpstream(HttpConnPool& http_conn_pool, + Tcp::ConnectionPool::UpstreamCallbacks& callbacks, + Http::StreamDecoderFilterCallbacks& decoder_callbacks, + const TunnelingConfigHelper& config, + StreamInfo::StreamInfo& downstream_info) + : config_(config), downstream_info_(downstream_info), parent_(http_conn_pool), + decoder_filter_callbacks_(decoder_callbacks), response_decoder_(*this), + upstream_callbacks_(callbacks) { + auto is_ssl = downstream_info_.downstreamAddressProvider().sslConnection(); + downstream_headers_ = Http::createHeaderMap({ + {Http::Headers::get().Method, config_.usePost() ? "POST" : "CONNECT"}, + {Http::Headers::get().Host, config_.host(downstream_info_)}, + }); + + if (config_.usePost()) { + const std::string& scheme = + is_ssl ? Http::Headers::get().SchemeValues.Https : Http::Headers::get().SchemeValues.Http; + downstream_headers_->addReference(Http::Headers::get().Path, config_.postPath()); + downstream_headers_->addReference(Http::Headers::get().Scheme, scheme); + } + + config_.headerEvaluator().evaluateHeaders( + *downstream_headers_, {downstream_info_.getRequestHeaders()}, downstream_info_); +} + +void CombinedUpstream::setRouterUpstreamRequest( + Router::UpstreamRequestPtr router_upstream_request) { + LinkedList::moveIntoList(std::move(router_upstream_request), upstream_requests_); +} + +void CombinedUpstream::newStream(GenericConnectionPoolCallbacks&) { + upstream_requests_.front()->acceptHeadersFromRouter(false); +} + +void CombinedUpstream::encodeData(Buffer::Instance& data, bool end_stream) { + if (upstream_requests_.empty()) { + return; + } + upstream_requests_.front()->acceptDataFromRouter(data, end_stream); + if (end_stream) { + doneWriting(); + } +} + +bool CombinedUpstream::readDisable(bool disable) { + if (upstream_requests_.empty()) { + return false; + } + if (disable) { + upstream_requests_.front()->onAboveWriteBufferHighWatermark(); + } + return true; +} + +Tcp::ConnectionPool::ConnectionData* +CombinedUpstream::onDownstreamEvent(Network::ConnectionEvent event) { + if (upstream_requests_.empty()) { + return nullptr; + } + + if (event == Network::ConnectionEvent::LocalClose || + event == Network::ConnectionEvent::RemoteClose) { + upstream_requests_.front()->resetStream(); + } + return nullptr; +} + +bool CombinedUpstream::isValidResponse(const Http::ResponseHeaderMap& headers) { + switch (parent_.codecType()) { + case Http::CodecType::HTTP1: + // According to RFC7231 any 2xx response indicates that the connection is + // established. + // Any 'Content-Length' or 'Transfer-Encoding' header fields MUST be ignored. + // https://tools.ietf.org/html/rfc7231#section-4.3.6 + return Http::CodeUtility::is2xx(Http::Utility::getResponseStatus(headers)); + case Http::CodecType::HTTP2: + case Http::CodecType::HTTP3: + if (Http::Utility::getResponseStatus(headers) != 200) { + return false; + } + return true; + } + return true; +} + +void CombinedUpstream::onResetEncoder(Network::ConnectionEvent event, bool inform_downstream) { + if (event == Network::ConnectionEvent::LocalClose || + event == Network::ConnectionEvent::RemoteClose) { + if (!upstream_requests_.empty()) { + upstream_requests_.front()->resetStream(); + } + } + + // If we did not receive a valid CONNECT response yet we treat this as a pool + // failure, otherwise we forward the event downstream. + if (conn_pool_callbacks_ != nullptr) { + conn_pool_callbacks_->onFailure(); + return; + } + + if (inform_downstream) { + upstream_callbacks_.onEvent(event); + } +} + +// Router::RouterFilterInterface +void CombinedUpstream::onUpstreamHeaders([[maybe_unused]] uint64_t response_code, + Http::ResponseHeaderMapPtr&& headers, + [[maybe_unused]] UpstreamRequest& upstream_request, + bool end_stream) { + responseDecoder().decodeHeaders(std::move(headers), end_stream); +} + +void CombinedUpstream::onUpstreamData(Buffer::Instance& data, + [[maybe_unused]] UpstreamRequest& upstream_request, + bool end_stream) { + responseDecoder().decodeData(data, end_stream); +} + +void CombinedUpstream::onUpstreamTrailers(Http::ResponseTrailerMapPtr&& trailers, + UpstreamRequest&) { + responseDecoder().decodeTrailers(std::move(trailers)); +} + +Http::RequestHeaderMap* CombinedUpstream::downstreamHeaders() { return downstream_headers_.get(); } + +void CombinedUpstream::doneReading() { + read_half_closed_ = true; + if (write_half_closed_) { + onResetEncoder(Network::ConnectionEvent::LocalClose); + } +} + +void CombinedUpstream::onUpstreamReset(Http::StreamResetReason, absl::string_view, + UpstreamRequest&) { + upstream_callbacks_.onEvent(Network::ConnectionEvent::RemoteClose); +} + +void CombinedUpstream::doneWriting() { + write_half_closed_ = true; + if (read_half_closed_) { + onResetEncoder(Network::ConnectionEvent::LocalClose); + } +} + +void CombinedUpstream::onResetStream(Http::StreamResetReason, absl::string_view) { + read_half_closed_ = true; + write_half_closed_ = true; + onResetEncoder(Network::ConnectionEvent::LocalClose); +} + +void CombinedUpstream::onAboveWriteBufferHighWatermark() { + upstream_callbacks_.onAboveWriteBufferHighWatermark(); +} + +void CombinedUpstream::onBelowWriteBufferLowWatermark() { + upstream_callbacks_.onBelowWriteBufferLowWatermark(); +} + } // namespace TcpProxy } // namespace Envoy diff --git a/source/common/tcp_proxy/upstream.h b/source/common/tcp_proxy/upstream.h index d16126547cc5..23af532b37cb 100644 --- a/source/common/tcp_proxy/upstream.h +++ b/source/common/tcp_proxy/upstream.h @@ -1,7 +1,11 @@ #pragma once +#include + #include "envoy/http/conn_pool.h" +#include "envoy/http/header_map.h" #include "envoy/network/connection.h" +#include "envoy/router/router_ratelimit.h" #include "envoy/tcp/conn_pool.h" #include "envoy/tcp/upstream.h" #include "envoy/upstream/load_balancer.h" @@ -11,7 +15,13 @@ #include "source/common/buffer/buffer_impl.h" #include "source/common/common/dump_state_utils.h" #include "source/common/http/codec_client.h" +#include "source/common/http/hash_policy.h" +#include "source/common/http/null_route_impl.h" +#include "source/common/network/utility.h" +#include "source/common/router/config_impl.h" #include "source/common/router/header_parser.h" +#include "source/common/router/router.h" +#include "source/extensions/early_data/default_early_data_policy.h" namespace Envoy { namespace TcpProxy { @@ -47,16 +57,25 @@ class TcpConnPool : public GenericConnPool, public Tcp::ConnectionPool::Callback }; class HttpUpstream; +class CombinedUpstream; class HttpConnPool : public GenericConnPool, public Http::ConnectionPool::Callbacks { public: HttpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, Upstream::LoadBalancerContext* context, const TunnelingConfigHelper& config, - Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, Http::CodecType type, + Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, + Http::StreamDecoderFilterCallbacks&, Http::CodecType type, StreamInfo::StreamInfo& downstream_info); + + using RouterUpstreamRequest = Router::UpstreamRequest; + using RouterUpstreamRequestPtr = std::unique_ptr; ~HttpConnPool() override; - bool valid() const { return conn_pool_data_.has_value(); } + bool valid() const { return conn_pool_data_.has_value() || generic_conn_pool_; } + Http::CodecType codecType() const { return type_; } + std::unique_ptr createConnPool(Upstream::ThreadLocalCluster&, + Upstream::LoadBalancerContext* context, + absl::optional protocol); // GenericConnPool void newStream(GenericConnectionPoolCallbacks& callbacks) override; @@ -69,16 +88,32 @@ class HttpConnPool : public GenericConnPool, public Http::ConnectionPool::Callba Upstream::HostDescriptionConstSharedPtr host, StreamInfo::StreamInfo& info, absl::optional) override; + void onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr, bool); + void onHttpPoolReady(Upstream::HostDescriptionConstSharedPtr& host, + Ssl::ConnectionInfoConstSharedPtr ssl_info); + class Callbacks { public: Callbacks(HttpConnPool& conn_pool, Upstream::HostDescriptionConstSharedPtr host, Ssl::ConnectionInfoConstSharedPtr ssl_info) : conn_pool_(&conn_pool), host_(host), ssl_info_(ssl_info) {} virtual ~Callbacks() = default; - virtual void onSuccess(Http::RequestEncoder& request_encoder) { + virtual void onSuccess(Http::RequestEncoder* request_encoder) { ASSERT(conn_pool_ != nullptr); - conn_pool_->onGenericPoolReady(host_, request_encoder.getStream().connectionInfoProvider(), - ssl_info_); + if (!Runtime::runtimeFeatureEnabled( + "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { + ASSERT(request_encoder != nullptr); + conn_pool_->onGenericPoolReady(host_, request_encoder->getStream().connectionInfoProvider(), + ssl_info_); + return; + } + + Network::ConnectionInfoProviderSharedPtr local_connection_info_provider( + std::make_shared( + Network::Utility::getCanonicalIpv4LoopbackAddress(), + Network::Utility::getCanonicalIpv4LoopbackAddress())); + + conn_pool_->onGenericPoolReady(host_, *local_connection_info_provider.get(), ssl_info_); } virtual void onFailure() { ASSERT(conn_pool_ != nullptr); @@ -104,9 +139,12 @@ class HttpConnPool : public GenericConnPool, public Http::ConnectionPool::Callba absl::optional conn_pool_data_{}; Http::ConnectionPool::Cancellable* upstream_handle_{}; GenericConnectionPoolCallbacks* callbacks_{}; + Http::StreamDecoderFilterCallbacks* decoder_filter_callbacks_; Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks_; std::unique_ptr upstream_; + std::unique_ptr combined_upstream_; StreamInfo::StreamInfo& downstream_info_; + std::unique_ptr generic_conn_pool_; }; class TcpUpstream : public GenericUpstream { @@ -130,6 +168,7 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { public: using TunnelingConfig = envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig; + HttpUpstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, const TunnelingConfigHelper& config, StreamInfo::StreamInfo& downstream_info, Http::CodecType type); @@ -155,7 +194,7 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { void onAboveWriteBufferHighWatermark() override; void onBelowWriteBufferLowWatermark() override; - void setRequestEncoder(Http::RequestEncoder& request_encoder, bool is_ssl); + virtual void setRequestEncoder(Http::RequestEncoder& request_encoder, bool is_ssl); void setConnPoolCallbacks(std::unique_ptr&& callbacks) { conn_pool_callbacks_ = std::move(callbacks); } @@ -170,12 +209,13 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { const TunnelingConfigHelper& config_; // The downstream info that is owned by the downstream connection. StreamInfo::StreamInfo& downstream_info_; + std::unique_ptr downstream_headers_; private: + Upstream::ClusterInfoConstSharedPtr cluster_; class DecoderShim : public Http::ResponseDecoder { public: DecoderShim(HttpUpstream& parent) : parent_(parent) {} - // Http::ResponseDecoder void decode1xxHeaders(Http::ResponseHeaderMapPtr&&) override {} void decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) override { bool is_valid_response = parent_.isValidResponse(*headers); @@ -184,7 +224,7 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { if (!is_valid_response || end_stream) { parent_.resetEncoder(Network::ConnectionEvent::LocalClose); } else if (parent_.conn_pool_callbacks_ != nullptr) { - parent_.conn_pool_callbacks_->onSuccess(*parent_.request_encoder_); + parent_.conn_pool_callbacks_->onSuccess(parent_.request_encoder_); parent_.conn_pool_callbacks_.reset(); } } @@ -224,5 +264,133 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { std::unique_ptr conn_pool_callbacks_; }; +class CombinedUpstream : public GenericUpstream, + protected Http::StreamCallbacks, + public Envoy::Router::RouterFilterInterface { +public: + CombinedUpstream(HttpConnPool& http_conn_pool, Tcp::ConnectionPool::UpstreamCallbacks& callbacks, + Http::StreamDecoderFilterCallbacks& decoder_callbacks, + const TunnelingConfigHelper& config, StreamInfo::StreamInfo& downstream_info); + ~CombinedUpstream() override = default; + using UpstreamRequest = Router::UpstreamRequest; + Http::ResponseDecoder& responseDecoder() { return response_decoder_; } + void doneReading(); + void doneWriting(); + using UpstreamRequestPtr = std::unique_ptr; + void setRouterUpstreamRequest(UpstreamRequestPtr); + void newStream(GenericConnectionPoolCallbacks& callbacks); + void encodeData(Buffer::Instance& data, bool end_stream) override; + Tcp::ConnectionPool::ConnectionData* onDownstreamEvent(Network::ConnectionEvent event) override; + bool isValidResponse(const Http::ResponseHeaderMap&); + bool readDisable(bool disable) override; + void setConnPoolCallbacks(std::unique_ptr&& callbacks) { + conn_pool_callbacks_ = std::move(callbacks); + } + void addBytesSentCallback(Network::Connection::BytesSentCb) override{}; + // HTTP upstream must not implement converting upstream transport + // socket from non-secure to secure mode. + bool startUpstreamSecureTransport() override { return false; } + Ssl::ConnectionInfoConstSharedPtr getUpstreamConnectionSslInfo() override { return nullptr; } + + // Http::StreamCallbacks + void onResetStream(Http::StreamResetReason reason, + absl::string_view transport_failure_reason) override; + void onAboveWriteBufferHighWatermark() override; + void onBelowWriteBufferLowWatermark() override; + + // Router::RouterFilterInterface + void onUpstreamHeaders(uint64_t response_code, Http::ResponseHeaderMapPtr&& headers, + UpstreamRequest& upstream_request, bool end_stream) override; + void onUpstreamData(Buffer::Instance& data, UpstreamRequest& upstream_request, + bool end_stream) override; + void onUpstream1xxHeaders(Http::ResponseHeaderMapPtr&&, UpstreamRequest&) override {} + void onUpstreamTrailers(Http::ResponseTrailerMapPtr&&, UpstreamRequest&) override; + void onUpstreamMetadata(Http::MetadataMapPtr&&) override {} + void onUpstreamReset(Http::StreamResetReason stream_reset_reason, + absl::string_view transport_failure_reason, UpstreamRequest&) override; + void onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host, + bool pool_success) override { + parent_.onUpstreamHostSelected(host, pool_success); + } + void onPerTryTimeout(UpstreamRequest&) override {} + void onPerTryIdleTimeout(UpstreamRequest&) override {} + void onStreamMaxDurationReached(UpstreamRequest&) override {} + Http::StreamDecoderFilterCallbacks* callbacks() override { return &decoder_filter_callbacks_; } + Upstream::ClusterInfoConstSharedPtr cluster() override { + return decoder_filter_callbacks_.clusterInfo(); + } + Router::FilterConfig& config() override { + return const_cast(config_.routerFilterConfig()); + } + Router::TimeoutData timeout() override { return {}; } + absl::optional dynamicMaxStreamDuration() const override { + return absl::nullopt; + } + Http::RequestHeaderMap* downstreamHeaders() override; + Http::RequestTrailerMap* downstreamTrailers() override { return nullptr; } + bool downstreamResponseStarted() const override { return false; } + bool downstreamEndStream() const override { return false; } + uint32_t attemptCount() const override { return 0; } + +protected: + void onResetEncoder(Network::ConnectionEvent event, bool inform_downstream = true); + + // The config object that is owned by the downstream network filter chain factory. + const TunnelingConfigHelper& config_; + // The downstream info that is owned by the downstream connection. + StreamInfo::StreamInfo& downstream_info_; + std::list upstream_requests_; + std::unique_ptr downstream_headers_; + HttpConnPool& parent_; + +private: + Http::StreamDecoderFilterCallbacks& decoder_filter_callbacks_; + class DecoderShim : public Http::ResponseDecoder { + public: + DecoderShim(CombinedUpstream& parent) : parent_(parent) {} + // Http::ResponseDecoder + void decode1xxHeaders(Http::ResponseHeaderMapPtr&&) override {} + void decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) override { + bool is_valid_response = parent_.isValidResponse(*headers); + parent_.config_.propagateResponseHeaders(std::move(headers), + parent_.downstream_info_.filterState()); + if (!is_valid_response || end_stream) { + parent_.onResetEncoder(Network::ConnectionEvent::LocalClose); + } else if (parent_.conn_pool_callbacks_ != nullptr) { + parent_.conn_pool_callbacks_->onSuccess(nullptr /*parent_.request_encoder_*/); + parent_.conn_pool_callbacks_.reset(); + } + } + void decodeData(Buffer::Instance& data, bool end_stream) override { + parent_.upstream_callbacks_.onUpstreamData(data, end_stream); + if (end_stream) { + parent_.doneReading(); + } + } + void decodeTrailers(Http::ResponseTrailerMapPtr&& trailers) override { + parent_.config_.propagateResponseTrailers(std::move(trailers), + parent_.downstream_info_.filterState()); + if (Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.tcp_tunneling_send_downstream_fin_on_upstream_trailers")) { + Buffer::OwnedImpl data; + parent_.upstream_callbacks_.onUpstreamData(data, /* end_stream = */ true); + } + parent_.doneReading(); + } + void decodeMetadata(Http::MetadataMapPtr&&) override {} + void dumpState(std::ostream& os, int indent_level) const override { + DUMP_STATE_UNIMPLEMENTED(DecoderShim); + } + + private: + CombinedUpstream& parent_; + }; + DecoderShim response_decoder_; + Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks_; + std::unique_ptr conn_pool_callbacks_; + bool read_half_closed_{}; + bool write_half_closed_{}; +}; + } // namespace TcpProxy } // namespace Envoy diff --git a/source/extensions/upstreams/http/http/upstream_request.h b/source/extensions/upstreams/http/http/upstream_request.h index d7b29d7c8d53..db8adfc2cc14 100644 --- a/source/extensions/upstreams/http/http/upstream_request.h +++ b/source/extensions/upstreams/http/http/upstream_request.h @@ -73,6 +73,7 @@ class HttpUpstream : public Router::GenericUpstream, public Envoy::Http::StreamC void encodeTrailers(const Envoy::Http::RequestTrailerMap& trailers) override { request_encoder_->encodeTrailers(trailers); } + void enableHalfClose() override { request_encoder_->enableTcpTunneling(); } void readDisable(bool disable) override { request_encoder_->getStream().readDisable(disable); } diff --git a/source/extensions/upstreams/http/tcp/upstream_request.h b/source/extensions/upstreams/http/tcp/upstream_request.h index 87e9431fbd30..8c7431250c1e 100644 --- a/source/extensions/upstreams/http/tcp/upstream_request.h +++ b/source/extensions/upstreams/http/tcp/upstream_request.h @@ -72,6 +72,7 @@ class TcpUpstream : public Router::GenericUpstream, void encodeMetadata(const Envoy::Http::MetadataMapVector&) override {} Envoy::Http::Status encodeHeaders(const Envoy::Http::RequestHeaderMap&, bool end_stream) override; void encodeTrailers(const Envoy::Http::RequestTrailerMap&) override; + void enableHalfClose() override {} void readDisable(bool disable) override; void resetStream() override; void setAccount(Buffer::BufferMemoryAccountSharedPtr) override {} diff --git a/source/extensions/upstreams/http/udp/upstream_request.h b/source/extensions/upstreams/http/udp/upstream_request.h index 4d4a132411e1..b5f6e25e6540 100644 --- a/source/extensions/upstreams/http/udp/upstream_request.h +++ b/source/extensions/upstreams/http/udp/upstream_request.h @@ -77,6 +77,7 @@ class UdpUpstream : public Router::GenericUpstream, void encodeTrailers(const Envoy::Http::RequestTrailerMap&) override {} void readDisable(bool) override {} void resetStream() override; + void enableHalfClose() override {} void setAccount(Buffer::BufferMemoryAccountSharedPtr) override {} const StreamInfo::BytesMeterSharedPtr& bytesMeter() override { return bytes_meter_; } diff --git a/source/extensions/upstreams/tcp/generic/BUILD b/source/extensions/upstreams/tcp/generic/BUILD index a29fa7133934..ea02233167e3 100644 --- a/source/extensions/upstreams/tcp/generic/BUILD +++ b/source/extensions/upstreams/tcp/generic/BUILD @@ -18,6 +18,7 @@ envoy_cc_extension( ], visibility = ["//visibility:public"], deps = [ + "//envoy/http:filter_interface", "//envoy/stream_info:bool_accessor_interface", "//envoy/stream_info:filter_state_interface", "//source/common/http:codec_client_lib", diff --git a/source/extensions/upstreams/tcp/generic/config.cc b/source/extensions/upstreams/tcp/generic/config.cc index e688ab84a510..d3e33fdb83f8 100644 --- a/source/extensions/upstreams/tcp/generic/config.cc +++ b/source/extensions/upstreams/tcp/generic/config.cc @@ -18,6 +18,7 @@ TcpProxy::GenericConnPoolPtr GenericConnPoolFactory::createGenericConnPool( Upstream::ThreadLocalCluster& thread_local_cluster, TcpProxy::TunnelingConfigHelperOptConstRef config, Upstream::LoadBalancerContext* context, Envoy::Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, + Http::StreamDecoderFilterCallbacks& stream_decoder_callbacks, StreamInfo::StreamInfo& downstream_info) const { if (config.has_value() && !disableTunnelingByFilterState(downstream_info)) { Http::CodecType pool_type; @@ -30,7 +31,8 @@ TcpProxy::GenericConnPoolPtr GenericConnPoolFactory::createGenericConnPool( pool_type = Http::CodecType::HTTP1; } auto ret = std::make_unique( - thread_local_cluster, context, *config, upstream_callbacks, pool_type, downstream_info); + thread_local_cluster, context, *config, upstream_callbacks, stream_decoder_callbacks, + pool_type, downstream_info); return (ret->valid() ? std::move(ret) : nullptr); } auto ret = std::make_unique(thread_local_cluster, context, diff --git a/source/extensions/upstreams/tcp/generic/config.h b/source/extensions/upstreams/tcp/generic/config.h index c4fee935ef96..e7e87a0145c3 100644 --- a/source/extensions/upstreams/tcp/generic/config.h +++ b/source/extensions/upstreams/tcp/generic/config.h @@ -1,6 +1,7 @@ #pragma once #include "envoy/extensions/upstreams/tcp/generic/v3/generic_connection_pool.pb.h" +#include "envoy/http/filter.h" #include "envoy/registry/registry.h" #include "envoy/tcp/upstream.h" @@ -22,6 +23,7 @@ class GenericConnPoolFactory : public TcpProxy::GenericConnPoolFactory { TcpProxy::TunnelingConfigHelperOptConstRef config, Upstream::LoadBalancerContext* context, Envoy::Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, + Http::StreamDecoderFilterCallbacks& stream_decoder_callbacks, StreamInfo::StreamInfo& downstream_info) const override; ProtobufTypes::MessagePtr createEmptyConfigProto() override { diff --git a/test/common/router/upstream_request_test.cc b/test/common/router/upstream_request_test.cc index 67a6b003cd36..bb9a8185d4b4 100644 --- a/test/common/router/upstream_request_test.cc +++ b/test/common/router/upstream_request_test.cc @@ -33,8 +33,9 @@ class UpstreamRequestTest : public testing::Test { void initialize() { auto conn_pool = std::make_unique>(); conn_pool_ = conn_pool.get(); - upstream_request_ = std::make_unique(router_filter_interface_, - std::move(conn_pool), false, true); + upstream_request_ = + std::make_unique(router_filter_interface_, std::move(conn_pool), false, + true, false /*enable_tcp_tunneling*/); } Http::FilterFactoryCb createDecoderFilterFactoryCb(Http::StreamDecoderFilterSharedPtr filter) { return [filter](Http::FilterChainFactoryCallbacks& callbacks) { diff --git a/test/common/tcp_proxy/BUILD b/test/common/tcp_proxy/BUILD index 0630b106fd5c..d6c47b8a9cbc 100644 --- a/test/common/tcp_proxy/BUILD +++ b/test/common/tcp_proxy/BUILD @@ -79,9 +79,13 @@ envoy_cc_test( deps = [ "//source/common/tcp_proxy", "//test/mocks/http:http_mocks", + "//test/mocks/router:router_filter_interface", + "//test/mocks/router:upstream_request", "//test/mocks/server:factory_context_mocks", + "//test/mocks/stats:stats_mocks", "//test/mocks/tcp:tcp_mocks", "//test/mocks/upstream:cluster_manager_mocks", + "//test/mocks/upstream:load_balancer_context_mock", "//test/test_common:test_runtime_lib", ], ) diff --git a/test/common/tcp_proxy/tcp_proxy_test.cc b/test/common/tcp_proxy/tcp_proxy_test.cc index 41ad163dba41..144e50f2fbbc 100644 --- a/test/common/tcp_proxy/tcp_proxy_test.cc +++ b/test/common/tcp_proxy/tcp_proxy_test.cc @@ -171,7 +171,7 @@ class TcpProxyTest : public TcpProxyTestBase { std::shared_ptr> mock_access_logger_; }; -TEST_F(TcpProxyTest, ExplicitCluster) { +TEST_P(TcpProxyTest, ExplicitCluster) { configure(defaultConfig()); NiceMock connection; @@ -179,7 +179,7 @@ TEST_F(TcpProxyTest, ExplicitCluster) { } // Tests that half-closes are proxied and don't themselves cause any connection to be closed. -TEST_F(TcpProxyTest, HalfCloseProxy) { +TEST_P(TcpProxyTest, HalfCloseProxy) { setup(1); EXPECT_CALL(filter_callbacks_.connection_, close(_)).Times(0); @@ -200,7 +200,7 @@ TEST_F(TcpProxyTest, HalfCloseProxy) { } // Test with an explicitly configured upstream. -TEST_F(TcpProxyTest, ExplicitFactory) { +TEST_P(TcpProxyTest, ExplicitFactory) { // Explicitly configure an HTTP upstream, to test factory creation. auto& info = factory_context_.server_factory_context_.cluster_manager_.thread_local_cluster_ .cluster_.info_; @@ -224,7 +224,7 @@ TEST_F(TcpProxyTest, ExplicitFactory) { } // Test nothing bad happens if an invalid factory is configured. -TEST_F(TcpProxyTest, BadFactory) { +TEST_P(TcpProxyTest, BadFactory) { auto& info = factory_context_.server_factory_context_.cluster_manager_.thread_local_cluster_ .cluster_.info_; info->upstream_config_ = std::make_unique(); @@ -262,7 +262,7 @@ TEST_F(TcpProxyTest, BadFactory) { } // Test that downstream is closed after an upstream LocalClose. -TEST_F(TcpProxyTest, UpstreamLocalDisconnect) { +TEST_P(TcpProxyTest, UpstreamLocalDisconnect) { setup(1); raiseEventUpstreamConnected(0); @@ -280,7 +280,7 @@ TEST_F(TcpProxyTest, UpstreamLocalDisconnect) { } // Test that downstream is closed after an upstream RemoteClose. -TEST_F(TcpProxyTest, UpstreamRemoteDisconnect) { +TEST_P(TcpProxyTest, UpstreamRemoteDisconnect) { setup(1); timeSystem().advanceTimeWait(std::chrono::microseconds(20)); @@ -304,7 +304,7 @@ TEST_F(TcpProxyTest, UpstreamRemoteDisconnect) { } // Test that reconnect is attempted after a local connect failure -TEST_F(TcpProxyTest, ConnectAttemptsUpstreamLocalFail) { +TEST_P(TcpProxyTest, ConnectAttemptsUpstreamLocalFail) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_max_connect_attempts()->set_value(2); @@ -326,7 +326,7 @@ TEST_F(TcpProxyTest, ConnectAttemptsUpstreamLocalFail) { } // Make sure that the tcp proxy code handles reentrant calls to onPoolFailure. -TEST_F(TcpProxyTest, ConnectAttemptsUpstreamLocalFailReentrant) { +TEST_P(TcpProxyTest, ConnectAttemptsUpstreamLocalFailReentrant) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_max_connect_attempts()->set_value(2); @@ -350,7 +350,7 @@ TEST_F(TcpProxyTest, ConnectAttemptsUpstreamLocalFailReentrant) { } // Test that reconnect is attempted after a remote connect failure -TEST_F(TcpProxyTest, ConnectAttemptsUpstreamRemoteFail) { +TEST_P(TcpProxyTest, ConnectAttemptsUpstreamRemoteFail) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_max_connect_attempts()->set_value(2); setup(2, config); @@ -364,7 +364,7 @@ TEST_F(TcpProxyTest, ConnectAttemptsUpstreamRemoteFail) { } // Test that reconnect is attempted after a connect timeout. -TEST_F(TcpProxyTest, ConnectAttemptsUpstreamTimeout) { +TEST_P(TcpProxyTest, ConnectAttemptsUpstreamTimeout) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_max_connect_attempts()->set_value(2); setup(2, config); @@ -378,7 +378,7 @@ TEST_F(TcpProxyTest, ConnectAttemptsUpstreamTimeout) { } // Test that only the configured number of connect attempts occur -TEST_F(TcpProxyTest, ConnectAttemptsLimit) { +TEST_P(TcpProxyTest, ConnectAttemptsLimit) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = accessLogConfig("%RESPONSE_FLAGS%"); config.mutable_max_connect_attempts()->set_value(3); @@ -409,7 +409,7 @@ TEST_F(TcpProxyTest, ConnectAttemptsLimit) { EXPECT_EQ(access_log_data_, "UF,URX"); } -TEST_F(TcpProxyTest, ConnectedNoOp) { +TEST_P(TcpProxyTest, ConnectedNoOp) { setup(1); raiseEventUpstreamConnected(0); @@ -419,7 +419,7 @@ TEST_F(TcpProxyTest, ConnectedNoOp) { } // Test that the tcp proxy sends the correct notifications to the outlier detector -TEST_F(TcpProxyTest, OutlierDetection) { +TEST_P(TcpProxyTest, OutlierDetection) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_max_connect_attempts()->set_value(3); setup(3, config); @@ -437,7 +437,7 @@ TEST_F(TcpProxyTest, OutlierDetection) { raiseEventUpstreamConnected(2); } -TEST_F(TcpProxyTest, UpstreamDisconnectDownstreamFlowControl) { +TEST_P(TcpProxyTest, UpstreamDisconnectDownstreamFlowControl) { setup(1); raiseEventUpstreamConnected(0); @@ -459,7 +459,7 @@ TEST_F(TcpProxyTest, UpstreamDisconnectDownstreamFlowControl) { filter_callbacks_.connection_.runLowWatermarkCallbacks(); } -TEST_F(TcpProxyTest, DownstreamDisconnectRemote) { +TEST_P(TcpProxyTest, DownstreamDisconnectRemote) { setup(1); raiseEventUpstreamConnected(0); @@ -476,7 +476,7 @@ TEST_F(TcpProxyTest, DownstreamDisconnectRemote) { filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); } -TEST_F(TcpProxyTest, DownstreamDisconnectLocal) { +TEST_P(TcpProxyTest, DownstreamDisconnectLocal) { setup(1); raiseEventUpstreamConnected(0); @@ -493,7 +493,7 @@ TEST_F(TcpProxyTest, DownstreamDisconnectLocal) { filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); } -TEST_F(TcpProxyTest, UpstreamConnectTimeout) { +TEST_P(TcpProxyTest, UpstreamConnectTimeout) { setup(1, accessLogConfig("%RESPONSE_FLAGS%")); EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); @@ -503,7 +503,7 @@ TEST_F(TcpProxyTest, UpstreamConnectTimeout) { EXPECT_EQ(access_log_data_, "UF,URX"); } -TEST_F(TcpProxyTest, UpstreamClusterNotFound) { +TEST_P(TcpProxyTest, UpstreamClusterNotFound) { setup(0, accessLogConfig("%RESPONSE_FLAGS%")); EXPECT_CALL(factory_context_.server_factory_context_.cluster_manager_, getThreadLocalCluster(_)) @@ -514,7 +514,7 @@ TEST_F(TcpProxyTest, UpstreamClusterNotFound) { EXPECT_EQ(access_log_data_.value(), "NC"); } -TEST_F(TcpProxyTest, NoHost) { +TEST_P(TcpProxyTest, NoHost) { EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); setup(0, accessLogConfig("%RESPONSE_FLAGS%")); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); @@ -522,7 +522,78 @@ TEST_F(TcpProxyTest, NoHost) { EXPECT_EQ(access_log_data_, "UH"); } -TEST_F(TcpProxyTest, RouteWithMetadataMatch) { +// Tests StreamDecoderFilterCallbacks interface implementation +TEST_P(TcpProxyTest, StreamDecoderFilterCallbacks) { + envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = + accessLogConfig("%RESPONSE_FLAGS%"); + config.mutable_tunneling_config()->set_hostname("www.example.com"); + configure(config); + NiceMock thread_local_cluster_; + auto cluster_info = std::make_shared>(); + // EXPECT_CALL(factory_context_.serverFactoryContext().clusterManager(), getThreadLocalCluster(_)) + // .WillRepeatedly(Return(&thread_local_cluster_)); + EXPECT_CALL(thread_local_cluster_, info()).WillRepeatedly(Return(cluster_info)); + filter_ = + std::make_unique(config_, factory_context_.serverFactoryContext().clusterManager()); + filter_->initializeReadFilterCallbacks(filter_callbacks_); + auto stream_decoder_callbacks = Filter::HttpStreamDecoderFilterCallbacks(filter_.get()); + EXPECT_NO_THROW(stream_decoder_callbacks.streamId()); + EXPECT_NO_THROW(stream_decoder_callbacks.connection()); + EXPECT_NO_THROW(stream_decoder_callbacks.dispatcher()); + EXPECT_ENVOY_BUG( + { stream_decoder_callbacks.resetStream(Http::StreamResetReason::RemoteReset, ""); }, + "Not implemented"); + EXPECT_NO_THROW(stream_decoder_callbacks.streamInfo()); + EXPECT_NO_THROW(stream_decoder_callbacks.scope()); + EXPECT_NO_THROW(stream_decoder_callbacks.route()); + EXPECT_NO_THROW(stream_decoder_callbacks.continueDecoding()); + EXPECT_NO_THROW(stream_decoder_callbacks.requestHeaders()); + EXPECT_NO_THROW(stream_decoder_callbacks.requestTrailers()); + EXPECT_NO_THROW(stream_decoder_callbacks.responseHeaders()); + EXPECT_NO_THROW(stream_decoder_callbacks.responseTrailers()); + EXPECT_NO_THROW(stream_decoder_callbacks.encodeMetadata(nullptr)); + EXPECT_NO_THROW(stream_decoder_callbacks.onDecoderFilterAboveWriteBufferHighWatermark()); + EXPECT_NO_THROW(stream_decoder_callbacks.onDecoderFilterBelowWriteBufferLowWatermark()); + EXPECT_NO_THROW(stream_decoder_callbacks.setDecoderBufferLimit(uint32_t{0})); + EXPECT_NO_THROW(stream_decoder_callbacks.decoderBufferLimit()); + EXPECT_NO_THROW(stream_decoder_callbacks.recreateStream(nullptr)); + EXPECT_NO_THROW(stream_decoder_callbacks.getUpstreamSocketOptions()); + Network::Socket::OptionsSharedPtr sock_options = + Network::SocketOptionFactory::buildIpTransparentOptions(); + EXPECT_NO_THROW(stream_decoder_callbacks.addUpstreamSocketOptions(sock_options)); + EXPECT_NO_THROW(stream_decoder_callbacks.mostSpecificPerFilterConfig()); + EXPECT_NO_THROW(stream_decoder_callbacks.account()); + EXPECT_NO_THROW(stream_decoder_callbacks.setUpstreamOverrideHost( + Upstream::LoadBalancerContext::OverrideHost(std::make_pair("foo", true)))); + EXPECT_NO_THROW(stream_decoder_callbacks.http1StreamEncoderOptions()); + EXPECT_NO_THROW(stream_decoder_callbacks.downstreamCallbacks()); + EXPECT_NO_THROW(stream_decoder_callbacks.upstreamCallbacks()); + EXPECT_NO_THROW(stream_decoder_callbacks.upstreamOverrideHost()); + EXPECT_NO_THROW(stream_decoder_callbacks.resetIdleTimer()); + EXPECT_NO_THROW(stream_decoder_callbacks.filterConfigName()); + EXPECT_NO_THROW(stream_decoder_callbacks.activeSpan()); + EXPECT_NO_THROW(stream_decoder_callbacks.tracingConfig()); + Buffer::OwnedImpl inject_data; + EXPECT_NO_THROW(stream_decoder_callbacks.addDecodedData(inject_data, false)); + EXPECT_NO_THROW(stream_decoder_callbacks.injectDecodedDataToFilterChain(inject_data, false)); + EXPECT_NO_THROW(stream_decoder_callbacks.addDecodedData(inject_data, false)); + EXPECT_NO_THROW(stream_decoder_callbacks.addDecodedTrailers()); + EXPECT_NO_THROW(stream_decoder_callbacks.addDecodedMetadata()); + EXPECT_NO_THROW(stream_decoder_callbacks.decodingBuffer()); + auto func = [](Buffer::Instance&) {}; + EXPECT_NO_THROW(stream_decoder_callbacks.modifyDecodingBuffer(func)); + EXPECT_NO_THROW(stream_decoder_callbacks.encode1xxHeaders(nullptr)); + EXPECT_NO_THROW(stream_decoder_callbacks.informationalHeaders()); + EXPECT_NO_THROW(stream_decoder_callbacks.encodeHeaders(nullptr, false, "")); + EXPECT_NO_THROW(stream_decoder_callbacks.encodeData(inject_data, false)); + EXPECT_NO_THROW(stream_decoder_callbacks.encodeTrailers(nullptr)); + EXPECT_NO_THROW(stream_decoder_callbacks.setDecoderBufferLimit(0)); + std::array buffer; + OutputBufferStream ostream{buffer.data(), buffer.size()}; + EXPECT_NO_THROW(stream_decoder_callbacks.dumpState(ostream, 0)); +} + +TEST_P(TcpProxyTest, RouteWithMetadataMatch) { auto v1 = ProtobufWkt::Value(); v1.set_string_value("v1"); auto v2 = ProtobufWkt::Value(); @@ -562,7 +633,7 @@ TEST_F(TcpProxyTest, RouteWithMetadataMatch) { // Tests that the endpoint selector of a weighted cluster gets included into the // LoadBalancerContext. -TEST_F(TcpProxyTest, WeightedClusterWithMetadataMatch) { +TEST_P(TcpProxyTest, WeightedClusterWithMetadataMatch) { const std::string yaml = R"EOF( stat_prefix: name weighted_clusters: @@ -659,7 +730,7 @@ TEST_F(TcpProxyTest, WeightedClusterWithMetadataMatch) { } // Test that metadata match criteria provided on the StreamInfo is used. -TEST_F(TcpProxyTest, StreamInfoDynamicMetadata) { +TEST_P(TcpProxyTest, StreamInfoDynamicMetadata) { configure(defaultConfig()); ProtobufWkt::Value val; @@ -697,7 +768,7 @@ TEST_F(TcpProxyTest, StreamInfoDynamicMetadata) { // Test that if both streamInfo and configuration add metadata match criteria, they // are merged. -TEST_F(TcpProxyTest, StreamInfoDynamicMetadataAndConfigMerged) { +TEST_P(TcpProxyTest, StreamInfoDynamicMetadataAndConfigMerged) { const std::string yaml = R"EOF( stat_prefix: name weighted_clusters: @@ -758,7 +829,7 @@ TEST_F(TcpProxyTest, StreamInfoDynamicMetadataAndConfigMerged) { EXPECT_EQ(hv2, effective_criterions[2]->value()); } -TEST_F(TcpProxyTest, DisconnectBeforeData) { +TEST_P(TcpProxyTest, DisconnectBeforeData) { configure(defaultConfig()); filter_ = std::make_unique(config_, factory_context_.server_factory_context_.cluster_manager_); @@ -769,7 +840,7 @@ TEST_F(TcpProxyTest, DisconnectBeforeData) { // Test that if the downstream connection is closed before the upstream connection // is established, the upstream connection is cancelled. -TEST_F(TcpProxyTest, RemoteClosedBeforeUpstreamConnected) { +TEST_P(TcpProxyTest, RemoteClosedBeforeUpstreamConnected) { setup(1); EXPECT_CALL(*conn_pool_handles_.at(0), cancel(Tcp::ConnectionPool::CancelPolicy::CloseExcess)); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); @@ -777,13 +848,13 @@ TEST_F(TcpProxyTest, RemoteClosedBeforeUpstreamConnected) { // Test that if the downstream connection is closed before the upstream connection // is established, the upstream connection is cancelled. -TEST_F(TcpProxyTest, LocalClosedBeforeUpstreamConnected) { +TEST_P(TcpProxyTest, LocalClosedBeforeUpstreamConnected) { setup(1); EXPECT_CALL(*conn_pool_handles_.at(0), cancel(Tcp::ConnectionPool::CancelPolicy::CloseExcess)); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); } -TEST_F(TcpProxyTest, UpstreamConnectFailure) { +TEST_P(TcpProxyTest, UpstreamConnectFailure) { setup(1, accessLogConfig("%RESPONSE_FLAGS%")); EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); @@ -799,7 +870,7 @@ TEST_F(TcpProxyTest, UpstreamConnectFailure) { EXPECT_EQ(access_log_data_, "UF,URX"); } -TEST_F(TcpProxyTest, UpstreamConnectionLimit) { +TEST_P(TcpProxyTest, UpstreamConnectionLimit) { configure(accessLogConfig("%RESPONSE_FLAGS%")); factory_context_.server_factory_context_.cluster_manager_.thread_local_cluster_.cluster_.info_ ->resetResourceManager(0, 0, 0, 0, 0); @@ -816,7 +887,7 @@ TEST_F(TcpProxyTest, UpstreamConnectionLimit) { EXPECT_EQ(access_log_data_, "UO"); } -TEST_F(TcpProxyTest, IdleTimeoutObjectFactory) { +TEST_P(TcpProxyTest, IdleTimeoutObjectFactory) { const std::string name = "envoy.tcp_proxy.per_connection_idle_timeout_ms"; auto* factory = Registry::FactoryRegistry::getFactory(name); @@ -828,7 +899,7 @@ TEST_F(TcpProxyTest, IdleTimeoutObjectFactory) { EXPECT_EQ(duration_in_milliseconds, object->serializeAsString()); } -TEST_F(TcpProxyTest, InvalidIdleTimeoutObjectFactory) { +TEST_P(TcpProxyTest, InvalidIdleTimeoutObjectFactory) { const std::string name = "envoy.tcp_proxy.per_connection_idle_timeout_ms"; auto* factory = Registry::FactoryRegistry::getFactory(name); @@ -837,7 +908,7 @@ TEST_F(TcpProxyTest, InvalidIdleTimeoutObjectFactory) { ASSERT_EQ(nullptr, factory->createFromBytes("not_a_number")); } -TEST_F(TcpProxyTest, IdleTimeoutWithFilterStateOverride) { +TEST_P(TcpProxyTest, IdleTimeoutWithFilterStateOverride) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -877,7 +948,7 @@ TEST_F(TcpProxyTest, IdleTimeoutWithFilterStateOverride) { // Tests that the idle timer closes both connections, and gets updated when either // connection has activity. -TEST_F(TcpProxyTest, IdleTimeout) { +TEST_P(TcpProxyTest, IdleTimeout) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -907,7 +978,7 @@ TEST_F(TcpProxyTest, IdleTimeout) { } // Tests that the idle timer is disabled when the downstream connection is closed. -TEST_F(TcpProxyTest, IdleTimerDisabledDownstreamClose) { +TEST_P(TcpProxyTest, IdleTimerDisabledDownstreamClose) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -921,7 +992,7 @@ TEST_F(TcpProxyTest, IdleTimerDisabledDownstreamClose) { } // Tests that the idle timer is disabled when the upstream connection is closed. -TEST_F(TcpProxyTest, IdleTimerDisabledUpstreamClose) { +TEST_P(TcpProxyTest, IdleTimerDisabledUpstreamClose) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -935,7 +1006,7 @@ TEST_F(TcpProxyTest, IdleTimerDisabledUpstreamClose) { } // Tests that flushing data during an idle timeout doesn't cause problems. -TEST_F(TcpProxyTest, IdleTimeoutWithOutstandingDataFlushed) { +TEST_P(TcpProxyTest, IdleTimeoutWithOutstandingDataFlushed) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -987,7 +1058,7 @@ TEST_F(TcpProxyTest, IdleTimeoutWithOutstandingDataFlushed) { // Checks that %UPSTREAM_WIRE_BYTES_SENT%, %UPSTREAM_WIRE_BYTES_RECEIVED%, // %DOWNSTREAM_WIRE_BYTES_SENT%, and %DOWNSTREAM_WIRE_BYTES_RECEIVED% are // correctly logged. -TEST_F(TcpProxyTest, AccessLogBytesMeterData) { +TEST_P(TcpProxyTest, AccessLogBytesMeterData) { setup(1, accessLogConfig("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " "%DOWNSTREAM_WIRE_BYTES_SENT% %DOWNSTREAM_WIRE_BYTES_RECEIVED%")); raiseEventUpstreamConnected(0); @@ -1005,7 +1076,7 @@ TEST_F(TcpProxyTest, AccessLogBytesMeterData) { // Test that access log fields %UPSTREAM_HOST% and %UPSTREAM_CLUSTER% are correctly logged with the // observability name. -TEST_F(TcpProxyTest, AccessLogUpstreamHost) { +TEST_P(TcpProxyTest, AccessLogUpstreamHost) { setup(1, accessLogConfig("%UPSTREAM_HOST% %UPSTREAM_CLUSTER%")); raiseEventUpstreamConnected(0); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); @@ -1014,7 +1085,7 @@ TEST_F(TcpProxyTest, AccessLogUpstreamHost) { } // Test that access log field %UPSTREAM_LOCAL_ADDRESS% is correctly logged. -TEST_F(TcpProxyTest, AccessLogUpstreamLocalAddress) { +TEST_P(TcpProxyTest, AccessLogUpstreamLocalAddress) { setup(1, accessLogConfig("%UPSTREAM_LOCAL_ADDRESS%")); raiseEventUpstreamConnected(0); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); @@ -1023,7 +1094,7 @@ TEST_F(TcpProxyTest, AccessLogUpstreamLocalAddress) { } // Test that access log fields %DOWNSTREAM_PEER_URI_SAN% is correctly logged. -TEST_F(TcpProxyTest, AccessLogPeerUriSan) { +TEST_P(TcpProxyTest, AccessLogPeerUriSan) { filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress( Network::Utility::resolveUrl("tcp://1.1.1.2:20000")); filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress( @@ -1041,7 +1112,7 @@ TEST_F(TcpProxyTest, AccessLogPeerUriSan) { } // Test that access log fields %DOWNSTREAM_TLS_SESSION_ID% is correctly logged. -TEST_F(TcpProxyTest, AccessLogTlsSessionId) { +TEST_P(TcpProxyTest, AccessLogTlsSessionId) { filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress( Network::Utility::resolveUrl("tcp://1.1.1.2:20000")); filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress( @@ -1061,7 +1132,7 @@ TEST_F(TcpProxyTest, AccessLogTlsSessionId) { // Test that access log fields %DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% and // %DOWNSTREAM_LOCAL_ADDRESS% are correctly logged. -TEST_F(TcpProxyTest, AccessLogDownstreamAddress) { +TEST_P(TcpProxyTest, AccessLogDownstreamAddress) { filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress( Network::Utility::resolveUrl("tcp://1.1.1.2:20000")); filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress( @@ -1073,7 +1144,7 @@ TEST_F(TcpProxyTest, AccessLogDownstreamAddress) { } // Test that intermediate log entry by field %ACCESS_LOG_TYPE%. -TEST_F(TcpProxyTest, IntermediateLogEntry) { +TEST_P(TcpProxyTest, IntermediateLogEntry) { auto config = accessLogConfig("%ACCESS_LOG_TYPE%"); config.mutable_access_log_options()->mutable_access_log_flush_interval()->set_seconds(1); config.mutable_idle_timeout()->set_seconds(0); @@ -1129,7 +1200,7 @@ TEST_F(TcpProxyTest, IntermediateLogEntry) { AccessLogType_Name(AccessLog::AccessLogType::TcpConnectionEnd)); } -TEST_F(TcpProxyTest, TestAccessLogOnUpstreamConnected) { +TEST_P(TcpProxyTest, TestAccessLogOnUpstreamConnected) { auto config = accessLogConfig("%UPSTREAM_HOST% %ACCESS_LOG_TYPE%"); config.mutable_access_log_options()->set_flush_access_log_on_connected(true); @@ -1151,7 +1222,7 @@ TEST_F(TcpProxyTest, TestAccessLogOnUpstreamConnected) { AccessLogType_Name(AccessLog::AccessLogType::TcpConnectionEnd))); } -TEST_F(TcpProxyTest, AccessLogUpstreamSSLConnection) { +TEST_P(TcpProxyTest, AccessLogUpstreamSSLConnection) { setup(1); NiceMock stream_info; @@ -1168,7 +1239,7 @@ TEST_F(TcpProxyTest, AccessLogUpstreamSSLConnection) { } // Tests that upstream flush works properly with no idle timeout configured. -TEST_F(TcpProxyTest, UpstreamFlushNoTimeout) { +TEST_P(TcpProxyTest, UpstreamFlushNoTimeout) { setup(1); raiseEventUpstreamConnected(0); @@ -1192,7 +1263,7 @@ TEST_F(TcpProxyTest, UpstreamFlushNoTimeout) { // Tests that upstream flush works with an idle timeout configured, but the connection // finishes draining before the timer expires. -TEST_F(TcpProxyTest, UpstreamFlushTimeoutConfigured) { +TEST_P(TcpProxyTest, UpstreamFlushTimeoutConfigured) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -1223,7 +1294,7 @@ TEST_F(TcpProxyTest, UpstreamFlushTimeoutConfigured) { } // Tests that upstream flush closes the connection when the idle timeout fires. -TEST_F(TcpProxyTest, UpstreamFlushTimeoutExpired) { +TEST_P(TcpProxyTest, UpstreamFlushTimeoutExpired) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -1251,7 +1322,7 @@ TEST_F(TcpProxyTest, UpstreamFlushTimeoutExpired) { // Tests that upstream flush will close a connection if it reads data from the upstream // connection after the downstream connection is closed (nowhere to send it). -TEST_F(TcpProxyTest, UpstreamFlushReceiveUpstreamData) { +TEST_P(TcpProxyTest, UpstreamFlushReceiveUpstreamData) { setup(1); raiseEventUpstreamConnected(0); @@ -1270,13 +1341,13 @@ TEST_F(TcpProxyTest, UpstreamFlushReceiveUpstreamData) { upstream_callbacks_->onUpstreamData(buffer, false); } -TEST_F(TcpProxyTest, UpstreamSocketOptionsReturnedEmpty) { +TEST_P(TcpProxyTest, UpstreamSocketOptionsReturnedEmpty) { setup(1); auto options = filter_->upstreamSocketOptions(); EXPECT_EQ(options, nullptr); } -TEST_F(TcpProxyTest, TcpProxySetRedirectRecordsToUpstream) { +TEST_P(TcpProxyTest, TcpProxySetRedirectRecordsToUpstream) { setup(1, true); EXPECT_TRUE(filter_->upstreamSocketOptions()); auto iterator = std::find_if( @@ -1296,7 +1367,7 @@ TEST_F(TcpProxyTest, TcpProxySetRedirectRecordsToUpstream) { } // Tests that downstream connection can access upstream connections filter state. -TEST_F(TcpProxyTest, ShareFilterState) { +TEST_P(TcpProxyTest, ShareFilterState) { setup(1); upstream_connections_.at(0)->streamInfo().filterState()->setData( @@ -1312,7 +1383,7 @@ TEST_F(TcpProxyTest, ShareFilterState) { } // Tests that filter callback can access downstream and upstream address and ssl properties. -TEST_F(TcpProxyTest, AccessDownstreamAndUpstreamProperties) { +TEST_P(TcpProxyTest, AccessDownstreamAndUpstreamProperties) { setup(1); raiseEventUpstreamConnected(0); @@ -1325,7 +1396,7 @@ TEST_F(TcpProxyTest, AccessDownstreamAndUpstreamProperties) { upstream_connections_.at(0)->streamInfo().downstreamAddressProvider().sslConnection()); } -TEST_F(TcpProxyTest, PickClusterOnUpstreamFailure) { +TEST_P(TcpProxyTest, PickClusterOnUpstreamFailure) { auto config = defaultConfig(); set2Cluster(config); config.mutable_max_connect_attempts()->set_value(2); @@ -1354,7 +1425,7 @@ TEST_F(TcpProxyTest, PickClusterOnUpstreamFailure) { } // Verify that odcds callback does not re-pick cluster. Upstream connect failure does. -TEST_F(TcpProxyTest, OnDemandCallbackStickToTheSelectedCluster) { +TEST_P(TcpProxyTest, OnDemandCallbackStickToTheSelectedCluster) { auto config = onDemandConfig(); set2Cluster(config); config.mutable_max_connect_attempts()->set_value(2); @@ -1413,7 +1484,7 @@ TEST_F(TcpProxyTest, OnDemandCallbackStickToTheSelectedCluster) { } // Verify the on demand api is not invoked when the target thread local cluster is present. -TEST_F(TcpProxyTest, OdcdsIsIgnoredIfClusterExists) { +TEST_P(TcpProxyTest, OdcdsIsIgnoredIfClusterExists) { auto config = onDemandConfig(); setup(1, config); @@ -1432,7 +1503,7 @@ TEST_F(TcpProxyTest, OdcdsIsIgnoredIfClusterExists) { } // Verify the on demand request is cancelled if the tcp downstream connection is closed. -TEST_F(TcpProxyTest, OdcdsCancelIfConnectionClose) { +TEST_P(TcpProxyTest, OdcdsCancelIfConnectionClose) { auto config = onDemandConfig(); mock_odcds_api_handle_ = Upstream::MockOdCdsApiHandle::create().release(); @@ -1454,7 +1525,7 @@ TEST_F(TcpProxyTest, OdcdsCancelIfConnectionClose) { } // Verify a request can be served after a successful on demand cluster request. -TEST_F(TcpProxyTest, OdcdsBasicDownstreamLocalClose) { +TEST_P(TcpProxyTest, OdcdsBasicDownstreamLocalClose) { auto config = onDemandConfig(); mock_odcds_api_handle_ = Upstream::MockOdCdsApiHandle::create().release(); @@ -1499,7 +1570,7 @@ TEST_F(TcpProxyTest, OdcdsBasicDownstreamLocalClose) { } // Verify the connection is closed after the cluster missing callback is triggered. -TEST_F(TcpProxyTest, OdcdsClusterMissingCauseConnectionClose) { +TEST_P(TcpProxyTest, OdcdsClusterMissingCauseConnectionClose) { auto config = onDemandConfig(); mock_odcds_api_handle_ = Upstream::MockOdCdsApiHandle::create().release(); @@ -1528,7 +1599,7 @@ TEST_F(TcpProxyTest, OdcdsClusterMissingCauseConnectionClose) { } // Test that upstream transport failure message is reflected in access logs. -TEST_F(TcpProxyTest, UpstreamConnectFailureStreamInfoAccessLog) { +TEST_P(TcpProxyTest, UpstreamConnectFailureStreamInfoAccessLog) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); setup(1, accessLogConfig("%UPSTREAM_TRANSPORT_FAILURE_REASON%")); @@ -1545,7 +1616,7 @@ TEST_F(TcpProxyTest, UpstreamConnectFailureStreamInfoAccessLog) { // Test that call to tcp_proxy filter's startUpstreamSecureTransport results // in upstream's startUpstreamSecureTransport call. -TEST_F(TcpProxyTest, UpstreamStartSecureTransport) { +TEST_P(TcpProxyTest, UpstreamStartSecureTransport) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); setup(1, config); @@ -1554,6 +1625,8 @@ TEST_F(TcpProxyTest, UpstreamStartSecureTransport) { filter_->startUpstreamSecureTransport(); } +INSTANTIATE_TEST_SUITE_P(WithOrWithoutUpstream, TcpProxyTest, ::testing::Bool()); + TEST(PerConnectionCluster, ObjectFactory) { const std::string name = "envoy.tcp_proxy.cluster"; auto* factory = diff --git a/test/common/tcp_proxy/tcp_proxy_test_base.h b/test/common/tcp_proxy/tcp_proxy_test_base.h index 9816df3871d9..a7a7770d218a 100644 --- a/test/common/tcp_proxy/tcp_proxy_test_base.h +++ b/test/common/tcp_proxy/tcp_proxy_test_base.h @@ -57,9 +57,11 @@ inline Config constructConfigFromYaml(const std::string& yaml, return {tcp_proxy, context}; } -class TcpProxyTestBase : public testing::Test { +class TcpProxyTestBase : public testing::TestWithParam { public: TcpProxyTestBase() { + scoped_runtime_.mergeValues({{"envoy.restart_features.upstream_http_filters_with_tcp_proxy", + GetParam() ? "true" : "false"}}); ON_CALL(*factory_context_.server_factory_context_.access_log_manager_.file_, write(_)) .WillByDefault(SaveArg<0>(&access_log_data_)); ON_CALL(filter_callbacks_.connection_.stream_info_, setUpstreamClusterInfo(_)) @@ -175,6 +177,7 @@ class TcpProxyTestBase : public testing::Test { Upstream::HostDescriptionConstSharedPtr upstream_host_{}; Upstream::ClusterInfoConstSharedPtr upstream_cluster_{}; std::string redirect_records_data_ = "some data"; + TestScopedRuntime scoped_runtime_; }; } // namespace TcpProxy diff --git a/test/common/tcp_proxy/upstream_test.cc b/test/common/tcp_proxy/upstream_test.cc index d48d9690c602..feca11eb4a60 100644 --- a/test/common/tcp_proxy/upstream_test.cc +++ b/test/common/tcp_proxy/upstream_test.cc @@ -6,8 +6,11 @@ #include "test/mocks/buffer/mocks.h" #include "test/mocks/http/mocks.h" #include "test/mocks/http/stream_encoder.h" +#include "test/mocks/router/router_filter_interface.h" +#include "test/mocks/router/upstream_request.h" #include "test/mocks/server/factory_context.h" #include "test/mocks/tcp/mocks.h" +#include "test/mocks/upstream/load_balancer_context.h" #include "test/test_common/environment.h" #include "test/test_common/network_utility.h" #include "test/test_common/test_runtime.h" @@ -23,7 +26,7 @@ using testing::Return; namespace Envoy { namespace TcpProxy { namespace { -using envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig; +using envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy; class HttpUpstreamTest : public testing::TestWithParam { public: @@ -37,11 +40,11 @@ class HttpUpstreamTest : public testing::TestWithParam { .WillByDefault(Return(Http::Http1StreamEncoderOptionsOptRef(stream_encoder_options_))); } EXPECT_CALL(stream_encoder_options_, enableHalfClose()).Times(AnyNumber()); - config_message_.set_hostname("default.host.com:443"); + tcp_proxy_.mutable_tunneling_config()->set_hostname("default.host.com:443"); } void setupUpstream() { - config_ = std::make_unique(config_message_, context_); + config_ = std::make_unique(scope_, tcp_proxy_, context_); upstream_ = std::make_unique(callbacks_, *this->config_, downstream_stream_info_, GetParam()); upstream_->setRequestEncoder(encoder_, true); @@ -51,7 +54,9 @@ class HttpUpstreamTest : public testing::TestWithParam { Http::MockRequestEncoder encoder_; Http::MockHttp1StreamEncoderOptions stream_encoder_options_; NiceMock callbacks_; - TcpProxy_TunnelingConfig config_message_; + TcpProxy tcp_proxy_; + NiceMock store_; + Stats::MockScope& scope_{store_.mockScope()}; std::unique_ptr config_; std::unique_ptr upstream_; NiceMock context_; @@ -146,7 +151,7 @@ TEST_P(HttpUpstreamTest, UpstreamWatermarks) { class MockHttpConnPoolCallbacks : public HttpConnPool::Callbacks { public: - MOCK_METHOD(void, onSuccess, (Http::RequestEncoder & request_encoder)); + MOCK_METHOD(void, onSuccess, (Http::RequestEncoder * request_encoder)); MOCK_METHOD(void, onFailure, ()); }; @@ -235,11 +240,11 @@ class HttpUpstreamRequestEncoderTest : public testing::TestWithParamset_hostname("default.host.com:443"); } void setupUpstream() { - config_ = std::make_unique(config_message_, context_); + config_ = std::make_unique(scope_, tcp_proxy_, context_); upstream_ = std::make_unique(callbacks_, *this->config_, this->downstream_stream_info_, GetParam()); } @@ -259,7 +264,9 @@ class HttpUpstreamRequestEncoderTest : public testing::TestWithParam context_; std::unique_ptr upstream_; - TcpProxy_TunnelingConfig config_message_; + TcpProxy tcp_proxy_; + NiceMock store_; + Stats::MockScope& scope_{store_.mockScope()}; std::unique_ptr config_; bool is_http2_ = true; }; @@ -281,7 +288,7 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoder) { } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderUsePost) { - this->config_message_.set_use_post(true); + this->tcp_proxy_.mutable_tunneling_config()->set_use_post(true); this->setupUpstream(); std::unique_ptr expected_headers; expected_headers = Http::createHeaderMap({ @@ -300,8 +307,8 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderUsePost) { } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderUsePostWithCustomPath) { - this->config_message_.set_use_post(true); - this->config_message_.set_post_path("/test"); + this->tcp_proxy_.mutable_tunneling_config()->set_use_post(true); + this->tcp_proxy_.mutable_tunneling_config()->set_post_path("/test"); this->setupUpstream(); std::unique_ptr expected_headers; expected_headers = Http::createHeaderMap({ @@ -320,25 +327,25 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderUsePostWithCustomPath) { } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderConnectWithCustomPath) { - this->config_message_.set_use_post(false); - this->config_message_.set_post_path("/test"); + this->tcp_proxy_.mutable_tunneling_config()->set_use_post(false); + this->tcp_proxy_.mutable_tunneling_config()->set_post_path("/test"); EXPECT_THROW_WITH_MESSAGE(this->setupUpstream(), EnvoyException, "Can't set a post path when POST method isn't used"); } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHeaders) { - auto* header = this->config_message_.add_headers_to_add(); + auto* header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); auto* hdr = header->mutable_header(); hdr->set_key("header0"); hdr->set_value("value0"); - header = this->config_message_.add_headers_to_add(); + header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); hdr = header->mutable_header(); hdr->set_key("header1"); hdr->set_value("value1"); header->set_append_action(envoy::config::core::v3::HeaderValueOption::APPEND_IF_EXISTS_OR_ADD); - header = this->config_message_.add_headers_to_add(); + header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); hdr = header->mutable_header(); hdr->set_key("header1"); hdr->set_value("value2"); @@ -360,13 +367,13 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHeaders) { } TEST_P(HttpUpstreamRequestEncoderTest, ConfigReuse) { - auto* header = this->config_message_.add_headers_to_add(); + auto* header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); auto* hdr = header->mutable_header(); hdr->set_key("key"); hdr->set_value("value1"); header->set_append_action(envoy::config::core::v3::HeaderValueOption::APPEND_IF_EXISTS_OR_ADD); - header = this->config_message_.add_headers_to_add(); + header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); hdr = header->mutable_header(); hdr->set_key("key"); hdr->set_value("value2"); @@ -404,12 +411,12 @@ TEST_P(HttpUpstreamRequestEncoderTest, ConfigReuse) { } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHeadersWithDownstreamInfo) { - auto* header = this->config_message_.add_headers_to_add(); + auto* header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); auto* hdr = header->mutable_header(); hdr->set_key("header0"); hdr->set_value("value0"); - header = this->config_message_.add_headers_to_add(); + header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); hdr = header->mutable_header(); hdr->set_key("downstream_local_port"); hdr->set_value("%DOWNSTREAM_LOCAL_PORT%"); @@ -438,7 +445,7 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHeadersWithDownstreamInfo) TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHostnameWithDownstreamInfoRequestedServerName) { - this->config_message_.set_hostname("%REQUESTED_SERVER_NAME%:443"); + this->tcp_proxy_.mutable_tunneling_config()->set_hostname("%REQUESTED_SERVER_NAME%:443"); this->setupUpstream(); std::unique_ptr expected_headers; @@ -462,7 +469,8 @@ TEST_P(HttpUpstreamRequestEncoderTest, } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHostnameWithDownstreamInfoDynamicMetadata) { - this->config_message_.set_hostname("%DYNAMIC_METADATA(tunnel:address)%:443"); + this->tcp_proxy_.mutable_tunneling_config()->set_hostname( + "%DYNAMIC_METADATA(tunnel:address)%:443"); this->setupUpstream(); std::unique_ptr expected_headers; @@ -482,6 +490,218 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHostnameWithDownstreamInfoD EXPECT_CALL(this->encoder_, encodeHeaders(HeaderMapEqualRef(expected_headers.get()), false)); this->upstream_->setRequestEncoder(this->encoder_, false); } + +class CombinedUpstreamTest : public testing::Test { +public: + CombinedUpstreamTest() { + EXPECT_CALL(encoder_, getStream()).Times(AnyNumber()); + EXPECT_CALL(encoder_, http1StreamEncoderOptions()).Times(AnyNumber()); + EXPECT_CALL(encoder_, enableTcpTunneling()).Times(AnyNumber()); + EXPECT_CALL(stream_encoder_options_, enableHalfClose()).Times(AnyNumber()); + tcp_proxy_.mutable_tunneling_config()->set_hostname("default.host.com:443"); + } + + void setup() { + tunnel_config_ = std::make_unique(scope_, tcp_proxy_, context_); + conn_pool_ = std::make_unique(cluster_, &lb_context_, *tunnel_config_, callbacks_, + decoder_callbacks_, Http::CodecType::HTTP2, + downstream_stream_info_); + upstream_ = std::make_unique(*conn_pool_, callbacks_, decoder_callbacks_, + *tunnel_config_, downstream_stream_info_); + auto mock_conn_pool = std::make_unique>(); + std::unique_ptr generic_conn_pool = std::move(mock_conn_pool); + config_ = std::make_shared(tcp_proxy_, factory_context_); + filter_ = + std::make_unique(config_, factory_context_.serverFactoryContext().clusterManager()); + filter_->initializeReadFilterCallbacks(filter_callbacks_); + auto mock_upst = std::make_unique>( + *upstream_, std::move(generic_conn_pool)); + mock_router_upstream_request_ = mock_upst.get(); + upstream_->setRouterUpstreamRequest(std::move(mock_upst)); + EXPECT_CALL(*mock_router_upstream_request_, acceptHeadersFromRouter(false)); + EXPECT_NO_THROW(tunnel_config_->serverFactoryContext()); + upstream_->newStream(*filter_); + } + + Router::MockUpstreamRequest* mock_router_upstream_request_{}; + NiceMock router_filter_interface_; + NiceMock factory_context_; + ConfigSharedPtr config_; + NiceMock filter_callbacks_; + std::unique_ptr filter_; + + NiceMock downstream_stream_info_; + Http::MockRequestEncoder encoder_; + Http::MockHttp1StreamEncoderOptions stream_encoder_options_; + NiceMock callbacks_; + TcpProxy tcp_proxy_; + NiceMock decoder_callbacks_; + NiceMock cluster_; + NiceMock lb_context_; + std::unique_ptr conn_pool_; + NiceMock store_; + Stats::MockScope& scope_{store_.mockScope()}; + std::unique_ptr tunnel_config_; + std::unique_ptr upstream_; + NiceMock context_; +}; +TEST_F(CombinedUpstreamTest, RouterFilterInterface) { + this->setup(); + EXPECT_EQ(this->upstream_->startUpstreamSecureTransport(), false); + EXPECT_EQ(this->upstream_->getUpstreamConnectionSslInfo(), nullptr); + auto mock_conn_pool = std::make_unique>(); + std::unique_ptr generic_conn_pool = std::move(mock_conn_pool); + auto mock_upst = std::make_unique>( + *this->upstream_, std::move(generic_conn_pool)); + EXPECT_NO_THROW(this->upstream_->onUpstream1xxHeaders(nullptr, *mock_upst.get())); + EXPECT_NO_THROW(this->upstream_->onUpstreamMetadata(nullptr)); + EXPECT_NO_THROW(this->upstream_->onPerTryTimeout(*mock_upst.get())); + EXPECT_NO_THROW(this->upstream_->onPerTryIdleTimeout(*mock_upst.get())); + EXPECT_NO_THROW(this->upstream_->onStreamMaxDurationReached(*mock_upst.get())); + EXPECT_EQ(this->upstream_->dynamicMaxStreamDuration(), absl::nullopt); + EXPECT_EQ(this->upstream_->downstreamTrailers(), nullptr); + EXPECT_EQ(this->upstream_->downstreamResponseStarted(), false); + EXPECT_EQ(this->upstream_->downstreamEndStream(), false); + EXPECT_EQ(this->upstream_->attemptCount(), 0); +} + +TEST_F(CombinedUpstreamTest, WriteUpstream) { + this->setup(); + EXPECT_CALL(*this->mock_router_upstream_request_, + acceptDataFromRouter(BufferStringEqual("foo"), false /*end_stream*/)); + Buffer::OwnedImpl buffer1("foo"); + this->upstream_->encodeData(buffer1, false); + + EXPECT_CALL(*this->mock_router_upstream_request_, + acceptDataFromRouter(BufferStringEqual("bar"), true /*end_stream*/)); + Buffer::OwnedImpl buffer2("bar"); + this->upstream_->encodeData(buffer2, true); + + // New upstream with no encoder. + this->upstream_ = std::make_unique(*conn_pool_, callbacks_, decoder_callbacks_, + *tunnel_config_, downstream_stream_info_); + this->upstream_->encodeData(buffer2, true); +} + +TEST_F(CombinedUpstreamTest, WriteDownstream) { + this->setup(); + EXPECT_CALL(this->callbacks_, onUpstreamData(BufferStringEqual("foo"), false)); + Buffer::OwnedImpl buffer1("foo"); + this->upstream_->responseDecoder().decodeData(buffer1, false); + + EXPECT_CALL(this->callbacks_, onUpstreamData(BufferStringEqual("bar"), true)); + Buffer::OwnedImpl buffer2("bar"); + this->upstream_->responseDecoder().decodeData(buffer2, true); +} + +TEST_F(CombinedUpstreamTest, InvalidUpgradeWithEarlyFin) { + this->setup(); + EXPECT_CALL(this->callbacks_, onEvent(_)); + Http::ResponseHeaderMapPtr headers{new Http::TestResponseHeaderMapImpl{{":status", "200"}}}; + this->upstream_->responseDecoder().decodeHeaders(std::move(headers), true); +} + +TEST_F(CombinedUpstreamTest, InvalidUpgradeWithNon200) { + this->setup(); + EXPECT_CALL(this->callbacks_, onEvent(_)); + Http::ResponseHeaderMapPtr headers{new Http::TestResponseHeaderMapImpl{{":status", "301"}}}; + this->upstream_->responseDecoder().decodeHeaders(std::move(headers), false); +} + +TEST_F(CombinedUpstreamTest, ReadDisable) { + this->setup(); + EXPECT_CALL(*this->mock_router_upstream_request_, onAboveWriteBufferHighWatermark()); + EXPECT_TRUE(this->upstream_->readDisable(true)); + + EXPECT_CALL(*this->mock_router_upstream_request_, onAboveWriteBufferHighWatermark()).Times(0); + EXPECT_TRUE(this->upstream_->readDisable(false)); + + // New upstream with no encoder. + this->upstream_ = std::make_unique(*conn_pool_, callbacks_, decoder_callbacks_, + *tunnel_config_, downstream_stream_info_); + EXPECT_FALSE(this->upstream_->readDisable(true)); +} + +TEST_F(CombinedUpstreamTest, AddBytesSentCallbackForCoverage) { + this->setup(); + this->upstream_->addBytesSentCallback([&](uint64_t) { return true; }); +} + +TEST_F(CombinedUpstreamTest, DownstreamDisconnect) { + this->setup(); + EXPECT_CALL(*this->mock_router_upstream_request_, resetStream()); + EXPECT_CALL(this->callbacks_, onEvent(_)).Times(0); + EXPECT_TRUE(this->upstream_->onDownstreamEvent(Network::ConnectionEvent::LocalClose) == nullptr); +} + +TEST_F(CombinedUpstreamTest, UpstreamReset) { + this->setup(); + EXPECT_CALL(*this->mock_router_upstream_request_, resetStream()); + EXPECT_CALL(this->callbacks_, onEvent(_)); + this->upstream_->onResetStream(Http::StreamResetReason::ConnectionTermination, ""); +} + +TEST_F(CombinedUpstreamTest, UpstreamWatermarks) { + this->setup(); + EXPECT_CALL(this->callbacks_, onAboveWriteBufferHighWatermark()); + this->upstream_->onAboveWriteBufferHighWatermark(); + + EXPECT_CALL(this->callbacks_, onBelowWriteBufferLowWatermark()); + this->upstream_->onBelowWriteBufferLowWatermark(); +} + +TEST_F(CombinedUpstreamTest, OnSuccessCalledOnValidResponse) { + this->setup(); + auto conn_pool_callbacks = std::make_unique(); + auto conn_pool_callbacks_raw = conn_pool_callbacks.get(); + this->upstream_->setConnPoolCallbacks(std::move(conn_pool_callbacks)); + EXPECT_CALL(*conn_pool_callbacks_raw, onFailure()).Times(0); + EXPECT_CALL(*conn_pool_callbacks_raw, onSuccess(_)); + Http::ResponseHeaderMapPtr headers{new Http::TestResponseHeaderMapImpl{{":status", "200"}}}; + this->upstream_->responseDecoder().decodeHeaders(std::move(headers), false); +} + +TEST_F(CombinedUpstreamTest, OnFailureCalledOnInvalidResponse) { + this->setup(); + auto conn_pool_callbacks = std::make_unique(); + auto conn_pool_callbacks_raw = conn_pool_callbacks.get(); + this->upstream_->setConnPoolCallbacks(std::move(conn_pool_callbacks)); + EXPECT_CALL(*conn_pool_callbacks_raw, onFailure()); + EXPECT_CALL(*conn_pool_callbacks_raw, onSuccess(_)).Times(0); + Http::ResponseHeaderMapPtr headers{new Http::TestResponseHeaderMapImpl{{":status", "404"}}}; + this->upstream_->responseDecoder().decodeHeaders(std::move(headers), false); +} + +TEST_F(CombinedUpstreamTest, DumpsResponseDecoderWithoutAllocatingMemory) { + std::array buffer; + OutputBufferStream ostream{buffer.data(), buffer.size()}; + this->setup(); + + Stats::TestUtil::MemoryTest memory_test; + this->upstream_->responseDecoder().dumpState(ostream, 1); + EXPECT_EQ(memory_test.consumedBytes(), 0); + EXPECT_THAT(ostream.contents(), EndsWith("has not implemented dumpState\n")); +} +TEST_F(CombinedUpstreamTest, UpstreamTrailersMarksDoneReading) { + this->setup(); + EXPECT_CALL(*this->mock_router_upstream_request_, resetStream()); + this->upstream_->doneWriting(); + Http::ResponseTrailerMapPtr trailers{new Http::TestResponseTrailerMapImpl{{"key", "value"}}}; + this->upstream_->responseDecoder().decodeTrailers(std::move(trailers)); +} + +TEST_F(CombinedUpstreamTest, UpstreamTrailersDontPropagateFinDownstreamWhenFeatureDisabled) { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.tcp_tunneling_send_downstream_fin_on_upstream_trailers", + "false"}}); + this->setup(); + EXPECT_CALL(*this->mock_router_upstream_request_, resetStream()); + upstream_->doneWriting(); + EXPECT_CALL(callbacks_, onUpstreamData(_, _)).Times(0); + Http::ResponseTrailerMapPtr trailers{new Http::TestResponseTrailerMapImpl{{"key", "value"}}}; + upstream_->responseDecoder().decodeTrailers(std::move(trailers)); +} } // namespace } // namespace TcpProxy } // namespace Envoy diff --git a/test/extensions/filters/http/cache/BUILD b/test/extensions/filters/http/cache/BUILD index 1d1f5f0ab270..ce6100368be6 100644 --- a/test/extensions/filters/http/cache/BUILD +++ b/test/extensions/filters/http/cache/BUILD @@ -124,6 +124,7 @@ envoy_extension_cc_test( "cache_filter_integration_test.cc", ], extension_names = ["envoy.filters.http.cache"], + shard_count = 2, deps = [ "//source/extensions/filters/http/cache:config", "//source/extensions/filters/http/cache:http_cache_lib", diff --git a/test/extensions/filters/http/csrf/BUILD b/test/extensions/filters/http/csrf/BUILD index 5a958bce60e8..2a3b5ef8c3a4 100644 --- a/test/extensions/filters/http/csrf/BUILD +++ b/test/extensions/filters/http/csrf/BUILD @@ -33,6 +33,7 @@ envoy_extension_cc_test( size = "large", srcs = ["csrf_filter_integration_test.cc"], extension_names = ["envoy.filters.http.csrf"], + shard_count = 2, deps = [ "//source/extensions/filters/http/csrf:config", "//test/config:utility_lib", diff --git a/test/extensions/filters/http/custom_response/BUILD b/test/extensions/filters/http/custom_response/BUILD index 704c20d197f4..01c7daba1d5c 100644 --- a/test/extensions/filters/http/custom_response/BUILD +++ b/test/extensions/filters/http/custom_response/BUILD @@ -81,7 +81,7 @@ envoy_extension_cc_test( "custom_response_integration_test.cc", ], extension_names = ["envoy.filters.http.custom_response"], - shard_count = 2, + shard_count = 4, tags = [ "cpu:3", ], diff --git a/test/extensions/filters/http/fault/BUILD b/test/extensions/filters/http/fault/BUILD index a7413da5feaf..55c19ce038bf 100644 --- a/test/extensions/filters/http/fault/BUILD +++ b/test/extensions/filters/http/fault/BUILD @@ -55,6 +55,7 @@ envoy_extension_cc_test( size = "large", srcs = ["fault_filter_integration_test.cc"], extension_names = ["envoy.filters.http.fault"], + shard_count = 4, deps = [ "//source/extensions/filters/http/fault:config", "//test/integration:http_protocol_integration_lib", diff --git a/test/extensions/filters/http/health_check/BUILD b/test/extensions/filters/http/health_check/BUILD index 8d7a622ebf1d..7023b7b02bf6 100644 --- a/test/extensions/filters/http/health_check/BUILD +++ b/test/extensions/filters/http/health_check/BUILD @@ -45,6 +45,7 @@ envoy_extension_cc_test( "health_check_integration_test.cc", ], extension_names = ["envoy.filters.http.health_check"], + shard_count = 2, deps = [ "//source/extensions/filters/http/buffer:config", "//source/extensions/filters/http/health_check:config", diff --git a/test/extensions/filters/http/jwt_authn/BUILD b/test/extensions/filters/http/jwt_authn/BUILD index 9b84849c432f..321ebccf2c83 100644 --- a/test/extensions/filters/http/jwt_authn/BUILD +++ b/test/extensions/filters/http/jwt_authn/BUILD @@ -162,7 +162,7 @@ envoy_extension_cc_test( "envoy.filters.http.jwt_authn", "envoy.filters.http.set_filter_state", ], - shard_count = 4, + shard_count = 8, tags = [ "cpu:3", ], diff --git a/test/extensions/filters/http/rbac/BUILD b/test/extensions/filters/http/rbac/BUILD index 4c397d1e0389..33538419eebd 100644 --- a/test/extensions/filters/http/rbac/BUILD +++ b/test/extensions/filters/http/rbac/BUILD @@ -58,7 +58,7 @@ envoy_extension_cc_test( size = "large", srcs = ["rbac_filter_integration_test.cc"], extension_names = ["envoy.filters.http.rbac"], - shard_count = 3, + shard_count = 10, tags = ["skip_on_windows"], deps = [ "//source/extensions/clusters/dynamic_forward_proxy:cluster", diff --git a/test/extensions/upstreams/http/tcp/upstream_request_test.cc b/test/extensions/upstreams/http/tcp/upstream_request_test.cc index e48d6a4ad7e4..9bacca7d18af 100644 --- a/test/extensions/upstreams/http/tcp/upstream_request_test.cc +++ b/test/extensions/upstreams/http/tcp/upstream_request_test.cc @@ -114,7 +114,7 @@ class TcpUpstreamTest : public ::testing::Test { EXPECT_CALL(mock_router_filter_, callbacks()).Times(AnyNumber()); upstream_request_ = std::make_unique( mock_router_filter_, std::make_unique>(), false, - false); + false, false /*enable_tcp_tunneling*/); auto data = std::make_unique>(); EXPECT_CALL(*data, connection()).Times(AnyNumber()).WillRepeatedly(ReturnRef(connection())); tcp_upstream_ = std::make_unique(upstream_request_.get(), std::move(data)); diff --git a/test/extensions/upstreams/http/udp/upstream_request_test.cc b/test/extensions/upstreams/http/udp/upstream_request_test.cc index 423b21589589..c6020febfea6 100644 --- a/test/extensions/upstreams/http/udp/upstream_request_test.cc +++ b/test/extensions/upstreams/http/udp/upstream_request_test.cc @@ -46,6 +46,7 @@ class UdpUpstreamTest : public ::testing::Test { udp_upstream_ = std::make_unique(&mock_upstream_to_downstream_, std::move(mock_socket), std::move(mock_host), mock_dispatcher_); + EXPECT_NO_THROW(udp_upstream_->enableHalfClose()); } protected: diff --git a/test/extensions/upstreams/tcp/generic/BUILD b/test/extensions/upstreams/tcp/generic/BUILD index 2a9375294a53..57cc2e3fcc9a 100644 --- a/test/extensions/upstreams/tcp/generic/BUILD +++ b/test/extensions/upstreams/tcp/generic/BUILD @@ -17,5 +17,6 @@ envoy_cc_test( "//source/extensions/upstreams/tcp/generic:config", "//test/mocks/server:factory_context_mocks", "//test/mocks/upstream:upstream_mocks", + "@envoy_api//envoy/extensions/filters/network/tcp_proxy/v3:pkg_cc_proto", ], ) diff --git a/test/extensions/upstreams/tcp/generic/config_test.cc b/test/extensions/upstreams/tcp/generic/config_test.cc index f2c62b449174..08d2ff0ead46 100644 --- a/test/extensions/upstreams/tcp/generic/config_test.cc +++ b/test/extensions/upstreams/tcp/generic/config_test.cc @@ -1,7 +1,10 @@ +#include "envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.pb.h" + #include "source/common/stream_info/bool_accessor_impl.h" #include "source/common/tcp_proxy/tcp_proxy.h" #include "source/extensions/upstreams/tcp/generic/config.h" +#include "test/mocks/http/mocks.h" #include "test/mocks/server/factory_context.h" #include "test/mocks/tcp/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -14,13 +17,13 @@ using testing::_; using testing::AnyNumber; using testing::NiceMock; using testing::Return; +using testing::ReturnRef; namespace Envoy { namespace Extensions { namespace Upstreams { namespace Tcp { namespace Generic { - class TcpConnPoolTest : public ::testing::Test { public: TcpConnPoolTest() { @@ -32,6 +35,10 @@ class TcpConnPoolTest : public ::testing::Test { NiceMock downstream_stream_info_; NiceMock connection_; Upstream::MockLoadBalancerContext lb_context_; + envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy tcp_proxy_; + NiceMock store_; + Stats::MockScope& scope_{store_.mockScope()}; + NiceMock decoder_callbacks_; NiceMock context_; }; @@ -39,13 +46,13 @@ TEST_F(TcpConnPoolTest, TestNoTunnelingConfig) { EXPECT_CALL(thread_local_cluster_, tcpConnPool(_, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(), - &lb_context_, callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } TEST_F(TcpConnPoolTest, TestTunnelingDisabledByFilterState) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; - config_proto.set_hostname("host"); - const TcpProxy::TunnelingConfigHelperImpl config(config_proto, context_); + tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); + const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); downstream_stream_info_.filterState()->setData( TcpProxy::DisableTunnelingFilterStateKey, @@ -55,13 +62,13 @@ TEST_F(TcpConnPoolTest, TestTunnelingDisabledByFilterState) { EXPECT_CALL(thread_local_cluster_, tcpConnPool(_, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), - &lb_context_, callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } TEST_F(TcpConnPoolTest, TestTunnelingNotDisabledIfFilterStateHasFalseValue) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; - config_proto.set_hostname("host"); - const TcpProxy::TunnelingConfigHelperImpl config(config_proto, context_); + tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); + const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); downstream_stream_info_.filterState()->setData( TcpProxy::DisableTunnelingFilterStateKey, @@ -71,46 +78,47 @@ TEST_F(TcpConnPoolTest, TestTunnelingNotDisabledIfFilterStateHasFalseValue) { EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), - &lb_context_, callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } TEST_F(TcpConnPoolTest, TestNoConnPool) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; - config_proto.set_hostname("host"); - const TcpProxy::TunnelingConfigHelperImpl config(config_proto, context_); + tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); + const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), - &lb_context_, callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } TEST_F(TcpConnPoolTest, Http2Config) { auto info = std::make_shared(); - EXPECT_CALL(*info, features()).WillOnce(Return(Upstream::ClusterInfo::Features::HTTP2)); - EXPECT_CALL(thread_local_cluster_, info).WillOnce(Return(info)); + const std::string fake_cluster_name = "fake_cluster"; + envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; - config_proto.set_hostname("host"); - const TcpProxy::TunnelingConfigHelperImpl config(config_proto, context_); + tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); + const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), - &lb_context_, callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } TEST_F(TcpConnPoolTest, Http3Config) { auto info = std::make_shared(); + const std::string fake_cluster_name = "fake_cluster"; EXPECT_CALL(*info, features()) .Times(AnyNumber()) .WillRepeatedly(Return(Upstream::ClusterInfo::Features::HTTP3)); EXPECT_CALL(thread_local_cluster_, info).Times(AnyNumber()).WillRepeatedly(Return(info)); envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; - config_proto.set_hostname("host"); - const TcpProxy::TunnelingConfigHelperImpl config(config_proto, context_); + tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); + const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), - &lb_context_, callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } TEST(DisableTunnelingObjectFactory, CreateFromBytes) { diff --git a/test/integration/BUILD b/test/integration/BUILD index fce0e38d657d..72636c49d709 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -329,6 +329,7 @@ envoy_cc_test( srcs = envoy_select_admin_functionality([ "drain_close_integration_test.cc", ]), + shard_count = 6, tags = [ "cpu:3", ], @@ -614,6 +615,7 @@ envoy_cc_test( srcs = [ "buffer_accounting_integration_test.cc", ], + shard_count = 2, tags = [ "cpu:3", ], @@ -985,7 +987,7 @@ envoy_cc_test( srcs = ["idle_timeout_integration_test.cc"], # As this test has many pauses for idle timeouts, it takes a while to run. # Shard it enough to bring the run time in line with other integration tests. - shard_count = 4, + shard_count = 16, tags = [ "cpu:3", ], @@ -1355,7 +1357,7 @@ envoy_cc_test( srcs = [ "redirect_integration_test.cc", ], - shard_count = 2, + shard_count = 4, tags = [ "cpu:3", "nofips", @@ -1391,6 +1393,7 @@ envoy_cc_test( name = "websocket_integration_test", size = "large", srcs = ["websocket_integration_test.cc"], + shard_count = 2, tags = [ "cpu:3", ], @@ -1537,7 +1540,7 @@ envoy_cc_test( name = "overload_integration_test", size = "large", srcs = ["overload_integration_test.cc"], - shard_count = 4, + shard_count = 10, tags = [ "cpu:3", ], @@ -1811,7 +1814,7 @@ envoy_cc_test( data = [ "//test/config/integration/certs", ], - shard_count = 8, + shard_count = 30, tags = [ "cpu:3", ], @@ -1837,6 +1840,7 @@ envoy_cc_test( data = [ "//test/config/integration/certs", ], + shard_count = 4, deps = [ ":http_integration_lib", ":http_protocol_integration_lib", @@ -2281,6 +2285,7 @@ envoy_cc_test( srcs = [ "local_reply_integration_test.cc", ], + shard_count = 2, tags = [ "cpu:2", ], diff --git a/test/integration/base_integration_test.cc b/test/integration/base_integration_test.cc index 13295850af25..baab4dd3be91 100644 --- a/test/integration/base_integration_test.cc +++ b/test/integration/base_integration_test.cc @@ -427,6 +427,7 @@ void BaseIntegrationTest::registerTestServerPorts(const std::vector const auto admin_addr = test_server->server().admin()->socket().connectionInfoProvider().localAddress(); if (admin_addr->type() == Network::Address::Type::Ip) { + ENVOY_LOG(debug, "registered 'admin' as port {}.", admin_addr->ip()->port()); registerPort("admin", admin_addr->ip()->port()); } } diff --git a/test/integration/http_protocol_integration.cc b/test/integration/http_protocol_integration.cc index 50f79fdf6366..914fffefcd99 100644 --- a/test/integration/http_protocol_integration.cc +++ b/test/integration/http_protocol_integration.cc @@ -39,15 +39,17 @@ std::vector HttpProtocolIntegrationTest::getProtocolTest #else use_header_validator_values.push_back(false); #endif - for (Http1ParserImpl http1_implementation : http1_implementations) { - for (Http2Impl http2_implementation : http2_implementations) { - for (bool defer_processing : http2_bool_values) { - for (bool deprecate_callback_visitor : http2_bool_values) { - for (bool use_header_validator : use_header_validator_values) { - ret.push_back(HttpProtocolTestParams{ - ip_version, downstream_protocol, upstream_protocol, http1_implementation, - http2_implementation, defer_processing, use_header_validator, - deprecate_callback_visitor}); + for (const bool tunneling_with_upstream_filters : {false, true}) { + for (Http1ParserImpl http1_implementation : http1_implementations) { + for (Http2Impl http2_implementation : http2_implementations) { + for (bool defer_processing : http2_bool_values) { + for (bool deprecate_callback_visitor : http2_bool_values) { + for (bool use_header_validator : use_header_validator_values) { + ret.push_back(HttpProtocolTestParams{ + ip_version, downstream_protocol, upstream_protocol, http1_implementation, + http2_implementation, defer_processing, use_header_validator, + deprecate_callback_visitor, tunneling_with_upstream_filters}); + } } } } @@ -102,9 +104,11 @@ std::string HttpProtocolIntegrationTest::protocolTestParamsToString( http2ImplementationToString(params.param.http2_implementation), params.param.defer_processing_backedup_streams ? "WithDeferredProcessing" : "NoDeferredProcessing", + params.param.use_universal_header_validator ? "Uhv" : "Legacy", params.param.deprecate_callback_visitor ? "WithCallbackVisitor" : "NoCallbackVisitor", - params.param.use_universal_header_validator ? "Uhv" : "Legacy"); + params.param.tunneling_with_upstream_filters ? "WithUpstreamHttpFilters" + : "WithoutUpstreamHttpFilters"); } void HttpProtocolIntegrationTest::setUpstreamOverrideStreamErrorOnInvalidHttpMessage() { diff --git a/test/integration/http_protocol_integration.h b/test/integration/http_protocol_integration.h index 0c0dd6c1bbc0..1fa57f3d1992 100644 --- a/test/integration/http_protocol_integration.h +++ b/test/integration/http_protocol_integration.h @@ -15,6 +15,7 @@ struct HttpProtocolTestParams { Http2Impl http2_implementation; bool defer_processing_backedup_streams; bool use_universal_header_validator; + bool tunneling_with_upstream_filters; bool deprecate_callback_visitor; }; @@ -86,6 +87,9 @@ class HttpProtocolIntegrationTest : public testing::TestWithParam static void initializeMockStreamFilterCallbacks(T& callbacks) MockStreamDecoderFilterCallbacks::MockStreamDecoderFilterCallbacks() { initializeMockStreamFilterCallbacks(*this); + ON_CALL(*this, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); ON_CALL(*this, decodingBuffer()).WillByDefault(Invoke(&buffer_, &Buffer::InstancePtr::get)); ON_CALL(*this, addDownstreamWatermarkCallbacks(_)) diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 3ec3f7c5eb71..f7770ac37b96 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -109,6 +109,7 @@ class MockFilterManagerCallbacks : public FilterManagerCallbacks { MOCK_METHOD(OptRef, tracingConfig, (), (const)); MOCK_METHOD(const ScopeTrackedObject&, scope, ()); MOCK_METHOD(void, restoreContextOnContinue, (ScopeTrackedObjectStack&)); + MOCK_METHOD(bool, isHalfCloseEnabled, ()); ResponseHeaderMapPtr informational_headers_; ResponseHeaderMapPtr response_headers_; @@ -330,6 +331,7 @@ class MockStreamDecoderFilterCallbacks : public StreamDecoderFilterCallbacks, bool is_grpc_request_{}; bool is_head_request_{false}; bool stream_destroyed_{}; + NiceMock dispatcher_; }; class MockStreamEncoderFilterCallbacks : public StreamEncoderFilterCallbacks, diff --git a/test/mocks/router/BUILD b/test/mocks/router/BUILD index 79a35cb2b25d..2d1364e40bc1 100644 --- a/test/mocks/router/BUILD +++ b/test/mocks/router/BUILD @@ -49,3 +49,12 @@ envoy_cc_mock( "//test/test_common:test_time_lib", ], ) + +envoy_cc_mock( + name = "upstream_request", + srcs = ["upstream_request.cc"], + hdrs = ["upstream_request.h"], + deps = [ + "//source/common/router:router_lib", + ], +) diff --git a/test/mocks/router/upstream_request.cc b/test/mocks/router/upstream_request.cc new file mode 100644 index 000000000000..aabf4baf8a05 --- /dev/null +++ b/test/mocks/router/upstream_request.cc @@ -0,0 +1,13 @@ +#include "test/mocks/router/upstream_request.h" + +namespace Envoy { +namespace Router { + +MockUpstreamRequest::MockUpstreamRequest(RouterFilterInterface& router_interface, + std::unique_ptr&& conn_pool) + : UpstreamRequest(router_interface, std::move(conn_pool), false, true, true) {} + +MockUpstreamRequest::~MockUpstreamRequest() = default; + +} // namespace Router +} // namespace Envoy diff --git a/test/mocks/router/upstream_request.h b/test/mocks/router/upstream_request.h new file mode 100644 index 000000000000..10688fdde5d1 --- /dev/null +++ b/test/mocks/router/upstream_request.h @@ -0,0 +1,21 @@ +#pragma once + +#include "source/common/router/upstream_request.h" + +#include "gmock/gmock.h" + +namespace Envoy { +namespace Router { + +class MockUpstreamRequest : public UpstreamRequest { +public: + MockUpstreamRequest(RouterFilterInterface& router_interface, std::unique_ptr&&); + ~MockUpstreamRequest() override; + MOCK_METHOD(void, acceptHeadersFromRouter, (bool end_stream), (override)); + MOCK_METHOD(void, acceptDataFromRouter, (Buffer::Instance & data, bool end_stream), (override)); + MOCK_METHOD(void, onAboveWriteBufferHighWatermark, (), (override)); + MOCK_METHOD(void, resetStream, (), (override)); +}; + +} // namespace Router +} // namespace Envoy From d94c8eb3e072d26016403c8bc92aa47c25f56ea2 Mon Sep 17 00:00:00 2001 From: danzh Date: Mon, 18 Mar 2024 11:49:54 -0400 Subject: [PATCH 071/124] quic: remove unnecessary type cast in integration test (#32937) Signed-off-by: Dan Zhang Co-authored-by: Dan Zhang --- test/integration/http_integration.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 2064c9629c45..368054ba95a0 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -245,15 +245,13 @@ Network::ClientConnectionPtr HttpIntegrationTest::makeClientConnectionWithOption fmt::format("udp://{}:{}", Network::Test::getLoopbackAddressUrlString(version_), port)); Network::Address::InstanceConstSharedPtr local_addr = Network::Test::getCanonicalLoopbackAddress(version_); - auto& quic_transport_socket_factory_ref = - dynamic_cast(*quic_transport_socket_factory_); return Quic::createQuicNetworkConnection( - *quic_connection_persistent_info_, quic_transport_socket_factory_ref.getCryptoConfig(), + *quic_connection_persistent_info_, quic_transport_socket_factory_->getCryptoConfig(), quic::QuicServerId( - quic_transport_socket_factory_ref.clientContextConfig()->serverNameIndication(), + quic_transport_socket_factory_->clientContextConfig()->serverNameIndication(), static_cast(port)), *dispatcher_, server_addr, local_addr, quic_stat_names_, {}, *stats_store_.rootScope(), - options, nullptr, connection_id_generator_, quic_transport_socket_factory_ref); + options, nullptr, connection_id_generator_, *quic_transport_socket_factory_); #else ASSERT(false, "running a QUIC integration test without compiling QUIC"); return nullptr; From 19726b78c218801069d74b796727d76b89e2b54c Mon Sep 17 00:00:00 2001 From: phlax Date: Mon, 18 Mar 2024 15:55:00 +0000 Subject: [PATCH 072/124] wasm: Remove `wavm` (#32872) Signed-off-by: Ryan Northey --- api/envoy/extensions/wasm/v3/wasm.proto | 7 +- bazel/BUILD | 6 - bazel/README.md | 1 - bazel/envoy_build_system.bzl | 2 - bazel/envoy_select.bzl | 9 - bazel/foreign_cc/BUILD | 157 ------------------ bazel/foreign_cc/llvm.patch | 25 --- bazel/repositories.bzl | 24 --- bazel/repository_locations.bzl | 35 ---- ci/do_ci.sh | 17 +- .../configuration/other_features/wasm.rst | 3 +- source/extensions/common/wasm/wasm_vm.cc | 3 +- source/extensions/common/wasm/wasm_vm.h | 3 +- source/extensions/extensions_build_config.bzl | 1 - source/extensions/extensions_metadata.yaml | 8 - source/extensions/wasm_runtime/wavm/BUILD | 21 --- source/extensions/wasm_runtime/wavm/config.cc | 26 --- .../bootstrap/wasm/wasm_speed_test.cc | 6 - test/extensions/common/wasm/BUILD | 1 - test/extensions/common/wasm/wasm_runtime.cc | 3 - test/extensions/filters/http/wasm/BUILD | 2 - .../filters/http/wasm/test_data/BUILD | 19 --- test/per_file_coverage.sh | 1 - tools/spelling/spelling_dictionary.txt | 1 - 24 files changed, 9 insertions(+), 372 deletions(-) delete mode 100644 bazel/foreign_cc/llvm.patch delete mode 100644 source/extensions/wasm_runtime/wavm/BUILD delete mode 100644 source/extensions/wasm_runtime/wavm/config.cc diff --git a/api/envoy/extensions/wasm/v3/wasm.proto b/api/envoy/extensions/wasm/v3/wasm.proto index 54ef437cbaf7..58b7b57c489d 100644 --- a/api/envoy/extensions/wasm/v3/wasm.proto +++ b/api/envoy/extensions/wasm/v3/wasm.proto @@ -50,7 +50,7 @@ message VmConfig { string vm_id = 1; // The Wasm runtime type, defaults to the first available Wasm engine used at Envoy build-time. - // The priority to search for the available engine is: v8 -> wasmtime -> wamr -> wavm. + // The priority to search for the available engine is: v8 -> wasmtime -> wamr. // Available Wasm runtime types are registered as extensions. The following runtimes are included // in Envoy code base: // @@ -68,11 +68,6 @@ message VmConfig { // **envoy.wasm.runtime.wamr**: `WAMR `_-based WebAssembly runtime. // This runtime is not enabled in the official build. // - // .. _extension_envoy.wasm.runtime.wavm: - // - // **envoy.wasm.runtime.wavm**: `WAVM `_-based WebAssembly runtime. - // This runtime is not enabled in the official build. - // // .. _extension_envoy.wasm.runtime.wasmtime: // // **envoy.wasm.runtime.wasmtime**: `Wasmtime `_-based WebAssembly runtime. diff --git a/bazel/BUILD b/bazel/BUILD index 19578ec3c59e..c4749d3140d3 100644 --- a/bazel/BUILD +++ b/bazel/BUILD @@ -497,12 +497,6 @@ config_setting( values = {"define": "zlib=ng"}, ) -# TODO: consider converting WAVM VM support to an extension (https://github.com/envoyproxy/envoy/issues/12574) -config_setting( - name = "wasm_wavm", - values = {"define": "wasm=wavm"}, -) - config_setting( name = "wasm_v8", values = {"define": "wasm=v8"}, diff --git a/bazel/README.md b/bazel/README.md index 03a8008bcf05..02ad261e62b6 100644 --- a/bazel/README.md +++ b/bazel/README.md @@ -738,7 +738,6 @@ To enable a specific WebAssembly (Wasm) engine, you'll need to pass `--define wa * `v8` (the default included engine) * `wamr` * `wasmtime` -* `wavm` If you're building from a custom build repository, the parameters need to prefixed with `@envoy`, for example `--@envoy//source/extensions/filters/http/kill_request:enabled`. diff --git a/bazel/envoy_build_system.bzl b/bazel/envoy_build_system.bzl index 90fd023733b2..604e9f86bc61 100644 --- a/bazel/envoy_build_system.bzl +++ b/bazel/envoy_build_system.bzl @@ -38,7 +38,6 @@ load( _envoy_select_wasm_v8 = "envoy_select_wasm_v8", _envoy_select_wasm_wamr = "envoy_select_wasm_wamr", _envoy_select_wasm_wasmtime = "envoy_select_wasm_wasmtime", - _envoy_select_wasm_wavm = "envoy_select_wasm_wavm", ) load( ":envoy_test.bzl", @@ -252,7 +251,6 @@ envoy_select_wasm_cpp_tests = _envoy_select_wasm_cpp_tests envoy_select_wasm_rust_tests = _envoy_select_wasm_rust_tests envoy_select_wasm_v8 = _envoy_select_wasm_v8 envoy_select_wasm_wamr = _envoy_select_wasm_wamr -envoy_select_wasm_wavm = _envoy_select_wasm_wavm envoy_select_wasm_wasmtime = _envoy_select_wasm_wasmtime envoy_select_linkstatic = _envoy_linkstatic diff --git a/bazel/envoy_select.bzl b/bazel/envoy_select.bzl index 87d5c71eefa2..8caee534fab4 100644 --- a/bazel/envoy_select.bzl +++ b/bazel/envoy_select.bzl @@ -154,7 +154,6 @@ def envoy_select_wasm_v8(xs): "@envoy//bazel:wasm_v8": xs, "@envoy//bazel:wasm_wamr": [], "@envoy//bazel:wasm_wasmtime": [], - "@envoy//bazel:wasm_wavm": [], "@envoy//bazel:wasm_disabled": [], # TODO(phlax): re-enable once issues with llvm profiler are resolved # (see https://github.com/envoyproxy/envoy/issues/24164) @@ -168,7 +167,6 @@ def envoy_select_wasm_v8_bool(): "@envoy//bazel:wasm_v8": True, "@envoy//bazel:wasm_wamr": False, "@envoy//bazel:wasm_wasmtime": False, - "@envoy//bazel:wasm_wavm": False, "@envoy//bazel:wasm_disabled": False, # TODO(phlax): re-enable once issues with llvm profiler are resolved # (see https://github.com/envoyproxy/envoy/issues/24164) @@ -183,13 +181,6 @@ def envoy_select_wasm_wamr(xs): "//conditions:default": [], }) -# Selects the given values depending on the Wasm runtimes enabled in the current build. -def envoy_select_wasm_wavm(xs): - return select({ - "@envoy//bazel:wasm_wavm": xs, - "//conditions:default": [], - }) - # Selects the given values depending on the Wasm runtimes enabled in the current build. def envoy_select_wasm_wasmtime(xs): return select({ diff --git a/bazel/foreign_cc/BUILD b/bazel/foreign_cc/BUILD index d80b6b218293..b863192e58a7 100644 --- a/bazel/foreign_cc/BUILD +++ b/bazel/foreign_cc/BUILD @@ -349,133 +349,6 @@ envoy_cmake( }), ) -envoy_cmake( - name = "llvm", - cache_entries = { - # Disable both: BUILD and INCLUDE, since some of the INCLUDE - # targets build code instead of only generating build files. - "LLVM_BUILD_BENCHMARKS": "off", - "LLVM_INCLUDE_BENCHMARKS": "off", - "LLVM_BUILD_DOCS": "off", - "LLVM_INCLUDE_DOCS": "off", - "LLVM_BUILD_EXAMPLES": "off", - "LLVM_INCLUDE_EXAMPLES": "off", - "LLVM_BUILD_RUNTIME": "off", - "LLVM_BUILD_RUNTIMES": "off", - "LLVM_INCLUDE_RUNTIMES": "off", - "LLVM_BUILD_TESTS": "off", - "LLVM_INCLUDE_TESTS": "off", - "LLVM_BUILD_TOOLS": "off", - "LLVM_INCLUDE_TOOLS": "off", - "LLVM_BUILD_UTILS": "off", - "LLVM_INCLUDE_UTILS": "off", - "LLVM_ENABLE_IDE": "off", - "LLVM_ENABLE_LIBEDIT": "off", - "LLVM_ENABLE_LIBXML2": "off", - "LLVM_ENABLE_TERMINFO": "off", - "LLVM_ENABLE_ZLIB": "off", - "LLVM_TARGETS_TO_BUILD": "X86", - "CMAKE_CXX_COMPILER_FORCED": "on", - # Workaround for the issue with statically linked libstdc++ - # using -l:libstdc++.a. - "CMAKE_CXX_FLAGS": "-lstdc++", - }, - env = { - # Workaround for the -DDEBUG flag added in fastbuild on macOS, - # which conflicts with DEBUG macro used in LLVM. - "CFLAGS": "-UDEBUG", - "CXXFLAGS": "-UDEBUG", - "ASMFLAGS": "-UDEBUG", - }, - lib_source = "@org_llvm_llvm//:all", - out_static_libs = select({ - "//conditions:default": [ - # This list must be updated when the bazel llvm version is updated - # (in `bazel/repository_locations.bzl`) - # - # The list can be regenerated by compiling the correct/updated llvm version - # from sources and running: - # - # `llvm-config --libnames` - # - "libLLVMWindowsManifest.a", - "libLLVMXRay.a", - "libLLVMLibDriver.a", - "libLLVMDlltoolDriver.a", - "libLLVMCoverage.a", - "libLLVMLineEditor.a", - "libLLVMX86Disassembler.a", - "libLLVMX86AsmParser.a", - "libLLVMX86CodeGen.a", - "libLLVMX86Desc.a", - "libLLVMX86Info.a", - "libLLVMOrcJIT.a", - "libLLVMMCJIT.a", - "libLLVMJITLink.a", - "libLLVMOrcTargetProcess.a", - "libLLVMOrcShared.a", - "libLLVMInterpreter.a", - "libLLVMExecutionEngine.a", - "libLLVMRuntimeDyld.a", - "libLLVMSymbolize.a", - "libLLVMDebugInfoPDB.a", - "libLLVMDebugInfoGSYM.a", - "libLLVMOption.a", - "libLLVMObjectYAML.a", - "libLLVMMCA.a", - "libLLVMMCDisassembler.a", - "libLLVMLTO.a", - "libLLVMPasses.a", - "libLLVMCFGuard.a", - "libLLVMCoroutines.a", - "libLLVMObjCARCOpts.a", - "libLLVMHelloNew.a", - "libLLVMipo.a", - "libLLVMVectorize.a", - "libLLVMLinker.a", - "libLLVMInstrumentation.a", - "libLLVMFrontendOpenMP.a", - "libLLVMFrontendOpenACC.a", - "libLLVMExtensions.a", - "libLLVMDWARFLinker.a", - "libLLVMGlobalISel.a", - "libLLVMMIRParser.a", - "libLLVMAsmPrinter.a", - "libLLVMDebugInfoDWARF.a", - "libLLVMSelectionDAG.a", - "libLLVMCodeGen.a", - "libLLVMIRReader.a", - "libLLVMAsmParser.a", - "libLLVMInterfaceStub.a", - "libLLVMFileCheck.a", - "libLLVMFuzzMutate.a", - "libLLVMTarget.a", - "libLLVMScalarOpts.a", - "libLLVMInstCombine.a", - "libLLVMAggressiveInstCombine.a", - "libLLVMTransformUtils.a", - "libLLVMBitWriter.a", - "libLLVMAnalysis.a", - "libLLVMProfileData.a", - "libLLVMObject.a", - "libLLVMTextAPI.a", - "libLLVMMCParser.a", - "libLLVMMC.a", - "libLLVMDebugInfoCodeView.a", - "libLLVMDebugInfoMSF.a", - "libLLVMBitReader.a", - "libLLVMCore.a", - "libLLVMRemarks.a", - "libLLVMBitstreamReader.a", - "libLLVMBinaryFormat.a", - "libLLVMSupport.a", - "libLLVMDemangle.a", - ], - }), - tags = ["skip_on_windows"], - alwayslink = True, -) - envoy_cmake( name = "nghttp2", cache_entries = { @@ -522,36 +395,6 @@ envoy_cmake( tags = ["skip_on_windows"], ) -envoy_cmake( - name = "wavm", - cache_entries = { - "LLVM_DIR": "$EXT_BUILD_DEPS/copy_llvm/llvm/lib/cmake/llvm", - "WAVM_ENABLE_STATIC_LINKING": "on", - "WAVM_ENABLE_RELEASE_ASSERTS": "on", - "WAVM_ENABLE_UNWIND": "on", - # Workaround for the issue with statically linked libstdc++ - # using -l:libstdc++.a. - "CMAKE_CXX_FLAGS": "-lstdc++ -Wno-unused-command-line-argument", - }, - env = { - # Workaround for the -DDEBUG flag added in fastbuild on macOS, - # which conflicts with DEBUG macro used in LLVM. - "CFLAGS": "-UDEBUG", - "CXXFLAGS": "-UDEBUG -Wno-error=unused-but-set-variable", - "ASMFLAGS": "-UDEBUG", - }, - lib_source = "@com_github_wavm_wavm//:all", - out_binaries = ["wavm"], - out_static_libs = select({ - "//conditions:default": [ - "libWAVM.a", - "libWAVMUnwind.a", - ], - }), - tags = ["skip_on_windows"], - deps = [":llvm"], -) - envoy_cmake( name = "zlib", cache_entries = { diff --git a/bazel/foreign_cc/llvm.patch b/bazel/foreign_cc/llvm.patch deleted file mode 100644 index cd02f2842401..000000000000 --- a/bazel/foreign_cc/llvm.patch +++ /dev/null @@ -1,25 +0,0 @@ -# Workaround for Envoy's CMAKE_BUILD_TYPE=Bazel. ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -247,7 +247,7 @@ - string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) - - if (CMAKE_BUILD_TYPE AND -- NOT uppercase_CMAKE_BUILD_TYPE MATCHES "^(DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL)$") -+ NOT uppercase_CMAKE_BUILD_TYPE MATCHES "^(DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL|BAZEL)$") - message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") - endif() - -# Workaround for a missing -fuse-ld flag in CXXFLAGS, which results in -# different linkers being used during configure and compilation phases. ---- a/cmake/modules/HandleLLVMOptions.cmake -+++ b/cmake/modules/HandleLLVMOptions.cmake -@@ -718,8 +718,6 @@ endif() - if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja") - include(CheckLinkerFlag) - check_linker_flag("-Wl,--color-diagnostics" LINKER_SUPPORTS_COLOR_DIAGNOSTICS) -- append_if(LINKER_SUPPORTS_COLOR_DIAGNOSTICS "-Wl,--color-diagnostics" -- CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) - endif() - - # Add flags for add_dead_strip(). diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index d231f276c7ab..82922ec78b69 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -383,9 +383,7 @@ def envoy_dependencies(skip_targets = []): _rust_deps() _kafka_deps() - _org_llvm_llvm() _com_github_wamr() - _com_github_wavm_wavm() _com_github_wasmtime() _com_github_wasm_c_api() @@ -1368,18 +1366,6 @@ def _com_github_gperftools_gperftools(): actual = "@envoy//bazel/foreign_cc:gperftools", ) -def _org_llvm_llvm(): - external_http_archive( - name = "org_llvm_llvm", - build_file_content = BUILD_ALL_CONTENT, - patch_args = ["-p1"], - patches = ["@envoy//bazel/foreign_cc:llvm.patch"], - ) - native.bind( - name = "llvm", - actual = "@envoy//bazel/foreign_cc:llvm", - ) - def _com_github_wamr(): external_http_archive( name = "com_github_wamr", @@ -1390,16 +1376,6 @@ def _com_github_wamr(): actual = "@envoy//bazel/foreign_cc:wamr", ) -def _com_github_wavm_wavm(): - external_http_archive( - name = "com_github_wavm_wavm", - build_file_content = BUILD_ALL_CONTENT, - ) - native.bind( - name = "wavm", - actual = "@envoy//bazel/foreign_cc:wavm", - ) - def _com_github_wasmtime(): external_http_archive( name = "com_github_wasmtime", diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index ae267e507a16..f4026bfe6818 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -1074,26 +1074,6 @@ REPOSITORY_LOCATIONS_SPEC = dict( license = "Apache-2.0", license_url = "https://github.com/bazelbuild/rules_pkg/blob/{version}/LICENSE", ), - org_llvm_llvm = dict( - # When changing this, you must re-generate the list of llvm libs - # see `bazel/foreign_cc/BUILD` for further information. - project_name = "LLVM", - project_desc = "LLVM Compiler Infrastructure", - project_url = "https://llvm.org", - version = "12.0.1", - sha256 = "7d9a8405f557cefc5a21bf5672af73903b64749d9bc3a50322239f56f34ffddf", - strip_prefix = "llvm-{version}.src", - urls = ["https://github.com/llvm/llvm-project/releases/download/llvmorg-{version}/llvm-{version}.src.tar.xz"], - release_date = "2021-07-09", - use_category = ["dataplane_ext"], - extensions = [ - "envoy.wasm.runtime.wamr", - "envoy.wasm.runtime.wavm", - ], - cpe = "cpe:2.3:a:llvm:*:*", - license = "Apache-2.0", - license_url = "https://github.com/llvm/llvm-project/blob/llvmorg-{version}/llvm/LICENSE.TXT", - ), com_github_wamr = dict( project_name = "Webassembly Micro Runtime", project_desc = "A standalone runtime with a small footprint for WebAssembly", @@ -1109,19 +1089,6 @@ REPOSITORY_LOCATIONS_SPEC = dict( license = "Apache-2.0", license_url = "https://github.com/bytecodealliance/wasm-micro-runtime/blob/{version}/LICENSE", ), - com_github_wavm_wavm = dict( - project_name = "WAVM", - project_desc = "WebAssembly Virtual Machine", - project_url = "https://wavm.github.io", - version = "3f9a150cac7faf28eab357a2c5b83d2ec740c7d9", - sha256 = "82e05ade03fdac60cf863972d3e7420a771ef4a18afad26ac442554ab0be1207", - strip_prefix = "WAVM-{version}", - urls = ["https://github.com/WAVM/WAVM/archive/{version}.tar.gz"], - release_date = "2022-05-14", - use_category = ["dataplane_ext"], - extensions = ["envoy.wasm.runtime.wavm"], - cpe = "cpe:2.3:a:webassembly_virtual_machine_project:webassembly_virtual_machine:*", - ), com_github_wasmtime = dict( project_name = "wasmtime", project_desc = "A standalone runtime for WebAssembly", @@ -1432,7 +1399,6 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.wasm.runtime.null", "envoy.wasm.runtime.v8", "envoy.wasm.runtime.wamr", - "envoy.wasm.runtime.wavm", "envoy.wasm.runtime.wasmtime", ], release_date = "2023-05-01", @@ -1458,7 +1424,6 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.wasm.runtime.null", "envoy.wasm.runtime.v8", "envoy.wasm.runtime.wamr", - "envoy.wasm.runtime.wavm", "envoy.wasm.runtime.wasmtime", ], release_date = "2023-12-19", diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 1c04c4250292..2cecbff3f793 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -427,21 +427,12 @@ case $CI_TARGET in "${TEST_TARGETS[@]}" \ --test_tag_filters=-nofips \ --build_tests_only - echo "Building and testing with wasm=wavm: ${TEST_TARGETS[*]}" - bazel_with_collection \ - test "${BAZEL_BUILD_OPTIONS[@]}" \ - --config=compile-time-options \ - --define wasm=wavm \ - -c fastbuild \ - "${TEST_TARGETS[@]}" \ - --test_tag_filters=-nofips \ - --build_tests_only # "--define log_debug_assert_in_release=enabled" must be tested with a release build, so run only # these tests under "-c opt" to save time in CI. bazel_with_collection \ test "${BAZEL_BUILD_OPTIONS[@]}" \ --config=compile-time-options \ - --define wasm=wavm \ + --define wasm=wasmtime \ -c opt \ @envoy//test/common/common:assert_test \ @envoy//test/server:server_test @@ -449,15 +440,15 @@ case $CI_TARGET in bazel_with_collection \ test "${BAZEL_BUILD_OPTIONS[@]}" \ --config=compile-time-options \ - --define wasm=wavm \ + --define wasm=wamtime \ -c opt \ @envoy//test/common/common:assert_test \ --define log_fast_debug_assert_in_release=enabled \ --define log_debug_assert_in_release=disabled - echo "Building binary with wasm=wavm... and logging disabled" + echo "Building binary with wasm=wasmtime... and logging disabled" bazel build "${BAZEL_BUILD_OPTIONS[@]}" \ --config=compile-time-options \ - --define wasm=wavm \ + --define wasm=wasmtime \ --define enable_logging=disabled \ -c fastbuild \ @envoy//source/exe:envoy-static \ diff --git a/docs/root/configuration/other_features/wasm.rst b/docs/root/configuration/other_features/wasm.rst index f34146e68138..25c23c7ccf6e 100644 --- a/docs/root/configuration/other_features/wasm.rst +++ b/docs/root/configuration/other_features/wasm.rst @@ -12,10 +12,9 @@ The following runtimes are supported by Envoy: envoy.wasm.runtime.v8, "`V8 `_-based runtime" envoy.wasm.runtime.wamr, "`WAMR `_ runtime" envoy.wasm.runtime.wasmtime, "`Wasmtime `_ runtime" - envoy.wasm.runtime.wavm, "`WAVM `_ runtime" envoy.wasm.runtime.null, "Compiled modules linked into Envoy" -WAMR(WASM-Micro-Runtime), Wasmtime and WAVM runtimes are not included in Envoy release image by default. +WAMR(WASM-Micro-Runtime), Wasmtime runtime is not included in Envoy release image by default. Wasm runtime emits the following statistics: diff --git a/source/extensions/common/wasm/wasm_vm.cc b/source/extensions/common/wasm/wasm_vm.cc index fc225eb3045b..aaf20e95569a 100644 --- a/source/extensions/common/wasm/wasm_vm.cc +++ b/source/extensions/common/wasm/wasm_vm.cc @@ -79,8 +79,7 @@ bool isWasmEngineAvailable(absl::string_view runtime) { absl::string_view getFirstAvailableWasmEngineName() { constexpr absl::string_view wasm_engines[] = { - "envoy.wasm.runtime.v8", "envoy.wasm.runtime.wasmtime", "envoy.wasm.runtime.wamr", - "envoy.wasm.runtime.wavm"}; + "envoy.wasm.runtime.v8", "envoy.wasm.runtime.wasmtime", "envoy.wasm.runtime.wamr"}; for (const auto wasm_engine : wasm_engines) { if (isWasmEngineAvailable(wasm_engine)) { return wasm_engine; diff --git a/source/extensions/common/wasm/wasm_vm.h b/source/extensions/common/wasm/wasm_vm.h index 139d06c87f69..e347428e1155 100644 --- a/source/extensions/common/wasm/wasm_vm.h +++ b/source/extensions/common/wasm/wasm_vm.h @@ -38,7 +38,8 @@ class WasmException : public EnvoyException { using WasmVmPtr = std::unique_ptr; -// Create a new low-level Wasm VM using runtime of the given type (e.g. "envoy.wasm.runtime.wavm"). +// Create a new low-level Wasm VM using runtime of the given type (e.g. +// "envoy.wasm.runtime.wasmtime"). WasmVmPtr createWasmVm(absl::string_view runtime); /** diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index a3fa5c9831e2..1425006ffaf4 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -348,7 +348,6 @@ EXTENSIONS = { "envoy.wasm.runtime.null": "//source/extensions/wasm_runtime/null:config", "envoy.wasm.runtime.v8": "//source/extensions/wasm_runtime/v8:config", "envoy.wasm.runtime.wamr": "//source/extensions/wasm_runtime/wamr:config", - "envoy.wasm.runtime.wavm": "//source/extensions/wasm_runtime/wavm:config", "envoy.wasm.runtime.wasmtime": "//source/extensions/wasm_runtime/wasmtime:config", # diff --git a/source/extensions/extensions_metadata.yaml b/source/extensions/extensions_metadata.yaml index 771ae0f999c3..88957f155d5e 100644 --- a/source/extensions/extensions_metadata.yaml +++ b/source/extensions/extensions_metadata.yaml @@ -1354,14 +1354,6 @@ envoy.wasm.runtime.wasmtime: # is updated to capture additional Wasm runtimes". security_posture: unknown status: alpha -envoy.wasm.runtime.wavm: - categories: - - envoy.wasm.runtime - # "This may never change from unknown until the threat model at - # https://envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/threat_model#core-and-extensions - # is updated to capture additional Wasm runtimes". - security_posture: unknown - status: alpha envoy.watchdog.profile_action: categories: - envoy.guarddog_actions diff --git a/source/extensions/wasm_runtime/wavm/BUILD b/source/extensions/wasm_runtime/wavm/BUILD deleted file mode 100644 index fa65a029b17b..000000000000 --- a/source/extensions/wasm_runtime/wavm/BUILD +++ /dev/null @@ -1,21 +0,0 @@ -load( - "//bazel:envoy_build_system.bzl", - "envoy_cc_extension", - "envoy_extension_package", -) -load("//bazel:envoy_select.bzl", "envoy_select_wasm_wavm") - -licenses(["notice"]) # Apache 2 - -envoy_extension_package() - -envoy_cc_extension( - name = "config", - srcs = envoy_select_wasm_wavm(["config.cc"]), - deps = envoy_select_wasm_wavm([ - "//envoy/registry", - "//source/extensions/common/wasm:wasm_runtime_factory_interface", - "@proxy_wasm_cpp_host//:base_lib", - "@proxy_wasm_cpp_host//:wavm_lib", - ]), -) diff --git a/source/extensions/wasm_runtime/wavm/config.cc b/source/extensions/wasm_runtime/wavm/config.cc deleted file mode 100644 index c2b3d93a29e4..000000000000 --- a/source/extensions/wasm_runtime/wavm/config.cc +++ /dev/null @@ -1,26 +0,0 @@ -#include "envoy/registry/registry.h" - -#include "source/extensions/common/wasm/wasm_runtime_factory.h" - -#include "include/proxy-wasm/wavm.h" - -namespace Envoy { -namespace Extensions { -namespace Common { -namespace Wasm { - -class WavmRuntimeFactory : public WasmRuntimeFactory { -public: - WasmVmPtr createWasmVm() override { return proxy_wasm::createWavmVm(); } - - std::string name() const override { return "envoy.wasm.runtime.wavm"; } -}; - -#if defined(PROXY_WASM_HAS_RUNTIME_WAVM) -REGISTER_FACTORY(WavmRuntimeFactory, WasmRuntimeFactory); -#endif - -} // namespace Wasm -} // namespace Common -} // namespace Extensions -} // namespace Envoy diff --git a/test/extensions/bootstrap/wasm/wasm_speed_test.cc b/test/extensions/bootstrap/wasm/wasm_speed_test.cc index aaadd74ba6e2..2c1333fbb8a3 100644 --- a/test/extensions/bootstrap/wasm/wasm_speed_test.cc +++ b/test/extensions/bootstrap/wasm/wasm_speed_test.cc @@ -91,12 +91,6 @@ static void bmWasmSimpleCallSpeedTest(benchmark::State& state, std::string test, std::string("null")); \ BENCHMARK_CAPTURE(bmWasmSimpleCallSpeedTest, WasmSpeedTest_##_t, std::string(#_t), \ std::string("wamr")); -#elif defined(PROXY_WASM_HAS_RUNTIME_WAVM) -#define B(_t) \ - BENCHMARK_CAPTURE(bmWasmSimpleCallSpeedTest, NullSpeedTest_##_t, std::string(#_t), \ - std::string("null")); \ - BENCHMARK_CAPTURE(bmWasmSimpleCallSpeedTest, WasmSpeedTest_##_t, std::string(#_t), \ - std::string("wavm")); #elif defined(PROXY_WASM_HAS_RUNTIME_WASMTIME) #define B(_t) \ BENCHMARK_CAPTURE(bmWasmSimpleCallSpeedTest, NullSpeedTest_##_t, std::string(#_t), \ diff --git a/test/extensions/common/wasm/BUILD b/test/extensions/common/wasm/BUILD index 62daee5c4c16..cbd4bb7ade1e 100644 --- a/test/extensions/common/wasm/BUILD +++ b/test/extensions/common/wasm/BUILD @@ -100,7 +100,6 @@ envoy_cc_test_library( "//source/extensions/wasm_runtime/v8:config", "//source/extensions/wasm_runtime/wamr:config", "//source/extensions/wasm_runtime/wasmtime:config", - "//source/extensions/wasm_runtime/wavm:config", ], ) diff --git a/test/extensions/common/wasm/wasm_runtime.cc b/test/extensions/common/wasm/wasm_runtime.cc index db5ecc6e400b..e21f9835cea0 100644 --- a/test/extensions/common/wasm/wasm_runtime.cc +++ b/test/extensions/common/wasm/wasm_runtime.cc @@ -10,9 +10,6 @@ std::vector sandboxRuntimes() { #if defined(PROXY_WASM_HAS_RUNTIME_V8) runtimes.push_back("v8"); #endif -#if defined(PROXY_WASM_HAS_RUNTIME_WAVM) - runtimes.push_back("wavm"); -#endif #if defined(PROXY_WASM_HAS_RUNTIME_WAMR) runtimes.push_back("wamr"); #endif diff --git a/test/extensions/filters/http/wasm/BUILD b/test/extensions/filters/http/wasm/BUILD index 2b4f39eb4631..004163e9c65f 100644 --- a/test/extensions/filters/http/wasm/BUILD +++ b/test/extensions/filters/http/wasm/BUILD @@ -18,7 +18,6 @@ envoy_package() envoy_extension_cc_test( name = "wasm_filter_test", - size = "enormous", # For WAVM without precompilation. TODO: add precompilation. srcs = ["wasm_filter_test.cc"], data = envoy_select_wasm_cpp_tests([ "//test/extensions/filters/http/wasm/test_data:test_cpp.wasm", @@ -55,7 +54,6 @@ envoy_extension_cc_test( envoy_extension_cc_test( name = "config_test", - size = "enormous", # For WAVM without precompilation. TODO: add precompilation. srcs = ["config_test.cc"], data = envoy_select_wasm_cpp_tests([ "//test/extensions/filters/http/wasm/test_data:test_cpp.wasm", diff --git a/test/extensions/filters/http/wasm/test_data/BUILD b/test/extensions/filters/http/wasm/test_data/BUILD index fe2816d95412..c9e28cd8de9d 100644 --- a/test/extensions/filters/http/wasm/test_data/BUILD +++ b/test/extensions/filters/http/wasm/test_data/BUILD @@ -178,22 +178,3 @@ cc_proto_library( name = "test_cc_proto", deps = [":test_proto"], ) - -# TODO: FIXME -# -#filegroup( -# name = "wavm_binary", -# srcs = ["//bazel/foreign_cc:wavm"], -# output_group = "wavm", -#) -# -#genrule( -# name = "test_cpp_wavm_compile", -# srcs = [":test_cpp.wasm"], -# outs = ["test_cpp.wavm_compiled.wasm"], -# cmd = "./$(location wavm_binary) compile $(location test_cpp.wasm) $(location test_cpp.wavm_compiled.wasm)", -# tools = [ -# ":test_cpp.wasm", -# ":wavm_binary", -# ], -#) diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index cd8604d486dc..d34afbad937e 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -56,7 +56,6 @@ declare -a KNOWN_LOW_COVERAGE=( "source/common/tls/private_key:88.9" "source/extensions/wasm_runtime/wamr:0.0" # Not enabled in coverage build "source/extensions/wasm_runtime/wasmtime:0.0" # Not enabled in coverage build -"source/extensions/wasm_runtime/wavm:0.0" # Not enabled in coverage build "source/extensions/watchdog:83.3" # Death tests within extensions "source/extensions/listener_managers:70.5" "source/extensions/listener_managers/validation_listener_manager:70.5" diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 1edc5139573f..2b4096b313e5 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -471,7 +471,6 @@ VM VPN WAITFORONE WASM -WAVM WIP WKT WRONGPASS From efd9bef80764663be31b115301812a5bb647fad5 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Mon, 18 Mar 2024 12:59:44 -0400 Subject: [PATCH 073/124] quic: lowering quic brokenness timer (#32910) turns out we always ran with roughly this override in production Testing: updated tests Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a Signed-off-by: Alyssa Wilk --- .../common/http/http3_status_tracker_impl.cc | 12 ++-- .../http/http3_status_tracker_impl_test.cc | 68 +++++++------------ 2 files changed, 30 insertions(+), 50 deletions(-) diff --git a/source/common/http/http3_status_tracker_impl.cc b/source/common/http/http3_status_tracker_impl.cc index c065fe6e3202..4f0dc6db28b9 100644 --- a/source/common/http/http3_status_tracker_impl.cc +++ b/source/common/http/http3_status_tracker_impl.cc @@ -5,10 +5,10 @@ namespace Http { namespace { -// Initially, HTTP/3 is be marked broken for 5 minutes. -const std::chrono::minutes DefaultExpirationTime{5}; -// Cap the broken period at just under 1 day. -const int MaxConsecutiveBrokenCount = 8; +// Initially, HTTP/3 is marked broken for 1 second. +const std::chrono::seconds DefaultExpirationTime{1}; +// Cap the broken period around a day and a half. +const int MaxConsecutiveBrokenCount = 17; } // namespace Http3StatusTrackerImpl::Http3StatusTrackerImpl(Event::Dispatcher& dispatcher) @@ -25,10 +25,10 @@ bool Http3StatusTrackerImpl::hasHttp3FailedRecently() const { void Http3StatusTrackerImpl::markHttp3Broken() { state_ = State::Broken; if (!expiration_timer_->enabled()) { - std::chrono::minutes expiration_in_min = + std::chrono::seconds expiration_in_sec = DefaultExpirationTime * (1 << consecutive_broken_count_); expiration_timer_->enableTimer( - std::chrono::duration_cast(expiration_in_min)); + std::chrono::duration_cast(expiration_in_sec)); if (consecutive_broken_count_ < MaxConsecutiveBrokenCount) { ++consecutive_broken_count_; } diff --git a/test/common/http/http3_status_tracker_impl_test.cc b/test/common/http/http3_status_tracker_impl_test.cc index 8d6ce3b88d82..19c6a582f090 100644 --- a/test/common/http/http3_status_tracker_impl_test.cc +++ b/test/common/http/http3_status_tracker_impl_test.cc @@ -33,7 +33,7 @@ TEST_F(Http3StatusTrackerImplTest, Initialized) { TEST_F(Http3StatusTrackerImplTest, MarkBroken) { EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); @@ -42,7 +42,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkBroken) { TEST_F(Http3StatusTrackerImplTest, MarkBrokenRepeatedly) { EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); @@ -55,7 +55,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenRepeatedly) { TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenExpires) { EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); @@ -67,19 +67,19 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenExpires) { TEST_F(Http3StatusTrackerImplTest, MarkBrokenWithBackoff) { EXPECT_CALL(*timer_, enabled()).WillRepeatedly(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(10 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(2 * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); EXPECT_FALSE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(20 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(4 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); @@ -88,7 +88,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenWithBackoff) { EXPECT_FALSE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(40 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(8 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); @@ -101,51 +101,31 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenWithBackoff) { TEST_F(Http3StatusTrackerImplTest, MarkBrokenWithBackoffMax) { EXPECT_CALL(*timer_, enabled()).WillRepeatedly(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(10 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(20 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(40 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(80 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(160 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(320 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(640 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); + for (int i = 0; i < 17; ++i) { + EXPECT_CALL( + *timer_, + enableTimer(std::chrono::milliseconds(1 * static_cast(pow(2, i)) * 1000), nullptr)); + tracker_.markHttp3Broken(); + timer_->invokeCallback(); + } - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1280 * 60 * 1000), nullptr)); + EXPECT_CALL( + *timer_, + enableTimer(std::chrono::milliseconds(1 * static_cast(pow(2, 17)) * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); // Broken period no longer increases. - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1280 * 60 * 1000), nullptr)); + EXPECT_CALL( + *timer_, + enableTimer(std::chrono::milliseconds(1 * static_cast(pow(2, 17)) * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); } TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenExpiresThenConfirmedThenBroken) { EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); @@ -157,7 +137,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenExpiresThenConfirmedThenBroken) // markConfirmed will have reset the timeout back to the initial value. EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); @@ -167,7 +147,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenExpiresThenConfirmedThenBroken) TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenConfirmed) { EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); @@ -185,7 +165,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkFailedRecentlyAndThenBroken) { EXPECT_FALSE(tracker_.isHttp3Confirmed()); EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); From 87655e546f7d4218959f29957abaeafd5110d4fc Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 18 Mar 2024 13:27:25 -0400 Subject: [PATCH 074/124] admin: add streaming variant of the admin API (#32346) Commit Message: In https://github.com/envoyproxy/envoy/pull/19693 a streaming stats implementation was introduced, dramatically reducing the amount of time and memory it takes to generate an admin http response. /config_dump (#32054) and /clusters_ (#32054) can also be improved in this way. However, in some environments we do not want to expose an HTTP port for admin requests, and we must use the C++ API. However that API is not streaming: it buffers the entire content. This PR adds a new streaming API. Mechanically this new functionality was easy to add as an externally callble C++ API as the Admin class already supported this model on behalf of /stats. However there's a fair amount of complexity managing potential races between active streaming requests, server exit, and explicit cancellation of an in-progress request. So much of the effort and complexity in this PR is due to making that thread-safe and testing it. Additional Description: ![Life of an AdminResponse (1)](https://github.com/envoyproxy/envoy/assets/1942589/28387991-406c-45d4-812e-ccd1be910c36) Note to reviewers: if this PR is too big it can be broken into 2. The significant additions to main_common.cc, main_common.h, and main_common_test.cc need to stay together in one PR, the rest of the changes are non-functional refactors which are needed for the larger changes to work; they could be reviewed and merged first. Risk Level: medium -- this is a new API but it does refactor some existing functionality. Using the new functionality will add some risk also as there is some careful thought required to believe we have protected against all possibilities of shutdown/cancel/admin-request races. Testing: //test/..., and lots of tsan repeated tests for the new streaming tests. Lots of iteration to ensure every new line of main_common.cc is hit by coverage tests. Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a Fixes: #31755 Signed-off-by: Joshua Marantz --- envoy/server/admin.h | 5 + source/exe/BUILD | 16 +- source/exe/admin_response.cc | 191 ++++++++++ source/exe/admin_response.h | 188 ++++++++++ source/exe/main_common.cc | 39 ++- source/exe/main_common.h | 56 ++- source/server/admin/BUILD | 1 + source/server/admin/admin.cc | 4 +- source/server/admin/admin.h | 8 +- source/server/admin/admin_filter.cc | 6 +- source/server/admin/admin_filter.h | 10 +- source/server/config_validation/admin.h | 1 + test/exe/BUILD | 40 ++- test/exe/admin_response_test.cc | 351 +++++++++++++++++++ test/exe/main_common_test.cc | 150 +------- test/exe/main_common_test_base.cc | 117 +++++++ test/exe/main_common_test_base.h | 77 ++++ test/integration/admin_html/test_server.cc | 3 +- test/mocks/server/admin.h | 1 + test/per_file_coverage.sh | 2 +- test/server/admin/admin_filter_test.cc | 11 +- test/server/admin/admin_instance.cc | 2 +- test/server/config_validation/BUILD | 1 + test/server/config_validation/server_test.cc | 3 + 24 files changed, 1111 insertions(+), 172 deletions(-) create mode 100644 source/exe/admin_response.cc create mode 100644 source/exe/admin_response.h create mode 100644 test/exe/admin_response_test.cc create mode 100644 test/exe/main_common_test_base.cc create mode 100644 test/exe/main_common_test_base.h diff --git a/envoy/server/admin.h b/envoy/server/admin.h index 251b0cf47b44..48c1629fef44 100644 --- a/envoy/server/admin.h +++ b/envoy/server/admin.h @@ -281,6 +281,11 @@ class Admin { * Closes the listening socket for the admin. */ virtual void closeSocket() PURE; + + /** + * Creates a streaming request context from the url path in the admin stream. + */ + virtual RequestPtr makeRequest(AdminStream& admin_stream) const PURE; }; } // namespace Server diff --git a/source/exe/BUILD b/source/exe/BUILD index a4651aae9b4f..0f7947c1d07a 100644 --- a/source/exe/BUILD +++ b/source/exe/BUILD @@ -7,6 +7,7 @@ load( "envoy_cc_posix_without_linux_library", "envoy_cc_win32_library", "envoy_package", + "envoy_select_admin_functionality", "envoy_select_enable_http3", "envoy_select_signal_trace", ) @@ -102,7 +103,7 @@ envoy_cc_library( hdrs = [ "main_common.h", ], - deps = [ + deps = envoy_select_admin_functionality([":admin_response_lib"]) + [ ":platform_impl_lib", ":process_wide_lib", ":stripped_main_base_lib", @@ -118,6 +119,19 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "admin_response_lib", + srcs = ["admin_response.cc"], + hdrs = ["admin_response.h"], + deps = [ + "//source/common/buffer:buffer_lib", + "//source/common/http:header_map_lib", + "//source/server:server_lib", + "//source/server/admin:admin_lib", + "//source/server/admin:utils_lib", + ], +) + envoy_cc_library( name = "main_common_with_all_extensions_lib", deps = [ diff --git a/source/exe/admin_response.cc b/source/exe/admin_response.cc new file mode 100644 index 000000000000..0c1ab0958bec --- /dev/null +++ b/source/exe/admin_response.cc @@ -0,0 +1,191 @@ +#include "source/exe/admin_response.h" + +#include "envoy/server/admin.h" + +#include "source/server/admin/admin_filter.h" +#include "source/server/admin/utils.h" + +namespace Envoy { + +AdminResponse::AdminResponse(Server::Instance& server, absl::string_view path, + absl::string_view method, SharedPtrSet response_set) + : server_(server), opt_admin_(server.admin()), shared_response_set_(response_set) { + request_headers_->setMethod(method); + request_headers_->setPath(path); +} + +AdminResponse::~AdminResponse() { + cancel(); + shared_response_set_->detachResponse(this); +} + +void AdminResponse::getHeaders(HeadersFn fn) { + auto request_headers = [response = shared_from_this()]() { response->requestHeaders(); }; + + // First check for cancelling or termination. + { + absl::MutexLock lock(&mutex_); + ASSERT(headers_fn_ == nullptr); + if (cancelled_) { + return; + } + headers_fn_ = fn; + if (terminated_ || !opt_admin_) { + sendErrorLockHeld(); + return; + } + } + server_.dispatcher().post(request_headers); +} + +void AdminResponse::nextChunk(BodyFn fn) { + auto request_next_chunk = [response = shared_from_this()]() { response->requestNextChunk(); }; + + // Note the caller may race a call to nextChunk with the server being + // terminated. + { + absl::MutexLock lock(&mutex_); + ASSERT(body_fn_ == nullptr); + if (cancelled_) { + return; + } + body_fn_ = fn; + if (terminated_ || !opt_admin_) { + sendAbortChunkLockHeld(); + return; + } + } + + // Note that nextChunk may be called from any thread -- it's the callers choice, + // including the Envoy main thread, which would occur if the caller initiates + // the request of a chunk upon receipt of the previous chunk. + // + // In that case it may race against the AdminResponse object being deleted, + // in which case the callbacks, held in a shared_ptr, will be cancelled + // from the destructor. If that happens *before* we post to the main thread, + // we will just skip and never call fn. + server_.dispatcher().post(request_next_chunk); +} + +// Called by the user if it is not longer interested in the result of the +// admin request. After calling cancel() the caller must not call nextChunk or +// getHeaders. +void AdminResponse::cancel() { + absl::MutexLock lock(&mutex_); + cancelled_ = true; + headers_fn_ = nullptr; + body_fn_ = nullptr; +} + +bool AdminResponse::cancelled() const { + absl::MutexLock lock(&mutex_); + return cancelled_; +} + +// Called from terminateAdminRequests when the Envoy server +// terminates. After this is called, the caller may need to complete the +// admin response, and so calls to getHeader and nextChunk remain valid, +// resulting in 503 and an empty body. +void AdminResponse::terminate() { + ASSERT_IS_MAIN_OR_TEST_THREAD(); + absl::MutexLock lock(&mutex_); + if (!terminated_) { + terminated_ = true; + sendErrorLockHeld(); + sendAbortChunkLockHeld(); + } +} + +void AdminResponse::requestHeaders() { + ASSERT_IS_MAIN_OR_TEST_THREAD(); + { + absl::MutexLock lock(&mutex_); + if (cancelled_ || terminated_) { + return; + } + } + Server::AdminFilter filter(*opt_admin_); + filter.decodeHeaders(*request_headers_, false); + request_ = opt_admin_->makeRequest(filter); + code_ = request_->start(*response_headers_); + { + absl::MutexLock lock(&mutex_); + if (headers_fn_ == nullptr || cancelled_) { + return; + } + Server::Utility::populateFallbackResponseHeaders(code_, *response_headers_); + headers_fn_(code_, *response_headers_); + headers_fn_ = nullptr; + } +} + +void AdminResponse::requestNextChunk() { + ASSERT_IS_MAIN_OR_TEST_THREAD(); + { + absl::MutexLock lock(&mutex_); + if (cancelled_ || terminated_ || !more_data_) { + return; + } + } + ASSERT(response_.length() == 0); + more_data_ = request_->nextChunk(response_); + { + absl::MutexLock lock(&mutex_); + if (sent_end_stream_ || cancelled_) { + return; + } + sent_end_stream_ = !more_data_; + body_fn_(response_, more_data_); + ASSERT(response_.length() == 0); + body_fn_ = nullptr; + } +} + +void AdminResponse::sendAbortChunkLockHeld() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) { + if (!sent_end_stream_ && body_fn_ != nullptr) { + response_.drain(response_.length()); + body_fn_(response_, false); + sent_end_stream_ = true; + } + body_fn_ = nullptr; +} + +void AdminResponse::sendErrorLockHeld() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) { + if (headers_fn_ != nullptr) { + code_ = Http::Code::InternalServerError; + Server::Utility::populateFallbackResponseHeaders(code_, *response_headers_); + headers_fn_(code_, *response_headers_); + headers_fn_ = nullptr; + } +} + +void AdminResponse::PtrSet::terminateAdminRequests() { + ASSERT_IS_MAIN_OR_TEST_THREAD(); + + absl::MutexLock lock(&mutex_); + accepting_admin_requests_ = false; + for (AdminResponse* response : response_set_) { + // Consider the possibility of response being deleted due to its creator + // dropping its last reference right here. From its destructor it will call + // detachResponse(), which is mutex-ed against this loop, so before the + // memory becomes invalid, the call to terminate will complete. + response->terminate(); + } + response_set_.clear(); +} + +void AdminResponse::PtrSet::attachResponse(AdminResponse* response) { + absl::MutexLock lock(&mutex_); + if (accepting_admin_requests_) { + response_set_.insert(response); + } else { + response->terminate(); + } +} + +void AdminResponse::PtrSet::detachResponse(AdminResponse* response) { + absl::MutexLock lock(&mutex_); + response_set_.erase(response); +} + +} // namespace Envoy diff --git a/source/exe/admin_response.h b/source/exe/admin_response.h new file mode 100644 index 000000000000..b3683b5707c7 --- /dev/null +++ b/source/exe/admin_response.h @@ -0,0 +1,188 @@ +#pragma once + +#include + +#include "envoy/server/instance.h" + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/http/header_map_impl.h" + +#include "absl/container/flat_hash_set.h" +#include "absl/synchronization/mutex.h" + +namespace Envoy { + +class AdminResponse; + +// Holds context for a streaming response from the admin system, enabling +// flow-control into another system. This is particularly important when the +// generated response is very large, such that holding it in memory may cause +// fragmentation or out-of-memory failures. It is possible to interleave xDS +// response handling, overload management, and other admin requests during the +// streaming of a long admin response. +// +// There can be be multiple AdminResponses at a time; each are separately +// managed. However they will obtain their data from Envoy functions that +// run on the main thread. +// +// Responses may still be active after the server has shut down, and is no +// longer running its main thread dispatcher. In this state, the callbacks +// will be called with appropriate error codes. +// +// Requests can also be cancelled explicitly by calling cancel(). After +// cancel() is called, no further callbacks will be called by the response. +// +// The lifecycle of an AdminResponse is rendered as a finite state machine +// bubble diagram: +// https://docs.google.com/drawings/d/1njUl1twApEMoxmjaG4b7optTh5fcb_YNcfSnkHbdfq0/view +class AdminResponse : public std::enable_shared_from_this { +public: + // AdminResponse can outlive MainCommonBase. But AdminResponse needs a + // reliable way of knowing whether MainCommonBase is alive, so we do this with + // PtrSet, which is held by MainCommonBase and all the active AdminResponses. + // via shared_ptr. This gives MainCommonBase a reliable way of notifying all + // active responses that it is being shut down, and thus all responses need to + // be terminated. And it gives a reliable way for AdminResponse to detach + // itself, whether or not MainCommonBase is already deleted. + // + // In summary: + // * MainCommonBase can outlive AdminResponse so we need detachResponse. + // * AdminResponse can outlive MainCommonBase, so we need shared_ptr. + class PtrSet { + public: + /** + * Called when an AdminResponse is created. When terminateAdminRequests is + * called, all outstanding response objects have their terminate() methods + * called. + * + * @param response the response pointer to be added to the set. + */ + void attachResponse(AdminResponse* response); + + /** + * Called when an AdminResponse is terminated, either by completing normally + * or having the caller call cancel on it. Either way it needs to be removed + * from the set that will be used by terminateAdminRequests below. + * + * @param response the response pointer to be removed from the set. + */ + void detachResponse(AdminResponse* response); + + /** + * Called after the server run-loop finishes; any outstanding streaming + * admin requests will otherwise hang as the main-thread dispatcher loop + * will no longer run. + */ + void terminateAdminRequests(); + + mutable absl::Mutex mutex_; + absl::flat_hash_set response_set_ ABSL_GUARDED_BY(mutex_); + bool accepting_admin_requests_ ABSL_GUARDED_BY(mutex_) = true; + }; + using SharedPtrSet = std::shared_ptr; + + AdminResponse(Server::Instance& server, absl::string_view path, absl::string_view method, + SharedPtrSet response_set); + ~AdminResponse(); + + /** + * Requests the headers for the response. This can be called from any + * thread, and HeaderFn may also be called from any thread. + * + * HeadersFn will not be called after cancel(). It is invalid to + * to call nextChunk from within HeadersFn -- the caller must trigger + * such a call on another thread, after HeadersFn returns. Calling + * nextChunk from HeadersFn may deadlock. + * + * If the server is shut down during the operation, headersFn may + * be called with a 503, if it has not already been called. + * + * @param fn The function to be called with the headers and status code. + */ + using HeadersFn = std::function; + void getHeaders(HeadersFn fn); + + /** + * Requests a new chunk. This can be called from any thread, and the BodyFn + * callback may also be called from any thread. BodyFn will be called in a + * loop until the Buffer passed to it is fully drained. When 'false' is + * passed as the second arg to BodyFn, that signifies the end of the + * response, and nextChunk must not be called again. + * + * BodyFn will not be called after cancel(). It is invalid to + * to call nextChunk from within BodyFn -- the caller must trigger + * such a call on another thread, after BodyFn returns. Calling + * nextChunk from BodyFn may deadlock. + * + * If the server is shut down during the operation, bodyFn will + * be called with an empty body and 'false' for more_data, if + * this has not already occurred. + * + * @param fn A function to be called on each chunk. + */ + using BodyFn = std::function; + void nextChunk(BodyFn fn); + + /** + * Requests that any outstanding callbacks be dropped. This can be called + * when the context in which the request is made is destroyed. This enables + * an application to implement a. The Response itself is held as a + * shared_ptr as that makes it much easier to manage cancellation across + * multiple threads. + */ + void cancel(); + + /** + * @return whether the request was cancelled. + */ + bool cancelled() const; + +private: + /** + * Called when the server is terminated. This calls any outstanding + * callbacks to be called. If nextChunk is called after termination, + * its callback is called false for the second arg, indicating + * end of stream. + */ + void terminate(); + + void requestHeaders(); + void requestNextChunk(); + void sendAbortChunkLockHeld() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void sendErrorLockHeld() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Server::Instance& server_; + OptRef opt_admin_; + Buffer::OwnedImpl response_; + Http::Code code_; + Server::Admin::RequestPtr request_; + Http::RequestHeaderMapPtr request_headers_{Http::RequestHeaderMapImpl::create()}; + Http::ResponseHeaderMapPtr response_headers_{Http::ResponseHeaderMapImpl::create()}; + bool more_data_ = true; + + // True if cancel() was explicitly called by the user; headers and body + // callbacks are never called after cancel(). + bool cancelled_ ABSL_GUARDED_BY(mutex_) = false; + + // True if the Envoy server has stopped running its main loop. Headers and + // body requests can be initiated and called back are called after terminate, + // so callers do not have to special case this -- the request will simply fail + // with an empty response. + bool terminated_ ABSL_GUARDED_BY(mutex_) = false; + + // Used to indicate whether the body function has been called with false + // as its second argument. That must always happen at most once, even + // if terminate races with the normal end-of-stream marker. more=false + // may never be sent if the request is cancelled, nor deleted prior to + // it being requested. + bool sent_end_stream_ ABSL_GUARDED_BY(mutex_) = false; + + HeadersFn headers_fn_ ABSL_GUARDED_BY(mutex_); + BodyFn body_fn_ ABSL_GUARDED_BY(mutex_); + mutable absl::Mutex mutex_; + + SharedPtrSet shared_response_set_; +}; +using AdminResponseSharedPtr = std::shared_ptr; + +} // namespace Envoy diff --git a/source/exe/main_common.cc b/source/exe/main_common.cc index d18a575f2dd0..87b3342f3335 100644 --- a/source/exe/main_common.cc +++ b/source/exe/main_common.cc @@ -10,6 +10,7 @@ #include "source/common/common/compiler_requirements.h" #include "source/common/common/logger.h" #include "source/common/common/perf_annotation.h" +#include "source/common/common/thread.h" #include "source/common/network/utility.h" #include "source/common/stats/thread_local_store.h" #include "source/exe/platform_impl.h" @@ -56,26 +57,46 @@ MainCommonBase::MainCommonBase(const Server::Options& options, Event::TimeSystem std::unique_ptr process_context) : StrippedMainBase(options, time_system, listener_hooks, component_factory, std::move(platform_impl), std::move(random_generator), - std::move(process_context), createFunction()) {} + std::move(process_context), createFunction()) +#ifdef ENVOY_ADMIN_FUNCTIONALITY + , + shared_response_set_(std::make_shared()) +#endif +{ +} bool MainCommonBase::run() { + // Avoid returning from inside switch cases to minimize uncovered lines + // while avoiding gcc warnings by hitting the final return. + bool ret = false; + switch (options_.mode()) { case Server::Mode::Serve: runServer(); - return true; +#ifdef ENVOY_ADMIN_FUNCTIONALITY + shared_response_set_->terminateAdminRequests(); +#endif + ret = true; + break; case Server::Mode::Validate: - return Server::validateConfig( + ret = Server::validateConfig( options_, Network::Utility::getLocalAddress(options_.localAddressIpVersion()), component_factory_, platform_impl_->threadFactory(), platform_impl_->fileSystem(), process_context_ ? ProcessContextOptRef(std::ref(*process_context_)) : absl::nullopt); + break; case Server::Mode::InitOnly: PERF_DUMP(); - return true; + ret = true; + break; } - return false; // for gcc. + return ret; } #ifdef ENVOY_ADMIN_FUNCTIONALITY + +// This request variant buffers the entire response in one string. New uses +// should opt for the streaming version below, where an AdminResponse object +// is created and used to stream data with flow-control. void MainCommonBase::adminRequest(absl::string_view path_and_query, absl::string_view method, const AdminRequestFn& handler) { std::string path_and_query_buf = std::string(path_and_query); @@ -89,6 +110,14 @@ void MainCommonBase::adminRequest(absl::string_view path_and_query, absl::string handler(*response_headers, body); }); } + +AdminResponseSharedPtr MainCommonBase::adminRequest(absl::string_view path_and_query, + absl::string_view method) { + auto response = + std::make_shared(*server(), path_and_query, method, shared_response_set_); + shared_response_set_->attachResponse(response.get()); + return response; +} #endif MainCommon::MainCommon(const std::vector& args) diff --git a/source/exe/main_common.h b/source/exe/main_common.h index 349decdec0cc..500f293ce7f1 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -10,6 +10,10 @@ #include "source/common/stats/symbol_table.h" #include "source/common/stats/thread_local_store.h" #include "source/common/thread_local/thread_local_impl.h" + +#ifdef ENVOY_ADMIN_FUNCTIONALITY +#include "source/exe/admin_response.h" +#endif #include "source/exe/process_wide.h" #include "source/exe/stripped_main_base.h" #include "source/server/listener_hooks.h" @@ -37,20 +41,45 @@ class MainCommonBase : public StrippedMainBase { using AdminRequestFn = std::function; - // Makes an admin-console request by path, calling handler() when complete. - // The caller can initiate this from any thread, but it posts the request - // onto the main thread, so the handler is called asynchronously. - // - // This is designed to be called from downstream consoles, so they can access - // the admin console information stream without opening up a network port. - // - // This should only be called while run() is active; ensuring this is the - // responsibility of the caller. - // - // TODO(jmarantz): consider std::future for encapsulating this delayed request - // semantics, rather than a handler callback. + /** + * Makes an admin-console request by path, calling handler() when complete. + * The caller can initiate this from any thread, but it posts the request + * onto the main thread, so the handler is called asynchronously. + * + * This is designed to be called from downstream consoles, so they can access + * the admin console information stream without opening up a network port. + * + * This should only be called while run() is active; ensuring this is the + * responsibility of the caller. + * + * TODO(jmarantz): consider std::future for encapsulating this delayed request + * semantics, rather than a handler callback. + * + * Consider using the 2-arg version of adminRequest, below, which enables + * streaming of large responses one chunk at a time, without holding + * potentially huge response text in memory. + * + * @param path_and_query the URL to send to admin, including any query params. + * @param method the HTTP method: "GET" or "POST" + * @param handler an async callback that will be sent the serialized headers + * and response. + */ void adminRequest(absl::string_view path_and_query, absl::string_view method, const AdminRequestFn& handler); + + /** + * Initiates a streaming response to an admin request. The caller interacts + * with the returned AdminResponse object, and can thus control the pace of + * handling chunks of response text. + * + * @param path_and_query the URL to send to admin, including any query params. + * @param method the HTTP method: "GET" or "POST" + * @return AdminResponseSharedPtr the response object + */ + AdminResponseSharedPtr adminRequest(absl::string_view path_and_query, absl::string_view method); + +private: + AdminResponse::SharedPtrSet shared_response_set_; #endif }; @@ -82,6 +111,9 @@ class MainCommon { const MainCommonBase::AdminRequestFn& handler) { base_.adminRequest(path_and_query, method, handler); } + AdminResponseSharedPtr adminRequest(absl::string_view path_and_query, absl::string_view method) { + return base_.adminRequest(path_and_query, method); + } #endif static std::string hotRestartVersion(bool hot_restart_enabled); diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 3913191068d4..1c453e712a8e 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -388,6 +388,7 @@ envoy_cc_library( name = "utils_lib", srcs = ["utils.cc"], hdrs = ["utils.h"], + visibility = ["//visibility:public"], deps = [ "//envoy/init:manager_interface", "//source/common/common:enum_to_int", diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 07f3b271381a..0b21be7fef7f 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -293,7 +293,7 @@ bool AdminImpl::createNetworkFilterChain(Network::Connection& connection, bool AdminImpl::createFilterChain(Http::FilterChainManager& manager, bool, const Http::FilterChainOptions&) const { Http::FilterFactoryCb factory = [this](Http::FilterChainFactoryCallbacks& callbacks) { - callbacks.addStreamFilter(std::make_shared(createRequestFunction())); + callbacks.addStreamFilter(std::make_shared(*this)); }; manager.applyFilterFactoryCb({}, factory); return true; @@ -494,7 +494,7 @@ bool AdminImpl::removeHandler(const std::string& prefix) { Http::Code AdminImpl::request(absl::string_view path_and_query, absl::string_view method, Http::ResponseHeaderMap& response_headers, std::string& body) { - AdminFilter filter(createRequestFunction()); + AdminFilter filter(*this); auto request_headers = Http::RequestHeaderMapImpl::create(); request_headers->setMethod(method); diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index 86c6ab7f57d3..ef0cde79927b 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -216,9 +216,6 @@ class AdminImpl : public Admin, void closeSocket() override; void addListenerToHandler(Network::ConnectionHandler* handler) override; - GenRequestFn createRequestFunction() const { - return [this](AdminStream& admin_stream) -> RequestPtr { return makeRequest(admin_stream); }; - } uint64_t maxRequestsPerConnection() const override { return 0; } const HttpConnectionManagerProto::ProxyStatusConfig* proxyStatusConfig() const override { return proxy_status_config_.get(); @@ -246,10 +243,7 @@ class AdminImpl : public Admin, ::Envoy::Http::HeaderValidatorStats& getHeaderValidatorStats(Http::Protocol protocol); #endif - /** - * Creates a Request from the request in the admin stream. - */ - RequestPtr makeRequest(AdminStream& admin_stream) const; + RequestPtr makeRequest(AdminStream& admin_stream) const override; /** * Creates a UrlHandler structure from a non-chunked callback. diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index 7a11c251dc88..d9dee2576612 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -5,8 +5,7 @@ namespace Envoy { namespace Server { -AdminFilter::AdminFilter(Admin::GenRequestFn admin_handler_fn) - : admin_handler_fn_(admin_handler_fn) {} +AdminFilter::AdminFilter(const Admin& admin) : admin_(admin) {} Http::FilterHeadersStatus AdminFilter::decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) { @@ -87,12 +86,13 @@ void AdminFilter::onComplete() { auto header_map = Http::ResponseHeaderMapImpl::create(); RELEASE_ASSERT(request_headers_, ""); - Admin::RequestPtr handler = admin_handler_fn_(*this); + Admin::RequestPtr handler = admin_.makeRequest(*this); Http::Code code = handler->start(*header_map); Utility::populateFallbackResponseHeaders(code, *header_map); decoder_callbacks_->encodeHeaders(std::move(header_map), false, StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); + // TODO(#31087): use high/lower watermarks to apply flow-control to the admin http port. bool more_data; do { Buffer::OwnedImpl response; diff --git a/source/server/admin/admin_filter.h b/source/server/admin/admin_filter.h index e163029b2283..2dbe6d01df94 100644 --- a/source/server/admin/admin_filter.h +++ b/source/server/admin/admin_filter.h @@ -28,7 +28,13 @@ class AdminFilter : public Http::PassThroughFilter, absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::OwnedImpl& response, AdminFilter& filter)>; - AdminFilter(Admin::GenRequestFn admin_handler_func); + /** + * Instantiates an AdminFilter. + * + * @param admin the admin context from which to create the filter. This is used + * to create a request object based on the path. + */ + AdminFilter(const Admin& admin); // Http::StreamFilterBase // Handlers relying on the reference should use addOnDestroyCallback() @@ -58,7 +64,7 @@ class AdminFilter : public Http::PassThroughFilter, * Called when an admin request has been completely received. */ void onComplete(); - Admin::GenRequestFn admin_handler_fn_; + const Admin& admin_; Http::RequestHeaderMap* request_headers_{}; std::list> on_destroy_callbacks_; bool end_stream_on_complete_ = true; diff --git a/source/server/config_validation/admin.h b/source/server/config_validation/admin.h index 173afb29d355..8e75607e3f8f 100644 --- a/source/server/config_validation/admin.h +++ b/source/server/config_validation/admin.h @@ -39,6 +39,7 @@ class ValidationAdmin : public Admin { void addListenerToHandler(Network::ConnectionHandler* handler) override; uint32_t concurrency() const override { return 1; } void closeSocket() override {} + RequestPtr makeRequest(AdminStream&) const override { return nullptr; } private: ConfigTrackerImpl config_tracker_; diff --git a/test/exe/BUILD b/test/exe/BUILD index 7be94770af65..065b80e97014 100644 --- a/test/exe/BUILD +++ b/test/exe/BUILD @@ -1,6 +1,7 @@ load( "//bazel:envoy_build_system.bzl", "envoy_cc_test", + "envoy_cc_test_library", "envoy_package", "envoy_select_admin_functionality", "envoy_sh_test", @@ -60,13 +61,50 @@ envoy_sh_test( ], ) +envoy_cc_test_library( + name = "main_common_test_base_lib", + srcs = ["main_common_test_base.cc"], + hdrs = ["main_common_test_base.h"], + data = [ + "//test/config/integration:google_com_proxy_port_0", + ], + deps = [ + "//source/common/api:api_lib", + "//source/common/stats:isolated_store_lib", + "//source/exe:envoy_main_common_with_core_extensions_lib", + "//source/exe:platform_impl_lib", + "//source/extensions/clusters/logical_dns:logical_dns_cluster_lib", + "//test/mocks/runtime:runtime_mocks", + "//test/test_common:contention_lib", + "//test/test_common:environment_lib", + "//test/test_common:thread_factory_for_test_lib", + ], +) + envoy_cc_test( name = "main_common_test", srcs = envoy_select_admin_functionality(["main_common_test.cc"]), data = [ "//test/config/integration:google_com_proxy_port_0", ], - deps = [ + deps = envoy_select_admin_functionality([":main_common_test_base_lib"]) + [ + "//source/common/api:api_lib", + "//source/exe:envoy_main_common_with_core_extensions_lib", + "//source/exe:platform_impl_lib", + "//source/extensions/clusters/logical_dns:logical_dns_cluster_lib", + "//test/mocks/runtime:runtime_mocks", + "//test/test_common:contention_lib", + "//test/test_common:environment_lib", + ], +) + +envoy_cc_test( + name = "admin_response_test", + srcs = envoy_select_admin_functionality(["admin_response_test.cc"]), + data = [ + "//test/config/integration:google_com_proxy_port_0", + ], + deps = envoy_select_admin_functionality([":main_common_test_base_lib"]) + [ "//source/common/api:api_lib", "//source/exe:envoy_main_common_with_core_extensions_lib", "//source/exe:platform_impl_lib", diff --git a/test/exe/admin_response_test.cc b/test/exe/admin_response_test.cc new file mode 100644 index 000000000000..33a7f10248e5 --- /dev/null +++ b/test/exe/admin_response_test.cc @@ -0,0 +1,351 @@ +#include "test/exe/main_common_test_base.h" + +#include "gtest/gtest.h" + +namespace Envoy { + +class AdminStreamingTest : public AdminRequestTestBase, public testing::Test { +protected: + static constexpr absl::string_view StreamingEndpoint = "/stream"; + + class StreamingAdminRequest : public Envoy::Server::Admin::Request { + public: + static constexpr uint64_t NumChunks = 10; + static constexpr uint64_t BytesPerChunk = 10000; + + StreamingAdminRequest(std::function& get_headers_hook, + std::function& next_chunk_hook) + : chunk_(BytesPerChunk, 'a'), get_headers_hook_(get_headers_hook), + next_chunk_hook_(next_chunk_hook) {} + Http::Code start(Http::ResponseHeaderMap&) override { + get_headers_hook_(); + return Http::Code::OK; + } + bool nextChunk(Buffer::Instance& response) override { + next_chunk_hook_(); + response.add(chunk_); + return --chunks_remaining_ > 0; + } + + private: + const std::string chunk_; + uint64_t chunks_remaining_{NumChunks}; + std::function& get_headers_hook_; + std::function& next_chunk_hook_; + }; + + AdminStreamingTest() : AdminRequestTestBase(Network::Address::IpVersion::v4) { + startEnvoy(); + started_.WaitForNotification(); + Server::Admin& admin = *main_common_->server()->admin(); + admin.addStreamingHandler( + std::string(StreamingEndpoint), "streaming api", + [this](Server::AdminStream&) -> Server::Admin::RequestPtr { + return std::make_unique(get_headers_hook_, next_chunk_hook_); + }, + true, false); + } + + struct ResponseData { + uint64_t num_chunks_{0}; + uint64_t num_bytes_{0}; + Http::Code code_; + std::string content_type_; + }; + + ResponseData runStreamingRequest(AdminResponseSharedPtr response, + std::function chunk_hook = nullptr) { + absl::Notification done; + std::vector out; + absl::Notification headers_notify; + ResponseData response_data; + response->getHeaders( + [&headers_notify, &response_data](Http::Code code, Http::ResponseHeaderMap& headers) { + response_data.code_ = code; + response_data.content_type_ = headers.getContentTypeValue(); + headers_notify.Notify(); + }); + headers_notify.WaitForNotification(); + bool cont = true; + while (cont && !response->cancelled()) { + absl::Notification chunk_notify; + response->nextChunk( + [&chunk_notify, &response_data, &cont](Buffer::Instance& chunk, bool more) { + cont = more; + response_data.num_bytes_ += chunk.length(); + chunk.drain(chunk.length()); + ++response_data.num_chunks_; + chunk_notify.Notify(); + }); + chunk_notify.WaitForNotification(); + if (chunk_hook != nullptr) { + chunk_hook(); + } + } + + return response_data; + } + + /** + * @return a streaming response to a GET of StreamingEndpoint. + */ + AdminResponseSharedPtr streamingResponse() { + return main_common_->adminRequest(StreamingEndpoint, "GET"); + } + + /** + * In order to trigger certain early-exit criteria in a test, we can exploit + * the fact that all the admin responses are delivered on the main thread. + * So we can pause those by blocking the main thread indefinitely. + * + * The provided lambda runs in the main thread, between two notifications + * controlled by this function. + * + * @param fn function to run in the main thread, before interlockMainThread returns. + */ + void interlockMainThread(std::function fn) { + main_common_->dispatcherForTest().post([this, fn] { + resume_.WaitForNotification(); + fn(); + pause_point_.Notify(); + }); + resume_.Notify(); + pause_point_.WaitForNotification(); + } + + /** + * Requests the headers and waits until the headers have been sent. + * + * @param response the response from which to get headers. + */ + void waitForHeaders(AdminResponseSharedPtr response) { + absl::Notification headers_notify; + response->getHeaders( + [&headers_notify](Http::Code, Http::ResponseHeaderMap&) { headers_notify.Notify(); }); + headers_notify.WaitForNotification(); + } + + /** + * Initiates a '/quitquitquit' call and requests the headers for that call, + * but does not wait for the call to complete. We avoid waiting in order to + * trigger a potential race to ensure that MainCommon handles it properly. + */ + void quitAndRequestHeaders() { + AdminResponseSharedPtr quit_response = main_common_->adminRequest("/quitquitquit", "POST"); + quit_response->getHeaders([](Http::Code, Http::ResponseHeaderMap&) {}); + } + + // This variable provides a hook to allow a test method to specify a hook to + // run when nextChunk() is called. This is currently used only for one test, + // CancelAfterAskingForChunk, that initiates a cancel() from within the chunk + // handler. + std::function get_headers_hook_ = []() {}; + std::function next_chunk_hook_ = []() {}; +}; + +TEST_F(AdminStreamingTest, RequestGetStatsAndQuit) { + AdminResponseSharedPtr response = streamingResponse(); + ResponseData response_data = runStreamingRequest(response); + EXPECT_EQ(StreamingAdminRequest::NumChunks, response_data.num_chunks_); + EXPECT_EQ(StreamingAdminRequest::NumChunks * StreamingAdminRequest::BytesPerChunk, + response_data.num_bytes_); + EXPECT_EQ(Http::Code::OK, response_data.code_); + EXPECT_EQ("text/plain; charset=UTF-8", response_data.content_type_); + EXPECT_TRUE(quitAndWait()); +} + +TEST_F(AdminStreamingTest, QuitDuringChunks) { + int quit_counter = 0; + static constexpr int chunks_to_send_before_quitting = 3; + AdminResponseSharedPtr response = streamingResponse(); + ResponseData response_data = runStreamingRequest(response, [&quit_counter, this]() { + if (++quit_counter == chunks_to_send_before_quitting) { + EXPECT_TRUE(quitAndWait()); + } + }); + EXPECT_EQ(4, response_data.num_chunks_); + EXPECT_EQ(chunks_to_send_before_quitting * StreamingAdminRequest::BytesPerChunk, + response_data.num_bytes_); + EXPECT_EQ(Http::Code::OK, response_data.code_); + EXPECT_EQ("text/plain; charset=UTF-8", response_data.content_type_); +} + +TEST_F(AdminStreamingTest, CancelDuringChunks) { + int quit_counter = 0; + static constexpr int chunks_to_send_before_quitting = 3; + AdminResponseSharedPtr response = streamingResponse(); + ResponseData response_data = runStreamingRequest(response, [response, &quit_counter]() { + if (++quit_counter == chunks_to_send_before_quitting) { + response->cancel(); + } + }); + EXPECT_EQ(3, response_data.num_chunks_); // no final call to the chunk handler after cancel. + EXPECT_EQ(chunks_to_send_before_quitting * StreamingAdminRequest::BytesPerChunk, + response_data.num_bytes_); + EXPECT_EQ(Http::Code::OK, response_data.code_); + EXPECT_EQ("text/plain; charset=UTF-8", response_data.content_type_); + EXPECT_TRUE(quitAndWait()); +} + +TEST_F(AdminStreamingTest, CancelBeforeAskingForHeader) { + AdminResponseSharedPtr response = streamingResponse(); + interlockMainThread([response]() { response->cancel(); }); + int header_calls = 0; + + // After 'cancel', the headers function will not be called. + response->getHeaders([&header_calls](Http::Code, Http::ResponseHeaderMap&) { ++header_calls; }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, header_calls); +} + +TEST_F(AdminStreamingTest, CancelAfterAskingForHeader1) { + int header_calls = 0; + AdminResponseSharedPtr response = streamingResponse(); + interlockMainThread([&header_calls, response]() { + response->getHeaders([&header_calls](Http::Code, Http::ResponseHeaderMap&) { ++header_calls; }); + response->cancel(); + }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, header_calls); +} + +TEST_F(AdminStreamingTest, CancelAfterAskingForHeader2) { + int header_calls = 0; + AdminResponseSharedPtr response = streamingResponse(); + get_headers_hook_ = [&response]() { response->cancel(); }; + response->getHeaders([&header_calls](Http::Code, Http::ResponseHeaderMap&) { ++header_calls; }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, header_calls); +} + +TEST_F(AdminStreamingTest, DeleteAfterAskingForHeader1) { + int header_calls = 0; + AdminResponseSharedPtr response = streamingResponse(); + interlockMainThread([&response, &header_calls]() { + response->getHeaders([&header_calls](Http::Code, Http::ResponseHeaderMap&) { ++header_calls; }); + response.reset(); + }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(1, header_calls); +} + +TEST_F(AdminStreamingTest, DeleteAfterAskingForHeader2) { + int header_calls = 0; + AdminResponseSharedPtr response = streamingResponse(); + get_headers_hook_ = [&response]() { response.reset(); }; + response->getHeaders([&header_calls](Http::Code, Http::ResponseHeaderMap&) { ++header_calls; }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(1, header_calls); +} + +TEST_F(AdminStreamingTest, CancelBeforeAskingForChunk1) { + AdminResponseSharedPtr response = streamingResponse(); + waitForHeaders(response); + response->cancel(); + int chunk_calls = 0; + response->nextChunk([&chunk_calls](Buffer::Instance&, bool) { ++chunk_calls; }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, chunk_calls); +} + +TEST_F(AdminStreamingTest, CancelBeforeAskingForChunk2) { + AdminResponseSharedPtr response = streamingResponse(); + waitForHeaders(response); + int chunk_calls = 0; + interlockMainThread([&response, &chunk_calls]() { + response->nextChunk([&chunk_calls](Buffer::Instance&, bool) { ++chunk_calls; }); + response->cancel(); + }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, chunk_calls); +} + +TEST_F(AdminStreamingTest, CancelAfterAskingForChunk) { + AdminResponseSharedPtr response = streamingResponse(); + waitForHeaders(response); + int chunk_calls = 0; + + // Cause the /streaming handler to pause while yielding the next chunk, to hit + // an early exit in requestNextChunk. + next_chunk_hook_ = [response]() { response->cancel(); }; + + interlockMainThread([&chunk_calls, response]() { + response->nextChunk([&chunk_calls](Buffer::Instance&, bool) { ++chunk_calls; }); + }); + + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, chunk_calls); +} + +TEST_F(AdminStreamingTest, QuitBeforeHeaders) { + AdminResponseSharedPtr response = streamingResponse(); + EXPECT_TRUE(quitAndWait()); + ResponseData response_data = runStreamingRequest(response); + EXPECT_EQ(1, response_data.num_chunks_); + EXPECT_EQ(0, response_data.num_bytes_); + EXPECT_EQ(Http::Code::InternalServerError, response_data.code_); + EXPECT_EQ("text/plain; charset=UTF-8", response_data.content_type_); +} + +TEST_F(AdminStreamingTest, QuitDeleteRace1) { + AdminResponseSharedPtr response = streamingResponse(); + // Initiates a streaming quit on the main thread, but do not wait for it. + quitAndRequestHeaders(); + response.reset(); // Races with the quitquitquit + EXPECT_TRUE(waitForEnvoyToExit()); +} + +TEST_F(AdminStreamingTest, QuitDeleteRace2) { + AdminResponseSharedPtr response = streamingResponse(); + adminRequest("/quitquitquit", "POST"); + response.reset(); + EXPECT_TRUE(waitForEnvoyToExit()); +} + +TEST_F(AdminStreamingTest, QuitCancelRace) { + AdminResponseSharedPtr response = streamingResponse(); + quitAndRequestHeaders(); + response->cancel(); // Races with the quitquitquit + EXPECT_TRUE(waitForEnvoyToExit()); +} + +TEST_F(AdminStreamingTest, QuitBeforeCreatingResponse) { + // Initiates a streaming quit on the main thread, and wait for headers, which + // will trigger the termination of the event loop, and subsequent nulling of + // main_common_. However we can pause the test infrastructure after the quit + // takes hold leaving main_common_ in tact, to reproduce a potential race. + pause_after_run_ = true; + adminRequest("/quitquitquit", "POST"); + pause_point_.WaitForNotification(); // run() finished, but main_common_ still exists. + AdminResponseSharedPtr response = streamingResponse(); + ResponseData response_data = runStreamingRequest(response); + EXPECT_EQ(1, response_data.num_chunks_); + EXPECT_EQ(0, response_data.num_bytes_); + EXPECT_EQ(Http::Code::InternalServerError, response_data.code_); + EXPECT_EQ("text/plain; charset=UTF-8", response_data.content_type_); + resume_.Notify(); + EXPECT_TRUE(waitForEnvoyToExit()); + response.reset(); +} + +TEST_F(AdminStreamingTest, TimeoutGettingResponse) { + absl::Notification got_headers; + AdminResponseSharedPtr response = streamingResponse(); + + // Mimics a slow admin response by adding a blocking notification in front + // of a call to initiate an admin request. + main_common_->dispatcherForTest().post([this, response, &got_headers] { + resume_.WaitForNotification(); + response->getHeaders( + [&got_headers](Http::Code, Http::ResponseHeaderMap&) { got_headers.Notify(); }); + pause_point_.Notify(); + }); + + ENVOY_LOG_MISC(info, "Blocking for 5 seconds to test timeout functionality..."); + ASSERT_FALSE(got_headers.WaitForNotificationWithTimeout(absl::Seconds(5))); + resume_.Notify(); + pause_point_.WaitForNotification(); + EXPECT_TRUE(quitAndWait()); +} + +} // namespace Envoy diff --git a/test/exe/main_common_test.cc b/test/exe/main_common_test.cc index 68d142e5b3b8..942a53e3e3b1 100644 --- a/test/exe/main_common_test.cc +++ b/test/exe/main_common_test.cc @@ -1,6 +1,5 @@ #include "envoy/common/platform.h" -#include "source/common/common/lock_guard.h" #include "source/common/common/mutex_tracer_impl.h" #include "source/common/common/random_generator.h" #include "source/common/common/thread.h" @@ -9,6 +8,7 @@ #include "source/exe/platform_impl.h" #include "source/server/options_impl.h" +#include "test/exe/main_common_test_base.h" #include "test/mocks/common.h" #include "test/test_common/contention.h" #include "test/test_common/environment.h" @@ -52,34 +52,10 @@ const std::string& outOfMemoryPattern() { * an argv array that is terminated with nullptr. Identifies the config * file relative to runfiles directory. */ -class MainCommonTest : public testing::TestWithParam { +class MainCommonTest : public MainCommonTestBase, + public testing::TestWithParam { protected: - MainCommonTest() - : config_file_(TestEnvironment::temporaryFileSubstitute( - "test/config/integration/google_com_proxy_port_0.yaml", TestEnvironment::ParamMap(), - TestEnvironment::PortMap(), GetParam())), - argv_({"envoy-static", "--use-dynamic-base-id", "-c", config_file_.c_str(), nullptr}) {} - - const char* const* argv() { return &argv_[0]; } - int argc() { return argv_.size() - 1; } - - // Adds an argument, assuring that argv remains null-terminated. - void addArg(const char* arg) { - ASSERT(!argv_.empty()); - const size_t last = argv_.size() - 1; - ASSERT(argv_[last] == nullptr); // invariant established in ctor, maintained below. - argv_[last] = arg; // guaranteed non-empty - argv_.push_back(nullptr); - } - - // Adds options to make Envoy exit immediately after initialization. - void initOnly() { - addArg("--mode"); - addArg("init_only"); - } - - std::string config_file_; - std::vector argv_; + MainCommonTest() : MainCommonTestBase(GetParam()) {} }; INSTANTIATE_TEST_SUITE_P(IpVersions, MainCommonTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), @@ -223,6 +199,16 @@ TEST_P(MainCommonTest, RetryDynamicBaseIdFails) { #endif } +// Verifies that the Logger::Registry is usable after constructing and +// destructing MainCommon. +TEST_P(MainCommonTest, ConstructDestructLogger) { + VERBOSE_EXPECT_NO_THROW(MainCommon main_common(argc(), argv())); + + const std::string logger_name = "logger"; + spdlog::details::log_msg log_msg(logger_name, spdlog::level::level_enum::err, "error"); + Logger::Registry::getSink()->log(log_msg); +} + // Test that std::set_new_handler() was called and the callback functions as expected. // This test fails under TSAN and ASAN, so don't run it in that build: // [ DEATH ] ==845==ERROR: ThreadSanitizer: requested allocation size 0x3e800000000 @@ -268,96 +254,10 @@ TEST_P(MainCommonDeathTest, OutOfMemoryHandler) { #endif } -class AdminRequestTest : public MainCommonTest { +class AdminRequestTest : public AdminRequestTestBase, + public testing::TestWithParam { protected: - AdminRequestTest() { addArg("--disable-hot-restart"); } - - // Runs an admin request specified in path, blocking until completion, and - // returning the response body. - std::string adminRequest(absl::string_view path, absl::string_view method) { - absl::Notification done; - std::string out; - main_common_->adminRequest( - path, method, - [&done, &out](const Http::HeaderMap& /*response_headers*/, absl::string_view body) { - out = std::string(body); - done.Notify(); - }); - done.WaitForNotification(); - return out; - } - - // Initiates Envoy running in its own thread. - void startEnvoy() { - envoy_thread_ = Thread::threadFactoryForTest().createThread([this]() { - // Note: main_common_ is accessed in the testing thread, but - // is race-free, as MainCommon::run() does not return until - // triggered with an adminRequest POST to /quitquitquit, which - // is done in the testing thread. - main_common_ = std::make_unique(argc(), argv()); - envoy_started_ = true; - started_.Notify(); - pauseResumeInterlock(pause_before_run_); - bool status = main_common_->run(); - pauseResumeInterlock(pause_after_run_); - main_common_.reset(); - envoy_finished_ = true; - envoy_return_ = status; - finished_.Notify(); - }); - } - - // Conditionally pauses at a critical point in the Envoy thread, waiting for - // the test thread to trigger something at that exact line. The test thread - // can then call resume_.Notify() to allow the Envoy thread to resume. - void pauseResumeInterlock(bool enable) { - if (enable) { - pause_point_.Notify(); - resume_.WaitForNotification(); - } - } - - // Wait until Envoy is inside the main server run loop proper. Before entering, Envoy runs any - // pending post callbacks, so it's not reliable to use adminRequest() or post() to do this. - // Generally, tests should not depend on this for correctness, but as a result of - // https://github.com/libevent/libevent/issues/779 we need to for TSAN. This is because the entry - // to event_base_loop() is where the signal base race occurs, but once we're in that loop in - // blocking mode, we're safe to take signals. - // TODO(htuch): Remove when https://github.com/libevent/libevent/issues/779 is fixed. - void waitForEnvoyRun() { - absl::Notification done; - main_common_->dispatcherForTest().post([this, &done] { - struct Sacrifice : Event::DeferredDeletable { - Sacrifice(absl::Notification& notify) : notify_(notify) {} - ~Sacrifice() override { notify_.Notify(); } - absl::Notification& notify_; - }; - auto sacrifice = std::make_unique(done); - // Wait for a deferred delete cleanup, this only happens in the main server run loop. - main_common_->dispatcherForTest().deferredDelete(std::move(sacrifice)); - }); - done.WaitForNotification(); - } - - // Having triggered Envoy to quit (via signal or /quitquitquit), this blocks until Envoy exits. - bool waitForEnvoyToExit() { - finished_.WaitForNotification(); - envoy_thread_->join(); - return envoy_return_; - } - - Stats::IsolatedStoreImpl stats_store_; - std::unique_ptr envoy_thread_; - std::unique_ptr main_common_; - absl::Notification started_; - absl::Notification finished_; - absl::Notification resume_; - absl::Notification pause_point_; - bool envoy_return_{false}; - bool envoy_started_{false}; - bool envoy_finished_{false}; - bool pause_before_run_{false}; - bool pause_after_run_{false}; + AdminRequestTest() : AdminRequestTestBase(GetParam()) {} }; INSTANTIATE_TEST_SUITE_P(IpVersions, AdminRequestTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), @@ -367,8 +267,7 @@ TEST_P(AdminRequestTest, AdminRequestGetStatsAndQuit) { startEnvoy(); started_.WaitForNotification(); EXPECT_THAT(adminRequest("/stats", "GET"), HasSubstr("filesystem.reopen_failed")); - adminRequest("/quitquitquit", "POST"); - EXPECT_TRUE(waitForEnvoyToExit()); + quitAndWait(); } // no signals on Windows -- could probably make this work with GenerateConsoleCtrlEvent @@ -459,8 +358,7 @@ TEST_P(AdminRequestTest, AdminRequestBeforeRun) { // We don't get a notification when run(), so it's not safe to check whether the // admin handler is called until after we quit. - adminRequest("/quitquitquit", "POST"); - EXPECT_TRUE(waitForEnvoyToExit()); + quitAndWait(); EXPECT_TRUE(admin_handler_was_called); // This just checks that some stat output was reported. We could pick any stat. @@ -515,14 +413,4 @@ TEST_P(AdminRequestTest, AdminRequestAfterRun) { EXPECT_EQ(1, lambda_destroy_count); } -// Verifies that the Logger::Registry is usable after constructing and -// destructing MainCommon. -TEST_P(MainCommonTest, ConstructDestructLogger) { - VERBOSE_EXPECT_NO_THROW(MainCommon main_common(argc(), argv())); - - const std::string logger_name = "logger"; - spdlog::details::log_msg log_msg(logger_name, spdlog::level::level_enum::err, "error"); - Logger::Registry::getSink()->log(log_msg); -} - } // namespace Envoy diff --git a/test/exe/main_common_test_base.cc b/test/exe/main_common_test_base.cc new file mode 100644 index 000000000000..d0f3960b4148 --- /dev/null +++ b/test/exe/main_common_test_base.cc @@ -0,0 +1,117 @@ +#include "test/exe/main_common_test_base.h" + +#include "source/common/common/thread.h" + +#include "test/test_common/thread_factory_for_test.h" + +namespace Envoy { + +MainCommonTestBase::MainCommonTestBase(Network::Address::IpVersion version) + : config_file_(TestEnvironment::temporaryFileSubstitute( + "test/config/integration/google_com_proxy_port_0.yaml", TestEnvironment::ParamMap(), + TestEnvironment::PortMap(), version)), + argv_({"envoy-static", "--use-dynamic-base-id", "-c", config_file_.c_str(), nullptr}) {} + +const char* const* MainCommonTestBase::argv() { return &argv_[0]; } +int MainCommonTestBase::argc() { return argv_.size() - 1; } + +// Adds an argument, assuring that argv remains null-terminated. +void MainCommonTestBase::addArg(const char* arg) { + ASSERT(!argv_.empty()); + const size_t last = argv_.size() - 1; + ASSERT(argv_[last] == nullptr); // invariant established in ctor, maintained below. + argv_[last] = arg; // guaranteed non-empty + argv_.push_back(nullptr); +} + +// Adds options to make Envoy exit immediately after initialization. +void MainCommonTestBase::initOnly() { + addArg("--mode"); + addArg("init_only"); +} + +AdminRequestTestBase::AdminRequestTestBase(Network::Address::IpVersion version) + : MainCommonTestBase(version) { + addArg("--disable-hot-restart"); +} + +// Runs an admin request specified in path, blocking until completion, and +// returning the response body. +std::string AdminRequestTestBase::adminRequest(absl::string_view path, absl::string_view method) { + absl::Notification done; + std::string out; + main_common_->adminRequest( + path, method, + [&done, &out](const Http::HeaderMap& /*response_headers*/, absl::string_view body) { + out = std::string(body); + done.Notify(); + }); + done.WaitForNotification(); + return out; +} + +// Initiates Envoy running in its own thread. +void AdminRequestTestBase::startEnvoy() { + envoy_thread_ = Thread::threadFactoryForTest().createThread([this]() { + // Note: main_common_ is accessed in the testing thread, but + // is race-free, as MainCommon::run() does not return until + // triggered with an adminRequest POST to /quitquitquit, which + // is done in the testing thread. + main_common_ = std::make_unique(argc(), argv()); + envoy_started_ = true; + started_.Notify(); + pauseResumeInterlock(pause_before_run_); + bool status = main_common_->run(); + pauseResumeInterlock(pause_after_run_); + main_common_.reset(); + envoy_finished_ = true; + envoy_return_ = status; + finished_.Notify(); + }); +} + +// Conditionally pauses at a critical point in the Envoy thread, waiting for +// the test thread to trigger something at that exact line. The test thread +// can then call resume_.Notify() to allow the Envoy thread to resume. +void AdminRequestTestBase::pauseResumeInterlock(bool enable) { + if (enable) { + pause_point_.Notify(); + resume_.WaitForNotification(); + } +} + +// Wait until Envoy is inside the main server run loop proper. Before entering, Envoy runs any +// pending post callbacks, so it's not reliable to use adminRequest() or post() to do this. +// Generally, tests should not depend on this for correctness, but as a result of +// https://github.com/libevent/libevent/issues/779 we need to for TSAN. This is because the entry +// to event_base_loop() is where the signal base race occurs, but once we're in that loop in +// blocking mode, we're safe to take signals. +// TODO(htuch): Remove when https://github.com/libevent/libevent/issues/779 is fixed. +void AdminRequestTestBase::waitForEnvoyRun() { + absl::Notification done; + main_common_->dispatcherForTest().post([this, &done] { + struct Sacrifice : Event::DeferredDeletable { + Sacrifice(absl::Notification& notify) : notify_(notify) {} + ~Sacrifice() override { notify_.Notify(); } + absl::Notification& notify_; + }; + auto sacrifice = std::make_unique(done); + // Wait for a deferred delete cleanup, this only happens in the main server run loop. + main_common_->dispatcherForTest().deferredDelete(std::move(sacrifice)); + }); + done.WaitForNotification(); +} + +// Having triggered Envoy to quit (via signal or /quitquitquit), this blocks until Envoy exits. +bool AdminRequestTestBase::waitForEnvoyToExit() { + finished_.WaitForNotification(); + envoy_thread_->join(); + return envoy_return_; +} + +bool AdminRequestTestBase::quitAndWait() { + adminRequest("/quitquitquit", "POST"); + return waitForEnvoyToExit(); +} + +} // namespace Envoy diff --git a/test/exe/main_common_test_base.h b/test/exe/main_common_test_base.h new file mode 100644 index 000000000000..91ae895dd69f --- /dev/null +++ b/test/exe/main_common_test_base.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +#include "source/common/stats/isolated_store_impl.h" +#include "source/exe/main_common.h" + +#include "test/test_common/environment.h" + +#include "absl/synchronization/notification.h" + +namespace Envoy { + +class MainCommonTestBase { +protected: + MainCommonTestBase(Network::Address::IpVersion version); + const char* const* argv(); + int argc(); + + // Adds an argument, assuring that argv remains null-terminated. + void addArg(const char* arg); + + // Adds options to make Envoy exit immediately after initialization. + void initOnly(); + + std::string config_file_; + std::vector argv_; +}; + +class AdminRequestTestBase : public MainCommonTestBase { +protected: + AdminRequestTestBase(Network::Address::IpVersion version); + + // Runs an admin request specified in path, blocking until completion, and + // returning the response body. + std::string adminRequest(absl::string_view path, absl::string_view method); + + // Initiates Envoy running in its own thread. + void startEnvoy(); + + // Conditionally pauses at a critical point in the Envoy thread, waiting for + // the test thread to trigger something at that exact line. The test thread + // can then call resume_.Notify() to allow the Envoy thread to resume. + void pauseResumeInterlock(bool enable); + + // Wait until Envoy is inside the main server run loop proper. Before entering, Envoy runs any + // pending post callbacks, so it's not reliable to use adminRequest() or post() to do this. + // Generally, tests should not depend on this for correctness, but as a result of + // https://github.com/libevent/libevent/issues/779 we need to for TSAN. This is because the entry + // to event_base_loop() is where the signal base race occurs, but once we're in that loop in + // blocking mode, we're safe to take signals. + // TODO(htuch): Remove when https://github.com/libevent/libevent/issues/779 is fixed. + void waitForEnvoyRun(); + + // Having triggered Envoy to quit (via signal or /quitquitquit), this blocks until Envoy exits. + bool waitForEnvoyToExit(); + + // Sends a quit request to the server, and waits for Envoy to exit. Returns + // true if successful. + bool quitAndWait(); + + Stats::IsolatedStoreImpl stats_store_; + std::unique_ptr envoy_thread_; + std::unique_ptr main_common_; + absl::Notification started_; + absl::Notification finished_; + absl::Notification resume_; + absl::Notification pause_point_; + bool envoy_return_{false}; + bool envoy_started_{false}; + bool envoy_finished_{false}; + bool pause_before_run_{false}; + bool pause_after_run_{false}; +}; + +} // namespace Envoy diff --git a/test/integration/admin_html/test_server.cc b/test/integration/admin_html/test_server.cc index 4a53d7661bb9..d371d7b22108 100644 --- a/test/integration/admin_html/test_server.cc +++ b/test/integration/admin_html/test_server.cc @@ -14,7 +14,8 @@ namespace { * a query param but it could not be found. * * This test-server is only for testing; it potentially makes the - * entire file-system avail + * entire file-system available to HTTP clients, so this should not + * be used for production systems. */ Http::Code testCallback(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, Server::AdminStream& admin_stream) { diff --git a/test/mocks/server/admin.h b/test/mocks/server/admin.h index c7308d6a044e..a5be3ab379a0 100644 --- a/test/mocks/server/admin.h +++ b/test/mocks/server/admin.h @@ -40,6 +40,7 @@ class MockAdmin : public Admin { MOCK_METHOD(void, addListenerToHandler, (Network::ConnectionHandler * handler)); MOCK_METHOD(uint32_t, concurrency, (), (const)); MOCK_METHOD(void, closeSocket, ()); + MOCK_METHOD(RequestPtr, makeRequest, (AdminStream & admin_stream), (const)); NiceMock config_tracker_; NiceMock socket_; diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index d34afbad937e..5ee457fe70b3 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -21,7 +21,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/common/signal:87.2" # Death tests don't report LCOV "source/common/thread:0.0" # Death tests don't report LCOV "source/common/watchdog:58.6" # Death tests don't report LCOV -"source/exe:90.3" +"source/exe:94.0" # increased by #32346, need coverage for terminate_handler and hot restart failures "source/extensions/clusters/common:91.5" # This can be increased again once `#24903` lands "source/extensions/common:93.0" #flaky: be careful adjusting "source/extensions/common/proxy_protocol:93.8" # Adjusted for security patch diff --git a/test/server/admin/admin_filter_test.cc b/test/server/admin/admin_filter_test.cc index 9627cfd1ca31..fbf6c93cd72d 100644 --- a/test/server/admin/admin_filter_test.cc +++ b/test/server/admin/admin_filter_test.cc @@ -7,27 +7,28 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +using testing::ByMove; using testing::InSequence; using testing::NiceMock; +using testing::Return; namespace Envoy { namespace Server { class AdminFilterTest : public testing::TestWithParam { public: - AdminFilterTest() : filter_(adminHandlerCallback), request_headers_{{":path", "/"}} { + AdminFilterTest() : filter_(admin_), request_headers_{{":path", "/"}} { + EXPECT_CALL(admin_, makeRequest(_)).WillOnce(Return(ByMove(adminHandlerCallback()))); filter_.setDecoderFilterCallbacks(callbacks_); } - NiceMock server_; + NiceMock admin_; Stats::IsolatedStoreImpl listener_scope_; AdminFilter filter_; NiceMock callbacks_; Http::TestRequestHeaderMapImpl request_headers_; - static Admin::RequestPtr adminHandlerCallback(AdminStream& admin_stream) { - // silence compiler warnings for unused params - UNREFERENCED_PARAMETER(admin_stream); + static Admin::RequestPtr adminHandlerCallback() { return AdminImpl::makeStaticTextRequest("OK\n", Http::Code::OK); } }; diff --git a/test/server/admin/admin_instance.cc b/test/server/admin/admin_instance.cc index 7b18c448e18f..a2b4d2f15d17 100644 --- a/test/server/admin/admin_instance.cc +++ b/test/server/admin/admin_instance.cc @@ -10,7 +10,7 @@ namespace Server { AdminInstanceTest::AdminInstanceTest() : cpu_profile_path_(TestEnvironment::temporaryPath("envoy.prof")), admin_(cpu_profile_path_, server_, false), request_headers_{{":path", "/"}}, - admin_filter_(admin_.createRequestFunction()) { + admin_filter_(admin_) { std::list access_logs; Filesystem::FilePathAndType file_info{Filesystem::DestinationType::File, "/dev/null"}; access_logs.emplace_back(new Extensions::AccessLoggers::File::FileAccessLog( diff --git a/test/server/config_validation/BUILD b/test/server/config_validation/BUILD index 6d838b121c91..ed55171c3d66 100644 --- a/test/server/config_validation/BUILD +++ b/test/server/config_validation/BUILD @@ -59,6 +59,7 @@ envoy_cc_test( "//source/extensions/filters/network/http_connection_manager:config", "//source/extensions/listener_managers/validation_listener_manager:validation_listener_manager_lib", "//source/extensions/transport_sockets/tls:config", + "//source/server/admin:admin_filter_lib", "//source/server/config_validation:server_lib", "//test/integration:integration_lib", "//test/mocks/network:network_mocks", diff --git a/test/server/config_validation/server_test.cc b/test/server/config_validation/server_test.cc index 8102b84b01d4..6462503b2cc8 100644 --- a/test/server/config_validation/server_test.cc +++ b/test/server/config_validation/server_test.cc @@ -4,6 +4,7 @@ #include "envoy/server/filter_config.h" #include "source/extensions/listener_managers/validation_listener_manager/validation_listener_manager.h" +#include "source/server/admin/admin_filter.h" #include "source/server/config_validation/server.h" #include "source/server/process_context_impl.h" @@ -207,6 +208,8 @@ TEST_P(ValidationServerTest, DummyMethodsTest) { server.admin()->addListenerToHandler(nullptr); server.admin()->closeSocket(); server.admin()->startHttpListener({}, nullptr, nullptr); + AdminFilter filter(*server.admin()); + EXPECT_TRUE(server.admin()->makeRequest(filter) == nullptr); Network::MockTcpListenerCallbacks listener_callbacks; Network::MockListenerConfig listener_config; From e6ba54383a5832232efadeb40b2c9c85bafcd83b Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Mon, 18 Mar 2024 15:51:00 -0400 Subject: [PATCH 075/124] tooling: envoy-ci ping (#32905) Signed-off-by: Alyssa Wilk --- tools/repo/notify.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/repo/notify.py b/tools/repo/notify.py index 275666b358d7..ec6495494446 100644 --- a/tools/repo/notify.py +++ b/tools/repo/notify.py @@ -300,6 +300,11 @@ async def post_to_oncall(self): text=( f"*{num_issues} Untriaged Issues* (please tag and cc area experts)\n<{ISSUE_LINK}|{ISSUE_LINK}>" )) + await self.send_message( + channel='#envoy-ci', + text=( + f"<@{oncall_handle}> please triage flakes per " + )) except SlackApiError as e: self.log.error(f"Unexpected error {e.response['error']}") From 6197741605052ce8eb60d42a94cf9b2cd146e8a1 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Mon, 18 Mar 2024 15:18:47 -0500 Subject: [PATCH 076/124] mobile: Remove jni_log (#32958) Signed-off-by: Fredy Wijaya --- mobile/library/jni/android_network_utility.cc | 3 - mobile/library/jni/java_jni_support.cc | 4 -- mobile/library/jni/jni_impl.cc | 59 +------------------ mobile/library/jni/jni_support.h | 4 -- mobile/library/jni/ndk_jni_support.cc | 12 ---- mobile/test/jni/test_jni_impl.cc | 12 ---- 6 files changed, 2 insertions(+), 92 deletions(-) diff --git a/mobile/library/jni/android_network_utility.cc b/mobile/library/jni/android_network_utility.cc index e8e2f71604cd..0a0fb87b2702 100644 --- a/mobile/library/jni/android_network_utility.cc +++ b/mobile/library/jni/android_network_utility.cc @@ -88,7 +88,6 @@ LocalRefUniquePtr callJvmVerifyX509CertChain(Envoy::JNI::JniHelper& jni const std::vector& cert_chain, std::string auth_type, absl::string_view hostname) { - jni_log("[Envoy]", "jvmVerifyX509CertChain"); LocalRefUniquePtr jcls_AndroidNetworkLibrary = findClass("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); jmethodID jmid_verifyServerCertificates = jni_helper.getStaticMethodId( @@ -107,8 +106,6 @@ LocalRefUniquePtr callJvmVerifyX509CertChain(Envoy::JNI::JniHelper& jni envoy_cert_validation_result verifyX509CertChain(const std::vector& certs, absl::string_view hostname) { - jni_log("[Envoy]", "verifyX509CertChain"); - envoy_cert_verify_status_t result; bool is_issued_by_known_root; std::vector verified_chain; diff --git a/mobile/library/jni/java_jni_support.cc b/mobile/library/jni/java_jni_support.cc index 003cc31754b6..346e2cc1afbd 100644 --- a/mobile/library/jni/java_jni_support.cc +++ b/mobile/library/jni/java_jni_support.cc @@ -2,10 +2,6 @@ // NOLINT(namespace-envoy) -int jni_log_fmt(const char* /*tag*/, const char* /*fmt*/, void* /*value*/) { return 0; } - -int jni_log(const char* /*tag*/, const char* /*str*/) { return 0; } - jint attach_jvm(JavaVM* vm, JNIEnv** p_env, void* thr_args) { return vm->AttachCurrentThread(reinterpret_cast(p_env), thr_args); } diff --git a/mobile/library/jni/jni_impl.cc b/mobile/library/jni/jni_impl.cc index 78dd725fd581..a2ec47ba23f4 100644 --- a/mobile/library/jni/jni_impl.cc +++ b/mobile/library/jni/jni_impl.cc @@ -38,7 +38,6 @@ static void jvm_on_engine_running(void* context) { return; } - jni_log("[Envoy]", "jvm_on_engine_running"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); Envoy::JNI::LocalRefUniquePtr jcls_JvmonEngineRunningContext = @@ -72,7 +71,6 @@ static void jvm_on_log(envoy_log_level log_level, envoy_data data, const void* c } static void jvm_on_exit(void*) { - jni_log("[Envoy]", "library is exiting"); // Note that this is not dispatched because the thread that // needs to be detached is the engine thread. // This function is called from the context of the engine's @@ -81,7 +79,6 @@ static void jvm_on_exit(void*) { } static void jvm_on_track(envoy_map events, const void* context) { - jni_log("[Envoy]", "jvm_on_track"); if (context == nullptr) { return; } @@ -125,7 +122,6 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr // TODO(goaway): The retained_context leaks, but it's tied to the life of the engine. // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332. jobject retained_context = env->NewGlobalRef(j_event_tracker); - jni_log_fmt("[Envoy]", "retained_context: %p", retained_context); event_tracker.track = jvm_on_track; event_tracker.context = retained_context; } @@ -180,7 +176,6 @@ extern "C" JNIEXPORT jstring JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_dumpStats(JNIEnv* env, jclass, // class jlong engine_handle) { - jni_log("[Envoy]", "dumpStats"); auto engine = reinterpret_cast(engine_handle); std::string stats = engine->dumpStats(); Envoy::JNI::JniHelper jni_helper(env); @@ -228,7 +223,6 @@ static void passHeaders(const char* method, const Envoy::Types::ManagedEnvoyHead static Envoy::JNI::LocalRefUniquePtr jvm_on_headers(const char* method, const Envoy::Types::ManagedEnvoyHeaders& headers, bool end_stream, envoy_stream_intel stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_headers"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); passHeaders("passHeader", headers, j_context); @@ -337,7 +331,6 @@ static Envoy::JNI::LocalRefUniquePtr jvm_on_data(const char* metho bool end_stream, envoy_stream_intel stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_data"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); @@ -432,18 +425,13 @@ static envoy_filter_data_status jvm_http_filter_on_response_data(envoy_data data /*pending_headers*/ pending_headers}; } -static void jvm_on_metadata(envoy_headers metadata, envoy_stream_intel /*stream_intel*/, - void* /*context*/) { - jni_log("[Envoy]", "jvm_on_metadata"); - jni_log("[Envoy]", std::to_string(metadata.length).c_str()); -} +static void jvm_on_metadata(envoy_headers /* metadata */, envoy_stream_intel /*stream_intel*/, + void* /*context*/) {} static Envoy::JNI::LocalRefUniquePtr jvm_on_trailers(const char* method, envoy_headers trailers, envoy_stream_intel stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_trailers"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); passHeaders("passHeader", trailers, j_context); @@ -555,8 +543,6 @@ jvm_http_filter_on_response_trailers(envoy_headers trailers, envoy_stream_intel static void jvm_http_filter_set_request_callbacks(envoy_http_filter_callbacks callbacks, const void* context) { - jni_log("[Envoy]", "jvm_http_filter_set_request_callbacks"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); Envoy::JNI::LocalRefUniquePtr jcls_JvmCallbackContext = @@ -575,8 +561,6 @@ static void jvm_http_filter_set_request_callbacks(envoy_http_filter_callbacks ca static void jvm_http_filter_set_response_callbacks(envoy_http_filter_callbacks callbacks, const void* context) { - jni_log("[Envoy]", "jvm_http_filter_set_response_callbacks"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); Envoy::JNI::LocalRefUniquePtr jcls_JvmCallbackContext = @@ -596,8 +580,6 @@ static envoy_filter_resume_status jvm_http_filter_on_resume(const char* method, envoy_headers* headers, envoy_data* data, envoy_headers* trailers, bool end_stream, envoy_stream_intel stream_intel, const void* context) { - jni_log("[Envoy]", "jvm_on_resume"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); jlong headers_length = -1; @@ -666,8 +648,6 @@ jvm_http_filter_on_resume_response(envoy_headers* headers, envoy_data* data, static void call_jvm_on_complete(envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_complete"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); @@ -686,7 +666,6 @@ static void call_jvm_on_complete(envoy_stream_intel stream_intel, static void call_jvm_on_error(envoy_error error, envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_error"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); @@ -717,8 +696,6 @@ static void jvm_on_error(envoy_error error, envoy_stream_intel stream_intel, static void call_jvm_on_cancel(envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_cancel"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); @@ -761,8 +738,6 @@ static void jvm_http_filter_on_cancel(envoy_stream_intel stream_intel, } static void jvm_on_send_window_available(envoy_stream_intel stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_send_window_available"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); @@ -780,7 +755,6 @@ static void jvm_on_send_window_available(envoy_stream_intel stream_intel, void* // JvmKeyValueStoreContext static envoy_data jvm_kv_store_read(envoy_data key, const void* context) { - jni_log("[Envoy]", "jvm_kv_store_read"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); @@ -799,7 +773,6 @@ static envoy_data jvm_kv_store_read(envoy_data key, const void* context) { } static void jvm_kv_store_remove(envoy_data key, const void* context) { - jni_log("[Envoy]", "jvm_kv_store_remove"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); @@ -814,7 +787,6 @@ static void jvm_kv_store_remove(envoy_data key, const void* context) { } static void jvm_kv_store_save(envoy_data key, envoy_data value, const void* context) { - jni_log("[Envoy]", "jvm_kv_store_save"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); @@ -833,15 +805,11 @@ static void jvm_kv_store_save(envoy_data key, envoy_data value, const void* cont // JvmFilterFactoryContext static const void* jvm_http_filter_init(const void* context) { - jni_log("[Envoy]", "jvm_filter_init"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); envoy_http_filter* c_filter = static_cast(const_cast(context)); jobject j_context = static_cast(const_cast(c_filter->static_context)); - jni_log_fmt("[Envoy]", "j_context: %p", j_context); - Envoy::JNI::LocalRefUniquePtr jcls_JvmFilterFactoryContext = jni_helper.getObjectClass(j_context); jmethodID jmid_create = @@ -850,7 +818,6 @@ static const void* jvm_http_filter_init(const void* context) { Envoy::JNI::LocalRefUniquePtr j_filter = jni_helper.callObjectMethod(j_context, jmid_create); - jni_log_fmt("[Envoy]", "j_filter: %p", j_filter.get()); Envoy::JNI::GlobalRefUniquePtr retained_filter = jni_helper.newGlobalRef(j_filter.get()); return retained_filter.release(); @@ -913,10 +880,7 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_registerKeyValueStore(JNIEnv* e // TODO(goaway): The java context here leaks, but it's tied to the life of the engine. // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332 - jni_log("[Envoy]", "registerKeyValueStore"); - jni_log_fmt("[Envoy]", "j_context: %p", j_context); jobject retained_context = env->NewGlobalRef(j_context); - jni_log_fmt("[Envoy]", "retained_context: %p", retained_context); envoy_kv_store* api = static_cast(safe_malloc(sizeof(envoy_kv_store))); api->save = jvm_kv_store_save; api->read = jvm_kv_store_read; @@ -938,10 +902,7 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_registerFilterFactory(JNIEnv* e // TODO(goaway): Everything here leaks, but it's all be tied to the life of the engine. // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332 - jni_log("[Envoy]", "registerFilterFactory"); - jni_log_fmt("[Envoy]", "j_context: %p", j_context); jobject retained_context = env->NewGlobalRef(j_context); - jni_log_fmt("[Envoy]", "retained_context: %p", retained_context); envoy_http_filter* api = static_cast(safe_malloc(sizeof(envoy_http_filter))); api->init_filter = jvm_http_filter_init; api->on_request_headers = jvm_http_filter_on_request_headers; @@ -970,7 +931,6 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_registerFilterFactory(JNIEnv* e extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_EnvoyHTTPFilterCallbacksImpl_callResumeIteration( JNIEnv* env, jclass, jlong callback_handle, jobject j_context) { - jni_log("[Envoy]", "callResumeIteration"); // Context is only passed here to ensure it's not inadvertently gc'd during execution of this // function. To be extra safe, do an explicit retain with a GlobalRef. jobject retained_context = env->NewGlobalRef(j_context); @@ -983,7 +943,6 @@ Java_io_envoyproxy_envoymobile_engine_EnvoyHTTPFilterCallbacksImpl_callResumeIte extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_EnvoyHTTPFilterCallbacksImpl_callResetIdleTimer( JNIEnv* env, jclass, jlong callback_handle, jobject j_context) { - jni_log("[Envoy]", "callResetIdleTimer"); // Context is only passed here to ensure it's not inadvertently gc'd during execution of this // function. To be extra safe, do an explicit retain with a GlobalRef. jobject retained_context = env->NewGlobalRef(j_context); @@ -996,7 +955,6 @@ Java_io_envoyproxy_envoymobile_engine_EnvoyHTTPFilterCallbacksImpl_callResetIdle extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_EnvoyHTTPFilterCallbacksImpl_callReleaseCallbacks( JNIEnv* /*env*/, jclass, jlong callback_handle) { - jni_log("[Envoy]", "callReleaseCallbacks"); envoy_http_filter_callbacks* callbacks = reinterpret_cast(callback_handle); callbacks->release_callbacks(callbacks->callback_context); @@ -1017,9 +975,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra JNIEnv* env, jclass, jlong engine_handle, jlong stream_handle, jobject data, jint length, jboolean end_stream) { Envoy::JNI::JniHelper jni_helper(env); - if (end_stream) { - jni_log("[Envoy]", "jvm_send_data_end_stream"); - } return reinterpret_cast(engine_handle) ->sendData(static_cast(stream_handle), Envoy::JNI::javaByteBufferToEnvoyData(jni_helper, data, length), end_stream); @@ -1036,9 +991,6 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_sendDataByteArray(JNIEnv* env, jbyteArray data, jint length, jboolean end_stream) { Envoy::JNI::JniHelper jni_helper(env); - if (end_stream) { - jni_log("[Envoy]", "jvm_send_data_end_stream"); - } return reinterpret_cast(engine_handle) ->sendData(static_cast(stream_handle), Envoy::JNI::javaByteArrayToEnvoyData(jni_helper, data, length), end_stream); @@ -1057,7 +1009,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_sendTrailers( JNIEnv* env, jclass, jlong engine_handle, jlong stream_handle, jobjectArray trailers) { Envoy::JNI::JniHelper jni_helper(env); - jni_log("[Envoy]", "jvm_send_trailers"); return reinterpret_cast(engine_handle) ->sendTrailers(static_cast(stream_handle), Envoy::JNI::javaArrayOfObjectArrayToEnvoyHeaders(jni_helper, trailers)); @@ -1341,7 +1292,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_resetConnectivityState(JNIEnv* /*env*/, jclass, // class jlong engine) { - jni_log("[Envoy]", "resetConnectivityState"); return reinterpret_cast(engine)->resetConnectivityState(); } @@ -1349,7 +1299,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_setPreferredNetwork(JNIEnv* /*env*/, jclass, // class jlong engine, jint network) { - jni_log("[Envoy]", "setting preferred network"); return reinterpret_cast(engine)->setPreferredNetwork( static_cast(network)); } @@ -1358,8 +1307,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra JNIEnv* env, jclass, // class jlong engine, jstring host, jint port) { - jni_log("[Envoy]", "setProxySettings"); - Envoy::JNI::JniHelper jni_helper(env); Envoy::JNI::StringUtfUniquePtr java_host = jni_helper.getStringUtfChars(host, nullptr); const uint16_t native_port = static_cast(port); @@ -1371,7 +1318,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra } static void jvm_add_test_root_certificate(const uint8_t* cert, size_t len) { - jni_log("[Envoy]", "jvm_add_test_root_certificate"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); Envoy::JNI::LocalRefUniquePtr jcls_AndroidNetworkLibrary = Envoy::JNI::findClass("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); @@ -1385,7 +1331,6 @@ static void jvm_add_test_root_certificate(const uint8_t* cert, size_t len) { } static void jvm_clear_test_root_certificate() { - jni_log("[Envoy]", "jvm_clear_test_root_certificate"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); Envoy::JNI::LocalRefUniquePtr jcls_AndroidNetworkLibrary = Envoy::JNI::findClass("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); diff --git a/mobile/library/jni/jni_support.h b/mobile/library/jni/jni_support.h index 45fef67f3fa3..a66c87648710 100644 --- a/mobile/library/jni/jni_support.h +++ b/mobile/library/jni/jni_support.h @@ -4,8 +4,4 @@ // NOLINT(namespace-envoy) -extern "C" int jni_log_fmt(const char* tag, const char* fmt, void* value); - -extern "C" int jni_log(const char* tag, const char* str); - extern "C" jint attach_jvm(JavaVM* vm, JNIEnv** p_env, void* thr_args); diff --git a/mobile/library/jni/ndk_jni_support.cc b/mobile/library/jni/ndk_jni_support.cc index ecd4f2c98a82..98403edbbfef 100644 --- a/mobile/library/jni/ndk_jni_support.cc +++ b/mobile/library/jni/ndk_jni_support.cc @@ -1,19 +1,7 @@ -#include - #include "library/jni/jni_support.h" // NOLINT(namespace-envoy) -int jni_log_fmt(const char* /*tag*/, const char* /*fmt*/, void* /*value*/) { - // For debug logging, use __android_log_print(ANDROID_LOG_VERBOSE, tag, fmt, value); - return 0; -} - -int jni_log(const char* /*tag*/, const char* /*str*/) { - // For debug logging, use __android_log_write(ANDROID_LOG_VERBOSE, tag, str); - return 0; -} - jint attach_jvm(JavaVM* vm, JNIEnv** p_env, void* thr_args) { return vm->AttachCurrentThread(p_env, thr_args); } diff --git a/mobile/test/jni/test_jni_impl.cc b/mobile/test/jni/test_jni_impl.cc index 455665ac3b45..5e7477d47554 100644 --- a/mobile/test/jni/test_jni_impl.cc +++ b/mobile/test/jni/test_jni_impl.cc @@ -13,70 +13,60 @@ extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeStartHttpProxyTestServer(JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "starting server"); start_server(Envoy::TestServerType::HTTP_PROXY); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeStartHttpsProxyTestServer( JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "starting server"); start_server(Envoy::TestServerType::HTTPS_PROXY); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeStartHttp3TestServer(JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "starting server"); start_server(Envoy::TestServerType::HTTP3); } extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeGetServerPort(JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "getting server port"); return get_server_port(); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeStartHttp2TestServer(JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "starting server"); start_server(Envoy::TestServerType::HTTP2_WITH_TLS); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeShutdownTestServer(JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "shutting down server"); shutdown_server(); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeInitXdsTestServer(JNIEnv* env, jclass clazz) { - jni_log("[XTS]", "initializing xDS server"); initXdsServer(); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeStartXdsTestServer(JNIEnv* env, jclass clazz) { - jni_log("[XTS]", "starting xDS server"); startXdsServer(); } extern "C" JNIEXPORT jstring JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeGetXdsTestServerHost(JNIEnv* env, jclass clazz) { - jni_log("[XTS]", "getting xDS server host"); return env->NewStringUTF(getXdsServerHost()); } extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeGetXdsTestServerPort(JNIEnv* env, jclass clazz) { - jni_log("[XTS]", "getting xDS server port"); return getXdsServerPort(); } @@ -85,7 +75,6 @@ extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeSendDiscoveryResponse(JNIEnv* env, jclass clazz, jstring yaml) { - jni_log("[XTS]", "sending DiscoveryResponse from the xDS server"); const char* yaml_chars = env->GetStringUTFChars(yaml, /* isCopy= */ nullptr); // The yaml utilities have non-relevant thread asserts. Envoy::Thread::SkipAsserts skip; @@ -99,7 +88,6 @@ Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeSendDiscoveryRespons extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeShutdownXdsTestServer(JNIEnv* env, jclass clazz) { - jni_log("[XTS]", "shutting down xDS server"); shutdownXdsServer(); } From eaa9d0aacb44f4c6e9ff3d4af20fb82cab8816c2 Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Mon, 18 Mar 2024 19:43:24 -0400 Subject: [PATCH 077/124] Make the instance_interface dependency explicit (#32935) Signed-off-by: Yan Avlasov --- source/server/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/source/server/BUILD b/source/server/BUILD index c86be8637fc7..bca0cd626d7e 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -397,6 +397,7 @@ envoy_cc_library( hdrs = ["listener_manager_factory.h"], deps = [ "//envoy/server:factory_context_interface", + "//envoy/server:instance_interface", "//envoy/server:listener_manager_interface", "//envoy/server:worker_interface", "//source/common/quic:quic_stat_names_lib", From 307218e7ac4db4320ea0003952adcf2f93d4d8de Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Mon, 18 Mar 2024 19:49:26 -0400 Subject: [PATCH 078/124] Make macro work outside of the Envoy namespace (#32893) --------- Signed-off-by: Yan Avlasov --- envoy/common/exception.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/envoy/common/exception.h b/envoy/common/exception.h index 4d5d9fd45bc0..946e92788739 100644 --- a/envoy/common/exception.h +++ b/envoy/common/exception.h @@ -17,7 +17,7 @@ namespace Envoy { #define throwEnvoyExceptionOrPanic(x) PANIC(x) #define throwExceptionOrPanic(x, y) PANIC(y) #else -#define throwEnvoyExceptionOrPanic(x) throw EnvoyException(x) +#define throwEnvoyExceptionOrPanic(x) throw ::Envoy::EnvoyException(x) #define throwExceptionOrPanic(y, x) throw y(x) #endif From 7fec609a507371d7176c61aa4623f445543f294f Mon Sep 17 00:00:00 2001 From: Thomas <1607559+thomasvnoort@users.noreply.github.com> Date: Tue, 19 Mar 2024 02:22:28 +0100 Subject: [PATCH 079/124] rbac: rules_stat_prefix to emit metrics with a prefix (#31835) This is akin to shadow_rules_stat_prefix but for non-shadowing rules. Since only shadow rules emit dynamic metadata, this prefix only applies to metrics. --------- Signed-off-by: Thomas van Noort --- .../filters/http/rbac/v3/rbac.proto | 7 +- changelogs/current.yaml | 4 + .../http/http_filters/rbac_filter.rst | 7 +- source/common/config/well_known_names.cc | 3 + source/common/config/well_known_names.h | 2 + .../extensions/filters/common/rbac/utility.cc | 11 ++- .../extensions/filters/common/rbac/utility.h | 6 +- .../filters/http/rbac/rbac_filter.cc | 2 +- .../filters/network/rbac/rbac_filter.cc | 2 +- test/common/stats/tag_extractor_impl_test.cc | 11 +++ .../filters/http/rbac/rbac_filter_test.cc | 81 +++++++++++------ .../filters/network/rbac/filter_test.cc | 88 ++++++++++++------- 12 files changed, 152 insertions(+), 72 deletions(-) diff --git a/api/envoy/extensions/filters/http/rbac/v3/rbac.proto b/api/envoy/extensions/filters/http/rbac/v3/rbac.proto index eeb505a17fb7..b14478b70e8f 100644 --- a/api/envoy/extensions/filters/http/rbac/v3/rbac.proto +++ b/api/envoy/extensions/filters/http/rbac/v3/rbac.proto @@ -22,7 +22,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#extension: envoy.filters.http.rbac] // RBAC filter config. -// [#next-free-field: 6] +// [#next-free-field: 7] message RBAC { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.rbac.v2.RBAC"; @@ -34,6 +34,11 @@ message RBAC { config.rbac.v3.RBAC rules = 1 [(udpa.annotations.field_migrate).oneof_promotion = "rules_specifier"]; + // If specified, rules will emit stats with the given prefix. + // This is useful to distinguish the stat when there are more than 1 RBAC filter configured with + // rules. + string rules_stat_prefix = 6; + // The match tree to use when resolving RBAC action for incoming requests. Requests do not // match any matcher will be denied. // If absent, no enforcing RBAC matcher will be applied. diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 092048320fae..aa29fe080592 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -339,6 +339,10 @@ new_features: Update ``aws_request_signing`` filter to support optionally sending the aws signature in query parameters rather than headers, by specifying the :ref:`query_string ` configuration section. +- area: rbac + change: | + Added :ref:`rules_stat_prefix ` + to allow adding custom prefix to the stats emitted by rules. deprecated: - area: listener diff --git a/docs/root/configuration/http/http_filters/rbac_filter.rst b/docs/root/configuration/http/http_filters/rbac_filter.rst index db30b5123a01..4e2e5990392e 100644 --- a/docs/root/configuration/http/http_filters/rbac_filter.rst +++ b/docs/root/configuration/http/http_filters/rbac_filter.rst @@ -33,7 +33,12 @@ The RBAC filter outputs statistics in the ``http..rbac.`` namespace ` comes from the owning HTTP connection manager. -For the shadow rule statistics ``shadow_allowed`` and ``shadow_denied``, the :ref:`shadow_rules_stat_prefix ` +For the rule statistics ``allowed`` and ``denied``, +the :ref:`rules_stat_prefix ` +can be used to add an extra prefix to output the statistics in the ``http..rbac..`` namespace. + +For the shadow rule statistics ``shadow_allowed`` and ``shadow_denied``, +the :ref:`shadow_rules_stat_prefix ` can be used to add an extra prefix to output the statistics in the ``http..rbac..`` namespace. .. csv-table:: diff --git a/source/common/config/well_known_names.cc b/source/common/config/well_known_names.cc index 4e390568c31d..104b182bfb1a 100644 --- a/source/common/config/well_known_names.cc +++ b/source/common/config/well_known_names.cc @@ -207,6 +207,9 @@ TagNameValues::TagNameValues() { // (.).rbac.** addTokenized(RBAC_PREFIX, "$.rbac.**"); + + // http..rbac.(.)* + addTokenized(RBAC_HTTP_PREFIX, "http.*.rbac.$.**"); } void TagNameValues::addRe2(const std::string& name, const std::string& regex, diff --git a/source/common/config/well_known_names.h b/source/common/config/well_known_names.h index a0a40cc79496..8cf5fbd14715 100644 --- a/source/common/config/well_known_names.h +++ b/source/common/config/well_known_names.h @@ -123,6 +123,8 @@ class TagNameValues { const std::string CONNECTION_LIMIT_PREFIX = "envoy.connection_limit_prefix"; // Stats prefix for the RBAC network filter const std::string RBAC_PREFIX = "envoy.rbac_prefix"; + // Stats prefix for the RBAC http filter + const std::string RBAC_HTTP_PREFIX = "envoy.rbac_http_prefix"; // Stats prefix for the TCP Proxy network filter const std::string TCP_PREFIX = "envoy.tcp_prefix"; // Stats prefix for the UDP Proxy network filter diff --git a/source/extensions/filters/common/rbac/utility.cc b/source/extensions/filters/common/rbac/utility.cc index 710342d7b8fd..cc14523ee1fb 100644 --- a/source/extensions/filters/common/rbac/utility.cc +++ b/source/extensions/filters/common/rbac/utility.cc @@ -10,11 +10,14 @@ namespace Filters { namespace Common { namespace RBAC { -RoleBasedAccessControlFilterStats -generateStats(const std::string& prefix, const std::string& shadow_prefix, Stats::Scope& scope) { +RoleBasedAccessControlFilterStats generateStats(const std::string& prefix, + const std::string& rules_prefix, + const std::string& shadow_rules_prefix, + Stats::Scope& scope) { const std::string final_prefix = Envoy::statPrefixJoin(prefix, "rbac."); - return {ENFORCE_RBAC_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix)) - SHADOW_RBAC_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix + shadow_prefix))}; + return { + ENFORCE_RBAC_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix + rules_prefix)) + SHADOW_RBAC_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix + shadow_rules_prefix))}; } std::string responseDetail(const std::string& policy_id) { diff --git a/source/extensions/filters/common/rbac/utility.h b/source/extensions/filters/common/rbac/utility.h index 79cd8be7d2bf..fe17def3c6e1 100644 --- a/source/extensions/filters/common/rbac/utility.h +++ b/source/extensions/filters/common/rbac/utility.h @@ -34,8 +34,10 @@ struct RoleBasedAccessControlFilterStats { SHADOW_RBAC_FILTER_STATS(GENERATE_COUNTER_STRUCT) }; -RoleBasedAccessControlFilterStats -generateStats(const std::string& prefix, const std::string& shadow_prefix, Stats::Scope& scope); +RoleBasedAccessControlFilterStats generateStats(const std::string& prefix, + const std::string& rules_prefix, + const std::string& shadow_rules_prefix, + Stats::Scope& scope); template std::unique_ptr diff --git a/source/extensions/filters/http/rbac/rbac_filter.cc b/source/extensions/filters/http/rbac/rbac_filter.cc index bb6950942611..f1ee12283824 100644 --- a/source/extensions/filters/http/rbac/rbac_filter.cc +++ b/source/extensions/filters/http/rbac/rbac_filter.cc @@ -61,7 +61,7 @@ RoleBasedAccessControlFilterConfig::RoleBasedAccessControlFilterConfig( const std::string& stats_prefix, Stats::Scope& scope, Server::Configuration::ServerFactoryContext& context, ProtobufMessage::ValidationVisitor& validation_visitor) - : stats_(Filters::Common::RBAC::generateStats(stats_prefix, + : stats_(Filters::Common::RBAC::generateStats(stats_prefix, proto_config.rules_stat_prefix(), proto_config.shadow_rules_stat_prefix(), scope)), shadow_rules_stat_prefix_(proto_config.shadow_rules_stat_prefix()), engine_(Filters::Common::RBAC::createEngine(proto_config, context, validation_visitor, diff --git a/source/extensions/filters/network/rbac/rbac_filter.cc b/source/extensions/filters/network/rbac/rbac_filter.cc index 500a0c2f617e..7d07de0f6d18 100644 --- a/source/extensions/filters/network/rbac/rbac_filter.cc +++ b/source/extensions/filters/network/rbac/rbac_filter.cc @@ -56,7 +56,7 @@ RoleBasedAccessControlFilterConfig::RoleBasedAccessControlFilterConfig( const envoy::extensions::filters::network::rbac::v3::RBAC& proto_config, Stats::Scope& scope, Server::Configuration::ServerFactoryContext& context, ProtobufMessage::ValidationVisitor& validation_visitor) - : stats_(Filters::Common::RBAC::generateStats(proto_config.stat_prefix(), + : stats_(Filters::Common::RBAC::generateStats(proto_config.stat_prefix(), "", proto_config.shadow_rules_stat_prefix(), scope)), shadow_rules_stat_prefix_(proto_config.shadow_rules_stat_prefix()), engine_(Filters::Common::RBAC::createEngine(proto_config, context, validation_visitor, diff --git a/test/common/stats/tag_extractor_impl_test.cc b/test/common/stats/tag_extractor_impl_test.cc index 43e74080a685..747f9daca0c1 100644 --- a/test/common/stats/tag_extractor_impl_test.cc +++ b/test/common/stats/tag_extractor_impl_test.cc @@ -450,6 +450,17 @@ TEST(TagExtractorTest, DefaultTagExtractors) { rbac_prefix.name_ = tag_names.RBAC_PREFIX; rbac_prefix.value_ = "my_rbac_prefix"; regex_tester.testRegex("my_rbac_prefix.rbac.allowed", "rbac.allowed", {rbac_prefix}); + + // RBAC HTTP Filter Prefix + Tag rbac_http_hcm_prefix; + rbac_http_hcm_prefix.name_ = tag_names.HTTP_CONN_MANAGER_PREFIX; + rbac_http_hcm_prefix.value_ = "hcm_prefix"; + + Tag rbac_http_prefix; + rbac_http_prefix.name_ = tag_names.RBAC_HTTP_PREFIX; + rbac_http_prefix.value_ = "prefix"; + regex_tester.testRegex("http.hcm_prefix.rbac.prefix.allowed", "http.rbac.allowed", + {rbac_http_hcm_prefix, rbac_http_prefix}); } TEST(TagExtractorTest, ExtAuthzTagExtractors) { diff --git a/test/extensions/filters/http/rbac/rbac_filter_test.cc b/test/extensions/filters/http/rbac/rbac_filter_test.cc index c8578f2e2622..93cacd425885 100644 --- a/test/extensions/filters/http/rbac/rbac_filter_test.cc +++ b/test/extensions/filters/http/rbac/rbac_filter_test.cc @@ -35,7 +35,8 @@ enum class LogResult { Yes, No, Undecided }; class RoleBasedAccessControlFilterTest : public testing::Test { public: - void setupPolicy(envoy::config::rbac::v3::RBAC::Action action) { + void setupPolicy(envoy::config::rbac::v3::RBAC::Action action, + std::string rules_stat_prefix = "") { envoy::extensions::filters::http::rbac::v3::RBAC config; envoy::config::rbac::v3::Policy policy; @@ -47,6 +48,7 @@ class RoleBasedAccessControlFilterTest : public testing::Test { policy.add_principals()->set_any(true); config.mutable_rules()->set_action(action); (*config.mutable_rules()->mutable_policies())["foo"] = policy; + config.set_rules_stat_prefix(rules_stat_prefix); envoy::config::rbac::v3::Policy shadow_policy; auto shadow_policy_rules = shadow_policy.add_permissions()->mutable_or_rules(); @@ -55,7 +57,7 @@ class RoleBasedAccessControlFilterTest : public testing::Test { shadow_policy.add_principals()->set_any(true); config.mutable_shadow_rules()->set_action(action); (*config.mutable_shadow_rules()->mutable_policies())["bar"] = shadow_policy; - config.set_shadow_rules_stat_prefix("prefix_"); + config.set_shadow_rules_stat_prefix("shadow_rules_prefix_"); setupConfig(std::make_shared( config, "test", *store_.rootScope(), context_, @@ -156,7 +158,7 @@ class RoleBasedAccessControlFilterTest : public testing::Test { TestUtility::loadFromYaml(fmt::format(shadow_matcher_yaml, action, on_no_match_action), shadow_matcher); *config.mutable_shadow_matcher() = shadow_matcher; - config.set_shadow_rules_stat_prefix("prefix_"); + config.set_shadow_rules_stat_prefix("shadow_rules_prefix_"); setupConfig(std::make_shared( config, "test", *store_.rootScope(), context_, @@ -238,7 +240,7 @@ class RoleBasedAccessControlFilterTest : public testing::Test { *config.mutable_matcher() = matcher; *config.mutable_shadow_matcher() = matcher; - config.set_shadow_rules_stat_prefix("prefix_"); + config.set_shadow_rules_stat_prefix("shadow_rules_prefix_"); setupConfig(std::make_shared( config, "test", *store_.rootScope(), context_, @@ -335,8 +337,9 @@ TEST_F(RoleBasedAccessControlFilterTest, Allowed) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -359,8 +362,9 @@ TEST_F(RoleBasedAccessControlFilterTest, RequestedServerName) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -404,12 +408,16 @@ TEST_F(RoleBasedAccessControlFilterTest, Denied) { EXPECT_EQ(1U, config_->stats().shadow_allowed_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); auto filter_meta = req_info_.dynamicMetadata().filter_metadata().at("envoy.filters.http.rbac"); - EXPECT_EQ("allowed", filter_meta.fields().at("prefix_shadow_engine_result").string_value()); - EXPECT_EQ("bar", filter_meta.fields().at("prefix_shadow_effective_policy_id").string_value()); + EXPECT_EQ("allowed", + filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); + EXPECT_EQ( + "bar", + filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); EXPECT_EQ("rbac_access_denied_matched_policy[none]", callbacks_.details()); checkAccessLogMetadata(LogResult::Undecided); } @@ -496,8 +504,9 @@ TEST_F(RoleBasedAccessControlFilterTest, MatcherAllowed) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -520,8 +529,9 @@ TEST_F(RoleBasedAccessControlFilterTest, RequestedServerNameMatcher) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -565,12 +575,16 @@ TEST_F(RoleBasedAccessControlFilterTest, MatcherDenied) { EXPECT_EQ(1U, config_->stats().shadow_allowed_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); auto filter_meta = req_info_.dynamicMetadata().filter_metadata().at("envoy.filters.http.rbac"); - EXPECT_EQ("allowed", filter_meta.fields().at("prefix_shadow_engine_result").string_value()); - EXPECT_EQ("bar", filter_meta.fields().at("prefix_shadow_effective_policy_id").string_value()); + EXPECT_EQ("allowed", + filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); + EXPECT_EQ( + "bar", + filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); EXPECT_EQ("rbac_access_denied_matched_policy[none]", callbacks_.details()); checkAccessLogMetadata(LogResult::Undecided); } @@ -618,8 +632,9 @@ TEST_F(RoleBasedAccessControlFilterTest, ShouldLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -639,8 +654,9 @@ TEST_F(RoleBasedAccessControlFilterTest, ShouldNotLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -660,8 +676,9 @@ TEST_F(RoleBasedAccessControlFilterTest, MatcherShouldLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -681,8 +698,9 @@ TEST_F(RoleBasedAccessControlFilterTest, MatcherShouldNotLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -691,6 +709,13 @@ TEST_F(RoleBasedAccessControlFilterTest, MatcherShouldNotLog) { checkAccessLogMetadata(LogResult::No); } +TEST_F(RoleBasedAccessControlFilterTest, RulesStatPrefix) { + setupPolicy(envoy::config::rbac::v3::RBAC::ALLOW, "rules_prefix_"); + + EXPECT_EQ("test.rbac.rules_prefix_.allowed", config_->stats().allowed_.name()); + EXPECT_EQ("test.rbac.rules_prefix_.denied", config_->stats().denied_.name()); +} + // Upstream Ip and Port matcher tests. class UpstreamIpPortMatcherTests : public RoleBasedAccessControlFilterTest { public: diff --git a/test/extensions/filters/network/rbac/filter_test.cc b/test/extensions/filters/network/rbac/filter_test.cc index df60598f379d..78b4d05d32ea 100644 --- a/test/extensions/filters/network/rbac/filter_test.cc +++ b/test/extensions/filters/network/rbac/filter_test.cc @@ -33,7 +33,7 @@ class RoleBasedAccessControlNetworkFilterTest : public testing::Test { envoy::extensions::filters::network::rbac::v3::RBAC config; config.set_stat_prefix("tcp."); - config.set_shadow_rules_stat_prefix("prefix_"); + config.set_shadow_rules_stat_prefix("shadow_rules_prefix_"); if (with_policy) { envoy::config::rbac::v3::Policy policy; @@ -69,7 +69,7 @@ class RoleBasedAccessControlNetworkFilterTest : public testing::Test { std::string on_no_match_action = "DENY") { envoy::extensions::filters::network::rbac::v3::RBAC config; config.set_stat_prefix("tcp."); - config.set_shadow_rules_stat_prefix("prefix_"); + config.set_shadow_rules_stat_prefix("shadow_rules_prefix_"); if (with_matcher) { constexpr absl::string_view matcher_yaml = R"EOF( @@ -250,8 +250,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithOneTimeEnforcement) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithContinuousEnforcement) { @@ -270,8 +271,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithContinuousEnforcement EXPECT_EQ(2U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, RequestedServerName) { @@ -291,8 +293,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, RequestedServerName) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithNoPolicy) { @@ -309,8 +312,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithNoPolicy) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, Denied) { @@ -330,13 +334,17 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, Denied) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); auto filter_meta = stream_info_.dynamicMetadata().filter_metadata().at(NetworkFilterNames::get().Rbac); - EXPECT_EQ("bar", filter_meta.fields().at("prefix_shadow_effective_policy_id").string_value()); - EXPECT_EQ("allowed", filter_meta.fields().at("prefix_shadow_engine_result").string_value()); + EXPECT_EQ( + "bar", + filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); + EXPECT_EQ("allowed", + filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherAllowedWithOneTimeEnforcement) { @@ -355,8 +363,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherAllowedWithOneTimeEnforce EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherAllowedWithContinuousEnforcement) { @@ -375,8 +384,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherAllowedWithContinuousEnfo EXPECT_EQ(2U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, RequestedServerNameMatcher) { @@ -396,8 +406,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, RequestedServerNameMatcher) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithNoMatcher) { @@ -414,8 +425,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithNoMatcher) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherDenied) { @@ -435,13 +447,17 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherDenied) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); auto filter_meta = stream_info_.dynamicMetadata().filter_metadata().at(NetworkFilterNames::get().Rbac); - EXPECT_EQ("bar", filter_meta.fields().at("prefix_shadow_effective_policy_id").string_value()); - EXPECT_EQ("allowed", filter_meta.fields().at("prefix_shadow_engine_result").string_value()); + EXPECT_EQ( + "bar", + filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); + EXPECT_EQ("allowed", + filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); } // Log Tests @@ -456,8 +472,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, ShouldLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); checkAccessLogMetadata(true); } @@ -473,8 +490,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, ShouldNotLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); checkAccessLogMetadata(false); } @@ -504,8 +522,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherShouldLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); checkAccessLogMetadata(true); } @@ -521,8 +540,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherShouldNotLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); checkAccessLogMetadata(false); } From d3c90ed4a92119fa20167e61750efafa687ae2e5 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Mon, 18 Mar 2024 22:07:30 -0400 Subject: [PATCH 080/124] filesystem: move addWatch to statusor (#32820) Risk Level: low Testing: updated tests Docs Changes: n/a Release Notes: n/a envoyproxy/envoy-mobile#176 Signed-off-by: Alyssa Wilk --- envoy/filesystem/watcher.h | 4 +- source/common/config/watched_directory.cc | 5 +- .../common/filesystem/inotify/watcher_impl.cc | 7 +- .../common/filesystem/inotify/watcher_impl.h | 2 +- .../common/filesystem/kqueue/watcher_impl.cc | 6 +- .../common/filesystem/kqueue/watcher_impl.h | 2 +- .../common/filesystem/win32/watcher_impl.cc | 9 ++- source/common/filesystem/win32/watcher_impl.h | 2 +- source/common/runtime/runtime_impl.cc | 8 +- source/common/secret/sds_api.cc | 6 +- .../zstd/common/dictionary_manager.h | 4 +- .../filesystem_subscription_impl.cc | 11 +-- .../injected_resource_monitor.cc | 4 +- test/common/config/watched_directory_test.cc | 3 +- test/common/filesystem/watcher_impl_test.cc | 77 +++++++++++-------- test/common/runtime/runtime_impl_test.cc | 15 ++++ test/common/secret/sds_api_test.cc | 4 + .../compression/zstd/zstd_compression_test.cc | 1 + .../filesystem_subscription_impl_test.cc | 7 +- .../filesystem_subscription_test_harness.h | 7 +- test/mocks/filesystem/mocks.h | 2 +- tools/code_format/config.yaml | 3 +- 22 files changed, 121 insertions(+), 68 deletions(-) diff --git a/envoy/filesystem/watcher.h b/envoy/filesystem/watcher.h index dd5c97b286e2..3cfd754a6cdb 100644 --- a/envoy/filesystem/watcher.h +++ b/envoy/filesystem/watcher.h @@ -8,6 +8,7 @@ #include "envoy/common/platform.h" #include "envoy/common/pure.h" +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" namespace Envoy { @@ -35,8 +36,9 @@ class Watcher { * for the given directory. * @param events supplies the events to watch. * @param cb supplies the callback to invoke when a change occurs. + * @return a failure status if the file does not exist */ - virtual void addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) PURE; + virtual absl::Status addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) PURE; }; using WatcherPtr = std::unique_ptr; diff --git a/source/common/config/watched_directory.cc b/source/common/config/watched_directory.cc index fc2a3a832a50..483cc08460d5 100644 --- a/source/common/config/watched_directory.cc +++ b/source/common/config/watched_directory.cc @@ -6,8 +6,9 @@ namespace Config { WatchedDirectory::WatchedDirectory(const envoy::config::core::v3::WatchedDirectory& config, Event::Dispatcher& dispatcher) { watcher_ = dispatcher.createFilesystemWatcher(); - watcher_->addWatch(absl::StrCat(config.path(), "/"), Filesystem::Watcher::Events::MovedTo, - [this](uint32_t) { cb_(); }); + THROW_IF_NOT_OK(watcher_->addWatch(absl::StrCat(config.path(), "/"), + Filesystem::Watcher::Events::MovedTo, + [this](uint32_t) { cb_(); })); } } // namespace Config diff --git a/source/common/filesystem/inotify/watcher_impl.cc b/source/common/filesystem/inotify/watcher_impl.cc index 864da79f5b59..1f53022ddcb7 100644 --- a/source/common/filesystem/inotify/watcher_impl.cc +++ b/source/common/filesystem/inotify/watcher_impl.cc @@ -32,23 +32,24 @@ WatcherImpl::WatcherImpl(Event::Dispatcher& dispatcher, Filesystem::Instance& fi WatcherImpl::~WatcherImpl() { close(inotify_fd_); } -void WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb callback) { +absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb callback) { // Because of general inotify pain, we always watch the directory that the file lives in, // and then synthetically raise per file events. auto result_or_error = file_system_.splitPathFromFilename(path); - THROW_IF_STATUS_NOT_OK(result_or_error, throw); + RETURN_IF_STATUS_NOT_OK(result_or_error); const PathSplitResult result = result_or_error.value(); const uint32_t watch_mask = IN_MODIFY | IN_MOVED_TO; int watch_fd = inotify_add_watch(inotify_fd_, std::string(result.directory_).c_str(), watch_mask); if (watch_fd == -1) { - throwEnvoyExceptionOrPanic( + return absl::InvalidArgumentError( fmt::format("unable to add filesystem watch for file {}: {}", path, errorDetails(errno))); } ENVOY_LOG(debug, "added watch for directory: '{}' file: '{}' fd: {}", result.directory_, result.file_, watch_fd); callback_map_[watch_fd].watches_.push_back({std::string(result.file_), events, callback}); + return absl::OkStatus(); } void WatcherImpl::onInotifyEvent() { diff --git a/source/common/filesystem/inotify/watcher_impl.h b/source/common/filesystem/inotify/watcher_impl.h index f2474b3c1d73..bf76f5f17de1 100644 --- a/source/common/filesystem/inotify/watcher_impl.h +++ b/source/common/filesystem/inotify/watcher_impl.h @@ -26,7 +26,7 @@ class WatcherImpl : public Watcher, Logger::Loggable { ~WatcherImpl() override; // Filesystem::Watcher - void addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; + absl::Status addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; private: struct FileWatch { diff --git a/source/common/filesystem/kqueue/watcher_impl.cc b/source/common/filesystem/kqueue/watcher_impl.cc index 200c08dc8104..ca20aab97a90 100644 --- a/source/common/filesystem/kqueue/watcher_impl.cc +++ b/source/common/filesystem/kqueue/watcher_impl.cc @@ -32,11 +32,13 @@ WatcherImpl::~WatcherImpl() { watches_.clear(); } -void WatcherImpl::addWatch(absl::string_view path, uint32_t events, Watcher::OnChangedCb cb) { +absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, + Watcher::OnChangedCb cb) { FileWatchPtr watch = addWatch(path, events, cb, false); if (watch == nullptr) { - throwEnvoyExceptionOrPanic(absl::StrCat("invalid watch path ", path)); + return absl::InvalidArgumentError(absl::StrCat("invalid watch path ", path)); } + return absl::OkStatus(); } WatcherImpl::FileWatchPtr WatcherImpl::addWatch(absl::string_view path, uint32_t events, diff --git a/source/common/filesystem/kqueue/watcher_impl.h b/source/common/filesystem/kqueue/watcher_impl.h index ba5d908a05a0..22e7f9f06dbc 100644 --- a/source/common/filesystem/kqueue/watcher_impl.h +++ b/source/common/filesystem/kqueue/watcher_impl.h @@ -27,7 +27,7 @@ class WatcherImpl : public Watcher, Logger::Loggable { ~WatcherImpl(); // Filesystem::Watcher - void addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; + absl::Status addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; private: struct FileWatch : LinkedObject { diff --git a/source/common/filesystem/win32/watcher_impl.cc b/source/common/filesystem/win32/watcher_impl.cc index 601c787461bf..6cb9d00a1bc7 100644 --- a/source/common/filesystem/win32/watcher_impl.cc +++ b/source/common/filesystem/win32/watcher_impl.cc @@ -50,13 +50,13 @@ WatcherImpl::~WatcherImpl() { ::CloseHandle(thread_exit_event_); } -void WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) { +absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) { if (path == Platform::null_device_path) { - return; + return absl::OkStatus(); } const absl::StatusOr result_or_error = file_system_.splitPathFromFilename(path); - THROW_IF_STATUS_NOT_OK(result_or_error, throw); + RETURN_IF_STATUS_NOT_OK(result_or_error); const PathSplitResult& result = result_or_error.value(); // ReadDirectoryChangesW only has a Unicode version, so we need // to use wide strings here @@ -67,7 +67,7 @@ void WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb directory.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); if (dir_handle == INVALID_HANDLE_VALUE) { - throw EnvoyException( + return absl::InvalidArgumentError( fmt::format("unable to open directory {}: {}", result.directory_, GetLastError())); } std::string fii_key(sizeof(FILE_ID_INFO), '\0'); @@ -108,6 +108,7 @@ void WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb callback_map_[fii_key]->watches_.push_back({file, events, cb}); ENVOY_LOG(debug, "added watch for file '{}' in directory '{}'", result.file_, result.directory_); + return absl::OkStatus(); } void WatcherImpl::onDirectoryEvent() { diff --git a/source/common/filesystem/win32/watcher_impl.h b/source/common/filesystem/win32/watcher_impl.h index ce6acd518b49..5431d5f3f0a2 100644 --- a/source/common/filesystem/win32/watcher_impl.h +++ b/source/common/filesystem/win32/watcher_impl.h @@ -31,7 +31,7 @@ class WatcherImpl : public Watcher, Logger::Loggable { ~WatcherImpl(); // Filesystem::Watcher - void addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; + absl::Status addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; private: static void issueFirstRead(ULONG_PTR param); diff --git a/source/common/runtime/runtime_impl.cc b/source/common/runtime/runtime_impl.cc index ab2843df4888..2dada0d1be29 100644 --- a/source/common/runtime/runtime_impl.cc +++ b/source/common/runtime/runtime_impl.cc @@ -576,8 +576,12 @@ LoaderImpl::LoaderImpl(Event::Dispatcher& dispatcher, ThreadLocal::SlotAllocator if (watcher_ == nullptr) { watcher_ = dispatcher.createFilesystemWatcher(); } - watcher_->addWatch(layer.disk_layer().symlink_root(), Filesystem::Watcher::Events::MovedTo, - [this](uint32_t) -> void { THROW_IF_NOT_OK(loadNewSnapshot()); }); + creation_status = watcher_->addWatch( + layer.disk_layer().symlink_root(), Filesystem::Watcher::Events::MovedTo, + [this](uint32_t) -> void { THROW_IF_NOT_OK(loadNewSnapshot()); }); + if (!creation_status.ok()) { + return; + } break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kRtdsLayer: subscriptions_.emplace_back( diff --git a/source/common/secret/sds_api.cc b/source/common/secret/sds_api.cc index 344da3ff5833..e378652c313c 100644 --- a/source/common/secret/sds_api.cc +++ b/source/common/secret/sds_api.cc @@ -122,9 +122,9 @@ absl::Status SdsApi::onConfigUpdate(const std::vectoraddWatch(absl::StrCat(result_or_error.value().directory_, "/"), - Filesystem::Watcher::Events::MovedTo, - [this](uint32_t) { onWatchUpdate(); }); + RETURN_IF_NOT_OK(watcher_->addWatch(absl::StrCat(result_or_error.value().directory_, "/"), + Filesystem::Watcher::Events::MovedTo, + [this](uint32_t) { onWatchUpdate(); })); } } else { watcher_.reset(); // Destroy the old watch if any diff --git a/source/extensions/compression/zstd/common/dictionary_manager.h b/source/extensions/compression/zstd/common/dictionary_manager.h index 45501345e5ec..43d8c4974281 100644 --- a/source/extensions/compression/zstd/common/dictionary_manager.h +++ b/source/extensions/compression/zstd/common/dictionary_manager.h @@ -44,9 +44,9 @@ template class envoy::config::core::v3::DataSource::SpecifierCase::kFilename) { is_watch_added = true; const auto& filename = source.filename(); - watcher_->addWatch( + THROW_IF_NOT_OK(watcher_->addWatch( filename, Filesystem::Watcher::Events::Modified | Filesystem::Watcher::Events::MovedTo, - [this, id, filename](uint32_t) { onDictionaryUpdate(id, filename); }); + [this, id, filename](uint32_t) { onDictionaryUpdate(id, filename); })); } } diff --git a/source/extensions/config_subscription/filesystem/filesystem_subscription_impl.cc b/source/extensions/config_subscription/filesystem/filesystem_subscription_impl.cc index bc33cecee33e..b149e552b398 100644 --- a/source/extensions/config_subscription/filesystem/filesystem_subscription_impl.cc +++ b/source/extensions/config_subscription/filesystem/filesystem_subscription_impl.cc @@ -27,11 +27,12 @@ FilesystemSubscriptionImpl::FilesystemSubscriptionImpl( stats_(stats), api_(api), validation_visitor_(validation_visitor) { if (!path_config_source.has_watched_directory()) { file_watcher_ = dispatcher.createFilesystemWatcher(); - file_watcher_->addWatch(path_, Filesystem::Watcher::Events::MovedTo, [this](uint32_t) { - if (started_) { - refresh(); - } - }); + THROW_IF_NOT_OK( + file_watcher_->addWatch(path_, Filesystem::Watcher::Events::MovedTo, [this](uint32_t) { + if (started_) { + refresh(); + } + })); } else { directory_watcher_ = std::make_unique(path_config_source.watched_directory(), dispatcher); diff --git a/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.cc b/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.cc index 23e7ab516d33..bb64744cda07 100644 --- a/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.cc +++ b/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.cc @@ -17,8 +17,8 @@ InjectedResourceMonitor::InjectedResourceMonitor( Server::Configuration::ResourceMonitorFactoryContext& context) : filename_(config.filename()), watcher_(context.mainThreadDispatcher().createFilesystemWatcher()), api_(context.api()) { - watcher_->addWatch(filename_, Filesystem::Watcher::Events::MovedTo, - [this](uint32_t) { onFileChanged(); }); + THROW_IF_NOT_OK(watcher_->addWatch(filename_, Filesystem::Watcher::Events::MovedTo, + [this](uint32_t) { onFileChanged(); })); } void InjectedResourceMonitor::onFileChanged() { file_changed_ = true; } diff --git a/test/common/config/watched_directory_test.cc b/test/common/config/watched_directory_test.cc index b22dd7dbb983..6ae512b7b481 100644 --- a/test/common/config/watched_directory_test.cc +++ b/test/common/config/watched_directory_test.cc @@ -7,6 +7,7 @@ #include "gtest/gtest.h" +using testing::DoAll; using testing::Return; using testing::SaveArg; @@ -21,7 +22,7 @@ TEST(WatchedDirectory, All) { EXPECT_CALL(dispatcher, createFilesystemWatcher_()).WillOnce(Return(watcher)); Filesystem::Watcher::OnChangedCb cb; EXPECT_CALL(*watcher, addWatch("foo/bar/", Filesystem::Watcher::Events::MovedTo, _)) - .WillOnce(SaveArg<2>(&cb)); + .WillOnce(DoAll(SaveArg<2>(&cb), Return(absl::OkStatus()))); WatchedDirectory wd(config, dispatcher); bool called = false; wd.setCallback([&called] { called = true; }); diff --git a/test/common/filesystem/watcher_impl_test.cc b/test/common/filesystem/watcher_impl_test.cc index d110ad4b8f25..29282ff84949 100644 --- a/test/common/filesystem/watcher_impl_test.cc +++ b/test/common/filesystem/watcher_impl_test.cc @@ -47,11 +47,14 @@ TEST_F(WatcherImplTest, All) { WatchCallback callback; EXPECT_CALL(callback, called(Watcher::Events::MovedTo)).Times(2); - watcher->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_link"), - Watcher::Events::MovedTo, [&](uint32_t events) -> void { - callback.called(events); - dispatcher_->exit(); - }); + ASSERT_TRUE(watcher + ->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_link"), + Watcher::Events::MovedTo, + [&](uint32_t events) -> void { + callback.called(events); + dispatcher_->exit(); + }) + .ok()); TestEnvironment::renameFile(TestEnvironment::temporaryPath("envoy_test/watcher_new_link"), TestEnvironment::temporaryPath("envoy_test/watcher_link")); dispatcher_->run(Event::Dispatcher::RunType::Block); @@ -75,11 +78,14 @@ TEST_F(WatcherImplTest, Create) { { std::ofstream file(TestEnvironment::temporaryPath("envoy_test/watcher_target")); } WatchCallback callback; - watcher->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_link"), - Watcher::Events::MovedTo, [&](uint32_t events) -> void { - callback.called(events); - dispatcher_->exit(); - }); + ASSERT_TRUE(watcher + ->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_link"), + Watcher::Events::MovedTo, + [&](uint32_t events) -> void { + callback.called(events); + dispatcher_->exit(); + }) + .ok()); { std::ofstream file(TestEnvironment::temporaryPath("envoy_test/other_file")); } dispatcher_->run(Event::Dispatcher::RunType::NonBlock); @@ -99,11 +105,14 @@ TEST_F(WatcherImplTest, Modify) { std::ofstream file(TestEnvironment::temporaryPath("envoy_test/watcher_target")); WatchCallback callback; - watcher->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_target"), - Watcher::Events::Modified, [&](uint32_t events) -> void { - callback.called(events); - dispatcher_->exit(); - }); + ASSERT_TRUE(watcher + ->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_target"), + Watcher::Events::Modified, + [&](uint32_t events) -> void { + callback.called(events); + dispatcher_->exit(); + }) + .ok()); dispatcher_->run(Event::Dispatcher::RunType::NonBlock); file << "text" << std::flush; @@ -115,13 +124,14 @@ TEST_F(WatcherImplTest, Modify) { TEST_F(WatcherImplTest, BadPath) { Filesystem::WatcherPtr watcher = dispatcher_->createFilesystemWatcher(); - EXPECT_THROW( - watcher->addWatch("this_is_not_a_file", Watcher::Events::MovedTo, [&](uint32_t) -> void {}), - EnvoyException); + EXPECT_FALSE( + watcher->addWatch("this_is_not_a_file", Watcher::Events::MovedTo, [&](uint32_t) -> void {}) + .ok()); - EXPECT_THROW(watcher->addWatch("this_is_not_a_dir/file", Watcher::Events::MovedTo, - [&](uint32_t) -> void {}), - EnvoyException); + EXPECT_FALSE( + watcher + ->addWatch("this_is_not_a_dir/file", Watcher::Events::MovedTo, [&](uint32_t) -> void {}) + .ok()); } TEST_F(WatcherImplTest, ParentDirectoryRemoved) { @@ -132,9 +142,11 @@ TEST_F(WatcherImplTest, ParentDirectoryRemoved) { WatchCallback callback; EXPECT_CALL(callback, called(testing::_)).Times(0); - watcher->addWatch(TestEnvironment::temporaryPath("envoy_test_empty/watcher_link"), - Watcher::Events::MovedTo, - [&](uint32_t events) -> void { callback.called(events); }); + ASSERT_TRUE(watcher + ->addWatch(TestEnvironment::temporaryPath("envoy_test_empty/watcher_link"), + Watcher::Events::MovedTo, + [&](uint32_t events) -> void { callback.called(events); }) + .ok()); int rc = rmdir(TestEnvironment::temporaryPath("envoy_test_empty").c_str()); EXPECT_EQ(0, rc); @@ -146,9 +158,9 @@ TEST_F(WatcherImplTest, RootDirectoryPath) { Filesystem::WatcherPtr watcher = dispatcher_->createFilesystemWatcher(); #ifndef WIN32 - EXPECT_NO_THROW(watcher->addWatch("/", Watcher::Events::MovedTo, [&](uint32_t) -> void {})); + EXPECT_TRUE(watcher->addWatch("/", Watcher::Events::MovedTo, [&](uint32_t) -> void {}).ok()); #else - EXPECT_NO_THROW(watcher->addWatch("c:\\", Watcher::Events::MovedTo, [&](uint32_t) -> void {})); + EXPECT_TRUE(watcher->addWatch("c:\\", Watcher::Events::MovedTo, [&](uint32_t) -> void {}).ok()); #endif } @@ -169,11 +181,14 @@ TEST_F(WatcherImplTest, SymlinkAtomicRename) { WatchCallback callback; EXPECT_CALL(callback, called(Watcher::Events::MovedTo)); - watcher->addWatch(TestEnvironment::temporaryPath("envoy_test/"), Watcher::Events::MovedTo, - [&](uint32_t events) -> void { - callback.called(events); - dispatcher_->exit(); - }); + ASSERT_TRUE(watcher + ->addWatch(TestEnvironment::temporaryPath("envoy_test/"), + Watcher::Events::MovedTo, + [&](uint32_t events) -> void { + callback.called(events); + dispatcher_->exit(); + }) + .ok()); TestEnvironment::createPath(TestEnvironment::temporaryPath("envoy_test/..timestamp2")); { std::ofstream file(TestEnvironment::temporaryPath("envoy_test/..timestamp2/watched_file")); } diff --git a/test/common/runtime/runtime_impl_test.cc b/test/common/runtime/runtime_impl_test.cc index b3bfb11fa75d..54cebe55ecfb 100644 --- a/test/common/runtime/runtime_impl_test.cc +++ b/test/common/runtime/runtime_impl_test.cc @@ -55,6 +55,7 @@ class LoaderImplTest : public testing::Test { Invoke([this](absl::string_view path, uint32_t, Filesystem::Watcher::OnChangedCb cb) { EXPECT_EQ(path, expected_watch_root_); on_changed_cbs_.emplace_back(cb); + return absl::OkStatus(); })); return mock_watcher; })); @@ -97,6 +98,7 @@ class DiskLoaderImplTest : public LoaderImplTest { absl::Status creation_status; loader_ = std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, store_, generator_, validation_visitor_, *api_, creation_status); + THROW_IF_NOT_OK(creation_status); } void write(const std::string& path, const std::string& value) { @@ -329,6 +331,19 @@ TEST_F(DiskLoaderImplTest, OverrideFolderDoesNotExist) { EXPECT_EQ(1, store_.counter("runtime.override_dir_not_exists").value()); } +TEST_F(DiskLoaderImplTest, FileDoesNotExist) { + EXPECT_CALL(dispatcher_, createFilesystemWatcher_()).WillRepeatedly(InvokeWithoutArgs([] { + Filesystem::MockWatcher* mock_watcher = new NiceMock(); + EXPECT_CALL(*mock_watcher, addWatch(_, Filesystem::Watcher::Events::MovedTo, _)) + .WillRepeatedly(Return(absl::InvalidArgumentError("file does not exist"))); + return mock_watcher; + })); + + EXPECT_THROW_WITH_MESSAGE( + run("test/common/runtime/test_data/current", "envoy_override_does_not_exist"), EnvoyException, + "file does not exist"); +} + TEST_F(DiskLoaderImplTest, PercentHandling) { setup(); run("test/common/runtime/test_data/current", "envoy_override"); diff --git a/test/common/secret/sds_api_test.cc b/test/common/secret/sds_api_test.cc index edc3327cbfb9..8d5e933f6e46 100644 --- a/test/common/secret/sds_api_test.cc +++ b/test/common/secret/sds_api_test.cc @@ -248,6 +248,7 @@ class TlsCertificateSdsRotationApiTest : public testing::TestWithParam, .WillOnce( Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { watch_cbs_.push_back(cb); + return absl::OkStatus(); })); EXPECT_CALL(filesystem_, fileReadToEnd(cert_path_)).WillOnce(Return(cert_value)); EXPECT_CALL(filesystem_, fileReadToEnd(key_path_)).WillOnce(Return(key_value)); @@ -262,6 +263,7 @@ class TlsCertificateSdsRotationApiTest : public testing::TestWithParam, .WillRepeatedly( Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { watch_cbs_.push_back(cb); + return absl::OkStatus(); })); } EXPECT_TRUE( @@ -318,10 +320,12 @@ class CertificateValidationContextSdsRotationApiTest : public testing::TestWithP EXPECT_CALL(*watcher, addWatch(watch_path, Filesystem::Watcher::Events::MovedTo, _)) .WillOnce(Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { watch_cbs_.push_back(cb); + return absl::OkStatus(); })); EXPECT_CALL(*watcher, addWatch(watch_path, Filesystem::Watcher::Events::MovedTo, _)) .WillOnce(Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { watch_cbs_.push_back(cb); + return absl::OkStatus(); })); EXPECT_TRUE( subscription_factory_.callbacks_->onConfigUpdate(decoded_resources.refvec_, "").ok()); diff --git a/test/extensions/compression/zstd/zstd_compression_test.cc b/test/extensions/compression/zstd/zstd_compression_test.cc index 25a5b3efcaac..f6ece8fbf7f7 100644 --- a/test/extensions/compression/zstd/zstd_compression_test.cc +++ b/test/extensions/compression/zstd/zstd_compression_test.cc @@ -49,6 +49,7 @@ class ZstdCompressionTest { .WillRepeatedly( Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { watch_cbs_.push_back(cb); + return absl::OkStatus(); })); return mock_watcher; })); diff --git a/test/extensions/config_subscription/filesystem/filesystem_subscription_impl_test.cc b/test/extensions/config_subscription/filesystem/filesystem_subscription_impl_test.cc index e358f635a989..ae959a94a79d 100644 --- a/test/extensions/config_subscription/filesystem/filesystem_subscription_impl_test.cc +++ b/test/extensions/config_subscription/filesystem/filesystem_subscription_impl_test.cc @@ -107,8 +107,11 @@ class FilesystemCollectionSubscriptionImplTest : public testing::Test, EXPECT_CALL(*dispatcher, createFilesystemWatcher_()).WillOnce(InvokeWithoutArgs([this] { Filesystem::MockWatcher* mock_watcher = new Filesystem::MockWatcher(); EXPECT_CALL(*mock_watcher, addWatch(path_.path(), Filesystem::Watcher::Events::MovedTo, _)) - .WillOnce(Invoke([this](absl::string_view, uint32_t, - Filesystem::Watcher::OnChangedCb cb) { on_changed_cb_ = cb; })); + .WillOnce( + Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { + on_changed_cb_ = cb; + return absl::OkStatus(); + })); return mock_watcher; })); return dispatcher; diff --git a/test/extensions/config_subscription/filesystem/filesystem_subscription_test_harness.h b/test/extensions/config_subscription/filesystem/filesystem_subscription_test_harness.h index a91fbc5e4a1b..14a4d6d08743 100644 --- a/test/extensions/config_subscription/filesystem/filesystem_subscription_test_harness.h +++ b/test/extensions/config_subscription/filesystem/filesystem_subscription_test_harness.h @@ -46,8 +46,11 @@ class FilesystemSubscriptionTestHarness : public SubscriptionTestHarness { EXPECT_CALL(*dispatcher, createFilesystemWatcher_()).WillOnce(InvokeWithoutArgs([this] { Filesystem::MockWatcher* mock_watcher = new Filesystem::MockWatcher(); EXPECT_CALL(*mock_watcher, addWatch(path_.path(), Filesystem::Watcher::Events::MovedTo, _)) - .WillOnce(Invoke([this](absl::string_view, uint32_t, - Filesystem::Watcher::OnChangedCb cb) { on_changed_cb_ = cb; })); + .WillOnce( + Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { + on_changed_cb_ = cb; + return absl::OkStatus(); + })); return mock_watcher; })); return dispatcher; diff --git a/test/mocks/filesystem/mocks.h b/test/mocks/filesystem/mocks.h index 524cefd269f3..7032ce506d4f 100644 --- a/test/mocks/filesystem/mocks.h +++ b/test/mocks/filesystem/mocks.h @@ -75,7 +75,7 @@ class MockWatcher : public Watcher { MockWatcher(); ~MockWatcher() override; - MOCK_METHOD(void, addWatch, (absl::string_view, uint32_t, OnChangedCb)); + MOCK_METHOD(absl::Status, addWatch, (absl::string_view, uint32_t, OnChangedCb)); }; } // namespace Filesystem diff --git a/tools/code_format/config.yaml b/tools/code_format/config.yaml index 01d341738e95..9e8952000b44 100644 --- a/tools/code_format/config.yaml +++ b/tools/code_format/config.yaml @@ -139,11 +139,9 @@ paths: - source/common/router/config_impl.cc - source/common/router/scoped_config_impl.cc - source/common/router/header_parser.cc - - source/common/filesystem/inotify/watcher_impl.cc - source/common/filesystem/posix/directory_iterator_impl.cc - source/common/filesystem/kqueue/watcher_impl.cc - source/common/filesystem/win32/directory_iterator_impl.cc - - source/common/filesystem/win32/watcher_impl.cc - source/common/common/utility.cc - source/common/common/regex.cc - source/common/common/matchers.cc @@ -172,6 +170,7 @@ paths: - source/common/local_reply/local_reply.cc - source/common/tls/context_impl.cc - source/common/tls/context_config_impl.cc + - source/common/config/watched_directory.cc # Only one C++ file should instantiate grpc_init grpc_init: From 1b768b49f9a85a865c35735a4944a001975055e9 Mon Sep 17 00:00:00 2001 From: ohadvano <49730675+ohadvano@users.noreply.github.com> Date: Tue, 19 Mar 2024 04:42:27 +0200 Subject: [PATCH 081/124] access_logs: extract commands parsing to a separate method (#32944) * extract commands parsing to a separate method Signed-off-by: ohadvano * fix format Signed-off-by: ohadvano --------- Signed-off-by: ohadvano --- .../formatter/substitution_format_string.h | 31 +++-- .../substitution_format_string_test.cc | 106 ++++++++++++++++++ 2 files changed, 129 insertions(+), 8 deletions(-) diff --git a/source/common/formatter/substitution_format_string.h b/source/common/formatter/substitution_format_string.h index 3cdd032a9eee..fc1d6f5e6e83 100644 --- a/source/common/formatter/substitution_format_string.h +++ b/source/common/formatter/substitution_format_string.h @@ -22,16 +22,18 @@ namespace Formatter { */ class SubstitutionFormatStringUtils { public: + using FormattersConfig = + ProtobufWkt::RepeatedPtrField; + /** - * Generate a formatter object from config SubstitutionFormatString. + * Parse list of formatter configurations to commands. */ template - static FormatterBasePtr - fromProtoConfig(const envoy::config::core::v3::SubstitutionFormatString& config, + static std::vector> + parseFormatters(const FormattersConfig& formatters, Server::Configuration::GenericFactoryContext& context) { - // Instantiate formatter extensions. std::vector> commands; - for (const auto& formatter : config.formatters()) { + for (const auto& formatter : formatters) { auto* factory = Envoy::Config::Utility::getFactory>(formatter); if (!factory) { @@ -47,12 +49,24 @@ class SubstitutionFormatStringUtils { commands.push_back(std::move(parser)); } + return commands; + } + + /** + * Generate a formatter object from config SubstitutionFormatString. + */ + template + static FormatterBasePtr + fromProtoConfig(const envoy::config::core::v3::SubstitutionFormatString& config, + Server::Configuration::GenericFactoryContext& context) { + // Instantiate formatter extensions. + auto commands = parseFormatters(config.formatters(), context); switch (config.format_case()) { case envoy::config::core::v3::SubstitutionFormatString::FormatCase::kTextFormat: return std::make_unique>( config.text_format(), config.omit_empty_values(), commands); case envoy::config::core::v3::SubstitutionFormatString::FormatCase::kJsonFormat: - return std::make_unique>( + return createJsonFormatter( config.json_format(), true, config.omit_empty_values(), config.has_json_format_options() ? config.json_format_options().sort_properties() : false, commands); @@ -75,9 +89,10 @@ class SubstitutionFormatStringUtils { template static FormatterBasePtr createJsonFormatter(const ProtobufWkt::Struct& struct_format, bool preserve_types, - bool omit_empty_values, bool sort_properties) { + bool omit_empty_values, bool sort_properties, + const std::vector>& commands = {}) { return std::make_unique>( - struct_format, preserve_types, omit_empty_values, sort_properties); + struct_format, preserve_types, omit_empty_values, sort_properties, commands); } }; diff --git a/test/common/formatter/substitution_format_string_test.cc b/test/common/formatter/substitution_format_string_test.cc index 4f012f7aa4e8..6d74620d2a0e 100644 --- a/test/common/formatter/substitution_format_string_test.cc +++ b/test/common/formatter/substitution_format_string_test.cc @@ -212,5 +212,111 @@ TEST_F(SubstitutionFormatStringUtilsTest, TestFromProtoConfigJsonWithMultipleExt EXPECT_TRUE(TestUtility::jsonStringEqual(out_json, expected)); } +TEST_F(SubstitutionFormatStringUtilsTest, TestParseFormattersWithUnknownExtension) { + const std::string yaml = R"EOF( + name: envoy.formatter.TestFormatterUnknown + typed_config: + "@type": type.googleapis.com/google.protobuf.Any + )EOF"; + + SubstitutionFormatStringUtils::FormattersConfig config; + auto* entry1 = config.Add(); + envoy::config::core::v3::TypedExtensionConfig proto; + TestUtility::loadFromYaml(yaml, proto); + *entry1 = proto; + + EXPECT_THROW_WITH_MESSAGE(SubstitutionFormatStringUtils::parseFormatters(config, context_), + EnvoyException, + "Formatter not found: envoy.formatter.TestFormatterUnknown"); +} + +TEST_F(SubstitutionFormatStringUtilsTest, TestParseFormattersWithInvalidFormatter) { + FailCommandFactory fail_factory; + Registry::InjectFactory command_register(fail_factory); + + const std::string yaml = R"EOF( + name: envoy.formatter.FailFormatter + typed_config: + "@type": type.googleapis.com/google.protobuf.UInt64Value + )EOF"; + + SubstitutionFormatStringUtils::FormattersConfig config; + auto* entry1 = config.Add(); + envoy::config::core::v3::TypedExtensionConfig proto; + TestUtility::loadFromYaml(yaml, proto); + *entry1 = proto; + + EXPECT_THROW_WITH_MESSAGE(SubstitutionFormatStringUtils::parseFormatters(config, context_), + EnvoyException, + "Failed to create command parser: envoy.formatter.FailFormatter"); +} + +TEST_F(SubstitutionFormatStringUtilsTest, TestParseFormattersWithSingleExtension) { + TestCommandFactory factory; + Registry::InjectFactory command_register(factory); + + const std::string yaml = R"EOF( + name: envoy.formatter.TestFormatter + typed_config: + "@type": type.googleapis.com/google.protobuf.StringValue + )EOF"; + + SubstitutionFormatStringUtils::FormattersConfig config; + auto* entry1 = config.Add(); + envoy::config::core::v3::TypedExtensionConfig proto; + TestUtility::loadFromYaml(yaml, proto); + *entry1 = proto; + + auto commands = SubstitutionFormatStringUtils::parseFormatters(config, context_); + ASSERT_EQ(1, commands.size()); + + absl::optional max_length = {}; + ASSERT_TRUE(commands[0] != nullptr); + auto provider = commands[0]->parse("COMMAND_EXTENSION", "", max_length); + ASSERT_TRUE(provider != nullptr); +} + +TEST_F(SubstitutionFormatStringUtilsTest, TestParseFormattersWithMultipleExtensions) { + TestCommandFactory factory; + Registry::InjectFactory command_register(factory); + AdditionalCommandFactory additional_factory; + Registry::InjectFactory additional_command_register(additional_factory); + + const std::string test_command_yaml = R"EOF( + name: envoy.formatter.TestFormatter + typed_config: + "@type": type.googleapis.com/google.protobuf.StringValue + )EOF"; + + const std::string additional_command_yaml = R"EOF( + name: envoy.formatter.AdditionalFormatter + typed_config: + "@type": type.googleapis.com/google.protobuf.UInt32Value + )EOF"; + + SubstitutionFormatStringUtils::FormattersConfig config; + + auto* entry1 = config.Add(); + envoy::config::core::v3::TypedExtensionConfig test_command_proto; + TestUtility::loadFromYaml(test_command_yaml, test_command_proto); + *entry1 = test_command_proto; + + auto* entry2 = config.Add(); + envoy::config::core::v3::TypedExtensionConfig additional_command_proto; + TestUtility::loadFromYaml(additional_command_yaml, additional_command_proto); + *entry2 = additional_command_proto; + + auto commands = SubstitutionFormatStringUtils::parseFormatters(config, context_); + ASSERT_EQ(2, commands.size()); + + absl::optional max_length = {}; + ASSERT_TRUE(commands[0] != nullptr); + auto test_command_provider = commands[0]->parse("COMMAND_EXTENSION", "", max_length); + ASSERT_TRUE(test_command_provider != nullptr); + ASSERT_TRUE(commands[1] != nullptr); + auto additional_command_provider = commands[1]->parse("ADDITIONAL_EXTENSION", "", max_length); + ASSERT_TRUE(additional_command_provider != nullptr); +} + } // namespace Formatter } // namespace Envoy From ffcc257e16c9046b2fec7497a6bf9293d8ada286 Mon Sep 17 00:00:00 2001 From: code Date: Tue, 19 Mar 2024 21:22:20 +0800 Subject: [PATCH 082/124] generic proxy: complete the development of HTTP1 codec (#32488) * generic proxy: complete the development of HTTP1 codec Signed-off-by: wbpcode * minor update Signed-off-by: wbpcode * add TODOs Signed-off-by: wbpcode * more validation and single frame mode for HTTP Signed-off-by: wbpcode * more test and validation Signed-off-by: wbpcode * handle the 100 continue and the 1xx response Signed-off-by: wbpcode * minor update Signed-off-by: wbpcode * address comments Signed-off-by: wbpcode * address comments Signed-off-by: wbpcode --------- Signed-off-by: wbpcode --- api/BUILD | 1 + .../generic_proxy/codecs/http1/v3/BUILD | 12 + .../generic_proxy/codecs/http1/v3/http1.proto | 49 + api/versioning/BUILD | 1 + contrib/contrib_build_config.bzl | 1 + contrib/extensions_metadata.yaml | 7 + .../network/source/codecs/dubbo/config.h | 3 +- .../filters/network/source/codecs/http1/BUILD | 28 + .../network/source/codecs/http1/config.cc | 653 ++++++++ .../network/source/codecs/http1/config.h | 394 +++++ .../filters/network/source/router/router.cc | 37 +- .../filters/network/source/router/router.h | 3 +- .../filters/network/source/upstream.cc | 40 +- .../filters/network/source/upstream.h | 1 - .../filters/network/test/codecs/http1/BUILD | 21 + .../network/test/codecs/http1/config_test.cc | 1482 +++++++++++++++++ tools/proto_format/format_api.py | 1 + 17 files changed, 2694 insertions(+), 40 deletions(-) create mode 100644 api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/BUILD create mode 100644 api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/http1.proto create mode 100644 contrib/generic_proxy/filters/network/source/codecs/http1/BUILD create mode 100644 contrib/generic_proxy/filters/network/source/codecs/http1/config.cc create mode 100644 contrib/generic_proxy/filters/network/source/codecs/http1/config.h create mode 100644 contrib/generic_proxy/filters/network/test/codecs/http1/BUILD create mode 100644 contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc diff --git a/api/BUILD b/api/BUILD index 829715418693..613bcf064591 100644 --- a/api/BUILD +++ b/api/BUILD @@ -83,6 +83,7 @@ proto_library( "//contrib/envoy/extensions/filters/network/client_ssl_auth/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/action/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/dubbo/v3:pkg", + "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/kafka/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/matcher/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/router/v3:pkg", diff --git a/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/BUILD b/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/BUILD new file mode 100644 index 000000000000..d49202b74ab4 --- /dev/null +++ b/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/BUILD @@ -0,0 +1,12 @@ +# 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 = [ + "@com_github_cncf_xds//udpa/annotations:pkg", + "@com_github_cncf_xds//xds/annotations/v3:pkg", + ], +) diff --git a/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/http1.proto b/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/http1.proto new file mode 100644 index 000000000000..973a190d8138 --- /dev/null +++ b/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/http1.proto @@ -0,0 +1,49 @@ +syntax = "proto3"; + +package envoy.extensions.filters.network.generic_proxy.codecs.http1.v3; + +import "google/protobuf/wrappers.proto"; + +import "xds/annotations/v3/status.proto"; + +import "udpa/annotations/status.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.network.generic_proxy.codecs.http1.v3"; +option java_outer_classname = "Http1Proto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3;http1v3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; +option (xds.annotations.v3.file_status).work_in_progress = true; + +// [#protodoc-title: HTTP1 codec configuration for Generic Proxy] +// [#extension: envoy.generic_proxy.codecs.http1] + +// Configuration for HTTP codec. This HTTP1 codec is used to parse and serialize HTTP1 messages +// for the generic proxy filter. +// Any decoding error will result in the generic proxy closing the connection. +// +// .. note:: +// This codec only supports HTTP1.1 messages and does not support HTTP1.0 messages. And it limits +// part of the HTTP1.1 features, such as upgrade, connect, etc. +// This codec is mainly designed for the features evaluation of the generic proxy filter. Please +// be cautious when using it in production. +message Http1CodecConfig { + // If true, the codec will parse and serialize HTTP1 messages in a single frame per message. + // + // A frame is a minimal unit of data that can be processed by the generic proxy. If false, the + // codec will parse and serialize HTTP1 messages in a streaming way. In this case, the codec + // will output multiple frames for a single HTTP1 message to the generic proxy. + // If true, the codec will buffer the entire HTTP1 message body before sending it to the generic + // proxy. This may have better performance in small message scenarios and is more friendly to + // handle the HTTP1 message body. This also may result in higher memory usage and latency if + // the message body is large. + // + // Default is true. + google.protobuf.BoolValue single_frame_mode = 1; + + // The maximum size of the HTTP1 message body in bytes. If not set, 8*1024*1024 (8MB) is used. + // This only makes sense when single_frame_mode is true. + // If the HTTP1 message body size exceeds this value, this will result in a decoding error + // and the generic proxy will close the connection. + google.protobuf.UInt32Value max_buffer_size = 2; +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index b74e3df2a867..56952b6ff2ad 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -21,6 +21,7 @@ proto_library( "//contrib/envoy/extensions/filters/network/client_ssl_auth/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/action/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/dubbo/v3:pkg", + "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/kafka/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/matcher/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/router/v3:pkg", diff --git a/contrib/contrib_build_config.bzl b/contrib/contrib_build_config.bzl index 49b1f7afde79..7be1cd02ffad 100644 --- a/contrib/contrib_build_config.bzl +++ b/contrib/contrib_build_config.bzl @@ -73,6 +73,7 @@ CONTRIB_EXTENSIONS = { # "envoy.filters.generic.router": "//contrib/generic_proxy/filters/network/source/router:config", "envoy.generic_proxy.codecs.dubbo": "//contrib/generic_proxy/filters/network/source/codecs/dubbo:config", + "envoy.generic_proxy.codecs.http1": "//contrib/generic_proxy/filters/network/source/codecs/http1:config", "envoy.generic_proxy.codecs.kafka": "//contrib/generic_proxy/filters/network/source/codecs/kafka:config", # diff --git a/contrib/extensions_metadata.yaml b/contrib/extensions_metadata.yaml index d8aee9d710fb..76218e5a2b85 100644 --- a/contrib/extensions_metadata.yaml +++ b/contrib/extensions_metadata.yaml @@ -144,6 +144,13 @@ envoy.generic_proxy.codecs.kafka: status: wip type_urls: - envoy.extensions.filters.network.generic_proxy.codecs.kafka.v3.KafkaCodecConfig +envoy.generic_proxy.codecs.http1: + categories: + - envoy.generic_proxy.codecs + security_posture: requires_trusted_downstream_and_upstream + status: wip + type_urls: + - envoy.extensions.filters.network.generic_proxy.codecs.http1.v3.Http1CodecConfig envoy.router.cluster_specifier_plugin.golang: categories: - envoy.router.cluster_specifier_plugin diff --git a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h index 6ef3932be244..86acc6f2aa9c 100644 --- a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h +++ b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h @@ -151,8 +151,7 @@ class DubboServerCodec public: using DubboDecoderBase::DubboDecoderBase; - ResponsePtr respond(absl::Status status, absl::string_view short_response_flags, - const Request& request) override; + ResponsePtr respond(absl::Status status, absl::string_view data, const Request& request) override; }; class DubboClientCodec diff --git a/contrib/generic_proxy/filters/network/source/codecs/http1/BUILD b/contrib/generic_proxy/filters/network/source/codecs/http1/BUILD new file mode 100644 index 000000000000..adbc830baa9d --- /dev/null +++ b/contrib/generic_proxy/filters/network/source/codecs/http1/BUILD @@ -0,0 +1,28 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_contrib_extension", + "envoy_contrib_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_contrib_package() + +envoy_cc_contrib_extension( + name = "config", + srcs = [ + "config.cc", + ], + hdrs = [ + "config.h", + ], + deps = [ + "//contrib/generic_proxy/filters/network/source/interface:codec_interface", + "//source/common/http:codes_lib", + "//source/common/http:header_utility_lib", + "//source/common/http:headers_lib", + "//source/common/http:utility_lib", + "//source/common/http/http1:balsa_parser_lib", + "@envoy_api//contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3:pkg_cc_proto", + ], +) diff --git a/contrib/generic_proxy/filters/network/source/codecs/http1/config.cc b/contrib/generic_proxy/filters/network/source/codecs/http1/config.cc new file mode 100644 index 000000000000..c16287b4db60 --- /dev/null +++ b/contrib/generic_proxy/filters/network/source/codecs/http1/config.cc @@ -0,0 +1,653 @@ +#include "contrib/generic_proxy/filters/network/source/codecs/http1/config.h" + +#include "source/common/http/codes.h" +#include "source/common/http/header_utility.h" +#include "source/common/http/utility.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace GenericProxy { +namespace Codec { +namespace Http1 { + +static constexpr absl::string_view CRLF = "\r\n"; +// Last chunk as defined here https://tools.ietf.org/html/rfc7230#section-4.1 +static constexpr absl::string_view LAST_CHUNK = "0\r\n"; + +static constexpr absl::string_view SPACE = " "; +static constexpr absl::string_view COLON_SPACE = ": "; + +static constexpr absl::string_view REQUEST_POSTFIX = " HTTP/1.1\r\n"; +static constexpr absl::string_view RESPONSE_PREFIX = "HTTP/1.1 "; + +static constexpr absl::string_view HOST_HEADER_PREFIX = ":a"; + +static constexpr uint32_t DEFAULT_MAX_BUFFER_SIZE = 8 * 1024 * 1024; + +static constexpr absl::string_view _100_CONTINUE_RESPONSE = "HTTP/1.1 100 Continue\r\n" + "content-length: 0\r\n" + "\r\n"; + +void encodeNormalHeaders(Buffer::Instance& buffer, const Http::RequestOrResponseHeaderMap& headers, + bool chunk_encoding) { + headers.iterate([&buffer](const Http::HeaderEntry& header) { + absl::string_view key_to_use = header.key().getStringView(); + // Translate :authority -> host so that upper layers do not need to deal with this. + if (absl::StartsWith(key_to_use, HOST_HEADER_PREFIX)) { + key_to_use = Http::Headers::get().HostLegacy; + } + + // Skip all headers starting with ':' that make it here. + if (key_to_use[0] == ':') { + return Http::HeaderMap::Iterate::Continue; + } + + buffer.addFragments({key_to_use, COLON_SPACE, header.value().getStringView(), CRLF}); + return Http::HeaderMap::Iterate::Continue; + }); + + if (chunk_encoding) { + if (headers.TransferEncoding() == nullptr) { + buffer.add("Transfer-Encoding: chunked\r\n"); + } + } +} + +absl::Status validateCommonHeaders(const Http::RequestOrResponseHeaderMap& headers) { + // Both Transfer-Encoding and Content-Length are set. + if (headers.TransferEncoding() != nullptr && headers.ContentLength() != nullptr) { + return absl::InvalidArgumentError("Both transfer-encoding and content-length are set"); + } + + // Transfer-Encoding is not chunked. + if (headers.TransferEncoding() != nullptr) { + absl::string_view encoding = headers.TransferEncoding()->value().getStringView(); + if (!absl::EqualsIgnoreCase(encoding, Http::Headers::get().TransferEncodingValues.Chunked)) { + return absl::InvalidArgumentError("transfer-encoding is not chunked"); + } + } + + return absl::OkStatus(); +} + +bool Utility::isChunked(const Http::RequestOrResponseHeaderMap& headers, bool bodiless) { + // If the message has body and the content length is not set, then treat it as chunked. + // Note all upgrade and connect requests are rejected so this is safe. + return !bodiless && headers.ContentLength() == nullptr; +} + +bool Utility::hasBody(const Envoy::Http::Http1::Parser& parser, bool response, + bool response_for_head_request) { + // Response for HEAD request should not have body. + if (response_for_head_request) { + ASSERT(response); + return false; + } + + // 1xx, 204, 304 responses should not have body. + if (response) { + const Envoy::Http::Code code = parser.statusCode(); + if (code < Http::Code::OK || code == Http::Code::NoContent || code == Http::Code::NotModified) { + return false; + } + } + + // Check the transfer-encoding and content-length headers in other cases. + return parser.isChunked() || parser.contentLength().value_or(0) > 0; +} + +absl::Status Utility::validateRequestHeaders(Http::RequestHeaderMap& headers) { + // No upgrade and connect support for now. + // TODO(wbpcode): add support for upgrade and connect in the future. + if (Http::Utility::isUpgrade(headers) || + headers.getMethodValue() == Http::Headers::get().MethodValues.Connect) { + return absl::InvalidArgumentError("upgrade or connect are not supported"); + } + + if (auto status = validateCommonHeaders(headers); !status.ok()) { + return status; + } + + // One of method, path, host is missing. + if (headers.Method() == nullptr || headers.Path() == nullptr || headers.Host() == nullptr) { + return absl::InvalidArgumentError("missing required headers"); + } + + return absl::OkStatus(); +} + +absl::Status Utility::validateResponseHeaders(Http::ResponseHeaderMap& headers, + Envoy::Http::Code code) { + if (auto status = validateCommonHeaders(headers); !status.ok()) { + return status; + } + + ASSERT(headers.Status() != nullptr); + + if (code < Http::Code::OK || code == Http::Code::NoContent || code == Http::Code::NotModified) { + // There is no clear description in the RFC about the transfer-encoding behavior + // of NotModified response. But 1xx, 204 responses should not have transfer-encoding. + // See https://datatracker.ietf.org/doc/html/rfc9112#section-6.1-6 + if (code != Http::Code::NotModified) { + if (headers.TransferEncoding() != nullptr) { + return absl::InvalidArgumentError("transfer-encoding is set for 1xx, 204 response"); + } + } + + // 1xx, 204, 304 responses should not have body. + if (headers.ContentLength() != nullptr) { + if (headers.ContentLength()->value().getStringView() != "0") { + return absl::InvalidArgumentError( + "content-length (non-zero) is set for 1xx, 204, 304 response"); + } + } + } + + return absl::OkStatus(); +} + +absl::Status Utility::encodeRequestHeaders(Buffer::Instance& buffer, + const Http::RequestHeaderMap& headers, + bool chunk_encoding) { + const Http::HeaderEntry* method = headers.Method(); + const Http::HeaderEntry* path = headers.Path(); + const Http::HeaderEntry* host = headers.Host(); + + if (method == nullptr || path == nullptr || host == nullptr) { + return absl::InvalidArgumentError("missing required headers"); + } + + absl::string_view host_or_path_view = path->value().getStringView(); + + buffer.addFragments({method->value().getStringView(), SPACE, host_or_path_view, REQUEST_POSTFIX}); + encodeNormalHeaders(buffer, headers, chunk_encoding); + buffer.add(CRLF); + + return absl::OkStatus(); +} + +uint64_t Utility::statusToHttpStatus(absl::StatusCode status_code) { + switch (status_code) { + case absl::StatusCode::kOk: + return 200; + case absl::StatusCode::kCancelled: + return 499; + case absl::StatusCode::kUnknown: + // Internal server error. + return 500; + case absl::StatusCode::kInvalidArgument: + // Bad request. + return 400; + case absl::StatusCode::kDeadlineExceeded: + // Gateway Time-out. + return 504; + case absl::StatusCode::kNotFound: + // Not found. + return 404; + case absl::StatusCode::kAlreadyExists: + // Conflict. + return 409; + case absl::StatusCode::kPermissionDenied: + // Forbidden. + return 403; + case absl::StatusCode::kResourceExhausted: + // Too many requests. + return 429; + case absl::StatusCode::kFailedPrecondition: + // Bad request. + return 400; + case absl::StatusCode::kAborted: + // Conflict. + return 409; + case absl::StatusCode::kOutOfRange: + // Bad request. + return 400; + case absl::StatusCode::kUnimplemented: + // Not implemented. + return 501; + case absl::StatusCode::kInternal: + // Internal server error. + return 500; + case absl::StatusCode::kUnavailable: + // Service unavailable. + return 503; + case absl::StatusCode::kDataLoss: + // Internal server error. + return 500; + case absl::StatusCode::kUnauthenticated: + // Unauthorized. + return 401; + default: + // Internal server error. + return 500; + } +} + +absl::Status Utility::encodeResponseHeaders(Buffer::Instance& buffer, + const Http::ResponseHeaderMap& headers, + bool chunk_encoding) { + const Http::HeaderEntry* status = headers.Status(); + if (status == nullptr) { + return absl::InvalidArgumentError("missing required headers"); + } + uint64_t numeric_status = Http::Utility::getResponseStatus(headers); + + absl::string_view reason_phrase; + const char* status_string = Http::CodeUtility::toString(static_cast(numeric_status)); + uint32_t status_string_len = strlen(status_string); + reason_phrase = {status_string, status_string_len}; + + buffer.addFragments({RESPONSE_PREFIX, absl::StrCat(numeric_status), SPACE, reason_phrase, CRLF}); + encodeNormalHeaders(buffer, headers, chunk_encoding); + buffer.add(CRLF); + + return absl::OkStatus(); +} + +void Utility::encodeBody(Buffer::Instance& dst_buffer, Buffer::Instance& src_buffer, + bool chunk_encoding, bool end_stream) { + if (src_buffer.length() > 0) { + // Chunk header. + if (chunk_encoding) { + dst_buffer.add(absl::StrCat(absl::Hex(src_buffer.length()), CRLF)); + } + + // Body. + dst_buffer.move(src_buffer); + + // Chunk footer. + if (chunk_encoding) { + dst_buffer.add(CRLF); + } + } + + // Add additional LAST_CHUNK if this is the last frame and chunk encoding is enabled. + if (end_stream) { + if (chunk_encoding) { + dst_buffer.addFragments({LAST_CHUNK, CRLF}); + } + } +} + +bool Http1CodecBase::decodeBuffer(Buffer::Instance& buffer) { + decoding_buffer_.move(buffer); + + // Always resume before decoding. + parser_->resume(); + + while (decoding_buffer_.length() > 0) { + const auto slice = decoding_buffer_.frontSlice(); + const auto nread = parser_->execute(static_cast(slice.mem_), slice.len_); + decoding_buffer_.drain(nread); + const auto status = parser_->getStatus(); + + // Parser is paused by the callback. Do nothing and return. Don't handle the buffered body + // because parser is paused and no callback should be called. + if (status == Http::Http1::ParserStatus::Paused) { + return true; + } + // Parser has encountered an error. Return false to indicate decoding failure. Ignore the + // buffered body. + if (status == Http::Http1::ParserStatus::Error) { + // Decoding error. + return false; + } + // No more data to read and parser is not paused, break to avoid infinite loop. This is + // preventive check. The parser should not be in this state in normal cases. + if (nread == 0) { + return true; + } + } + // Try to dispatch any buffered body. If the message is complete then this will be a no-op. + dispatchBufferedBody(false); + return true; +} + +void Http1CodecBase::dispatchBufferedBody(bool end_stream) { + if (single_frame_mode_) { + // Do nothing until the onMessageComplete callback if we are in single frame mode. + + // Check if the buffered body is too large. + if (bufferedBodyOverflow()) { + // Pause the parser to avoid further parsing. + parser_->pause(); + // Tell the caller that the decoding failed. + onDecodingFailure(); + } + return; + } + + if (buffered_body_.length() > 0 || end_stream) { + ENVOY_LOG(debug, + "Generic proxy HTTP1 codec: decoding request/response body (end_stream={} size={})", + end_stream, buffered_body_.length()); + auto frame = std::make_unique(buffered_body_, end_stream); + onDecodingSuccess(std::move(frame)); + } +} + +bool Http1CodecBase::bufferedBodyOverflow() { + if (buffered_body_.length() < max_buffer_size_) { + return false; + } + + ENVOY_LOG(warn, "Generic proxy HTTP1 codec: body size exceeds max size({} vs {})", + buffered_body_.length(), max_buffer_size_); + return true; +} + +Http::Http1::CallbackResult Http1ServerCodec::onMessageBeginImpl() { + if (active_request_.has_value()) { + ENVOY_LOG(error, + "Generic proxy HTTP1 codec: multiple requests on the same connection at same time."); + return Http::Http1::CallbackResult::Error; + } + active_request_ = ActiveRequest{}; + + active_request_->request_headers_ = Http::RequestHeaderMapImpl::create(); + return Http::Http1::CallbackResult::Success; +} + +Http::Http1::CallbackResult Http1ServerCodec::onHeadersCompleteImpl() { + if (!parser_->isHttp11()) { + ENVOY_LOG(error, + "Generic proxy HTTP1 codec: unsupported HTTP version, only HTTP/1.1 is supported."); + return Http::Http1::CallbackResult::Error; + } + + active_request_->request_headers_->setMethod(parser_->methodName()); + + // Validate request headers. + const auto validate_headers_status = + Utility::validateRequestHeaders(*active_request_->request_headers_); + if (!validate_headers_status.ok()) { + ENVOY_LOG(error, "Generic proxy HTTP1 codec: failed to validate request headers: {}", + validate_headers_status.message()); + return Http::Http1::CallbackResult::Error; + } + + const bool non_end_stream = Utility::hasBody(*parser_, false, false); + ENVOY_LOG(debug, "decoding request headers complete (end_stream={}):\n{}", !non_end_stream, + *active_request_->request_headers_); + + // Handle the Expect header first. + if (active_request_->request_headers_->Expect() != nullptr) { + if (absl::EqualsIgnoreCase(active_request_->request_headers_->getExpectValue(), + Envoy::Http::Headers::get().ExpectValues._100Continue)) { + // Remove the expect header then the upstream server won't handle it again. + active_request_->request_headers_->removeExpect(); + + // Send 100 Continue response directly. We won't proxy the 100 Continue because + // the complexity in the generic proxy framework is too high. + Buffer::OwnedImpl buffer(_100_CONTINUE_RESPONSE); + callbacks_->writeToConnection(buffer); + } + } + + if (single_frame_mode_) { + // Do nothing until the onMessageComplete callback if we are in single frame mode. + return Http::Http1::CallbackResult::Success; + } else if (non_end_stream) { + auto request = + std::make_unique(std::move(active_request_->request_headers_), false); + onDecodingSuccess(std::move(request)); + } else { + deferred_end_stream_headers_ = true; + } + + return Http::Http1::CallbackResult::Success; +} + +Http::Http1::CallbackResult Http1ServerCodec::onMessageCompleteImpl() { + active_request_->request_complete_ = true; + + if (single_frame_mode_) { + // Check if the buffered body is too large. + if (bufferedBodyOverflow()) { + // Pause the parser to avoid further parsing. + parser_->pause(); + // Tell the caller that the decoding failed. + return Http::Http1::CallbackResult::Error; + } + + ASSERT(!deferred_end_stream_headers_); + auto request = + std::make_unique(std::move(active_request_->request_headers_), true); + request->optionalBuffer().move(buffered_body_); + + if (request->optionalBuffer().length() > 0) { + request->headerMap().removeTransferEncoding(); + request->headerMap().setContentLength(request->optionalBuffer().length()); + } + onDecodingSuccess(std::move(request)); + } else if (deferred_end_stream_headers_) { + deferred_end_stream_headers_ = false; + auto request = + std::make_unique(std::move(active_request_->request_headers_), true); + onDecodingSuccess(std::move(request)); + } else { + dispatchBufferedBody(true); + } + + parser_->pause(); + return Http::Http1::CallbackResult::Success; +} + +void Http1ServerCodec::encode(const StreamFrame& frame, EncodingCallbacks& callbacks) { + const bool response_end_stream = frame.frameFlags().endStream(); + + if (auto* headers = dynamic_cast(&frame); headers != nullptr) { + ENVOY_LOG(debug, "encoding response headers (end_stream={}):\n{}", response_end_stream, + *headers->response_); + + active_request_->response_chunk_encoding_ = + Utility::isChunked(*headers->response_, response_end_stream); + + auto status = Utility::encodeResponseHeaders(encoding_buffer_, *headers->response_, + active_request_->response_chunk_encoding_); + if (!status.ok()) { + ENVOY_LOG(error, "Generic proxy HTTP1 codec: failed to encode response headers: {}", + status.message()); + callbacks_->connection()->close(Network::ConnectionCloseType::FlushWrite); + return; + } + + // Encode the optional buffer if it exists. This is used for local response or for the + // request/responses in single frame mode. + if (headers->optionalBuffer().length() > 0) { + ASSERT(response_end_stream); + Utility::encodeBody(encoding_buffer_, headers->optionalBuffer(), + active_request_->response_chunk_encoding_, response_end_stream); + } + + } else if (auto* body = dynamic_cast(&frame); body != nullptr) { + ENVOY_LOG(debug, "encoding response body (end_stream={} size={})", response_end_stream, + body->buffer().length()); + Utility::encodeBody(encoding_buffer_, body->buffer(), active_request_->response_chunk_encoding_, + response_end_stream); + } + + callbacks.onEncodingSuccess(encoding_buffer_, response_end_stream); + + if (response_end_stream) { + if (active_request_->request_complete_) { + active_request_.reset(); + return; + } + ENVOY_LOG(debug, "Generic proxy HTTP1 server codec: response complete before request complete"); + if (callbacks_->connection().has_value()) { + callbacks_->connection()->close(Network::ConnectionCloseType::FlushWrite); + } + } +} + +void Http1ClientCodec::encode(const StreamFrame& frame, EncodingCallbacks& callbacks) { + const bool request_end_stream = frame.frameFlags().endStream(); + + if (auto* headers = dynamic_cast(&frame); headers != nullptr) { + ENVOY_LOG(debug, "encoding request headers (end_stream={}):\n{}", request_end_stream, + *headers->request_); + + ASSERT(!expect_response_.has_value()); + expect_response_ = ExpectResponse{}; + expect_response_->request_chunk_encoding_ = + Utility::isChunked(*headers->request_, request_end_stream); + expect_response_->head_request_ = + headers->request_->getMethodValue() == Http::Headers::get().MethodValues.Head; + + auto status = Utility::encodeRequestHeaders(encoding_buffer_, *headers->request_, + expect_response_->request_chunk_encoding_); + if (!status.ok()) { + ENVOY_LOG(error, "Generic proxy HTTP1 codec: failed to encode request headers: {}", + status.message()); + callbacks_->connection()->close(Network::ConnectionCloseType::FlushWrite); + return; + } + + // Encode the optional buffer if it exists. This is used for local response or for the + // request/responses in single frame mode. + if (headers->optionalBuffer().length() > 0) { + ASSERT(request_end_stream); + Utility::encodeBody(encoding_buffer_, headers->optionalBuffer(), + expect_response_->request_chunk_encoding_, request_end_stream); + } + + } else if (auto* body = dynamic_cast(&frame); body != nullptr) { + ENVOY_LOG(debug, "encoding request body (end_stream={} size={})", request_end_stream, + body->buffer().length()); + Utility::encodeBody(encoding_buffer_, body->buffer(), expect_response_->request_chunk_encoding_, + request_end_stream); + } + + if (request_end_stream) { + expect_response_->request_complete_ = true; + } + + callbacks.onEncodingSuccess(encoding_buffer_, request_end_stream); +} + +Http::Http1::CallbackResult Http1ClientCodec::onMessageBeginImpl() { + if (!expect_response_.has_value()) { + ENVOY_LOG(error, "Generic proxy HTTP1 codec: unexpected HTTP response from upstream"); + return Http::Http1::CallbackResult::Error; + } + expect_response_->response_headers_ = Http::ResponseHeaderMapImpl::create(); + return Http::Http1::CallbackResult::Success; +} + +Http::Http1::CallbackResult Http1ClientCodec::onHeadersCompleteImpl() { + if (!parser_->isHttp11()) { + ENVOY_LOG(error, + "Generic proxy HTTP1 codec: unsupported HTTP version, only HTTP/1.1 is supported."); + return Http::Http1::CallbackResult::Error; + } + + expect_response_->response_headers_->setStatus( + std::to_string(static_cast(parser_->statusCode()))); + + // Validate response headers. + const auto validate_headers_status = + Utility::validateResponseHeaders(*expect_response_->response_headers_, parser_->statusCode()); + if (!validate_headers_status.ok()) { + ENVOY_LOG(error, "Generic proxy HTTP1 codec: failed to validate response headers: {}", + validate_headers_status.message()); + return Http::Http1::CallbackResult::Error; + } + + const bool non_end_stream = Utility::hasBody(*parser_, true, expect_response_->head_request_); + + ENVOY_LOG(debug, "decoding response headers complete (end_stream={}):\n{}", !non_end_stream, + *expect_response_->response_headers_); + + if (single_frame_mode_) { + // Do nothing until the onMessageComplete callback if we are in single frame mode. + return Http::Http1::CallbackResult::Success; + } else if (non_end_stream) { + auto request = + std::make_unique(std::move(expect_response_->response_headers_), false); + onDecodingSuccess(std::move(request)); + } else { + deferred_end_stream_headers_ = true; + } + + return Http::Http1::CallbackResult::Success; +} + +Http::Http1::CallbackResult Http1ClientCodec::onMessageCompleteImpl() { + const auto status_code = parser_->statusCode(); + if (status_code < Envoy::Http::Code::OK) { + // There is no difference bewteen single frame mode and normal mode for 1xx responses + // because they are headers only responses. + + ASSERT(buffered_body_.length() == 0); + + expect_response_->response_headers_.reset(); + buffered_body_.drain(buffered_body_.length()); + + // 100 Continue response. Ignore it. + // 101 Switching Protocols response. Ignore it because we don't support upgrade for now. + // 102 Processing response. Ignore it. + // 103 Early Hints response. Ignore it. + + // Return success to continue parsing the actual response. + return Http::Http1::CallbackResult::Success; + } + + if (single_frame_mode_) { + // Check if the buffered body is too large. + if (bufferedBodyOverflow()) { + // Pause the parser to avoid further parsing. + parser_->pause(); + // Tell the caller that the decoding failed. + return Http::Http1::CallbackResult::Error; + } + + ASSERT(!deferred_end_stream_headers_); + auto response = + std::make_unique(std::move(expect_response_->response_headers_), true); + response->optionalBuffer().move(buffered_body_); + + if (response->optionalBuffer().length() > 0) { + response->headerMap().removeTransferEncoding(); + response->headerMap().setContentLength(response->optionalBuffer().length()); + } + + onDecodingSuccess(std::move(response)); + } else if (deferred_end_stream_headers_) { + deferred_end_stream_headers_ = false; + auto response = + std::make_unique(std::move(expect_response_->response_headers_), true); + onDecodingSuccess(std::move(response)); + } else { + dispatchBufferedBody(true); + } + // If both request and response is complete, reset the state and pause the parser. + if (expect_response_->request_complete_) { + parser_->pause(); + expect_response_.reset(); + return Http::Http1::CallbackResult::Success; + } else { + ENVOY_LOG(debug, "Generic proxy HTTP1 client codec: response complete before request complete"); + return Http::Http1::CallbackResult::Error; + } +} + +CodecFactoryPtr +Http1CodecFactoryConfig::createCodecFactory(const Protobuf::Message& config, + Envoy::Server::Configuration::FactoryContext&) { + const auto& typed_config = dynamic_cast(config); + + return std::make_unique( + PROTOBUF_GET_WRAPPED_OR_DEFAULT(typed_config, single_frame_mode, true), + PROTOBUF_GET_WRAPPED_OR_DEFAULT(typed_config, max_buffer_size, DEFAULT_MAX_BUFFER_SIZE)); +} + +REGISTER_FACTORY(Http1CodecFactoryConfig, CodecFactoryConfig); + +} // namespace Http1 +} // namespace Codec +} // namespace GenericProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/generic_proxy/filters/network/source/codecs/http1/config.h b/contrib/generic_proxy/filters/network/source/codecs/http1/config.h new file mode 100644 index 000000000000..6a77b3329d64 --- /dev/null +++ b/contrib/generic_proxy/filters/network/source/codecs/http1/config.h @@ -0,0 +1,394 @@ +#pragma once + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/http/header_map_impl.h" +#include "source/common/http/http1/balsa_parser.h" +#include "source/common/http/http1/parser.h" + +#include "contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/http1.pb.h" +#include "contrib/generic_proxy/filters/network/source/interface/codec.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace GenericProxy { +namespace Codec { +namespace Http1 { + +using ProtoConfig = + envoy::extensions::filters::network::generic_proxy::codecs::http1::v3::Http1CodecConfig; + +template class HttpHeaderFrame : public Interface { +public: + absl::string_view protocol() const override { return "http1"; } + void forEach(StreamBase::IterateCallback callback) const override { + headerMap().iterate([cb = std::move(callback)](const Http::HeaderEntry& entry) { + if (cb(entry.key().getStringView(), entry.value().getStringView())) { + return Http::HeaderMap::Iterate::Continue; + } + return Http::HeaderMap::Iterate::Break; + }); + }; + absl::optional get(absl::string_view key) const override { + const Http::LowerCaseString lower_key{key}; + const auto entry = headerMap().get(lower_key); + if (!entry.empty()) { + return entry[0]->value().getStringView(); + } + return absl::nullopt; + } + void set(absl::string_view key, absl::string_view val) override { + headerMap().setCopy(Http::LowerCaseString(key), std::string(val)); + } + void erase(absl::string_view key) override { headerMap().remove(Http::LowerCaseString(key)); } + + FrameFlags frameFlags() const override { return frame_flags_; } + + virtual Http::RequestOrResponseHeaderMap& headerMap() const PURE; + + // Optional buffer for the raw body. This is only make sense for local response and + // request/responses in single frame mode. + Buffer::Instance& optionalBuffer() const { return buffer_; } + +protected: + FrameFlags frame_flags_; + mutable Envoy::Buffer::OwnedImpl buffer_; +}; + +class HttpRequestFrame : public HttpHeaderFrame { +public: + HttpRequestFrame(Http::RequestHeaderMapPtr request, bool end_stream) + : request_(std::move(request)) { + ASSERT(request_ != nullptr); + frame_flags_ = {StreamFlags{}, end_stream}; + } + + absl::string_view host() const override { return request_->getHostValue(); } + absl::string_view path() const override { return request_->getPathValue(); } + absl::string_view method() const override { return request_->getMethodValue(); } + + Http::RequestOrResponseHeaderMap& headerMap() const override { return *request_; } + Http::RequestHeaderMapPtr request_; +}; + +class HttpResponseFrame : public HttpHeaderFrame { +public: + HttpResponseFrame(Http::ResponseHeaderMapPtr response, bool end_stream) + : response_(std::move(response)) { + ASSERT(response_ != nullptr); + + const bool drain_close = Envoy::StringUtil::caseFindToken( + response_->getConnectionValue(), ",", Http::Headers::get().ConnectionValues.Close); + + frame_flags_ = {StreamFlags{0, false, drain_close, false}, end_stream}; + } + + StreamStatus status() const override { + auto status_view = response_->getStatusValue(); + int32_t status = 0; + if (absl::SimpleAtoi(status_view, &status)) { + return {status, status < 500 && status > 99}; + } + // Unknown HTTP status. Return -1 and false. + return {-1, false}; + } + + Http::RequestOrResponseHeaderMap& headerMap() const override { return *response_; } + + Http::ResponseHeaderMapPtr response_; +}; + +class HttpRawBodyFrame : public StreamFrame { +public: + HttpRawBodyFrame(Envoy::Buffer::Instance& buffer, bool end_stream) + : frame_flags_({StreamFlags{}, end_stream}) { + buffer_.move(buffer); + } + FrameFlags frameFlags() const override { return frame_flags_; } + + Buffer::Instance& buffer() const { return buffer_; } + +private: + mutable Buffer::OwnedImpl buffer_; + const FrameFlags frame_flags_; +}; + +class Utility { +public: + static absl::Status encodeRequestHeaders(Buffer::Instance& buffer, + const Http::RequestHeaderMap& headers, + bool chunk_encoding); + static absl::Status encodeResponseHeaders(Buffer::Instance& buffer, + const Http::ResponseHeaderMap& headers, + bool chunk_encoding); + static void encodeBody(Buffer::Instance& dst_buffer, Buffer::Instance& src_buffer, + bool chunk_encoding, bool end_stream); + + static absl::Status validateRequestHeaders(Http::RequestHeaderMap& headers); + static absl::Status validateResponseHeaders(Http::ResponseHeaderMap& headers, + Envoy::Http::Code code); + + static bool isChunked(const Http::RequestOrResponseHeaderMap& headers, bool bodiless); + + static bool hasBody(const Envoy::Http::Http1::Parser& parser, bool response, + bool response_for_head_request); + + static uint64_t statusToHttpStatus(absl::StatusCode status_code); +}; + +struct ActiveRequest { + Http::RequestHeaderMapPtr request_headers_; + + bool request_complete_{}; + bool response_chunk_encoding_{}; +}; + +struct ExpectResponse { + Http::ResponseHeaderMapPtr response_headers_; + + bool request_complete_{}; + bool head_request_{}; + bool request_chunk_encoding_{}; +}; + +class Http1CodecBase : public Http::Http1::ParserCallbacks, + public Envoy::Logger::Loggable { +public: + Http1CodecBase(bool single_frame_mode, uint32_t max_buffer_size, bool server_codec) + : single_frame_mode_(single_frame_mode), max_buffer_size_(max_buffer_size) { + if (server_codec) { + parser_ = Http::Http1::ParserPtr{new Http::Http1::BalsaParser( + Http::Http1::MessageType::Request, this, 64 * 1024, false, false)}; + } else { + parser_ = Http::Http1::ParserPtr{new Http::Http1::BalsaParser( + Http::Http1::MessageType::Response, this, 64 * 1024, false, false)}; + } + } + + // ParserCallbacks. + Http::Http1::CallbackResult onMessageBegin() override { + header_parsing_state_ = HeaderParsingState::Field; + return onMessageBeginImpl(); + } + Http::Http1::CallbackResult onUrl(const char* data, size_t length) override { + onUrlImpl(data, length); + return Http::Http1::CallbackResult::Success; + } + Http::Http1::CallbackResult onStatus(const char* data, size_t length) override { + onStatusImpl(data, length); + return Http::Http1::CallbackResult::Success; + } + Http::Http1::CallbackResult onHeaderField(const char* data, size_t length) override { + if (header_parsing_state_ == HeaderParsingState::Done) { + // Ignore trailers for now. + return Http::Http1::CallbackResult::Success; + } + if (header_parsing_state_ == HeaderParsingState::Value) { + completeCurrentHeader(); + } + current_header_field_.append(data, length); + + header_parsing_state_ = HeaderParsingState::Field; + return Http::Http1::CallbackResult::Success; + } + Http::Http1::CallbackResult onHeaderValue(const char* data, size_t length) override { + if (header_parsing_state_ == HeaderParsingState::Done) { + // Ignore trailers for now. + return Http::Http1::CallbackResult::Success; + } + + absl::string_view value(data, length); + if (current_header_value_.empty()) { + value = StringUtil::ltrim(value); + } + + current_header_value_.append(value.data(), value.size()); + + header_parsing_state_ = HeaderParsingState::Value; + return Http::Http1::CallbackResult::Success; + } + Http::Http1::CallbackResult onHeadersComplete() override { + completeCurrentHeader(); + header_parsing_state_ = HeaderParsingState::Done; + return onHeadersCompleteImpl(); + } + void bufferBody(const char* data, size_t length) override { buffered_body_.add(data, length); } + Http::Http1::CallbackResult onMessageComplete() override { return onMessageCompleteImpl(); } + void onChunkHeader(bool is_final_chunk) override { + if (is_final_chunk) { + dispatchBufferedBody(false); + } + } + + virtual Http::Http1::CallbackResult onMessageBeginImpl() PURE; + virtual void onUrlImpl(const char* data, size_t length) PURE; + virtual void onStatusImpl(const char* data, size_t length) PURE; + virtual Http::Http1::CallbackResult onHeadersCompleteImpl() PURE; + virtual Http::Http1::CallbackResult onMessageCompleteImpl() PURE; + + void completeCurrentHeader() { + current_header_value_.rtrim(); + current_header_field_.inlineTransform([](char c) { return absl::ascii_tolower(c); }); + headerMap().addViaMove(std::move(current_header_field_), std::move(current_header_value_)); + + ASSERT(current_header_field_.empty()); + ASSERT(current_header_value_.empty()); + } + + bool decodeBuffer(Buffer::Instance& buffer); + + void dispatchBufferedBody(bool end_stream); + bool bufferedBodyOverflow(); + + virtual Http::HeaderMap& headerMap() PURE; + + virtual void onDecodingSuccess(StreamFramePtr&& frame) PURE; + virtual void onDecodingFailure() PURE; + +protected: + enum class HeaderParsingState { Field, Value, Done }; + + Envoy::Buffer::OwnedImpl decoding_buffer_; + Envoy::Buffer::OwnedImpl encoding_buffer_; + + Buffer::OwnedImpl buffered_body_; + + Http::Http1::ParserPtr parser_; + Http::HeaderString current_header_field_; + Http::HeaderString current_header_value_; + HeaderParsingState header_parsing_state_{HeaderParsingState::Field}; + + const bool single_frame_mode_{}; + const uint32_t max_buffer_size_{}; + + bool deferred_end_stream_headers_{}; +}; + +class Http1ServerCodec : public Http1CodecBase, public ServerCodec { +public: + Http1ServerCodec(bool single_frame_mode, uint32_t max_buffer_size) + : Http1CodecBase(single_frame_mode, max_buffer_size, true) {} + + Http::Http1::CallbackResult onMessageBeginImpl() override; + void onUrlImpl(const char* data, size_t length) override { + ASSERT(active_request_.has_value()); + ASSERT(active_request_->request_headers_ != nullptr); + active_request_->request_headers_->setPath(absl::string_view(data, length)); + } + void onStatusImpl(const char*, size_t) override {} + Http::Http1::CallbackResult onHeadersCompleteImpl() override; + Http::Http1::CallbackResult onMessageCompleteImpl() override; + + Http::HeaderMap& headerMap() override { return *active_request_->request_headers_; } + + void setCodecCallbacks(ServerCodecCallbacks& callbacks) override { callbacks_ = &callbacks; } + void decode(Envoy::Buffer::Instance& buffer, bool) override { + if (!decodeBuffer(buffer)) { + callbacks_->onDecodingFailure(); + } + } + void encode(const StreamFrame& frame, EncodingCallbacks& callbacks) override; + ResponsePtr respond(absl::Status status, absl::string_view data, const Request&) override { + auto response = Http::ResponseHeaderMapImpl::create(); + response->setStatus(std::to_string(Utility::statusToHttpStatus(status.code()))); + response->setContentLength(data.size()); + response->addCopy(Http::LowerCaseString("reason"), status.message()); + auto response_frame = std::make_unique(std::move(response), true); + response_frame->optionalBuffer().add(data.data(), data.size()); + return response_frame; + } + + void onDecodingSuccess(StreamFramePtr&& frame) override { + if (callbacks_->connection().has_value()) { + callbacks_->onDecodingSuccess(std::move(frame)); + } + + // Connection may have been closed by the callback. + if (!callbacks_->connection().has_value() || + callbacks_->connection()->state() != Network::Connection::State::Open) { + parser_->pause(); + } + } + + // TODO(wbpcode): send 400 bad request to client as response and then call the callback. + void onDecodingFailure() override { callbacks_->onDecodingFailure(); } + + absl::optional active_request_; + ServerCodecCallbacks* callbacks_{}; +}; + +class Http1ClientCodec : public Http1CodecBase, public ClientCodec { +public: + Http1ClientCodec(bool single_frame_mode, uint32_t max_buffer_size) + : Http1CodecBase(single_frame_mode, max_buffer_size, false) {} + + Http::Http1::CallbackResult onMessageBeginImpl() override; + void onUrlImpl(const char*, size_t) override {} + void onStatusImpl(const char*, size_t) override {} + Http::Http1::CallbackResult onHeadersCompleteImpl() override; + Http::Http1::CallbackResult onMessageCompleteImpl() override; + + Http::HeaderMap& headerMap() override { return *expect_response_->response_headers_; } + + void setCodecCallbacks(ClientCodecCallbacks& callbacks) override { callbacks_ = &callbacks; } + void decode(Envoy::Buffer::Instance& buffer, bool) override { + if (!decodeBuffer(buffer)) { + callbacks_->onDecodingFailure(); + } + } + void encode(const StreamFrame& frame, EncodingCallbacks& callbacks) override; + + void onDecodingSuccess(StreamFramePtr&& frame) override { + if (callbacks_->connection().has_value()) { + callbacks_->onDecodingSuccess(std::move(frame)); + } + + // Connection may have been closed by the callback. + if (!callbacks_->connection().has_value() || + callbacks_->connection()->state() != Network::Connection::State::Open) { + parser_->pause(); + } + } + void onDecodingFailure() override { callbacks_->onDecodingFailure(); } + + absl::optional expect_response_; + + ClientCodecCallbacks* callbacks_{}; +}; + +class Http1CodecFactory : public CodecFactory { +public: + Http1CodecFactory(bool single_frame_mode, uint32_t max_buffer_size) + : single_frame_mode_(single_frame_mode), max_buffer_size_(max_buffer_size) {} + + ClientCodecPtr createClientCodec() const override { + return std::make_unique(single_frame_mode_, max_buffer_size_); + } + + ServerCodecPtr createServerCodec() const override { + return std::make_unique(single_frame_mode_, max_buffer_size_); + } + +private: + const bool single_frame_mode_{}; + const uint32_t max_buffer_size_{}; +}; + +class Http1CodecFactoryConfig : public CodecFactoryConfig { +public: + // CodecFactoryConfig + CodecFactoryPtr + createCodecFactory(const Envoy::Protobuf::Message& config, + Envoy::Server::Configuration::FactoryContext& context) override; + std::string name() const override { return "envoy.generic_proxy.codecs.http1"; } + Envoy::ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique(); + } +}; + +} // namespace Http1 +} // namespace Codec +} // namespace GenericProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/generic_proxy/filters/network/source/router/router.cc b/contrib/generic_proxy/filters/network/source/router/router.cc index 2a7cbd69900b..47f3eb5fa5e0 100644 --- a/contrib/generic_proxy/filters/network/source/router/router.cc +++ b/contrib/generic_proxy/filters/network/source/router/router.cc @@ -31,21 +31,15 @@ constexpr absl::string_view RouterFilterName = "envoy.filters.generic.router"; } // namespace void GenericUpstream::writeToConnection(Buffer::Instance& buffer) { - if (is_cleaned_up_) { - return; - } - - if (owned_conn_data_ != nullptr) { - ASSERT(owned_conn_data_->connection().state() == Network::Connection::State::Open); + if (owned_conn_data_ != nullptr && + owned_conn_data_->connection().state() == Network::Connection::State::Open) { owned_conn_data_->connection().write(buffer, false); } } OptRef GenericUpstream::connection() { - if (is_cleaned_up_) { - return {}; - } - if (owned_conn_data_ != nullptr) { + if (owned_conn_data_ != nullptr && + owned_conn_data_->connection().state() == Network::Connection::State::Open) { return {owned_conn_data_->connection()}; } return {}; @@ -301,10 +295,10 @@ void UpstreamRequest::startStream() { } void UpstreamRequest::resetStream(StreamResetReason reason) { - if (stream_reset_) { + if (reset_or_response_complete_) { return; } - stream_reset_ = true; + reset_or_response_complete_ = true; ENVOY_LOG(debug, "generic proxy upstream request: reset upstream request"); @@ -329,9 +323,10 @@ void UpstreamRequest::resetStream(StreamResetReason reason) { void UpstreamRequest::clearStream(bool close_connection) { // Set the upstream response complete flag to true first to ensure the possible // connection close event will not be handled. - response_complete_ = true; + reset_or_response_complete_ = true; - ENVOY_LOG(debug, "generic proxy upstream request: complete upstream request"); + ENVOY_LOG(debug, "generic proxy upstream request: complete upstream request ()", + close_connection); if (span_ != nullptr) { TraceContextBridge trace_context{*parent_.request_stream_}; @@ -463,12 +458,22 @@ void UpstreamRequest::onDecodingSuccess(StreamFramePtr response) { } } -void UpstreamRequest::onDecodingFailure() { resetStream(StreamResetReason::ProtocolError); } +void UpstreamRequest::onDecodingFailure() { + // Decoding failure after the response is complete, close the connection. + // This should only happen when some special cases, for example: + // The HTTP response is complete but the request is not fully sent. + // The codec will throw an error after the response is complete. + if (reset_or_response_complete_) { + generic_upstream_->cleanUp(true); + return; + } + resetStream(StreamResetReason::ProtocolError); +} void UpstreamRequest::onConnectionClose(Network::ConnectionEvent event) { // If the upstream response is complete or the upstream request is reset then // ignore the connection close event. - if (response_complete_ || stream_reset_) { + if (reset_or_response_complete_) { return; } diff --git a/contrib/generic_proxy/filters/network/source/router/router.h b/contrib/generic_proxy/filters/network/source/router/router.h index 64f41bba5a51..205908b6cdf8 100644 --- a/contrib/generic_proxy/filters/network/source/router/router.h +++ b/contrib/generic_proxy/filters/network/source/router/router.h @@ -183,8 +183,7 @@ class UpstreamRequest : public LinkedObject, absl::optional connecting_start_time_; // One of these flags should be set to true when the request is complete. - bool stream_reset_{}; - bool response_complete_{}; + bool reset_or_response_complete_{}; bool expects_response_{}; diff --git a/contrib/generic_proxy/filters/network/source/upstream.cc b/contrib/generic_proxy/filters/network/source/upstream.cc index 3af4c0b712a1..8124e03f0738 100644 --- a/contrib/generic_proxy/filters/network/source/upstream.cc +++ b/contrib/generic_proxy/filters/network/source/upstream.cc @@ -6,11 +6,14 @@ namespace NetworkFilters { namespace GenericProxy { UpstreamConnection::~UpstreamConnection() { - // Do clean up here again to ensure the cleanUp is called. This is safe to call - // multiple times because of the is_cleand_up_ flag. - // TODO(wbpcode): Clarify/resolve bypassing of virtual dispatch - // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall) - this->cleanUp(true); + // In case we doesn't clean up the pending connecting request. + if (tcp_pool_handle_ != nullptr) { + // Clear the data first. + auto local_handle = tcp_pool_handle_; + tcp_pool_handle_ = nullptr; + + local_handle->cancel(Tcp::ConnectionPool::CancelPolicy::Default); + } } void UpstreamConnection::initialize() { @@ -21,34 +24,33 @@ void UpstreamConnection::initialize() { } void UpstreamConnection::cleanUp(bool close_connection) { - // If the cleanUp is called multiple times, just return. - if (is_cleaned_up_) { - return; - } - - ENVOY_LOG(debug, "generic proxy upstream manager: clean up upstream connection"); - // Set is_cleaned_up_ flag to true to avoid double clean up. - is_cleaned_up_ = true; + ENVOY_LOG(debug, "generic proxy upstream manager: clean up upstream (close: {})", + close_connection); if (close_connection && owned_conn_data_ != nullptr) { ENVOY_LOG(debug, "generic proxy upstream request: close upstream connection"); ASSERT(tcp_pool_handle_ == nullptr); - owned_conn_data_->connection().close(Network::ConnectionCloseType::FlushWrite); + + // Clear the data first to avoid re-entering this function in the close callback. + auto local_data = std::move(owned_conn_data_); + owned_conn_data_.reset(); + + local_data->connection().close(Network::ConnectionCloseType::FlushWrite); } - owned_conn_data_.reset(); if (tcp_pool_handle_ != nullptr) { ENVOY_LOG(debug, "generic proxy upstream manager: cacel upstream connection"); - ASSERT(owned_conn_data_ == nullptr); - tcp_pool_handle_->cancel(Tcp::ConnectionPool::CancelPolicy::Default); + + // Clear the data first. + auto local_handle = tcp_pool_handle_; tcp_pool_handle_ = nullptr; + + local_handle->cancel(Tcp::ConnectionPool::CancelPolicy::Default); } } void UpstreamConnection::onUpstreamData(Buffer::Instance& data, bool end_stream) { - ASSERT(!is_cleaned_up_); - if (data.length() == 0) { return; } diff --git a/contrib/generic_proxy/filters/network/source/upstream.h b/contrib/generic_proxy/filters/network/source/upstream.h index dc88b29d72cf..8e4e1f062dbf 100644 --- a/contrib/generic_proxy/filters/network/source/upstream.h +++ b/contrib/generic_proxy/filters/network/source/upstream.h @@ -50,7 +50,6 @@ class UpstreamConnection : public Envoy::Event::DeferredDeletable, Upstream::TcpPoolData tcp_pool_data_; ClientCodecPtr client_codec_; - bool is_cleaned_up_{}; // Whether the upstream connection is created. This will be set to true when the initialize() // is called. bool initialized_{}; diff --git a/contrib/generic_proxy/filters/network/test/codecs/http1/BUILD b/contrib/generic_proxy/filters/network/test/codecs/http1/BUILD new file mode 100644 index 000000000000..d13a77613895 --- /dev/null +++ b/contrib/generic_proxy/filters/network/test/codecs/http1/BUILD @@ -0,0 +1,21 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_contrib_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_contrib_package() + +envoy_cc_test( + name = "config_test", + srcs = [ + "config_test.cc", + ], + deps = [ + "//contrib/generic_proxy/filters/network/source/codecs/http1:config", + "//contrib/generic_proxy/filters/network/test/mocks:codec_mocks", + "//test/mocks/server:factory_context_mocks", + ], +) diff --git a/contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc b/contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc new file mode 100644 index 000000000000..118c48d760c0 --- /dev/null +++ b/contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc @@ -0,0 +1,1482 @@ +#include +#include + +#include "test/mocks/server/factory_context.h" + +#include "contrib/generic_proxy/filters/network/source/codecs/http1/config.h" +#include "contrib/generic_proxy/filters/network/test/mocks/codec.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace GenericProxy { +namespace Codec { +namespace Http1 { +namespace { + +using testing::NiceMock; + +TEST(Http1MessageFrameTest, Http1MessageFrameTest) { + // Protocol. + { + auto headers = Http::RequestHeaderMapImpl::create(); + HttpRequestFrame frame(std::move(headers), true); + + EXPECT_EQ(frame.protocol(), "http1"); + } + + // ForEach. + { + auto headers = Http::RequestHeaderMapImpl::create(); + // Add some headers. + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + // Add some custom headers. + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame frame(std::move(headers), true); + + // Check that the headers are iterated correctly. + size_t count = 0; + frame.forEach([&count](absl::string_view, absl::string_view) -> bool { + count++; + return true; + }); + + EXPECT_EQ(count, 4); + } + + // Get. + { + auto headers = Http::RequestHeaderMapImpl::create(); + // Add some headers. + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + // Add some custom headers. + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame frame(std::move(headers), true); + + // Check that the headers are retrieved correctly. + EXPECT_EQ(frame.get("host").value(), "host"); + EXPECT_EQ(frame.get(":authority").value(), "host"); + EXPECT_EQ(frame.get(":path").value(), "/path"); + EXPECT_EQ(frame.get(":method").value(), "GET"); + EXPECT_EQ(frame.get("custom").value(), "value"); + } + + // Set. + { + auto headers = Http::RequestHeaderMapImpl::create(); + HttpRequestFrame frame(std::move(headers), true); + + // Set some headers. + frame.set("host", "host"); + frame.set(":path", "/path"); + frame.set(":method", "POST"); + frame.set("custom", "value"); + + // Check that the headers are set correctly. + EXPECT_EQ(frame.get("host").value(), "host"); + EXPECT_EQ(frame.get(":authority").value(), "host"); + EXPECT_EQ(frame.get(":path").value(), "/path"); + EXPECT_EQ(frame.get(":method").value(), "POST"); + EXPECT_EQ(frame.get("custom").value(), "value"); + } + + // Erase. + { + auto headers = Http::RequestHeaderMapImpl::create(); + // Add some headers. + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + // Add some custom headers. + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame frame(std::move(headers), true); + + // Erase some headers. + frame.erase("host"); + frame.erase(":path"); + frame.erase(":method"); + frame.erase("custom"); + + // Check that the headers are erased correctly. + EXPECT_EQ(frame.get("host"), absl::nullopt); + EXPECT_EQ(frame.get(":authority"), absl::nullopt); + EXPECT_EQ(frame.get(":path"), absl::nullopt); + EXPECT_EQ(frame.get(":method"), absl::nullopt); + EXPECT_EQ(frame.get("custom"), absl::nullopt); + } + + // Request FrameFlags. + { + auto headers = Http::RequestHeaderMapImpl::create(); + HttpRequestFrame frame(std::move(headers), true); + + EXPECT_EQ(true, frame.frameFlags().endStream()); + + auto headers2 = Http::RequestHeaderMapImpl::create(); + HttpRequestFrame frame2(std::move(headers2), false); + + EXPECT_EQ(false, frame2.frameFlags().endStream()); + } + + // Response FrameFlags. + { + auto headers = Http::ResponseHeaderMapImpl::create(); + HttpResponseFrame frame(std::move(headers), true); + + EXPECT_EQ(true, frame.frameFlags().endStream()); + EXPECT_EQ(false, frame.frameFlags().streamFlags().drainClose()); + + auto headers2 = Http::ResponseHeaderMapImpl::create(); + headers2->setConnection("close"); + HttpResponseFrame frame2(std::move(headers2), false); + + EXPECT_EQ(false, frame2.frameFlags().endStream()); + EXPECT_EQ(true, frame2.frameFlags().streamFlags().drainClose()); + } + + // Request FrameType. + { + auto headers = Http::RequestHeaderMapImpl::create(); + + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + + HttpRequestFrame frame(std::move(headers), true); + + EXPECT_EQ(frame.host(), "host"); + EXPECT_EQ(frame.path(), "/path"); + EXPECT_EQ(frame.method(), "GET"); + } + + // Response FrameType. + { + // 200 + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + HttpResponseFrame frame(std::move(headers), true); + + EXPECT_EQ(frame.status().code(), 200); + EXPECT_TRUE(frame.status().ok()); + + // 404 + auto headers2 = Http::ResponseHeaderMapImpl::create(); + headers2->setStatus(404); + HttpResponseFrame frame2(std::move(headers2), true); + + EXPECT_EQ(frame2.status().code(), 404); + EXPECT_TRUE(frame2.status().ok()); + + // 500 + auto headers3 = Http::ResponseHeaderMapImpl::create(); + headers3->setStatus(500); + HttpResponseFrame frame3(std::move(headers3), true); + + EXPECT_EQ(frame3.status().code(), 500); + EXPECT_FALSE(frame3.status().ok()); + + // 503 + auto headers4 = Http::ResponseHeaderMapImpl::create(); + headers4->setStatus(503); + HttpResponseFrame frame4(std::move(headers4), true); + + EXPECT_EQ(frame4.status().code(), 503); + EXPECT_FALSE(frame4.status().ok()); + + // Invalid status code value. + auto headers5 = Http::ResponseHeaderMapImpl::create(); + headers5->addCopy(Http::Headers::get().Status, "xxx"); + HttpResponseFrame frame5(std::move(headers5), true); + + EXPECT_EQ(frame5.status().code(), -1); + EXPECT_FALSE(frame5.status().ok()); + } + + // Raw body frame. + { + Buffer::OwnedImpl buffer("body"); + + HttpRawBodyFrame frame(buffer, true); + + EXPECT_EQ(frame.frameFlags().endStream(), true); + + EXPECT_EQ(frame.buffer().length(), 4); + EXPECT_EQ(frame.buffer().toString(), "body"); + + Buffer::OwnedImpl new_buffer; + new_buffer.move(frame.buffer()); + + EXPECT_EQ(new_buffer.toString(), "body"); + + EXPECT_EQ(frame.buffer().length(), 0); + } +} + +class Http1ServerCodecTest : public testing::Test { +public: + Http1ServerCodecTest() { initializeCodec(); } + + void initializeCodec(bool single_frame_mode = false, uint32_t max_buffer_size = 8 * 1024 * 1024) { + codec_ = std::make_unique(single_frame_mode, max_buffer_size); + codec_->setCodecCallbacks(codec_callbacks_); + } + + NiceMock codec_callbacks_; + NiceMock mock_connection; + std::unique_ptr codec_; +}; + +TEST_F(Http1ServerCodecTest, HeaderOnlyRequestDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Content-Length: 0\r\n" + "custom: value\r\n" + "\r\n"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + EXPECT_EQ(frame->frameFlags().endStream(), true); + + auto* request = dynamic_cast(frame.get()); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, RequestDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "body"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(2) + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + if (request != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + } + + auto* body = dynamic_cast(frame.get()); + if (body != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, ChunkedRequestDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(3) // One for headers and one for body, and one for the last chunk. + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + if (request != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + } + + auto* body = dynamic_cast(frame.get()); + if (body != nullptr) { + if (!frame->frameFlags().endStream()) { + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } else { + EXPECT_EQ(body->buffer().length(), 0); + } + } + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, MultipleBufferChunkedRequestDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n"); // Chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(2) + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + if (request != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + } else { + auto* body = dynamic_cast(frame.get()); + EXPECT_EQ(frame->frameFlags().endStream(), false); + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } + })); + + codec_->decode(buffer, false); + + EXPECT_EQ(buffer.length(), 0); + + buffer.add("0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* body = dynamic_cast(frame.get()); + EXPECT_EQ(frame->frameFlags().endStream(), true); + EXPECT_EQ(body->buffer().length(), 0); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, UnexpectedRequestTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Connect. + { + initializeCodec(); + + Buffer::OwnedImpl buffer; + + buffer.add("CONNECT host:443 HTTP/1.1\r\n" + "custom: value\r\n" + "\r\n"); + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } + + // Transfer-Encoding and Content-Length are set at same time. + { + initializeCodec(); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } + + // Unknown Transfer-Encoding. + { + initializeCodec(); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: gzip, chunked\r\n" // Only 'chunked' is supported. + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } + + // Lost required request headers. + { + initializeCodec(); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "custom: value\r\n" + "\r\n"); + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } + + // HTTP1.0 request. + { + initializeCodec(); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.0\r\n" + "Host: host\r\n" + "custom: value\r\n" + "\r\n"); + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } +} + +TEST_F(Http1ServerCodecTest, RespondTest) { + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame request(std::move(headers), true); + + // Respond with a response. + { + for (int i = 0; i <= 16; i++) { + auto response = + codec_->respond(absl::Status(static_cast(i), ""), "", request); + + EXPECT_NE(response, nullptr); + EXPECT_NE(dynamic_cast(response.get())->response_, nullptr); + + auto* response_headers = dynamic_cast(response.get())->response_.get(); + EXPECT_EQ(response_headers->getStatusValue(), + std::to_string(Utility::statusToHttpStatus(static_cast(i)))); + } + } +} + +TEST_F(Http1ServerCodecTest, HeaderOnlyResponseEncodingTest) { + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + + HttpResponseFrame response(std::move(headers), true); + + NiceMock encoding_callbacks; + + // Encode the response. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" + "\r\n"); + })); + + codec_->encode(response, encoding_callbacks); + } +} + +TEST_F(Http1ServerCodecTest, ResponseEncodingTest) { + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + headers->addCopy(Http::Headers::get().ContentLength, "4"); + + HttpResponseFrame response(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + + // Encode the response. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" + "content-length: 4\r\n" + "\r\n"); + buffer.drain(buffer.length()); + })); + + codec_->encode(response, encoding_callbacks); + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "body"); + + buffer.drain(buffer.length()); + })); + + codec_->encode(body, encoding_callbacks); + } +} + +TEST_F(Http1ServerCodecTest, ChunkedResponseEncodingTest) { + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpResponseFrame response(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + + // Encode the response. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" + "transfer-encoding: chunked\r\n" + "\r\n"); + buffer.drain(buffer.length()); + })); + + codec_->encode(response, encoding_callbacks); + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + buffer.drain(buffer.length()); + })); + + codec_->encode(body, encoding_callbacks); + } +} + +TEST_F(Http1ServerCodecTest, RequestAndResponseTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Do repeated request and response. + for (size_t i = 0; i < 100; i++) { + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).Times(3); + + codec_->decode(buffer, false); + + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpResponseFrame response(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)).Times(2); + + // Encode the response. + codec_->encode(response, encoding_callbacks); + codec_->encode(body, encoding_callbacks); + } +} + +TEST_F(Http1ServerCodecTest, ResponseCompleteBeforeRequestCompleteTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n"); // Chunk footer. No last chunk header and footer, this is an incomplete + // request. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).Times(2); + + codec_->decode(buffer, false); + + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpResponseFrame response(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)); + // Encode the response. + codec_->encode(response, encoding_callbacks); + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)); + // Response is complete, but request is not complete, so the codec should close the connection. + EXPECT_CALL(mock_connection, close(Network::ConnectionCloseType::FlushWrite)); + + codec_->encode(body, encoding_callbacks); +} + +TEST_F(Http1ServerCodecTest, NewRequestBeforeFirstRequestCompleteTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + Buffer::OwnedImpl buffer2; + buffer2.add(buffer); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).Times(3); + + codec_->decode(buffer, false); + + // First request is not complete, so the codec should close the connection. + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer2, false); +} + +TEST_F(Http1ServerCodecTest, SingleFrameModeRequestDecodingTest) { + initializeCodec(true, 8 * 1024 * 1024); + + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "body"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + + EXPECT_EQ(request->optionalBuffer().length(), 4); + EXPECT_EQ(request->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, SingleFrameModeRequestTooLargeTest) { + initializeCodec(true, 4); + + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "body~"); + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, SingleFrameModeChunkedRequestDecodingTest) { + initializeCodec(true, 8 * 1024 * 1024); + + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + + EXPECT_EQ(request->optionalBuffer().length(), 4); + EXPECT_EQ(request->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, SingleFrameModeMultipleBufferChunkedRequestDecodingTest) { + initializeCodec(true, 8 * 1024 * 1024); + + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n"); // Chunk footer. + + codec_->decode(buffer, false); + + EXPECT_EQ(buffer.length(), 0); + + buffer.add("0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + + EXPECT_EQ(request->optionalBuffer().length(), 4); + EXPECT_EQ(request->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, SingleFrameModeResponseEncodingTest) { + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + headers->setContentLength(4); + + HttpResponseFrame response(std::move(headers), true); + response.optionalBuffer().add("body"); + + NiceMock encoding_callbacks; + + // Encode the response. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" + "content-length: 4\r\n" + "\r\n" + "body"); + buffer.drain(buffer.length()); + })); + codec_->encode(response, encoding_callbacks); + } +} + +class Http1ClientCodecTest : public testing::Test { +public: + Http1ClientCodecTest() { initializeCodec(); } + + void initializeCodec(bool single_frame_mode = false, uint32_t max_buffer_size = 8 * 1024 * 1024) { + codec_ = std::make_unique(single_frame_mode, max_buffer_size); + codec_->setCodecCallbacks(codec_callbacks_); + } + + void encodingOneRequest() { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::Headers::get().ContentLength, "4"); + + HttpRequestFrame request(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + + // Encode the request. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" + "host: host\r\n" + "content-length: 4\r\n" + "\r\n"); + buffer.drain(buffer.length()); + })); + + codec_->encode(request, encoding_callbacks); + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "body"); + buffer.drain(buffer.length()); + })); + + codec_->encode(body, encoding_callbacks); + } + } + + NiceMock codec_callbacks_; + NiceMock mock_connection; + std::unique_ptr codec_; +}; + +TEST_F(Http1ClientCodecTest, HeaderOnlyResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "content-length: 0\r\n" + "\r\n"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + EXPECT_EQ(frame->frameFlags().endStream(), true); + + auto* response = dynamic_cast(frame.get()); + + EXPECT_EQ(response->response_->getStatusValue(), "200"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, ResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "body"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(2) + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + if (response != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->response_->getContentLengthValue(), "4"); + EXPECT_EQ(response->get("custom").value(), "value"); + } + + auto* body = dynamic_cast(frame.get()); + if (body != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, ChunkedResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(3) // One for headers and one for body, and one for the last chunk. + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + if (response != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->get("custom").value(), "value"); + } + + auto* body = dynamic_cast(frame.get()); + if (body != nullptr) { + if (!frame->frameFlags().endStream()) { + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } else { + EXPECT_EQ(body->buffer().length(), 0); + } + } + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, MultipleBufferChunkedResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n"); // Chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(2) + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + if (response != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->get("custom").value(), "value"); + } else { + auto* body = dynamic_cast(frame.get()); + EXPECT_EQ(frame->frameFlags().endStream(), false); + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } + })); + + codec_->decode(buffer, false); + + EXPECT_EQ(buffer.length(), 0); + + buffer.add("0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* body = dynamic_cast(frame.get()); + EXPECT_EQ(frame->frameFlags().endStream(), true); + EXPECT_EQ(body->buffer().length(), 0); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, UnexpectedResponseTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Transfer-Encoding and Content-Length are set at same time. + { + initializeCodec(); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } + + // Unknown Transfer-Encoding. + { + initializeCodec(); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: gzip, chunked\r\n" // Only 'chunked' is supported. + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } +} + +TEST_F(Http1ClientCodecTest, HeaderOnlyRequestEncodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame request(std::move(headers), true); + + NiceMock encoding_callbacks; + + // Encode the request. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" + "host: host\r\n" + "custom: value\r\n" + "\r\n"); + })); + + codec_->encode(request, encoding_callbacks); + } +} + +TEST_F(Http1ClientCodecTest, RequestEncodingTest) { encodingOneRequest(); } + +TEST_F(Http1ClientCodecTest, ChunkedRequestEncodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpRequestFrame request(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + + // Encode the request. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" + "host: host\r\n" + "transfer-encoding: chunked\r\n" + "\r\n"); + buffer.drain(buffer.length()); + })); + + codec_->encode(request, encoding_callbacks); + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + buffer.drain(buffer.length()); + })); + + codec_->encode(body, encoding_callbacks); + } +} + +TEST_F(Http1ClientCodecTest, RequestAndResponseTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Do repeated request and response. + for (size_t i = 0; i < 100; i++) { + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpRequestFrame request(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)).Times(2); + + // Encode the request. + codec_->encode(request, encoding_callbacks); + codec_->encode(body, encoding_callbacks); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + // Decode the response. + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).Times(3); + codec_->decode(buffer, false); + } +} + +TEST_F(Http1ClientCodecTest, ResponseCompleteBeforeRequestCompleteTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpRequestFrame request(std::move(headers), false); + + NiceMock encoding_callbacks; + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)); + + // Encode the request. Only the headers are encoded and the body is not encoded. + codec_->encode(request, encoding_callbacks); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + // Decode the response. + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).Times(3); + // Finally, the onDecodingFailure() is called because the request is not complete and the + // response is complete. + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, SingleFrameModeRequestEncodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + initializeCodec(true, 8 * 1024 * 1024); + + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->setContentLength(4); + + HttpRequestFrame request(std::move(headers), true); + request.optionalBuffer().add("body"); + + NiceMock encoding_callbacks; + + // Encode the request. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" + "host: host\r\n" + "content-length: 4\r\n" + "\r\n" + "body"); + })); + codec_->encode(request, encoding_callbacks); + } +} + +TEST_F(Http1ClientCodecTest, SingleFrameModeResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + initializeCodec(true, 8 * 1024 * 1024); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "body"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->response_->getContentLengthValue(), "4"); + EXPECT_EQ(response->get("custom").value(), "value"); + + EXPECT_EQ(response->optionalBuffer().length(), 4); + EXPECT_EQ(response->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, SingleFrameModeResponseTooLargeTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + initializeCodec(true, 4); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Content-Length: 5\r\n" + "custom: value\r\n" + "\r\n" + "body~"); + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, SingleFrameModeChunkedResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + initializeCodec(true, 8 * 1024 * 1024); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->get("custom").value(), "value"); + + EXPECT_EQ(response->optionalBuffer().length(), 4); + EXPECT_EQ(response->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, SingleFrameModeMultipleBufferChunkedResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + initializeCodec(true, 8 * 1024 * 1024); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n"); // Chunk footer. + + codec_->decode(buffer, false); + + EXPECT_EQ(buffer.length(), 0); + + buffer.add("0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->get("custom").value(), "value"); + + EXPECT_EQ(response->optionalBuffer().length(), 4); + EXPECT_EQ(response->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +} // namespace +} // namespace Http1 +} // namespace Codec +} // namespace GenericProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/tools/proto_format/format_api.py b/tools/proto_format/format_api.py index 8306a1f51515..d096f0677376 100644 --- a/tools/proto_format/format_api.py +++ b/tools/proto_format/format_api.py @@ -32,6 +32,7 @@ 'envoy.extensions.filters.network.client_ssl_auth.v3', 'envoy.extensions.filters.network.generic_proxy.action.v3', 'envoy.extensions.filters.network.generic_proxy.codecs.dubbo.v3', + 'envoy.extensions.filters.network.generic_proxy.codecs.http1.v3', 'envoy.extensions.filters.network.generic_proxy.codecs.kafka.v3', 'envoy.extensions.filters.network.generic_proxy.matcher.v3', 'envoy.extensions.filters.network.generic_proxy.router.v3', From 1e475b3fa5ba2facb424fe09dc7ca0678787fe07 Mon Sep 17 00:00:00 2001 From: code Date: Tue, 19 Mar 2024 21:58:19 +0800 Subject: [PATCH 083/124] dubbo: refactor the message implementation and generic proxy codecs (#32768) * dirty commit Signed-off-by: wbpcode * dirty commit Signed-off-by: wbpcode * complete refactoring and tests Signed-off-by: wbpcode * resolve coverage problem Signed-off-by: wbpcode * handle heartbeat and pipeline requests Signed-off-by: wbpcode --------- Signed-off-by: wbpcode --- .../network/source/codecs/dubbo/config.cc | 76 +-- .../network/source/codecs/dubbo/config.h | 46 +- .../network/test/codecs/dubbo/config_test.cc | 59 +- source/extensions/common/dubbo/BUILD | 3 +- source/extensions/common/dubbo/codec.cc | 15 +- .../common/dubbo/hessian2_serializer_impl.cc | 132 ++-- .../common/dubbo/hessian2_serializer_impl.h | 2 +- source/extensions/common/dubbo/message.cc | 440 ++++++++++++ source/extensions/common/dubbo/message.h | 161 ++++- .../extensions/common/dubbo/message_impl.cc | 95 --- source/extensions/common/dubbo/message_impl.h | 138 ---- source/extensions/common/dubbo/metadata.h | 6 +- test/extensions/common/dubbo/BUILD | 1 - test/extensions/common/dubbo/codec_test.cc | 68 +- .../dubbo/hessian2_serializer_impl_test.cc | 303 ++------- test/extensions/common/dubbo/message_test.cc | 626 +++++++++++++++--- test/extensions/common/dubbo/metadata_test.cc | 10 +- 17 files changed, 1327 insertions(+), 854 deletions(-) create mode 100644 source/extensions/common/dubbo/message.cc delete mode 100644 source/extensions/common/dubbo/message_impl.cc delete mode 100644 source/extensions/common/dubbo/message_impl.h diff --git a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.cc b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.cc index 4bbfa68e1b06..588a40191c1b 100644 --- a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.cc +++ b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.cc @@ -4,7 +4,7 @@ #include "envoy/registry/registry.h" -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" namespace Envoy { namespace Extensions { @@ -31,20 +31,9 @@ Common::Dubbo::ResponseStatus genericStatusToStatus(StatusCode code) { } // namespace void DubboRequest::forEach(IterateCallback callback) const { - const auto* typed_request = - dynamic_cast(&inner_metadata_->mutableRequest()); - ASSERT(typed_request != nullptr); - - for (const auto& pair : typed_request->attachment().attachment()) { - ASSERT(pair.first != nullptr && pair.second != nullptr); - - if (pair.first->type() == Hessian2::Object::Type::String && - pair.second->type() == Hessian2::Object::Type::String) { - ASSERT(pair.first->toString().has_value() && pair.second->toString().has_value()); - - if (!callback(pair.first->toString().value().get(), pair.second->toString().value().get())) { - break; - } + for (const auto& [key, val] : inner_metadata_->request().content().attachments()) { + if (!callback(key, val)) { + break; } } } @@ -53,27 +42,21 @@ absl::optional DubboRequest::get(absl::string_view key) const if (key == VERSION_KEY) { return inner_metadata_->request().serviceVersion(); } - const auto* typed_request = - dynamic_cast(&inner_metadata_->mutableRequest()); - ASSERT(typed_request != nullptr); - return typed_request->attachment().lookup(key); + auto it = inner_metadata_->request().content().attachments().find(key); + if (it == inner_metadata_->request().content().attachments().end()) { + return absl::nullopt; + } + + return absl::string_view{it->second}; } void DubboRequest::set(absl::string_view key, absl::string_view val) { - auto* typed_request = - dynamic_cast(&inner_metadata_->mutableRequest()); - ASSERT(typed_request != nullptr); - - typed_request->mutableAttachment()->insert(key, val); + inner_metadata_->request().content().setAttachment(key, val); } void DubboRequest::erase(absl::string_view key) { - auto* typed_request = - dynamic_cast(&inner_metadata_->mutableRequest()); - ASSERT(typed_request != nullptr); - - typed_request->mutableAttachment()->remove(key); + inner_metadata_->request().content().delAttachment(key); } void DubboResponse::refreshStatus() { @@ -85,19 +68,19 @@ void DubboResponse::refreshStatus() { const auto status = inner_metadata_->context().responseStatus(); const auto optional_type = inner_metadata_->response().responseType(); - // The final status is not ok if the response status is not ResponseStatus::Ok - // anyway. - bool response_ok = (status == Common::Dubbo::ResponseStatus::Ok); + if (status != Common::Dubbo::ResponseStatus::Ok) { + status_ = StreamStatus(static_cast(status), false); + return; + } + bool response_ok = true; // The final status is not ok if the response type is ResponseWithException or // ResponseWithExceptionWithAttachments even if the response status is Ok. - if (status == Common::Dubbo::ResponseStatus::Ok) { - ASSERT(optional_type.has_value()); - auto type = optional_type.value_or(RpcResponseType::ResponseWithException); - if (type == RpcResponseType::ResponseWithException || - type == RpcResponseType::ResponseWithExceptionWithAttachments) { - response_ok = false; - } + ASSERT(optional_type.has_value()); + auto type = optional_type.value_or(RpcResponseType::ResponseWithException); + if (type == RpcResponseType::ResponseWithException || + type == RpcResponseType::ResponseWithExceptionWithAttachments) { + response_ok = false; } status_ = StreamStatus(static_cast(status), response_ok); @@ -105,7 +88,7 @@ void DubboResponse::refreshStatus() { DubboCodecBase::DubboCodecBase(Common::Dubbo::DubboCodecPtr codec) : codec_(std::move(codec)) {} -ResponsePtr DubboServerCodec::respond(Status status, absl::string_view, +ResponsePtr DubboServerCodec::respond(Status status, absl::string_view data, const Request& origin_request) { const auto* typed_request = dynamic_cast(&origin_request); ASSERT(typed_request != nullptr); @@ -113,17 +96,20 @@ ResponsePtr DubboServerCodec::respond(Status status, absl::string_view, Common::Dubbo::ResponseStatus response_status = genericStatusToStatus(status.code()); absl::optional optional_type; - absl::string_view content; if (response_status == Common::Dubbo::ResponseStatus::Ok) { optional_type.emplace(Common::Dubbo::RpcResponseType::ResponseWithException); - content = "exception_via_proxy"; + } + auto response = Common::Dubbo::DirectResponseUtil::localResponse( + *typed_request->inner_metadata_, response_status, optional_type, data); + + if (!status.ok()) { + response->mutableResponse().content().setAttachment("reason", status.message()); } else { - content = status.message(); + response->mutableResponse().content().setAttachment("reason", "envoy_response"); } - return std::make_unique(Common::Dubbo::DirectResponseUtil::localResponse( - *typed_request->inner_metadata_, response_status, optional_type, content)); + return std::make_unique(std::move(response)); } CodecFactoryPtr diff --git a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h index 86acc6f2aa9c..840cb61a733c 100644 --- a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h +++ b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h @@ -35,9 +35,9 @@ class DubboRequest : public Request { void forEach(IterateCallback callback) const override; absl::optional get(absl::string_view key) const override; void set(absl::string_view key, absl::string_view val) override; - absl::string_view host() const override { return inner_metadata_->request().serviceName(); } - absl::string_view path() const override { return inner_metadata_->request().serviceName(); } - absl::string_view method() const override { return inner_metadata_->request().methodName(); } + absl::string_view host() const override { return inner_metadata_->request().service(); } + absl::string_view path() const override { return inner_metadata_->request().service(); } + absl::string_view method() const override { return inner_metadata_->request().method(); } void erase(absl::string_view key) override; // StreamFrame @@ -87,7 +87,7 @@ class DubboDecoderBase : public DubboCodecBase, public CodecType { void setCodecCallbacks(CallBackType& callback) override { callback_ = &callback; } - void decode(Buffer::Instance& buffer, bool) override { + Common::Dubbo::DecodeStatus decodeOne(Buffer::Instance& buffer) { if (metadata_ == nullptr) { metadata_ = std::make_shared(); } @@ -109,27 +109,59 @@ class DubboDecoderBase : public DubboCodecBase, public CodecType { ENVOY_LOG(warn, "Dubbo codec: unexpected decoding error"); metadata_.reset(); callback_->onDecodingFailure(); - return; + return Common::Dubbo::DecodeStatus::Failure; } if (decode_status == Common::Dubbo::DecodeStatus::Waiting) { ENVOY_LOG(debug, "Dubbo codec: waiting for more input data"); - return; + return Common::Dubbo::DecodeStatus::Waiting; } ASSERT(decode_status == Common::Dubbo::DecodeStatus::Success); + if (metadata_->context().heartbeat()) { + Buffer::OwnedImpl heartbeat_response; + + ENVOY_LOG(debug, "Dubbo codec: heartbeat from downstream/upstream"); + constexpr char first_four_bytes[] = {'\xda', '\xbb', '\x22', 20}; + heartbeat_response.add(first_four_bytes, 4); + + heartbeat_response.writeBEInt(metadata_->requestId()); + heartbeat_response.writeBEInt(1); + heartbeat_response.writeByte('N'); + + metadata_.reset(); + callback_->writeToConnection(heartbeat_response); + + return Common::Dubbo::DecodeStatus::Success; + } + auto message = std::make_unique(metadata_); message->stream_frame_flags_ = {{static_cast(metadata_->requestId()), !metadata_->context().isTwoWay(), false, metadata_->context().heartbeat()}, true}; - callback_->onDecodingSuccess(std::move(message)); metadata_.reset(); + callback_->onDecodingSuccess(std::move(message)); + + return Common::Dubbo::DecodeStatus::Success; } catch (const EnvoyException& error) { ENVOY_LOG(warn, "Dubbo codec: decoding error: {}", error.what()); + metadata_.reset(); callback_->onDecodingFailure(); + + return Common::Dubbo::DecodeStatus::Failure; + } + } + + void decode(Buffer::Instance& buffer, bool) override { + while (buffer.length() > 0) { + // Continue decoding if the buffer has more data and the previous decoding is + // successful. + if (decodeOne(buffer) != Common::Dubbo::DecodeStatus::Success) { + break; + } } } diff --git a/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc b/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc index 6a4e3abb8614..f689f79024e2 100644 --- a/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc +++ b/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc @@ -1,7 +1,7 @@ #include #include -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" #include "test/extensions/common/dubbo/mocks.h" #include "test/mocks/server/factory_context.h" @@ -25,26 +25,15 @@ using testing::Return; using namespace Common::Dubbo; MessageMetadataSharedPtr createDubboRequst(bool one_way_request) { - auto request = std::make_unique(); - request->setServiceName("fake_service"); - request->setMethodName("fake_method"); - request->setServiceVersion("fake_version"); - request->setParametersLazyCallback([]() -> RpcRequestImpl::ParametersPtr { - return std::make_unique(); - }); - request->setAttachmentLazyCallback([]() -> RpcRequestImpl::AttachmentPtr { - auto map = std::make_unique(); - Hessian2::ObjectPtr key_o = std::make_unique("group"); - Hessian2::ObjectPtr val_o = std::make_unique("fake_group"); - - map->toMutableUntypedMap().value().get().emplace(std::move(key_o), std::move(val_o)); - return std::make_unique(std::move(map), 0); - }); + auto request = std::make_unique("fake_dubbo_version", "fake_service", "fake_version", + "fake_method"); + + request->content().initialize("", {}, {}); + request->content().setAttachment("group", "fake_group"); auto context = std::make_unique(); context->setMessageType(one_way_request ? MessageType::Oneway : MessageType::Request); context->setRequestId(123456); - context->setSerializeType(SerializeType::Hessian2); auto metadata = std::make_shared(); metadata->setContext(std::move(context)); @@ -245,7 +234,7 @@ TEST(DubboServerCodecTest, DubboServerCodecTest) { buffer.add("anything"); EXPECT_CALL(*raw_serializer, deserializeRpcRequest(_, _)) - .WillOnce(Return(ByMove(std::make_unique()))); + .WillOnce(Return(ByMove(std::make_unique("a", "b", "c", "d")))); EXPECT_CALL(callbacks, onDecodingSuccess(_)); server_codec.decode(buffer, false); @@ -269,42 +258,44 @@ TEST(DubboServerCodecTest, DubboServerCodecTest) { Status status = absl::OkStatus(); DubboRequest request(createDubboRequst(false)); - auto response = server_codec.respond(status, "", request); + auto response = server_codec.respond(status, "anything", request); auto* typed_response = static_cast(response.get()); - auto* typed_inner_response = - static_cast(&typed_response->inner_metadata_->mutableResponse()); + auto& typed_inner_response = typed_response->inner_metadata_->mutableResponse(); EXPECT_EQ(ResponseStatus::Ok, typed_response->inner_metadata_->responseStatus()); - EXPECT_EQ(RpcResponseType::ResponseWithException, typed_inner_response->responseType().value()); - EXPECT_EQ("exception_via_proxy", typed_inner_response->localRawMessage().value()); + EXPECT_EQ(RpcResponseType::ResponseWithException, typed_inner_response.responseType().value()); + EXPECT_EQ("anything", typed_inner_response.content().result()->toString().value().get()); + EXPECT_EQ("envoy_response", typed_inner_response.content().attachments().at("reason")); } { Status status(StatusCode::kInvalidArgument, "test_message"); DubboRequest request(createDubboRequst(false)); - auto response = server_codec.respond(status, "", request); + auto response = server_codec.respond(status, "anything", request); auto* typed_response = static_cast(response.get()); - auto* typed_inner_response = - static_cast(&typed_response->inner_metadata_->mutableResponse()); + auto& typed_inner_response = typed_response->inner_metadata_->mutableResponse(); EXPECT_EQ(ResponseStatus::BadRequest, typed_response->inner_metadata_->responseStatus()); - EXPECT_EQ(false, typed_inner_response->responseType().has_value()); - EXPECT_EQ("test_message", typed_inner_response->localRawMessage().value()); + EXPECT_EQ(false, typed_inner_response.responseType().has_value()); + + EXPECT_EQ("anything", typed_inner_response.content().result()->toString().value().get()); + EXPECT_EQ("test_message", typed_inner_response.content().attachments().at("reason")); } { Status status(StatusCode::kAborted, "test_message2"); DubboRequest request(createDubboRequst(false)); - auto response = server_codec.respond(status, "", request); + auto response = server_codec.respond(status, "anything", request); auto* typed_response = static_cast(response.get()); - auto* typed_inner_response = - static_cast(&typed_response->inner_metadata_->mutableResponse()); + auto& typed_inner_response = typed_response->inner_metadata_->mutableResponse(); EXPECT_EQ(ResponseStatus::ServerError, typed_response->inner_metadata_->responseStatus()); - EXPECT_EQ(false, typed_inner_response->responseType().has_value()); - EXPECT_EQ("test_message2", typed_inner_response->localRawMessage().value()); + EXPECT_EQ(false, typed_inner_response.responseType().has_value()); + + EXPECT_EQ("anything", typed_inner_response.content().result()->toString().value().get()); + EXPECT_EQ("test_message2", typed_inner_response.content().attachments().at("reason")); } } @@ -365,7 +356,7 @@ TEST(DubboClientCodecTest, DubboClientCodecTest) { buffer.writeBEInt(8); buffer.add("anything"); - auto response = std::make_unique(); + auto response = std::make_unique(); response->setResponseType(RpcResponseType::ResponseWithValue); EXPECT_CALL(*raw_serializer, deserializeRpcResponse(_, _)) diff --git a/source/extensions/common/dubbo/BUILD b/source/extensions/common/dubbo/BUILD index e409d8e43339..d3c9340fdb89 100644 --- a/source/extensions/common/dubbo/BUILD +++ b/source/extensions/common/dubbo/BUILD @@ -48,11 +48,10 @@ envoy_cc_library( envoy_cc_library( name = "message_lib", srcs = [ - "message_impl.cc", + "message.cc", ], hdrs = [ "message.h", - "message_impl.h", ], deps = [ ":hessian2_utils_lib", diff --git a/source/extensions/common/dubbo/codec.cc b/source/extensions/common/dubbo/codec.cc index 5fdb34843cdf..d02012b88506 100644 --- a/source/extensions/common/dubbo/codec.cc +++ b/source/extensions/common/dubbo/codec.cc @@ -8,7 +8,6 @@ #include "source/common/common/assert.h" #include "source/extensions/common/dubbo/hessian2_serializer_impl.h" #include "source/extensions/common/dubbo/message.h" -#include "source/extensions/common/dubbo/message_impl.h" #include "source/extensions/common/dubbo/metadata.h" namespace Envoy { @@ -33,7 +32,7 @@ void encodeHeader(Buffer::Instance& buffer, Context& context, uint32_t body_size buffer.writeBEInt(MagicNumber); // Serialize type and flag. - uint8_t flag = static_cast(context.serializeType()); + uint8_t flag = static_cast(SerializeType::Hessian2); switch (context.messageType()) { case MessageType::Response: @@ -167,7 +166,6 @@ DecodeStatus DubboCodec::decodeHeader(Buffer::Instance& buffer, MessageMetadata& absl::StrCat("invalid dubbo message serialization type ", static_cast::type>(serialize_type))); } - context->setSerializeType(serialize_type); // Initial basic type of message. MessageType type = @@ -277,7 +275,6 @@ MessageMetadataSharedPtr DirectResponseUtil::heartbeatResponse(MessageMetadata& auto context = std::make_unique(); // Set context. - context->setSerializeType(request_context.serializeType()); context->setMessageType(MessageType::HeartbeatResponse); context->setResponseStatus(ResponseStatus::Ok); context->setRequestId(request_context.requestId()); @@ -299,7 +296,6 @@ MessageMetadataSharedPtr DirectResponseUtil::localResponse(MessageMetadata& requ auto context = std::make_unique(); // Set context. - context->setSerializeType(request_context.serializeType()); if (status != ResponseStatus::Ok) { context->setMessageType(MessageType::Exception); } else if (type.has_value() && @@ -309,16 +305,17 @@ MessageMetadataSharedPtr DirectResponseUtil::localResponse(MessageMetadata& requ } else { context->setMessageType(MessageType::Response); } + context->setResponseStatus(status); context->setRequestId(request_context.requestId()); // Set response. - auto response = std::make_unique(); - if (status == ResponseStatus::Ok && type.has_value()) { + auto response = std::make_unique(); + if (status == ResponseStatus::Ok) { // No response type for non-Ok response. - response->setResponseType(type.value()); + response->setResponseType(type.value_or(RpcResponseType::ResponseWithValue)); } - response->setLocalRawMessage(content); + response->content().initialize(std::make_unique(content), {}); auto metadata = std::make_shared(); metadata->setContext(std::move(context)); diff --git a/source/extensions/common/dubbo/hessian2_serializer_impl.cc b/source/extensions/common/dubbo/hessian2_serializer_impl.cc index 506e315c8d94..0e49a62bd4f8 100644 --- a/source/extensions/common/dubbo/hessian2_serializer_impl.cc +++ b/source/extensions/common/dubbo/hessian2_serializer_impl.cc @@ -8,7 +8,6 @@ #include "source/common/common/macros.h" #include "source/extensions/common/dubbo/hessian2_utils.h" #include "source/extensions/common/dubbo/message.h" -#include "source/extensions/common/dubbo/message_impl.h" #include "source/extensions/common/dubbo/metadata.h" #include "hessian2/object.hpp" @@ -32,7 +31,6 @@ RpcRequestPtr Hessian2SerializerImpl::deserializeRpcRequest(Buffer::Instance& bu ASSERT(context.messageType() == MessageType::Request || context.messageType() == MessageType::Oneway); ASSERT(context.bodySize() <= buffer.length()); - auto request = std::make_unique(); Hessian2::Decoder decoder(std::make_unique(buffer)); @@ -42,9 +40,11 @@ RpcRequestPtr Hessian2SerializerImpl::deserializeRpcRequest(Buffer::Instance& bu auto service_version = decoder.decode(); auto method_name = decoder.decode(); - if (context.bodySize() < decoder.offset()) { - throw EnvoyException(fmt::format("RpcRequest size({}) larger than body size({})", - decoder.offset(), context.bodySize())); + const auto decoded_size = decoder.offset(); + + if (context.bodySize() < decoded_size) { + throw EnvoyException(fmt::format("RpcRequest size({}) larger than body size({})", decoded_size, + context.bodySize())); } if (dubbo_version == nullptr || service_name == nullptr || service_version == nullptr || @@ -52,46 +52,11 @@ RpcRequestPtr Hessian2SerializerImpl::deserializeRpcRequest(Buffer::Instance& bu throw EnvoyException(fmt::format("RpcRequest has no request metadata")); } - request->setServiceName(*service_name); - request->setServiceVersion(*service_version); - request->setMethodName(*method_name); - - // Move original request message to the raw buffer to delay the decoding of complex body. - request->messageBuffer().move(buffer, context.bodySize()); - const size_t parsed_size = decoder.offset(); - auto delayed_decoder = std::make_shared( - std::make_unique(request->messageBuffer(), parsed_size)); - - request->setParametersLazyCallback([delayed_decoder]() -> RpcRequestImpl::ParametersPtr { - auto params = std::make_unique(); - - if (auto types = delayed_decoder->decode(); types != nullptr && !types->empty()) { - uint32_t number = Hessian2Utils::getParametersNumber(*types); - for (uint32_t i = 0; i < number; i++) { - if (auto result = delayed_decoder->decode(); result != nullptr) { - params->push_back(std::move(result)); - } else { - throw EnvoyException("Cannot parse RpcRequest parameter from buffer"); - } - } - } - return params; - }); - - request->setAttachmentLazyCallback([delayed_decoder]() -> RpcRequestImpl::AttachmentPtr { - size_t offset = delayed_decoder->offset(); - - auto result = delayed_decoder->decode(); - if (result != nullptr && result->type() == Hessian2::Object::Type::UntypedMap) { - return std::make_unique( - RpcRequestImpl::Attachment::MapPtr{ - dynamic_cast(result.release())}, - offset); - } else { - return std::make_unique( - std::make_unique(), offset); - } - }); + buffer.drain(decoded_size); + + auto request = std::make_unique(std::move(*dubbo_version), std::move(*service_name), + std::move(*service_version), std::move(*method_name)); + request->content().initialize(buffer, context.bodySize() - decoded_size); return request; } @@ -106,29 +71,29 @@ RpcResponsePtr Hessian2SerializerImpl::deserializeRpcResponse(Buffer::Instance& return nullptr; } + ASSERT(context.hasResponseStatus()); + // Handle normal response or exception response. ASSERT(context.messageType() == MessageType::Response || context.messageType() == MessageType::Exception); - auto response = std::make_unique(); + + ASSERT(context.hasResponseStatus()); + auto response = std::make_unique(); // Non `Ok` response body has no response type info and skip deserialization. if (context.messageType() == MessageType::Exception) { - ASSERT(context.hasResponseStatus()); ASSERT(context.responseStatus() != ResponseStatus::Ok); - response->messageBuffer().move(buffer, context.bodySize()); + response->content().initialize(buffer, context.bodySize()); return response; } - bool has_value = true; - Hessian2::Decoder decoder(std::make_unique(buffer)); auto type_value = decoder.decode(); if (type_value == nullptr) { throw EnvoyException(fmt::format("Cannot parse RpcResponse type from buffer")); } - RpcResponseType type = static_cast(*type_value); - response->setResponseType(type); + const RpcResponseType type = static_cast(*type_value); switch (type) { case RpcResponseType::ResponseWithException: @@ -136,8 +101,6 @@ RpcResponsePtr Hessian2SerializerImpl::deserializeRpcResponse(Buffer::Instance& context.setMessageType(MessageType::Exception); break; case RpcResponseType::ResponseWithNullValue: - has_value = false; - FALLTHRU; case RpcResponseType::ResponseNullValueWithAttachments: case RpcResponseType::ResponseWithValue: case RpcResponseType::ResponseValueWithAttachments: @@ -146,26 +109,25 @@ RpcResponsePtr Hessian2SerializerImpl::deserializeRpcResponse(Buffer::Instance& throw EnvoyException(fmt::format("not supported return type {}", static_cast(type))); } - size_t total_size = decoder.offset(); + const auto decoded_size = decoder.offset(); - if (context.bodySize() < total_size) { - throw EnvoyException(fmt::format("RpcResponse size({}) large than body size({})", total_size, + if (context.bodySize() < decoded_size) { + throw EnvoyException(fmt::format("RpcResponse size({}) large than body size({})", decoded_size, context.bodySize())); } - if (!has_value && context.bodySize() != total_size) { - throw EnvoyException( - fmt::format("RpcResponse is no value, but the rest of the body size({}) not equal 0", - (context.bodySize() - total_size))); - } + buffer.drain(decoded_size); - response->messageBuffer().move(buffer, context.bodySize()); + response->setResponseType(type); + response->content().initialize(buffer, context.bodySize() - decoded_size); return response; } void Hessian2SerializerImpl::serializeRpcResponse(Buffer::Instance& buffer, MessageMetadata& metadata) { ASSERT(metadata.hasContext()); + ASSERT(metadata.context().hasResponseStatus()); + const auto& context = metadata.context(); if (context.heartbeat()) { @@ -173,22 +135,13 @@ void Hessian2SerializerImpl::serializeRpcResponse(Buffer::Instance& buffer, return; } - ASSERT(dynamic_cast(&metadata.mutableResponse()) != nullptr); - auto* typed_response_info = dynamic_cast(&metadata.mutableResponse()); - - if (typed_response_info->localRawMessage().has_value()) { - // Local direct response. - Hessian2::Encoder encoder(std::make_unique(buffer)); - - if (typed_response_info->responseType().has_value()) { - encoder.encode(static_cast(typed_response_info->responseType().value())); - } - encoder.encode(typed_response_info->localRawMessage().value()); - } else { - // Update of dubbo response is not supported for now. And there is no retry for response - // encoding. So, it is safe to move the data directly. - buffer.move(typed_response_info->messageBuffer()); + ASSERT(metadata.hasResponse()); + if (auto type = metadata.response().responseType(); type.has_value()) { + ASSERT(metadata.context().responseStatus() == ResponseStatus::Ok); + buffer.writeByte(0x90 + static_cast(type.value())); } + + buffer.add(metadata.response().content().buffer()); } void Hessian2SerializerImpl::serializeRpcRequest(Buffer::Instance& buffer, @@ -206,25 +159,14 @@ void Hessian2SerializerImpl::serializeRpcRequest(Buffer::Instance& buffer, ASSERT(metadata.context().messageType() == MessageType::Request || metadata.context().messageType() == MessageType::Oneway); - ASSERT(dynamic_cast(&metadata.mutableRequest()) != nullptr); - auto* typed_request_info = dynamic_cast(&metadata.mutableRequest()); + Hessian2::Encoder encoder(std::make_unique(buffer)); - // Create a copy for possible retry. - Buffer::OwnedImpl copy_of_message = typed_request_info->messageBuffer(); + encoder.encode(metadata.request().version()); + encoder.encode(metadata.request().service()); + encoder.encode(metadata.request().serviceVersion()); + encoder.encode(metadata.request().method()); - if (typed_request_info->hasAttachment() && typed_request_info->attachment().attachmentUpdated()) { - const size_t attachment_offset = typed_request_info->attachment().attachmentOffset(); - ASSERT(attachment_offset <= copy_of_message.length()); - - // Move the parameters to buffer. - buffer.move(copy_of_message, attachment_offset); - - // Re-encode the dubbo attachment. - Hessian2::Encoder encoder(std::make_unique(buffer)); - encoder.encode(typed_request_info->attachment().attachment()); - } else { - buffer.move(copy_of_message); - } + buffer.add(metadata.request().content().buffer()); } } // namespace Dubbo diff --git a/source/extensions/common/dubbo/hessian2_serializer_impl.h b/source/extensions/common/dubbo/hessian2_serializer_impl.h index 1f9354ff93d2..974f41ac4dc6 100644 --- a/source/extensions/common/dubbo/hessian2_serializer_impl.h +++ b/source/extensions/common/dubbo/hessian2_serializer_impl.h @@ -1,6 +1,6 @@ #pragma once -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" #include "source/extensions/common/dubbo/serializer.h" namespace Envoy { diff --git a/source/extensions/common/dubbo/message.cc b/source/extensions/common/dubbo/message.cc new file mode 100644 index 000000000000..a85a0d28cb50 --- /dev/null +++ b/source/extensions/common/dubbo/message.cc @@ -0,0 +1,440 @@ +#include "source/extensions/common/dubbo/message.h" + +#include "source/common/common/logger.h" +#include "source/extensions/common/dubbo/hessian2_utils.h" + +namespace Envoy { +namespace Extensions { +namespace Common { +namespace Dubbo { + +void RequestContent::initialize(Buffer::Instance& buffer, uint64_t length) { + ASSERT(content_buffer_.length() == 0, "content buffer has been initialized"); + + content_buffer_.move(buffer, length); + + // Clear the types, arguments and attachments. + types_.clear(); + argvs_.clear(); + attachs_.clear(); + + // Set both decoded and updated to false since the content has been initialized + // by raw buffer. + decoded_ = false; + updated_ = false; +} + +void RequestContent::initialize(std::string&& types, ArgumentVec&& argvs, Attachments&& attachs) { + ASSERT(content_buffer_.length() == 0, "content buffer has been initialized"); + + // Set the types, arguments and attachments. + types_ = std::move(types); + argvs_ = std::move(argvs); + attachs_ = std::move(attachs); + + // Encode the types, arguments and attachments into the content buffer. + encodeEverything(); + + // Set decoded to true since the content has been initialized by types, + // arguments and attachments. + decoded_ = true; + updated_ = false; +} + +const Buffer::Instance& RequestContent::buffer() { + // Ensure the attachments in the buffer is latest. + + if (content_buffer_.length() == 0) { + encodeEverything(); + } else { + encodeAttachments(); + } + + return content_buffer_; +} + +void RequestContent::bufferMoveTo(Buffer::Instance& buffer) { buffer.move(content_buffer_); } + +const ArgumentVec& RequestContent::arguments() { + lazyDecode(); + + return argvs_; +} + +const Attachments& RequestContent::attachments() { + lazyDecode(); + + return attachs_; +} + +void RequestContent::setAttachment(absl::string_view key, absl::string_view val) { + lazyDecode(); + + updated_ = true; + attachs_[key] = val; +} + +void RequestContent::delAttachment(absl::string_view key) { + lazyDecode(); + + updated_ = true; + attachs_.erase(key); +} + +void RequestContent::lazyDecode() { + if (decoded_) { + return; + } + decoded_ = true; + + // Decode the content buffer into types, arguments and attachments. + Hessian2::Decoder decoder(std::make_unique(content_buffer_)); + + // Handle the types and arguments. + if (auto element = decoder.decode(); element != nullptr) { + uint32_t number = 0; + + if (element->type() == Hessian2::Object::Type::Integer) { + ASSERT(element->toInteger().has_value()); + if (int32_t direct_num = element->toInteger().value(); direct_num == -1) { + if (auto types = decoder.decode(); types != nullptr) { + types_ = *types; + number = Hessian2Utils::getParametersNumber(types_); + } else { + ENVOY_LOG(error, "Cannot parse RpcInvocation parameter types from buffer"); + handleBrokenValue(); + return; + } + } else if (direct_num >= 0) { + number = direct_num; + } else { + ENVOY_LOG(error, "Invalid RpcInvocation parameter number {}", direct_num); + handleBrokenValue(); + return; + } + } else if (element->type() == Hessian2::Object::Type::String) { + ASSERT(element->toString().has_value()); + types_ = element->toString().value().get(); + number = Hessian2Utils::getParametersNumber(types_); + } + + for (uint32_t i = 0; i < number; i++) { + if (auto result = decoder.decode(); result != nullptr) { + argvs_.push_back(std::move(result)); + } else { + ENVOY_LOG(error, "Cannot parse RpcInvocation parameter from buffer"); + handleBrokenValue(); + return; + } + } + } else { + ENVOY_LOG(error, "Cannot parse RpcInvocation from buffer"); + handleBrokenValue(); + return; + } + + // Record the size of the arguments in the content buffer. This is useful for + // re-encoding the attachments. + argvs_size_ = decoder.offset(); + + // Handle the attachments. + auto map = decoder.decode(); + if (map == nullptr || map->type() != Hessian2::Object::Type::UntypedMap) { + return; + } + + for (auto& [key, val] : map->toMutableUntypedMap().value().get()) { + if (key->type() != Hessian2::Object::Type::String || + val->type() != Hessian2::Object::Type::String) { + continue; + } + attachs_.emplace(std::move(key->toMutableString().value().get()), + std::move(val->toMutableString().value().get())); + } +} + +void RequestContent::encodeAttachments() { + // Do nothing if the attachments have not been updated. + if (!updated_) { + return; + } + + // Ensure the content has been decoded before re-encoding it. + lazyDecode(); + + const uint64_t buffer_length = content_buffer_.length(); + ASSERT(buffer_length > 0, "content buffer is empty"); + + // The size of arguments will be set when doing lazyDecode() or encodeEverything(). + if (buffer_length < argvs_size_) { + ENVOY_LOG(error, "arguments size {} is larger than content buffer {}", argvs_size_, + buffer_length); + handleBrokenValue(); + return; + } + + // Create a new buffer to hold the re-encoded content. + + Buffer::OwnedImpl new_content_buffer; + // Copy the types and arguments into the new buffer. + new_content_buffer.move(content_buffer_, argvs_size_); + + // Encode the attachments into the new buffer. + Hessian2::Encoder encoder(std::make_unique(new_content_buffer)); + new_content_buffer.writeByte('H'); + for (auto& [key, val] : attachs_) { + encoder.encode(key); + encoder.encode(val); + } + new_content_buffer.writeByte('Z'); + + // Clear the content buffer and move the new buffer into it. + content_buffer_.drain(content_buffer_.length()); + content_buffer_.move(new_content_buffer); + + updated_ = false; +} + +void RequestContent::encodeEverything() { + ASSERT(content_buffer_.length() == 0, "content buffer contains something"); + + // Encode the types, arguments and attachments into the content buffer. + Hessian2::Encoder encoder(std::make_unique(content_buffer_)); + + // Encode the types into the content buffer first. + if (!types_.empty()) { + encoder.encode(types_); + } else if (!argvs_.empty()) { + encoder.encode(static_cast(argvs_.size())); + } else { + encoder.encode(types_); + } + + // Encode the arguments into the content buffer. + for (auto& arg : argvs_) { + encoder.encode(*arg); + } + + // Record the size of the arguments in the content buffer. This is useful for + // re-encoding the attachments. + argvs_size_ = content_buffer_.length(); + + // Encode the attachments into the content buffer. + content_buffer_.writeByte('H'); + for (auto& [key, val] : attachs_) { + encoder.encode(key); + encoder.encode(val); + } + content_buffer_.writeByte('Z'); + + updated_ = false; +} + +void RequestContent::handleBrokenValue() { + // Because the lazy decoding is used, Envoy cannot reject the message with broken + // content. Instead, it will reset the whole content to an empty state. + + // Clear everything. + content_buffer_.drain(content_buffer_.length()); + types_.clear(); + argvs_.clear(); + attachs_.clear(); + + // Encode everything. + encodeEverything(); + + decoded_ = true; + updated_ = false; +} + +void ResponseContent::initialize(Buffer::Instance& buffer, uint64_t length) { + ASSERT(content_buffer_.length() == 0, "content buffer has been initialized"); + content_buffer_.move(buffer, length); + + // Clear the result and attachments. + result_ = nullptr; + attachs_.clear(); + + // Set both decoded and updated to false since the content has been initialized + // by raw buffer. + decoded_ = false; + updated_ = false; +} + +void ResponseContent::initialize(Hessian2::ObjectPtr&& value, Attachments&& attachs) { + ASSERT(content_buffer_.length() == 0, "content buffer has been initialized"); + + // Set the result and attachments. + result_ = std::move(value); + attachs_ = std::move(attachs); + + // Encode the result and attachments into the content buffer. + encodeEverything(); + + // Set decoded to true since the content has been initialized by result and attachments. + decoded_ = true; + updated_ = false; +} + +const Buffer::Instance& ResponseContent::buffer() { + // Ensure the attachments in the buffer is latest. + if (content_buffer_.length() == 0) { + encodeEverything(); + } else { + encodeAttachments(); + } + + return content_buffer_; +} + +void ResponseContent::bufferMoveTo(Buffer::Instance& buffer) { buffer.move(content_buffer_); } + +const Hessian2::Object* ResponseContent::result() { + lazyDecode(); + + return result_.get(); +} + +const Attachments& ResponseContent::attachments() { + lazyDecode(); + + return attachs_; +} + +void ResponseContent::setAttachment(absl::string_view key, absl::string_view val) { + lazyDecode(); + + updated_ = true; + attachs_[key] = val; +} + +void ResponseContent::delAttachment(absl::string_view key) { + lazyDecode(); + + updated_ = true; + attachs_.erase(key); +} + +void ResponseContent::lazyDecode() { + if (decoded_) { + return; + } + decoded_ = true; + + // Decode the content buffer into result and attachments. + Hessian2::Decoder decoder(std::make_unique(content_buffer_)); + + // Handle the result. + result_ = decoder.decode(); + + if (result_ == nullptr) { + ENVOY_LOG(error, "Cannot parse RpcResult from buffer"); + handleBrokenValue(); + return; + } + + // Record the size of the result in the content buffer. This is useful for + // re-encoding the attachments. + result_size_ = decoder.offset(); + + // Handle the attachments. + auto map = decoder.decode(); + if (map == nullptr || map->type() != Hessian2::Object::Type::UntypedMap) { + return; + } + + for (auto& [key, val] : map->toMutableUntypedMap().value().get()) { + if (key->type() != Hessian2::Object::Type::String || + val->type() != Hessian2::Object::Type::String) { + continue; + } + attachs_.emplace(std::move(key->toMutableString().value().get()), + std::move(val->toMutableString().value().get())); + } +} + +void ResponseContent::encodeAttachments() { + if (!updated_) { + return; + } + + // Ensure the content has been decoded before re-encoding it. + lazyDecode(); + + const uint64_t buffer_length = content_buffer_.length(); + ASSERT(buffer_length > 0, "content buffer is empty"); + + if (buffer_length < result_size_) { + ENVOY_LOG(error, "result size {} is larger than content buffer {}", result_size_, + buffer_length); + handleBrokenValue(); + return; + } + + // Create a new buffer to hold the re-encoded content. + Buffer::OwnedImpl new_content_buffer; + + // Copy the result into the new buffer. + new_content_buffer.move(content_buffer_, result_size_); + + // Encode the attachments into the new buffer. + Hessian2::Encoder encoder(std::make_unique(new_content_buffer)); + new_content_buffer.writeByte('H'); + for (auto& [key, val] : attachs_) { + encoder.encode(key); + encoder.encode(val); + } + new_content_buffer.writeByte('Z'); + + // Clear the content buffer and move the new buffer into it. + content_buffer_.drain(content_buffer_.length()); + content_buffer_.move(new_content_buffer); + + updated_ = false; +} + +void ResponseContent::encodeEverything() { + ASSERT(content_buffer_.length() == 0, "content buffer contains something"); + + // Encode the result and attachments into the content buffer. + Hessian2::Encoder encoder(std::make_unique(content_buffer_)); + if (result_ == nullptr) { + content_buffer_.writeByte('N'); + } else { + encoder.encode(*result_); + } + + // Record the size of the result in the content buffer. This is useful for + // re-encoding the attachments. + result_size_ = content_buffer_.length(); + + content_buffer_.writeByte('H'); + for (auto& [key, val] : attachs_) { + encoder.encode(key); + encoder.encode(val); + } + content_buffer_.writeByte('Z'); + + updated_ = false; +} + +void ResponseContent::handleBrokenValue() { + // Because the lazy decoding is used, Envoy cannot reject the message with broken + // content. Instead, it will reset the whole content to an empty state. + + // Clear everything. + content_buffer_.drain(content_buffer_.length()); + result_ = nullptr; + attachs_.clear(); + + // Encode everything. + encodeEverything(); + + decoded_ = true; + updated_ = false; +} + +} // namespace Dubbo +} // namespace Common +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/dubbo/message.h b/source/extensions/common/dubbo/message.h index 6eeff49bd189..5808e496a5cd 100644 --- a/source/extensions/common/dubbo/message.h +++ b/source/extensions/common/dubbo/message.h @@ -7,6 +7,7 @@ #include "envoy/common/pure.h" #include "source/common/buffer/buffer_impl.h" +#include "source/extensions/common/dubbo/hessian2_utils.h" #include "absl/container/node_hash_map.h" #include "absl/types/optional.h" @@ -84,29 +85,175 @@ enum class RpcResponseType : uint8_t { ResponseNullValueWithAttachments = 5, }; +using Attachments = absl::flat_hash_map; +using ArgumentVec = absl::InlinedVector; + +class RequestContent : Envoy::Logger::Loggable { +public: + // Initialize the content buffer with the given buffer and length. + void initialize(Buffer::Instance& buffer, uint64_t length); + + // Initialize the content buffer with the given types and arguments and attachments. + // The initialize() call will also encode these types and arguments into the + // content buffer. + void initialize(std::string&& types, ArgumentVec&& argvs, Attachments&& attachs); + + // Underlying content buffer. This may re-encode the result and attachments into the + // content buffer to ensure the returned buffer is up-to-date. + const Buffer::Instance& buffer(); + + // Move the content buffer to the given buffer. This only does the move and does not + // re-encode the result and attachments. + void bufferMoveTo(Buffer::Instance& buffer); + + // Get all the arguments of the request. + const ArgumentVec& arguments(); + + // Get all the attachments of the request. + const Attachments& attachments(); + + // Set the attachment with the given key and value. If the key already exists, the + // value will be updated. Otherwise, a new key-value pair will be added. + void setAttachment(absl::string_view key, absl::string_view val); + + // Remove the attachment with the given key. + void delAttachment(absl::string_view key); + +private: + // Decode the content buffer into types, arguments and attachments. The decoding is + // lazy and will be triggered when the content is accessed. + void lazyDecode(); + + // Re-encode the attachments into the content buffer. + void encodeAttachments(); + + // Re-encode the types, arguments and attachments into the content buffer. + void encodeEverything(); + + // Called when the content is broken. The whole content will be reset to an empty + // state. + void handleBrokenValue(); + + Buffer::OwnedImpl content_buffer_; + + // If the content has been decoded. This ensures the decoding is only performed once. + bool decoded_{false}; + + // If the attachments has been updated. This ensures the re-encoding is only + // when the attachment has been modified. + bool updated_{false}; + + uint64_t argvs_size_{0}; + + std::string types_; + ArgumentVec argvs_; + Attachments attachs_; +}; + /** * RpcRequest represent an rpc call. */ class RpcRequest { public: - virtual ~RpcRequest() = default; + RpcRequest(std::string&& version, std::string&& service, std::string&& service_version, + std::string&& method) + : version_(std::move(version)), service_(std::move(service)), + service_version_(std::move(service_version)), method_(std::move(method)) {} + + absl::string_view version() const { return version_; } + absl::string_view service() const { return service_; } + absl::string_view serviceVersion() const { return service_version_; } + absl::string_view method() const { return method_; } + + RequestContent& content() const { return content_; } - virtual absl::string_view serviceName() const PURE; - virtual absl::string_view methodName() const PURE; - virtual absl::string_view serviceVersion() const PURE; - virtual absl::optional serviceGroup() const PURE; +private: + std::string version_; + std::string service_; + std::string service_version_; + std::string method_; + + mutable RequestContent content_; }; using RpcRequestPtr = std::unique_ptr; +class ResponseContent : public Envoy::Logger::Loggable { +public: + // Initialize the content buffer with the given buffer and length. + void initialize(Buffer::Instance& buffer, uint64_t length); + + // Initialize the content buffer with the given result and attachments. The initialize() + // call will also encode the result and attachments into the content buffer. + void initialize(Hessian2::ObjectPtr&& value, Attachments&& attachs); + + // Underlying content buffer. This may re-encode the result and attachments into the + // content buffer to ensure the returned buffer is up-to-date. + const Buffer::Instance& buffer(); + + // Move the content buffer to the given buffer. This only does the move and does not + // re-encode the result and attachments. + void bufferMoveTo(Buffer::Instance& buffer); + + // Get the result of the response. If the content has not been decoded, the decoding + // will be triggered. + const Hessian2::Object* result(); + + // Get all the attachments of the response. + const Attachments& attachments(); + + // Set the attachment with the given key and value. If the key already exists, the + // value will be updated. Otherwise, a new key-value pair will be added. + void setAttachment(absl::string_view key, absl::string_view val); + + // Remove the attachment with the given key. + void delAttachment(absl::string_view key); + +private: + // Decode the content buffer into value and attachments. The decoding is lazy and will + // be triggered when the content is accessed. + void lazyDecode(); + + // Re-encode the attachments into the content buffer. + void encodeAttachments(); + + // Re-encode the result and attachments into the content buffer. + void encodeEverything(); + + // Called when the content is broken. The whole content will be reset to an empty + // state. + void handleBrokenValue(); + + Buffer::OwnedImpl content_buffer_; + + // If the content has been decoded. This ensures the decoding is only performed once. + bool decoded_{false}; + + // If the attachments has been updated. This ensures the re-encoding is only + // when the attachment has been modified. + bool updated_{false}; + + uint64_t result_size_{0}; + + Hessian2::ObjectPtr result_; + Attachments attachs_; +}; + /** * RpcResponse represent the result of an rpc call. */ class RpcResponse { public: - virtual ~RpcResponse() = default; + RpcResponse() = default; + + void setResponseType(RpcResponseType response_type) { response_type_ = response_type; } + absl::optional responseType() const { return response_type_; } + + ResponseContent& content() const { return content_; } - virtual absl::optional responseType() const PURE; +private: + absl::optional response_type_{}; + mutable ResponseContent content_; }; using RpcResponsePtr = std::unique_ptr; diff --git a/source/extensions/common/dubbo/message_impl.cc b/source/extensions/common/dubbo/message_impl.cc deleted file mode 100644 index 89b30dd92b4b..000000000000 --- a/source/extensions/common/dubbo/message_impl.cc +++ /dev/null @@ -1,95 +0,0 @@ -#include "source/extensions/common/dubbo/message_impl.h" - -#include - -namespace Envoy { -namespace Extensions { -namespace Common { -namespace Dubbo { - -RpcRequestImpl::Attachment::Attachment(MapPtr&& value, size_t offset) - : attachment_(std::move(value)), attachment_offset_(offset) { - ASSERT(attachment_ != nullptr); - ASSERT(attachment_->toMutableUntypedMap().has_value()); -} - -void RpcRequestImpl::Attachment::insert(absl::string_view key, absl::string_view value) { - ASSERT(attachment_->toMutableUntypedMap().has_value()); - - attachment_updated_ = true; - - Hessian2::ObjectPtr key_o = std::make_unique(key); - Hessian2::ObjectPtr val_o = std::make_unique(value); - attachment_->toMutableUntypedMap().value().get().insert_or_assign(std::move(key_o), - std::move(val_o)); -} - -void RpcRequestImpl::Attachment::remove(absl::string_view key) { - ASSERT(attachment_->toMutableUntypedMap().has_value()); - - attachment_updated_ = true; - attachment_->toMutableUntypedMap().value().get().erase(key); -} - -absl::optional RpcRequestImpl::Attachment::lookup(absl::string_view key) const { - ASSERT(attachment_->toMutableUntypedMap().has_value()); - - auto& map = attachment_->toMutableUntypedMap().value().get(); - auto result = map.find(key); - if (result != map.end() && result->second->type() == Hessian2::Object::Type::String) { - ASSERT(result->second->toString().has_value()); - return absl::make_optional(result->second->toString().value().get()); - } - return absl::nullopt; -} - -void RpcRequestImpl::assignParametersIfNeed() const { - ASSERT(parameters_lazy_callback_ != nullptr); - if (parameters_ == nullptr) { - parameters_ = parameters_lazy_callback_(); - } -} - -void RpcRequestImpl::assignAttachmentIfNeed() const { - ASSERT(attachment_lazy_callback_ != nullptr); - if (attachment_ != nullptr) { - return; - } - - assignParametersIfNeed(); - attachment_ = attachment_lazy_callback_(); - - if (auto g = attachment_->lookup("group"); g.has_value()) { - const_cast(this)->group_ = std::string(g.value()); - } -} - -absl::optional RpcRequestImpl::serviceGroup() const { - assignAttachmentIfNeed(); - return RpcRequestBase::serviceGroup(); -} - -const RpcRequestImpl::Attachment& RpcRequestImpl::attachment() const { - assignAttachmentIfNeed(); - return *attachment_; -} - -RpcRequestImpl::AttachmentPtr& RpcRequestImpl::mutableAttachment() const { - assignAttachmentIfNeed(); - return attachment_; -} - -const RpcRequestImpl::Parameters& RpcRequestImpl::parameters() const { - assignParametersIfNeed(); - return *parameters_; -} - -RpcRequestImpl::ParametersPtr& RpcRequestImpl::mutableParameters() const { - assignParametersIfNeed(); - return parameters_; -} - -} // namespace Dubbo -} // namespace Common -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/common/dubbo/message_impl.h b/source/extensions/common/dubbo/message_impl.h deleted file mode 100644 index 877cb5dba626..000000000000 --- a/source/extensions/common/dubbo/message_impl.h +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once - -#include - -#include "source/extensions/common/dubbo/hessian2_utils.h" -#include "source/extensions/common/dubbo/message.h" - -namespace Envoy { -namespace Extensions { -namespace Common { -namespace Dubbo { - -class RpcRequestBase : public RpcRequest { -public: - void setServiceName(absl::string_view name) { service_name_ = std::string(name); } - void setMethodName(absl::string_view name) { method_name_ = std::string(name); } - void setServiceVersion(absl::string_view version) { service_version_ = std::string(version); } - void setServiceGroup(absl::string_view group) { group_ = std::string(group); } - - // RpcRequest - absl::string_view serviceName() const override { return service_name_; } - absl::string_view methodName() const override { return method_name_; } - absl::string_view serviceVersion() const override { return service_version_; } - absl::optional serviceGroup() const override { - return group_.has_value() ? absl::make_optional(group_.value()) - : absl::nullopt; - } - -protected: - std::string service_name_; - std::string method_name_; - std::string service_version_; - absl::optional group_; -}; - -class RpcRequestImpl : public RpcRequestBase { -public: - // Each parameter consists of a parameter binary size and Hessian2::Object. - using Parameters = std::vector; - using ParametersPtr = std::unique_ptr; - - class Attachment { - public: - using Map = Hessian2::UntypedMapObject; - using MapPtr = std::unique_ptr; - using String = Hessian2::StringObject; - - Attachment(MapPtr&& value, size_t offset); - - const Map& attachment() const { return *attachment_; } - - void insert(absl::string_view key, absl::string_view value); - void remove(absl::string_view key); - absl::optional lookup(absl::string_view key) const; - - // Whether the attachment should be re-serialized. - bool attachmentUpdated() const { return attachment_updated_; } - - size_t attachmentOffset() const { return attachment_offset_; } - - private: - bool attachment_updated_{false}; - - MapPtr attachment_; - - // The binary offset of attachment in the original message. Retaining this value can help - // subsequent re-serialization of the attachment without re-serializing the parameters. - size_t attachment_offset_{}; - }; - using AttachmentPtr = std::unique_ptr; - - using AttachmentLazyCallback = std::function; - using ParametersLazyCallback = std::function; - - bool hasParameters() const { return parameters_ != nullptr; } - const Parameters& parameters() const; - ParametersPtr& mutableParameters() const; - - bool hasAttachment() const { return attachment_ != nullptr; } - const Attachment& attachment() const; - AttachmentPtr& mutableAttachment() const; - - void setParametersLazyCallback(ParametersLazyCallback&& callback) { - parameters_lazy_callback_ = std::move(callback); - } - - void setAttachmentLazyCallback(AttachmentLazyCallback&& callback) { - attachment_lazy_callback_ = std::move(callback); - } - - absl::optional serviceGroup() const override; - - Buffer::Instance& messageBuffer() { return message_buffer_; } - -private: - void assignParametersIfNeed() const; - void assignAttachmentIfNeed() const; - - // Original request message from downstream. - Buffer::OwnedImpl message_buffer_; - - AttachmentLazyCallback attachment_lazy_callback_; - ParametersLazyCallback parameters_lazy_callback_; - - mutable ParametersPtr parameters_{}; - mutable AttachmentPtr attachment_{}; -}; - -class RpcResponseImpl : public RpcResponse { -public: - void setResponseType(RpcResponseType type) { response_type_ = type; } - - // RpcResponse - absl::optional responseType() const override { return response_type_; } - - Buffer::Instance& messageBuffer() { return message_buffer_; } - - void setLocalRawMessage(absl::string_view val) { local_raw_message_ = std::string(val); } - absl::optional localRawMessage() { - return local_raw_message_.has_value() - ? absl::make_optional(local_raw_message_.value()) - : absl::nullopt; - } - -private: - // Original response message from upstream. - Buffer::OwnedImpl message_buffer_; - - // Optional raw content for local direct response. - absl::optional local_raw_message_; - - absl::optional response_type_; -}; - -} // namespace Dubbo -} // namespace Common -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/common/dubbo/metadata.h b/source/extensions/common/dubbo/metadata.h index 774b73054ce1..6c2790c0ce8f 100644 --- a/source/extensions/common/dubbo/metadata.h +++ b/source/extensions/common/dubbo/metadata.h @@ -7,6 +7,7 @@ #include "source/extensions/common/dubbo/message.h" #include "absl/types/optional.h" +#include "hessian2/object.hpp" namespace Envoy { namespace Extensions { @@ -18,9 +19,6 @@ namespace Dubbo { */ class Context { public: - void setSerializeType(SerializeType type) { serialize_type_ = type; } - SerializeType serializeType() const { return serialize_type_; } - void setMessageType(MessageType type) { message_type_ = type; } MessageType messageType() const { return message_type_; } @@ -47,12 +45,10 @@ class Context { } private: - SerializeType serialize_type_{SerializeType::Hessian2}; MessageType message_type_{MessageType::Request}; absl::optional response_status_{}; int64_t request_id_{}; - size_t body_size_{}; }; diff --git a/test/extensions/common/dubbo/BUILD b/test/extensions/common/dubbo/BUILD index 67e35e09e460..bd3c0a8acc67 100644 --- a/test/extensions/common/dubbo/BUILD +++ b/test/extensions/common/dubbo/BUILD @@ -13,7 +13,6 @@ envoy_cc_test( name = "message_test", srcs = ["message_test.cc"], deps = [ - "//source/extensions/common/dubbo:codec_lib", "//source/extensions/common/dubbo:message_lib", "//test/test_common:printers_lib", "//test/test_common:utility_lib", diff --git a/test/extensions/common/dubbo/codec_test.cc b/test/extensions/common/dubbo/codec_test.cc index 0ec32de8d4e0..89eb86df8c6f 100644 --- a/test/extensions/common/dubbo/codec_test.cc +++ b/test/extensions/common/dubbo/codec_test.cc @@ -1,7 +1,7 @@ #include #include "source/extensions/common/dubbo/codec.h" -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" #include "test/extensions/common/dubbo/mocks.h" #include "test/test_common/printers.h" @@ -227,7 +227,6 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setMessageType(MessageType::Request); context->setRequestId(1); context->setBodySize(buffer.length() + 1); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -245,12 +244,12 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setMessageType(MessageType::Request); context->setRequestId(1); context->setBodySize(buffer.length()); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); EXPECT_CALL(*raw_serializer, deserializeRpcRequest(_, _)) - .WillOnce(testing::Return(testing::ByMove(std::make_unique()))); + .WillOnce( + testing::Return(testing::ByMove(std::make_unique("a", "b", "c", "d")))); EXPECT_EQ(DecodeStatus::Success, codec.decodeData(buffer, metadata)); EXPECT_EQ(true, metadata.hasRequest()); @@ -267,7 +266,6 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setMessageType(MessageType::HeartbeatRequest); context->setRequestId(1); context->setBodySize(buffer.length()); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -290,12 +288,11 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setRequestId(1); context->setBodySize(buffer.length()); context->setResponseStatus(ResponseStatus::Ok); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); EXPECT_CALL(*raw_serializer, deserializeRpcResponse(_, _)) - .WillOnce(testing::Return(testing::ByMove(std::make_unique()))); + .WillOnce(testing::Return(testing::ByMove(std::make_unique()))); EXPECT_EQ(DecodeStatus::Success, codec.decodeData(buffer, metadata)); EXPECT_EQ(true, metadata.hasResponse()); @@ -313,7 +310,6 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setRequestId(1); context->setBodySize(buffer.length()); context->setResponseStatus(ResponseStatus::Ok); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -336,7 +332,6 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setRequestId(1); context->setBodySize(buffer.length()); context->setResponseStatus(ResponseStatus::Ok); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -360,7 +355,6 @@ TEST(DubboCodecTest, EncodeTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -397,7 +391,6 @@ TEST(DubboCodecTest, EncodeTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Oneway); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -433,7 +426,6 @@ TEST(DubboCodecTest, EncodeTest) { auto context = std::make_unique(); context->setMessageType(MessageType::HeartbeatRequest); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -470,7 +462,6 @@ TEST(DubboCodecTest, EncodeTest) { context->setMessageType(MessageType::Response); context->setResponseStatus(ResponseStatus::Ok); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -507,7 +498,6 @@ TEST(DubboCodecTest, EncodeTest) { context->setMessageType(MessageType::Exception); context->setResponseStatus(ResponseStatus::BadRequest); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -544,7 +534,6 @@ TEST(DubboCodecTest, EncodeTest) { context->setMessageType(MessageType::Exception); context->setResponseStatus(ResponseStatus::ServerError); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -581,7 +570,6 @@ TEST(DubboCodecTest, EncodeTest) { context->setMessageType(MessageType::HeartbeatResponse); context->setResponseStatus(ResponseStatus::Ok); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -618,7 +606,6 @@ TEST(DubboCodecTest, EncodeTest) { context->setMessageType(static_cast(6)); context->setResponseStatus(ResponseStatus::Ok); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -637,7 +624,6 @@ TEST(DubboCodecTest, EncodeHeaderForTestTest) { context->setMessageType(static_cast(6)); context->setResponseStatus(ResponseStatus::Ok); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); EXPECT_DEATH(codec.encodeHeaderForTest(buffer, *context), ".*panic: corrupted enum.*"); } @@ -650,7 +636,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::HeartbeatRequest); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -658,7 +643,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::HeartbeatResponse, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::Ok, response->responseStatus()); EXPECT_EQ(true, response->context().heartbeat()); } @@ -669,7 +653,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -678,14 +661,12 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Response, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::Ok, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); EXPECT_EQ(RpcResponseType::ResponseWithValue, response->response().responseType().value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } // Local normal response without response type. @@ -694,7 +675,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -703,14 +683,13 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Response, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::Ok, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); - EXPECT_EQ(false, response->response().responseType().has_value()); + EXPECT_EQ(true, response->response().responseType().has_value()); + EXPECT_EQ(RpcResponseType::ResponseWithValue, response->response().responseType().value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } // Local normal response with exception type. @@ -719,7 +698,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -728,14 +706,12 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Exception, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::Ok, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); EXPECT_EQ(RpcResponseType::ResponseWithException, response->response().responseType().value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } // Local normal response with exception type. @@ -744,7 +720,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -754,15 +729,13 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Exception, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::Ok, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); EXPECT_EQ(RpcResponseType::ResponseWithExceptionWithAttachments, response->response().responseType().value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } // Local exception response. @@ -771,7 +744,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -780,15 +752,13 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Exception, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::BadRequest, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); // Response type will be ignored for non-Ok response. EXPECT_EQ(false, response->response().responseType().has_value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } // Local response without request context. @@ -800,15 +770,13 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Exception, response->messageType()); EXPECT_EQ(0, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::BadRequest, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); // Response type will be ignored for non-Ok response. EXPECT_EQ(false, response->response().responseType().has_value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } } diff --git a/test/extensions/common/dubbo/hessian2_serializer_impl_test.cc b/test/extensions/common/dubbo/hessian2_serializer_impl_test.cc index ef78accf9773..067782a2bf37 100644 --- a/test/extensions/common/dubbo/hessian2_serializer_impl_test.cc +++ b/test/extensions/common/dubbo/hessian2_serializer_impl_test.cc @@ -35,8 +35,8 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequest) { auto result = serializer.deserializeRpcRequest(buffer, *context); ASSERT(result != nullptr); - EXPECT_EQ("test", result->methodName()); - EXPECT_EQ("test", result->serviceName()); + EXPECT_EQ("test", result->method()); + EXPECT_EQ("test", result->service()); EXPECT_EQ("0.0.0", result->serviceVersion()); } @@ -75,12 +75,17 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequest) { TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { Hessian2SerializerImpl serializer; - RpcRequestImpl::Attachment attach(std::make_unique(), 0); - attach.insert("test1", "test_value1"); - attach.insert("test2", "test_value2"); - attach.insert("test3", "test_value3"); + Hessian2::Object::UntypedMap untyped_map; + untyped_map.emplace(Hessian2::ObjectPtr{new Hessian2::StringObject("test1")}, + Hessian2::ObjectPtr{new Hessian2::StringObject("test_value1")}); + untyped_map.emplace(Hessian2::ObjectPtr{new Hessian2::StringObject("test2")}, + Hessian2::ObjectPtr{new Hessian2::StringObject("test_value2")}); + untyped_map.emplace(Hessian2::ObjectPtr{new Hessian2::StringObject("test3")}, + Hessian2::ObjectPtr{new Hessian2::StringObject("test_value3")}); - RpcRequestImpl::Parameters params; + auto map_object = std::make_unique(std::move(untyped_map)); + + ArgumentVec params; params.push_back(std::make_unique("test_string")); @@ -124,12 +129,10 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { encoder.encode(*param); } // Encode an untyped map object as fourth parameter. - encoder.encode(attach.attachment()); - - size_t expected_attachment_offset = buffer.length(); + encoder.encode(*map_object); // Encode attachment - encoder.encode(attach.attachment()); + encoder.encode(*map_object); auto context = std::make_unique(); context->setBodySize(buffer.length()); @@ -137,28 +140,18 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { auto result = serializer.deserializeRpcRequest(buffer, *context); EXPECT_NE(nullptr, result); - auto invo = dynamic_cast(result.get()); - // All data be moved to buffer in the request. EXPECT_EQ(0, buffer.length()); - EXPECT_EQ(context->bodySize(), invo->messageBuffer().length()); - - EXPECT_EQ(false, invo->hasAttachment()); - EXPECT_EQ(false, invo->hasParameters()); - - auto& result_params = invo->mutableParameters(); - // When parsing parameters, attachment will not be parsed. - EXPECT_EQ(false, invo->hasAttachment()); - EXPECT_EQ(true, invo->hasParameters()); + auto& result_params = result->content().arguments(); - EXPECT_EQ(4, result_params->size()); + EXPECT_EQ(4, result_params.size()); - EXPECT_EQ("test_string", result_params->at(0)->toString().value().get()); - EXPECT_EQ(4, result_params->at(1)->toBinary().value().get().at(4)); - EXPECT_EQ(233333, *result_params->at(2)->toLong()); - EXPECT_EQ(3, result_params->at(3)->toUntypedMap().value().get().size()); - EXPECT_EQ("test_value2", result_params->at(3) + EXPECT_EQ("test_string", result_params.at(0)->toString().value().get()); + EXPECT_EQ(4, result_params.at(1)->toBinary().value().get().at(4)); + EXPECT_EQ(233333, *result_params.at(2)->toLong()); + EXPECT_EQ(3, result_params.at(3)->toUntypedMap().value().get().size()); + EXPECT_EQ("test_value2", result_params.at(3) ->toUntypedMap() .value() .get() @@ -167,76 +160,9 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { .value() .get()); - auto& result_attach = invo->mutableAttachment(); - EXPECT_EQ("test_value2", result_attach->attachment() - .toUntypedMap() - .value() - .get() - .find("test2") - ->second->toString() - .value() - .get()); - - EXPECT_EQ(expected_attachment_offset, result_attach->attachmentOffset()); + EXPECT_EQ("test_value2", result->content().attachments().at("test2")); } - { - Buffer::OwnedImpl buffer; - buffer.add(std::string({ - 0x05, '2', '.', '0', '.', '2', // Dubbo version - 0x04, 't', 'e', 's', 't', // Service name - 0x05, '0', '.', '0', '.', '0', // Service version - 0x04, 't', 'e', 's', 't', // method name - })); - - Hessian2::Encoder encoder(std::make_unique(buffer)); - - encoder.encode(parameters_type); - - for (const auto& param : params) { - encoder.encode(*param); - } - // Encode an untyped map object as fourth parameter. - encoder.encode(attach.attachment()); - - // Encode attachment - encoder.encode(attach.attachment()); - - auto context = std::make_unique(); - context->setBodySize(buffer.length()); - - auto result = serializer.deserializeRpcRequest(buffer, *context); - EXPECT_NE(nullptr, result); - auto invo = dynamic_cast(result.get()); - - EXPECT_EQ(false, invo->hasAttachment()); - EXPECT_EQ(false, invo->hasParameters()); - - auto& result_attach = invo->mutableAttachment(); - - // When parsing attachment, parameters will also be parsed. - EXPECT_EQ(true, invo->hasAttachment()); - EXPECT_EQ(true, invo->hasParameters()); - - EXPECT_EQ("test_value2", result_attach->attachment() - .toUntypedMap() - .value() - .get() - .find("test2") - ->second->toString() - .value() - .get()); - - auto& result_params = invo->parameters(); - EXPECT_EQ("test_value2", result_params.at(3) - ->toUntypedMap() - .value() - .get() - .find("test2") - ->second->toString() - .value() - .get()); - } // Test case that request only have parameters. { Buffer::OwnedImpl buffer; @@ -255,7 +181,7 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { encoder.encode(*param); } // Encode an untyped map object as fourth parameter. - encoder.encode(attach.attachment()); + encoder.encode(*map_object); auto context = std::make_unique(); context->setBodySize(buffer.length()); @@ -263,28 +189,8 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { auto result = serializer.deserializeRpcRequest(buffer, *context); EXPECT_NE(nullptr, result); - auto invo = dynamic_cast(result.get()); - - EXPECT_EQ(false, invo->hasAttachment()); - EXPECT_EQ(false, invo->hasParameters()); - - auto& result_attach = invo->mutableAttachment(); - - // When parsing attachment, parameters will also be parsed. - EXPECT_EQ(true, invo->hasAttachment()); - EXPECT_EQ(true, invo->hasParameters()); - - auto& result_params = invo->parameters(); - EXPECT_EQ("test_value2", result_params.at(3) - ->toUntypedMap() - .value() - .get() - .find("test2") - ->second->toString() - .value() - .get()); - - EXPECT_EQ(true, result_attach->attachment().toUntypedMap().value().get().empty()); + EXPECT_EQ(4, result->content().arguments().size()); + EXPECT_EQ(true, result->content().attachments().empty()); } // Test the case where there are not enough parameters in the request buffer. { @@ -311,45 +217,9 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { auto result = serializer.deserializeRpcRequest(buffer, *context); EXPECT_NE(nullptr, result); - auto invo = dynamic_cast(result.get()); - - // There are not enough parameters and throws an exception. - EXPECT_THROW_WITH_MESSAGE(invo->mutableParameters(), EnvoyException, - "Cannot parse RpcRequest parameter from buffer"); - } - // Test for incorrect attachment types. - { - Buffer::OwnedImpl buffer; - buffer.add(std::string({ - 0x05, '2', '.', '0', '.', '2', // Dubbo version - 0x04, 't', 'e', 's', 't', // Service name - 0x05, '0', '.', '0', '.', '0', // Service version - 0x04, 't', 'e', 's', 't', // method name - })); - - Hessian2::Encoder encoder(std::make_unique(buffer)); - - encoder.encode(parameters_type); - - for (const auto& param : params) { - encoder.encode(*param); - } - // Encode an untyped map object as fourth parameter. - encoder.encode(attach.attachment()); - - // Encode a string object as attachment. - encoder.encode(*params[0]); - - auto context = std::make_unique(); - context->setBodySize(buffer.length()); - - auto result = serializer.deserializeRpcRequest(buffer, *context); - EXPECT_NE(nullptr, result); - - auto invo = dynamic_cast(result.get()); - - auto& result_attach = invo->mutableAttachment(); - EXPECT_EQ(true, result_attach->attachment().toUntypedMap().value().get().empty()); + // The request will be reset to an empty state. + EXPECT_EQ(true, result->content().arguments().empty()); + EXPECT_EQ(true, result->content().attachments().empty()); } } @@ -560,26 +430,6 @@ TEST(Hessian2ProtocolTest, deserializeRpcResponse) { EXPECT_THROW_WITH_MESSAGE(serializer.deserializeRpcResponse(buffer, *context), EnvoyException, "not supported return type 6"); } - - // incorrect value size - { - Buffer::OwnedImpl buffer; - buffer.add(std::string({ - '\x92', // without the value of the return type - 0x04, 't', 'e', 's', 't', // return body - })); - std::string exception_string = - fmt::format("RpcResponse is no value, but the rest of the body size({}) not equal 0", - buffer.length() - 1); - - auto context = std::make_unique(); - context->setMessageType(MessageType::Response); - context->setResponseStatus(ResponseStatus::Ok); - context->setBodySize(buffer.length()); - - EXPECT_THROW_WITH_MESSAGE(serializer.deserializeRpcResponse(buffer, *context), EnvoyException, - exception_string); - } } TEST(Hessian2ProtocolTest, serializeRpcRequest) { @@ -604,11 +454,12 @@ TEST(Hessian2ProtocolTest, serializeRpcRequest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); - auto request = std::make_unique(); - request->setServiceName("test.service"); - request->setMethodName("test.method"); - request->setServiceVersion("test.version"); - request->messageBuffer().add("anything_for_no_attachment_update"); + auto request = std::make_unique("v", "v", "v", "v"); + + ArgumentVec args; + args.push_back(std::make_unique(true)); + + request->content().initialize("Z", std::move(args), {}); auto metadata = std::make_shared(); metadata->setContext(std::move(context)); @@ -617,9 +468,9 @@ TEST(Hessian2ProtocolTest, serializeRpcRequest) { Buffer::OwnedImpl buffer; serializer.serializeRpcRequest(buffer, *metadata); - // No attachment update and the message buffer will be used directly to - // accelerate the encoding. - EXPECT_EQ("anything_for_no_attachment_update", buffer.toString()); + EXPECT_EQ( + std::string({'\x1', 'v', '\x1', 'v', '\x1', 'v', '\x1', 'v', '\x1', 'Z', 'T', 'H', 'Z'}), + buffer.toString()); } // Normal request with attachment update. @@ -628,23 +479,13 @@ TEST(Hessian2ProtocolTest, serializeRpcRequest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); - auto request = std::make_unique(); - request->setServiceName("test.service"); - request->setMethodName("test.method"); - request->setServiceVersion("test.version"); - request->messageBuffer().add("anything_for_attachment_update"); - - size_t fake_attachment_offset = 8; + auto request = std::make_unique("v", "v", "v", "v"); - request->setParametersLazyCallback([]() -> RpcRequestImpl::ParametersPtr { - return std::make_unique(); - }); + ArgumentVec args; + args.push_back(std::make_unique(true)); - request->setAttachmentLazyCallback([fake_attachment_offset]() -> RpcRequestImpl::AttachmentPtr { - return std::make_unique( - std::make_unique(), fake_attachment_offset); - }); - request->mutableAttachment()->insert("key", "value"); + request->content().initialize("Z", std::move(args), {}); + request->content().setAttachment("key", "value"); auto metadata = std::make_shared(); metadata->setContext(std::move(context)); @@ -653,9 +494,6 @@ TEST(Hessian2ProtocolTest, serializeRpcRequest) { Buffer::OwnedImpl buffer; serializer.serializeRpcRequest(buffer, *metadata); - // 8 (fake_attachment_offset) bytes for original parameters and 12 bytes for updated attachment. - EXPECT_EQ(20, buffer.length()); - EXPECT_EQ(true, absl::StrContains(buffer.toString(), "value")); } } @@ -684,28 +522,13 @@ TEST(Hessian2ProtocolTest, serializeRpcResponse) { context->setMessageType(MessageType::Response); context->setResponseStatus(ResponseStatus::Ok); - auto response = std::make_unique(); - response->messageBuffer().add("anything"); - - auto metadata = std::make_shared(); - metadata->setContext(std::move(context)); - metadata->setResponse(std::move(response)); - - Buffer::OwnedImpl buffer; - serializer.serializeRpcResponse(buffer, *metadata); - - // The data in message buffer will be used directly for normal response. - EXPECT_EQ("anything", buffer.toString()); - } - - // Local response without response type. - { - auto context = std::make_unique(); - context->setMessageType(MessageType::Response); - context->setResponseStatus(ResponseStatus::Ok); + auto response = std::make_unique(); + response->setResponseType(RpcResponseType::ResponseWithValue); - auto response = std::make_unique(); - response->setLocalRawMessage("anything"); + Buffer::OwnedImpl response_content; + response_content.writeByte('\x08'); + response_content.add("anything"); + response->content().initialize(response_content, 9); auto metadata = std::make_shared(); metadata->setContext(std::move(context)); @@ -714,34 +537,8 @@ TEST(Hessian2ProtocolTest, serializeRpcResponse) { Buffer::OwnedImpl buffer; serializer.serializeRpcResponse(buffer, *metadata); - auto buffer_string = buffer.toString(); - - EXPECT_EQ(0x08, static_cast(buffer_string[0])); // Check length. - EXPECT_EQ("anything", buffer_string.substr(1)); - } - - // Local response with response type. - { - auto context = std::make_unique(); - context->setMessageType(MessageType::Response); - context->setResponseStatus(ResponseStatus::Ok); - - auto response = std::make_unique(); - response->setResponseType(RpcResponseType::ResponseWithException); - response->setLocalRawMessage("anything"); - - auto metadata = std::make_shared(); - metadata->setContext(std::move(context)); - metadata->setResponse(std::move(response)); - - Buffer::OwnedImpl buffer; - serializer.serializeRpcResponse(buffer, *metadata); - - auto buffer_string = buffer.toString(); - - EXPECT_EQ(0x90, static_cast(buffer_string[0])); // Check response type. - EXPECT_EQ(0x08, static_cast(buffer_string[1])); // Check length. - EXPECT_EQ("anything", buffer_string.substr(2)); + // The data in message buffer will be used directly for normal response. + EXPECT_EQ("anything", buffer.toString().substr(2)); } } diff --git a/test/extensions/common/dubbo/message_test.cc b/test/extensions/common/dubbo/message_test.cc index faa933638736..ebe04176d896 100644 --- a/test/extensions/common/dubbo/message_test.cc +++ b/test/extensions/common/dubbo/message_test.cc @@ -1,4 +1,4 @@ -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" #include "test/test_common/printers.h" #include "test/test_common/utility.h" @@ -12,136 +12,552 @@ namespace Common { namespace Dubbo { namespace { -TEST(RpcRequestImplTest, RpcRequestAttachmentTest) { - auto map = std::make_unique(); +TEST(RpcRequestTest, SimpleSetAndGetTest) { + RpcRequest request("a", "b", "c", "d"); - map->emplace(std::make_unique("group"), - std::make_unique("fake_group")); - map->emplace(std::make_unique("fake_key"), - std::make_unique("fake_value")); + EXPECT_EQ("a", request.version()); - map->emplace(std::make_unique(), std::make_unique(0)); + EXPECT_EQ("b", request.service()); - map->emplace(std::make_unique("map_key"), - std::make_unique()); + EXPECT_EQ("c", request.serviceVersion()); - RpcRequestImpl::Attachment attachment(std::move(map), 23333); - - EXPECT_EQ(4, attachment.attachment().toUntypedMap().value().get().size()); - - // Test lookup. - EXPECT_EQ(absl::nullopt, attachment.lookup("map_key")); - EXPECT_EQ("fake_group", *attachment.lookup("group")); - - EXPECT_FALSE(attachment.attachmentUpdated()); - - // Test remove. Remove a normal string type key/value pair. - EXPECT_EQ("fake_value", *attachment.lookup("fake_key")); - attachment.remove("fake_key"); - EXPECT_EQ(absl::nullopt, attachment.lookup("fake_key")); - - EXPECT_EQ(3, attachment.attachment().toUntypedMap().value().get().size()); - - // Test remove. Delete a key/value pair whose value type is map. - attachment.remove("map_key"); - EXPECT_EQ(2, attachment.attachment().toUntypedMap().value().get().size()); - - // Test insert. - attachment.insert("test", "test_value"); - EXPECT_EQ(3, attachment.attachment().toUntypedMap().value().get().size()); - - EXPECT_EQ("test_value", *attachment.lookup("test")); - - EXPECT_TRUE(attachment.attachmentUpdated()); - EXPECT_EQ(23333, attachment.attachmentOffset()); + EXPECT_EQ("d", request.method()); } -TEST(RpcRequestImplTest, RpcRequestImplTest) { - RpcRequestImpl request; - - request.setServiceName("fake_service"); - EXPECT_EQ("fake_service", request.serviceName()); +TEST(RpcRequestTest, InitializeWithBufferTest) { + // Empty buffer. + { + RpcRequest request("a", "b", "c", "d"); + + // Initialize the request with an empty buffer. + Buffer::OwnedImpl buffer; + request.content().initialize(buffer, buffer.length()); + + // Try get the argument vector. + EXPECT_EQ(request.content().arguments().size(), 0); + + // Try get the attachment group. + EXPECT_FALSE(request.content().attachments().contains("group")); + + // attachments() call will make the content to be decoded. + // And the content is empty, so the content will be treated as broken. + // So the content will be reset to an empty state: + // empty types, empty arguments, empty attachments. + EXPECT_EQ(request.content().buffer().toString(), std::string({0x0, 'H', 'Z'})); + + // Set the group. + request.content().setAttachment("group", "group"); + EXPECT_EQ("group", request.content().attachments().at("group")); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + 0x0, // Empty string types + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + } + + // Buffer no argument but has attachment. + { + RpcRequest request("a", "b", "c", "d"); + + Buffer::OwnedImpl buffer(std::string({ + 0x0, // Empty string types + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z', // Attachments end + 'x' // Anything that make no sense + })); + + // Initialize the request with an empty buffer. + request.content().initialize(buffer, buffer.length() - 1); + + EXPECT_EQ(1, buffer.length()); // Other data should be consumed. + EXPECT_EQ(buffer.toString(), "x"); + + // Try get the argument vector. + EXPECT_EQ(request.content().arguments().size(), 0); + + // Try get the attachment group. + EXPECT_EQ("group", request.content().attachments().at("group")); + + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + 0x0, // Empty string types + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + + // Overwrite the group. + request.content().setAttachment("group", "groupx"); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + 0x0, // Empty string types + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x6, 'g', 'r', 'o', 'u', 'p', 'x', // Value + 'Z' // Attachments end + })); + + request.content().delAttachment("group"); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + 0x0, // Empty string types + 'H', // Attachments start + 'Z' // Attachments end + })); + } + + // Broken buffer where -1 is used to tell the arguments number but + // no following types string. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + '\x8f', // -1 means the following bytes is types string + 'H', + 'Z', + })); + + // Initialize the request with the broken buffer. + request.content().initialize(buffer, buffer.length()); + + EXPECT_EQ(0, request.content().arguments().size()); + + // The content is broken, so the content will be reset to an empty state: + // empty types, empty arguments, empty attachments. + EXPECT_EQ(request.content().buffer().toString(), std::string({0x0, 'H', 'Z'})); + } + + // Broken buffer where -2 is used to tell the arguments number. This is + // unexpected number. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + '\x8e', // -2 is unexpected + 'H', + 'Z', + })); + + // Initialize the request with the broken buffer. + request.content().initialize(buffer, buffer.length()); + + EXPECT_EQ(0, request.content().arguments().size()); + + // The content is broken, so the content will be reset to an empty state: + // empty types, empty arguments, empty attachments. + EXPECT_EQ(request.content().buffer().toString(), std::string({0x0, 'H', 'Z'})); + } + + // Correct buffer where -1 is used to tell the arguments number. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + '\x8f', // -1 means the following bytes is types string + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + + // Initialize the request with the correct buffer. + request.content().initialize(buffer, buffer.length()); + + // Get the argument vector. + const auto& args = request.content().arguments(); + + // There are 2 arguments. + EXPECT_EQ(2, args.size()); + + // The first argument is true. + EXPECT_EQ(true, args[0]->toBoolean().value().get()); + // The second argument is false. + EXPECT_EQ(false, args[1]->toBoolean().value().get()); + + // Get the attachment group. + EXPECT_EQ("group", request.content().attachments().at("group")); + + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + '\x8f', // -1 means the following bytes is types string + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + + // Move the buffer. + Buffer::OwnedImpl buffer2; + request.content().bufferMoveTo(buffer2); + + // The buffer is moved, and if we call buffer() again, everything will be + // re-encoded. + // Note: -1 is removed because the single types string is enough. + EXPECT_EQ(request.content().buffer().toString(), std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + } + + // Correct buffer with non-negative integer for the arguments number. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + '\x92', // 2 means the following are two arguments + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + + // Initialize the request with the correct buffer. + request.content().initialize(buffer, buffer.length()); + + // Get the argument vector. + const auto& args = request.content().arguments(); + + // There are 2 arguments. + EXPECT_EQ(2, args.size()); + + // The first argument is true. + EXPECT_EQ(true, args[0]->toBoolean().value().get()); + // The second argument is false. + EXPECT_EQ(false, args[1]->toBoolean().value().get()); + + // Get the attachment group. + EXPECT_EQ("group", request.content().attachments().at("group")); + + // Move the buffer. + Buffer::OwnedImpl buffer2; + request.content().bufferMoveTo(buffer2); + + // No types string is provided and the arguments are not empty. + // So the number of arguments will be used directly. + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + '\x92', // 2 means the following are two arguments + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + } + + // Normal correct buffer with normal types string. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'T', // Key + 'F', // Value + 'Z' // Attachments end + })); + + // Initialize the request with the correct buffer. + request.content().initialize(buffer, buffer.length()); + + // Get the argument vector. + const auto& args = request.content().arguments(); + + // There are 2 arguments. + EXPECT_EQ(2, args.size()); + + // The first argument is true. + EXPECT_EQ(true, args[0]->toBoolean().value().get()); + // The second argument is false. + EXPECT_EQ(false, args[1]->toBoolean().value().get()); + + // Only string key and value are used. + EXPECT_EQ(1, request.content().attachments().size()); + + // Get the attachment group. + EXPECT_EQ("group", request.content().attachments().at("group")); + + // Move the buffer. + Buffer::OwnedImpl buffer2; + request.content().bufferMoveTo(buffer2); + + EXPECT_EQ(request.content().buffer().toString(), std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z', // Attachments end + })); + } + + // Buffer without attachments. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + })); + + // Initialize the request with the correct buffer. + request.content().initialize(buffer, buffer.length()); + + // Get the argument vector. + const auto& args = request.content().arguments(); + + // There are 2 arguments. + EXPECT_EQ(2, args.size()); + + // The first argument is true. + EXPECT_EQ(true, args[0]->toBoolean().value().get()); + // The second argument is false. + EXPECT_EQ(false, args[1]->toBoolean().value().get()); + + EXPECT_TRUE(request.content().attachments().empty()); + + // Move the buffer. + Buffer::OwnedImpl buffer2; + request.content().bufferMoveTo(buffer2); + + // Empty attachments will be encoded to the buffer when buffer() is called + // and the underlying buffer is empty. + EXPECT_EQ(request.content().buffer().toString(), std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 'Z', // Attachments end + })); + + // Set the attachment. + request.content().setAttachment("group", "group"); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(request.content().buffer().toString(), std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + } +} - request.setMethodName("fake_method"); - EXPECT_EQ("fake_method", request.methodName()); +TEST(RpcRequestTest, InitializeWithDecodedValuesTest) { + RpcRequest request("a", "b", "c", "d"); - request.setServiceVersion("fake_version"); - EXPECT_EQ("fake_version", request.serviceVersion()); + ArgumentVec args; + args.push_back(std::make_unique(true)); + args.push_back(std::make_unique(false)); - bool set_parameters{false}; - bool set_attachment{false}; + Attachments attachs; + attachs.emplace("group", "group"); - request.setParametersLazyCallback([&set_parameters]() -> RpcRequestImpl::ParametersPtr { - set_parameters = true; - return std::make_unique(); - }); + // Initialize the request with the given types and arguments and attachments. + request.content().initialize(std::string("ZZ"), std::move(args), std::move(attachs)); - request.setAttachmentLazyCallback([&set_attachment]() -> RpcRequestImpl::AttachmentPtr { - auto map = std::make_unique(); + // Get the argument vector. + const auto& args2 = request.content().arguments(); - map->emplace(std::make_unique("group"), - std::make_unique("fake_group")); + // There are 2 arguments. + EXPECT_EQ(2, args2.size()); - auto attach = std::make_unique(std::move(map), 0); + // The first argument is true. + EXPECT_EQ(true, args2[0]->toBoolean().value().get()); + // The second argument is false. + EXPECT_EQ(false, args2[1]->toBoolean().value().get()); - set_attachment = true; + // Get the attachment group. + EXPECT_EQ("group", request.content().attachments().at("group")); - return attach; - }); + // Get the buffer. + EXPECT_EQ(request.content().buffer().toString(), std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); +} - EXPECT_EQ(false, request.hasParameters()); - EXPECT_EQ(false, request.hasAttachment()); +TEST(RpcResponseTest, SimpleSetAndGetTest) { + RpcResponse response; - // When parsing attachment, parameters will also be parsed. - EXPECT_NE(nullptr, request.mutableAttachment()); - request.attachment(); - EXPECT_EQ(true, set_parameters); - EXPECT_EQ(true, set_attachment); - EXPECT_EQ(true, request.hasParameters()); - EXPECT_EQ(true, request.hasAttachment()); - EXPECT_EQ("fake_group", request.serviceGroup().value()); + EXPECT_EQ(absl::nullopt, response.responseType()); - request.setServiceGroup("new_fake_group"); - EXPECT_EQ("new_fake_group", request.serviceGroup().value()); + // Set the response type and validate we can get it. + response.setResponseType(RpcResponseType::ResponseNullValueWithAttachments); + EXPECT_EQ(RpcResponseType::ResponseNullValueWithAttachments, response.responseType().value()); +} - // If parameters and attachment have values, the callback function will not be executed. - set_parameters = false; - set_attachment = false; - EXPECT_NE(nullptr, request.mutableParameters()); - EXPECT_NE(nullptr, request.mutableAttachment()); - EXPECT_EQ(false, set_parameters); - EXPECT_EQ(false, set_attachment); +TEST(RpcResponseTest, InitializeWithBufferTest) { + // Empty buffer. + { + RpcResponse response; + + // Initialize the response with an empty buffer. + Buffer::OwnedImpl buffer; + response.content().initialize(buffer, buffer.length()); + + // Try get the result. + EXPECT_EQ(response.content().result(), nullptr); + + // Try get the attachments. + EXPECT_FALSE(response.content().attachments().contains("group")); + + // attachments() call will make the content to be decoded. + // And the content is empty, so the content will be treated as broken. + // So the content will be reset to an empty state: + // empty result, empty attachments. + EXPECT_EQ(response.content().buffer().toString(), std::string({'N', 'H', 'Z'})); + + // Set the group. + response.content().setAttachment("group", "group"); + EXPECT_EQ("group", response.content().attachments().at("group")); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(response.content().buffer().toString(), std::string({ + 'N', + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + } + + // Buffer with result but no attachments. + { + RpcResponse response; + + Buffer::OwnedImpl buffer(std::string({ + 'T', // true + })); + + // Initialize the response with the buffer. + response.content().initialize(buffer, buffer.length()); + + // Get the result. + const auto* result = response.content().result(); + + // The result is a boolean. + EXPECT_EQ(true, result->toBoolean().value().get()); + + // Try get the attachments. + EXPECT_FALSE(response.content().attachments().contains("group")); + + // Move the buffer. + Buffer::OwnedImpl buffer2; + response.content().bufferMoveTo(buffer2); + + // Empty attachments will be encoded to the buffer when buffer() is called + // and the underlying buffer is empty. + EXPECT_EQ(response.content().buffer().toString(), std::string({ + 'T', // true + 'H', // Attachments start + 'Z', // Attachments end + })); + + // Set the attachment. + response.content().setAttachment("group", "group"); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(response.content().buffer().toString(), std::string({ + 'T', // true + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + + // Remove the attachment. + response.content().delAttachment("group"); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(response.content().buffer().toString(), std::string({ + 'T', // true + 'H', // Attachments start + 'Z' // Attachments end + })); + } + + // Buffer with result and attachments. + { + RpcResponse response; + + Buffer::OwnedImpl buffer(std::string({ + 'T', // true + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'T', // Key + 'F', // Value + 'Z' // Attachments end + })); + + // Initialize the response with the buffer. + response.content().initialize(buffer, buffer.length()); + + // Get the result. + const auto* result = response.content().result(); + + // The result is a boolean. + EXPECT_EQ(true, result->toBoolean().value().get()); + + // Only string key and value are used. + EXPECT_EQ(1, response.content().attachments().size()); + + // Get the attachment group. + EXPECT_EQ("group", response.content().attachments().at("group")); + } +} - // Reset attachment and parameters. - request.mutableParameters() = nullptr; - request.mutableAttachment() = nullptr; +TEST(RpcResponseTest, InitializeWithDecodedValuesTest) { + RpcResponse response; - // When parsing parameters, attachment will not be parsed. - request.mutableParameters(); - EXPECT_EQ(true, set_parameters); - EXPECT_EQ(false, set_attachment); - EXPECT_EQ(true, request.hasParameters()); - EXPECT_EQ(false, request.hasAttachment()); + Hessian2::ObjectPtr result = std::make_unique(true); + Attachments attachs; + attachs.emplace("group", "group"); - request.messageBuffer().add("abcdefg"); - EXPECT_EQ("abcdefg", request.messageBuffer().toString()); -} + // Initialize the response with the given result and attachments. + response.content().initialize(std::move(result), std::move(attachs)); -TEST(RpcResponseImplTest, RpcResponseImplTest) { - RpcResponseImpl result; + // Get the result. + const auto* result2 = response.content().result(); - EXPECT_EQ(false, result.responseType().has_value()); - result.setResponseType(RpcResponseType::ResponseWithValue); - EXPECT_EQ(true, result.responseType().has_value()); - EXPECT_EQ(RpcResponseType::ResponseWithValue, result.responseType().value()); + // The result is a boolean. + EXPECT_EQ(true, result2->toBoolean().value().get()); - EXPECT_EQ(false, result.localRawMessage().has_value()); - result.setLocalRawMessage("abcdefg"); - EXPECT_EQ(true, result.localRawMessage().has_value()); - EXPECT_EQ("abcdefg", result.localRawMessage().value()); + // Get the attachment group. + EXPECT_EQ("group", response.content().attachments().at("group")); - result.messageBuffer().add("abcdefg"); - EXPECT_EQ("abcdefg", result.messageBuffer().toString()); + // Get the buffer. + EXPECT_EQ(response.content().buffer().toString(), std::string({ + 'T', // true + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); } } // namespace diff --git a/test/extensions/common/dubbo/metadata_test.cc b/test/extensions/common/dubbo/metadata_test.cc index f4cf7b15869d..4f45a8e7fdd0 100644 --- a/test/extensions/common/dubbo/metadata_test.cc +++ b/test/extensions/common/dubbo/metadata_test.cc @@ -1,6 +1,6 @@ #include -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" #include "source/extensions/common/dubbo/metadata.h" #include "gtest/gtest.h" @@ -14,10 +14,6 @@ namespace { TEST(ContextTest, ContextTest) { Context context; - // Simple set and get of serialize type. - context.setSerializeType(SerializeType::Hessian2); - EXPECT_EQ(SerializeType::Hessian2, context.serializeType()); - // Simple set and get of message type. context.setMessageType(MessageType::HeartbeatResponse); EXPECT_EQ(MessageType::HeartbeatResponse, context.messageType()); @@ -63,10 +59,10 @@ TEST(MessageMetadataTest, MessageMetadataTest) { auto context = std::make_unique(); auto raw_context = context.get(); - auto request = std::make_unique(); + auto request = std::make_unique("a", "b", "c", "d"); auto raw_request = request.get(); - auto response = std::make_unique(); + auto response = std::make_unique(); auto raw_response = response.get(); // Simple set and get of context. From aa19504d52ad11906075063c2bf6ef48d7c9b27c Mon Sep 17 00:00:00 2001 From: "Adi (Suissa) Peleg" Date: Tue, 19 Mar 2024 10:25:21 -0400 Subject: [PATCH 084/124] fuzz: throwing an exception in case of an absl Status error upstream address (#32956) This is similar to #32546, but I missed the other field that may trigger a similar error. Risk Level: low - fuzz only Testing: Add corpus entry Signed-off-by: Adi Suissa-Peleg --- .../header_parser_corpus/wrong_upstream_address | 8 ++++++++ test/fuzz/utility.h | 14 +++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 test/common/router/header_parser_corpus/wrong_upstream_address diff --git a/test/common/router/header_parser_corpus/wrong_upstream_address b/test/common/router/header_parser_corpus/wrong_upstream_address new file mode 100644 index 000000000000..b45638c9ab38 --- /dev/null +++ b/test/common/router/header_parser_corpus/wrong_upstream_address @@ -0,0 +1,8 @@ +stream_info { + upstream_local_address { + socket_address { + address: "s" + named_port: "" + } + } +} diff --git a/test/fuzz/utility.h b/test/fuzz/utility.h index a450ac92e837..499f429e1e57 100644 --- a/test/fuzz/utility.h +++ b/test/fuzz/utility.h @@ -177,11 +177,15 @@ inline std::unique_ptr fromStreamInfo(const test::fuzz::StreamIn } else { address = Network::Utility::resolveUrl("tcp://10.0.0.1:443"); } - auto upstream_local_address = - stream_info.has_upstream_local_address() - ? Envoy::Network::Address::resolveProtoAddress(stream_info.upstream_local_address()) - .value() - : Network::Utility::resolveUrl("tcp://10.0.0.1:10000"); + Envoy::Network::Address::InstanceConstSharedPtr upstream_local_address; + if (stream_info.has_upstream_local_address()) { + auto upstream_local_address_or_error = + Envoy::Network::Address::resolveProtoAddress(stream_info.upstream_local_address()); + THROW_IF_STATUS_NOT_OK(upstream_local_address_or_error, throw); + upstream_local_address = upstream_local_address_or_error.value(); + } else { + upstream_local_address = Network::Utility::resolveUrl("tcp://10.0.0.1:10000"); + } test_stream_info->upstreamInfo()->setUpstreamLocalAddress(upstream_local_address); test_stream_info->downstream_connection_info_provider_ = std::make_shared(address, address); From 79d5a6d96a9121da655593c19cb17185b03b047d Mon Sep 17 00:00:00 2001 From: ohadvano <49730675+ohadvano@users.noreply.github.com> Date: Tue, 19 Mar 2024 16:40:16 +0200 Subject: [PATCH 085/124] fluentd_access_logger: add retry and backoff options (#32682) Signed-off-by: ohadvano --- .../access_loggers/fluentd/v3/BUILD | 5 +- .../access_loggers/fluentd/v3/fluentd.proto | 20 +- changelogs/current.yaml | 5 + .../observability/access_log/stats.rst | 1 + source/common/common/BUILD | 2 + source/common/common/backoff_strategy.h | 21 ++ .../access_loggers/fluentd/config.cc | 11 + .../fluentd/fluentd_access_log_impl.cc | 103 +++++-- .../fluentd/fluentd_access_log_impl.h | 28 +- test/common/common/backoff_strategy_test.cc | 25 ++ .../fluentd/fluentd_access_log_impl_test.cc | 278 ++++++++++++++++-- .../fluentd_access_log_integration_test.cc | 81 ++++- test/mocks/common.h | 11 + 13 files changed, 529 insertions(+), 62 deletions(-) diff --git a/api/envoy/extensions/access_loggers/fluentd/v3/BUILD b/api/envoy/extensions/access_loggers/fluentd/v3/BUILD index 29ebf0741406..09a37ad16b83 100644 --- a/api/envoy/extensions/access_loggers/fluentd/v3/BUILD +++ b/api/envoy/extensions/access_loggers/fluentd/v3/BUILD @@ -5,5 +5,8 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = ["@com_github_cncf_xds//udpa/annotations:pkg"], + deps = [ + "//envoy/config/core/v3:pkg", + "@com_github_cncf_xds//udpa/annotations:pkg", + ], ) diff --git a/api/envoy/extensions/access_loggers/fluentd/v3/fluentd.proto b/api/envoy/extensions/access_loggers/fluentd/v3/fluentd.proto index e6b2adfdc9c0..ce68c79e9d91 100644 --- a/api/envoy/extensions/access_loggers/fluentd/v3/fluentd.proto +++ b/api/envoy/extensions/access_loggers/fluentd/v3/fluentd.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.extensions.access_loggers.fluentd.v3; +import "envoy/config/core/v3/backoff.proto"; + import "google/protobuf/duration.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/wrappers.proto"; @@ -22,8 +24,19 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // the Fluentd Forward Protocol as described in: `Fluentd Forward Protocol Specification // `_. // [#extension: envoy.access_loggers.fluentd] -// [#next-free-field: 7] +// [#next-free-field: 8] message FluentdAccessLogConfig { + message RetryOptions { + // The number of times the logger will attempt to connect to the upstream during reconnects. + // By default, there is no limit. The logger will attempt to reconnect to the upstream each time + // connecting to the upstream failed or the upstream connection had been closed for any reason. + google.protobuf.UInt32Value max_connect_attempts = 1; + + // Sets the backoff strategy. If this value is not set, the default base backoff interval is 500 + // milliseconds and the default max backoff interval is 5 seconds (10 times the base interval). + config.core.v3.BackoffStrategy backoff_options = 2; + } + // The upstream cluster to connect to for streaming the Fluentd messages. string cluster = 1 [(validate.rules).string = {min_len: 1}]; @@ -67,4 +80,9 @@ message FluentdAccessLogConfig { // "message": "My error message" // } google.protobuf.Struct record = 6 [(validate.rules).message = {required: true}]; + + // Optional retry, in case upstream connection has failed. If this field is not set, the default values will be applied, + // as specified in the :ref:`RetryOptions ` + // configuration. + RetryOptions retry_options = 7; } diff --git a/changelogs/current.yaml b/changelogs/current.yaml index aa29fe080592..e95e090f0ed4 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -313,6 +313,11 @@ new_features: added a :ref:`configuration option ` to add ``x-envoy-local-overloaded`` header when Overload Manager is triggered. +- area: access_loggers + change: | + Added :ref:`retry options + ` to Fluentd Access Logger to + support upstream reconnect options, backoff intervals. - area: tracing change: | Added support to configure a Dynatrace sampler for the OpenTelemetry tracer. diff --git a/docs/root/configuration/observability/access_log/stats.rst b/docs/root/configuration/observability/access_log/stats.rst index 0a61df4e4518..5bc23c23ef67 100644 --- a/docs/root/configuration/observability/access_log/stats.rst +++ b/docs/root/configuration/observability/access_log/stats.rst @@ -46,4 +46,5 @@ The Fluentd access log has statistics rooted at the *access_logs.fluentd.(proto_config), context.serverFactoryContext().threadLocal(), + context.serverFactoryContext().api().randomGenerator(), getAccessLoggerCacheSingleton(context.serverFactoryContext())); } diff --git a/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.cc b/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.cc index ce1a96694d90..a6f312d66d96 100644 --- a/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.cc +++ b/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.cc @@ -12,17 +12,26 @@ namespace Fluentd { using MessagePackBuffer = msgpack::sbuffer; using MessagePackPacker = msgpack::packer; -FluentdAccessLoggerImpl::FluentdAccessLoggerImpl(Tcp::AsyncTcpClientPtr client, +FluentdAccessLoggerImpl::FluentdAccessLoggerImpl(Upstream::ThreadLocalCluster& cluster, + Tcp::AsyncTcpClientPtr client, Event::Dispatcher& dispatcher, const FluentdAccessLogConfig& config, + BackOffStrategyPtr backoff_strategy, Stats::Scope& parent_scope) : tag_(config.tag()), id_(dispatcher.name()), + max_connect_attempts_( + config.has_retry_options() && config.retry_options().has_max_connect_attempts() + ? absl::optional(config.retry_options().max_connect_attempts().value()) + : absl::nullopt), stats_scope_(parent_scope.createScope(config.stat_prefix())), fluentd_stats_( {ACCESS_LOG_FLUENTD_STATS(POOL_COUNTER(*stats_scope_), POOL_GAUGE(*stats_scope_))}), - client_(std::move(client)), - buffer_flush_interval_msec_(PROTOBUF_GET_MS_OR_DEFAULT(config, buffer_flush_interval, 1000)), - max_buffer_size_bytes_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, buffer_size_bytes, 16384)), + cluster_(cluster), backoff_strategy_(std::move(backoff_strategy)), client_(std::move(client)), + buffer_flush_interval_msec_( + PROTOBUF_GET_MS_OR_DEFAULT(config, buffer_flush_interval, DefaultBufferFlushIntervalMs)), + max_buffer_size_bytes_( + PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, buffer_size_bytes, DefaultMaxBufferSize)), + retry_timer_(dispatcher.createTimer([this]() -> void { onBackoffCallback(); })), flush_timer_(dispatcher.createTimer([this]() { flush(); flush_timer_->enableTimer(buffer_flush_interval_msec_); @@ -35,27 +44,21 @@ void FluentdAccessLoggerImpl::onEvent(Network::ConnectionEvent event) { connecting_ = false; if (event == Network::ConnectionEvent::Connected) { + backoff_strategy_->reset(); + retry_timer_->disableTimer(); flush(); } else if (event == Network::ConnectionEvent::LocalClose || event == Network::ConnectionEvent::RemoteClose) { ENVOY_LOG(debug, "upstream connection was closed"); - // TODO(ohadvano): add an option to reconnect to the upstream, if configured - fluentd_stats_.connections_closed_.inc(); - disconnected_ = true; - clearBuffer(); - - ASSERT(flush_timer_ != nullptr); - flush_timer_->disableTimer(); + maybeReconnect(); } } void FluentdAccessLoggerImpl::log(EntryPtr&& entry) { - if (disconnected_) { + if (disconnected_ || approximate_message_size_bytes_ >= max_buffer_size_bytes_) { fluentd_stats_.entries_lost_.inc(); // We will lose the data deliberately so the buffer doesn't grow infinitely. - // Since the client is disconnected, there's nothing much we can do with the data anyway. - // TODO(ohadvano): add an option to reconnect to the upstream, if configured return; } @@ -63,6 +66,8 @@ void FluentdAccessLoggerImpl::log(EntryPtr&& entry) { entries_.push_back(std::move(entry)); fluentd_stats_.entries_buffered_.inc(); if (approximate_message_size_bytes_ >= max_buffer_size_bytes_) { + // If we exceeded the buffer limit, immediately flush the logs instead of waiting for + // the next flush interval, to allow new logs to be buffered. flush(); } } @@ -76,8 +81,7 @@ void FluentdAccessLoggerImpl::flush() { } if (!client_->connected()) { - connecting_ = true; - client_->connect(); + connect(); return; } @@ -102,6 +106,42 @@ void FluentdAccessLoggerImpl::flush() { clearBuffer(); } +void FluentdAccessLoggerImpl::connect() { + connect_attempts_++; + if (!client_->connect()) { + ENVOY_LOG(debug, "no healthy upstream"); + maybeReconnect(); + return; + } + + connecting_ = true; +} + +void FluentdAccessLoggerImpl::maybeReconnect() { + if (max_connect_attempts_.has_value() && connect_attempts_ >= max_connect_attempts_) { + ENVOY_LOG(debug, "max connection attempts reached"); + cluster_.info()->trafficStats()->upstream_cx_connect_attempts_exceeded_.inc(); + setDisconnected(); + return; + } + + uint64_t next_backoff_ms = backoff_strategy_->nextBackOffMs(); + retry_timer_->enableTimer(std::chrono::milliseconds(next_backoff_ms)); + ENVOY_LOG(debug, "reconnect attempt scheduled for {} ms", next_backoff_ms); +} + +void FluentdAccessLoggerImpl::onBackoffCallback() { + fluentd_stats_.reconnect_attempts_.inc(); + this->connect(); +} + +void FluentdAccessLoggerImpl::setDisconnected() { + disconnected_ = true; + clearBuffer(); + ASSERT(flush_timer_ != nullptr); + flush_timer_->disableTimer(); +} + void FluentdAccessLoggerImpl::clearBuffer() { entries_.clear(); approximate_message_size_bytes_ = 0; @@ -117,7 +157,8 @@ FluentdAccessLoggerCacheImpl::FluentdAccessLoggerCacheImpl( } FluentdAccessLoggerSharedPtr -FluentdAccessLoggerCacheImpl::getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config) { +FluentdAccessLoggerCacheImpl::getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config, + Random::RandomGenerator& random) { auto& cache = tls_slot_->getTyped(); const auto cache_key = MessageUtil::hash(*config); const auto it = cache.access_loggers_.find(cache_key); @@ -125,25 +166,41 @@ FluentdAccessLoggerCacheImpl::getOrCreateLogger(const FluentdAccessLogConfigShar return it->second.lock(); } + auto* cluster = cluster_manager_.getThreadLocalCluster(config->cluster()); auto client = - cluster_manager_.getThreadLocalCluster(config->cluster()) - ->tcpAsyncClient(nullptr, std::make_shared(false)); + cluster->tcpAsyncClient(nullptr, std::make_shared(false)); + + uint64_t base_interval_ms = DefaultBaseBackoffIntervalMs; + uint64_t max_interval_ms = base_interval_ms * DefaultMaxBackoffIntervalFactor; + + if (config->has_retry_options() && config->retry_options().has_backoff_options()) { + base_interval_ms = PROTOBUF_GET_MS_OR_DEFAULT(config->retry_options().backoff_options(), + base_interval, DefaultBaseBackoffIntervalMs); + max_interval_ms = + PROTOBUF_GET_MS_OR_DEFAULT(config->retry_options().backoff_options(), max_interval, + base_interval_ms * DefaultMaxBackoffIntervalFactor); + } + + BackOffStrategyPtr backoff_strategy = std::make_unique( + base_interval_ms, max_interval_ms, random); const auto logger = std::make_shared( - std::move(client), cache.dispatcher_, *config, *stats_scope_); + *cluster, std::move(client), cache.dispatcher_, *config, std::move(backoff_strategy), + *stats_scope_); cache.access_loggers_.emplace(cache_key, logger); return logger; } FluentdAccessLog::FluentdAccessLog(AccessLog::FilterPtr&& filter, FluentdFormatterPtr&& formatter, const FluentdAccessLogConfigSharedPtr config, - ThreadLocal::SlotAllocator& tls, + ThreadLocal::SlotAllocator& tls, Random::RandomGenerator& random, FluentdAccessLoggerCacheSharedPtr access_logger_cache) : ImplBase(std::move(filter)), formatter_(std::move(formatter)), tls_slot_(tls.allocateSlot()), config_(config), access_logger_cache_(access_logger_cache) { tls_slot_->set( - [config = config_, access_logger_cache = access_logger_cache_](Event::Dispatcher&) { - return std::make_shared(access_logger_cache->getOrCreateLogger(config)); + [config = config_, &random, access_logger_cache = access_logger_cache_](Event::Dispatcher&) { + return std::make_shared( + access_logger_cache->getOrCreateLogger(config, random)); }); } diff --git a/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.h b/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.h index 11a03530d32d..95e84632177f 100644 --- a/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.h +++ b/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.h @@ -18,6 +18,11 @@ using FluentdAccessLogConfig = envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig; using FluentdAccessLogConfigSharedPtr = std::shared_ptr; +static constexpr uint64_t DefaultBaseBackoffIntervalMs = 500; +static constexpr uint64_t DefaultMaxBackoffIntervalFactor = 10; +static constexpr uint64_t DefaultBufferFlushIntervalMs = 1000; +static constexpr uint64_t DefaultMaxBufferSize = 16384; + // Entry represents a single Fluentd message, msgpack format based, as specified in: // https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1#entry class Entry { @@ -49,6 +54,7 @@ using FluentdAccessLoggerSharedPtr = std::shared_ptr; COUNTER(entries_lost) \ COUNTER(entries_buffered) \ COUNTER(events_sent) \ + COUNTER(reconnect_attempts) \ COUNTER(connections_closed) struct AccessLogFluentdStats { @@ -59,8 +65,9 @@ class FluentdAccessLoggerImpl : public Tcp::AsyncTcpClientCallbacks, public FluentdAccessLogger, public Logger::Loggable { public: - FluentdAccessLoggerImpl(Tcp::AsyncTcpClientPtr client, Event::Dispatcher& dispatcher, - const FluentdAccessLogConfig& config, Stats::Scope& parent_scope); + FluentdAccessLoggerImpl(Upstream::ThreadLocalCluster& cluster, Tcp::AsyncTcpClientPtr client, + Event::Dispatcher& dispatcher, const FluentdAccessLogConfig& config, + BackOffStrategyPtr backoff_strategy, Stats::Scope& parent_scope); // Tcp::AsyncTcpClientCallbacks void onEvent(Network::ConnectionEvent event) override; @@ -73,19 +80,28 @@ class FluentdAccessLoggerImpl : public Tcp::AsyncTcpClientCallbacks, private: void flush(); + void connect(); + void maybeReconnect(); + void onBackoffCallback(); + void setDisconnected(); void clearBuffer(); bool disconnected_ = false; bool connecting_ = false; std::string tag_; std::string id_; + uint32_t connect_attempts_{0}; + absl::optional max_connect_attempts_{}; const Stats::ScopeSharedPtr stats_scope_; AccessLogFluentdStats fluentd_stats_; std::vector entries_; uint64_t approximate_message_size_bytes_ = 0; + Upstream::ThreadLocalCluster& cluster_; + const BackOffStrategyPtr backoff_strategy_; const Tcp::AsyncTcpClientPtr client_; const std::chrono::milliseconds buffer_flush_interval_msec_; const uint64_t max_buffer_size_bytes_; + const Event::TimerPtr retry_timer_; const Event::TimerPtr flush_timer_; }; @@ -98,7 +114,8 @@ class FluentdAccessLoggerCache { * @return FluentdAccessLoggerSharedPtr ready for logging requests. */ virtual FluentdAccessLoggerSharedPtr - getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config) PURE; + getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config, + Random::RandomGenerator& random) PURE; }; using FluentdAccessLoggerCacheSharedPtr = std::shared_ptr; @@ -108,8 +125,8 @@ class FluentdAccessLoggerCacheImpl : public Singleton::Instance, public FluentdA FluentdAccessLoggerCacheImpl(Upstream::ClusterManager& cluster_manager, Stats::Scope& parent_scope, ThreadLocal::SlotAllocator& tls); - FluentdAccessLoggerSharedPtr - getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config) override; + FluentdAccessLoggerSharedPtr getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config, + Random::RandomGenerator& random) override; private: /** @@ -135,6 +152,7 @@ class FluentdAccessLog : public Common::ImplBase { public: FluentdAccessLog(AccessLog::FilterPtr&& filter, FluentdFormatterPtr&& formatter, const FluentdAccessLogConfigSharedPtr config, ThreadLocal::SlotAllocator& tls, + Random::RandomGenerator& random, FluentdAccessLoggerCacheSharedPtr access_logger_cache); private: diff --git a/test/common/common/backoff_strategy_test.cc b/test/common/common/backoff_strategy_test.cc index a5111809bf44..6dcb6a04aeaf 100644 --- a/test/common/common/backoff_strategy_test.cc +++ b/test/common/common/backoff_strategy_test.cc @@ -114,4 +114,29 @@ TEST(FixedBackOffStrategyTest, FixedBackOffBasicReset) { EXPECT_EQ(20, fixed_back_off.nextBackOffMs()); } +TEST(BackOffStrategyUtilsTest, InvalidConfig) { + { + // Valid config. + envoy::config::core::v3::BackoffStrategy backoff_strategy; + backoff_strategy.mutable_base_interval()->set_seconds(2); + backoff_strategy.mutable_max_interval()->set_seconds(3); + EXPECT_TRUE(BackOffStrategyUtils::validateBackOffStrategyConfig(backoff_strategy, 1, 10).ok()); + } + + { + // Max interval is lower than base interval. + envoy::config::core::v3::BackoffStrategy backoff_strategy; + backoff_strategy.mutable_base_interval()->set_seconds(3); + backoff_strategy.mutable_max_interval()->set_seconds(2); + EXPECT_TRUE(!BackOffStrategyUtils::validateBackOffStrategyConfig(backoff_strategy, 1, 10).ok()); + } + + { + // Max interval is lower than base interval. + envoy::config::core::v3::BackoffStrategy backoff_strategy; + backoff_strategy.mutable_max_interval()->set_nanos(2000000); + EXPECT_TRUE(!BackOffStrategyUtils::validateBackOffStrategyConfig(backoff_strategy, 3, 10).ok()); + } +} + } // namespace Envoy diff --git a/test/extensions/access_loggers/fluentd/fluentd_access_log_impl_test.cc b/test/extensions/access_loggers/fluentd/fluentd_access_log_impl_test.cc index a058c22dcf07..14c7bd089fca 100644 --- a/test/extensions/access_loggers/fluentd/fluentd_access_log_impl_test.cc +++ b/test/extensions/access_loggers/fluentd/fluentd_access_log_impl_test.cc @@ -31,16 +31,25 @@ class FluentdAccessLoggerImplTest : public testing::Test { public: FluentdAccessLoggerImplTest() : async_client_(new Tcp::AsyncClient::MockAsyncTcpClient()), - timer_(new Event::MockTimer(&dispatcher_)) {} + backoff_strategy_(new MockBackOffStrategy()), + flush_timer_(new Event::MockTimer(&dispatcher_)), + retry_timer_(new Event::MockTimer(&dispatcher_)) {} - void init(int buffer_size_bytes = 0) { + void init(int buffer_size_bytes = 1, absl::optional max_connect_attempts = absl::nullopt) { EXPECT_CALL(*async_client_, setAsyncTcpClientCallbacks(_)); - EXPECT_CALL(*timer_, enableTimer(_, _)); + EXPECT_CALL(*flush_timer_, enableTimer(_, _)); config_.set_tag(tag_); + + if (max_connect_attempts.has_value()) { + config_.mutable_retry_options()->mutable_max_connect_attempts()->set_value( + max_connect_attempts.value()); + } + config_.mutable_buffer_size_bytes()->set_value(buffer_size_bytes); logger_ = std::make_unique( - Tcp::AsyncTcpClientPtr{async_client_}, dispatcher_, config_, *stats_store_.rootScope()); + cluster_, Tcp::AsyncTcpClientPtr{async_client_}, dispatcher_, config_, + BackOffStrategyPtr{backoff_strategy_}, *stats_store_.rootScope()); } std::string getExpectedMsgpackPayload(int entries_count) { @@ -62,10 +71,13 @@ class FluentdAccessLoggerImplTest : public testing::Test { std::string tag_ = "test.tag"; uint64_t time_ = 123; std::vector data_ = {10, 20}; + NiceMock cluster_; Tcp::AsyncClient::MockAsyncTcpClient* async_client_; + MockBackOffStrategy* backoff_strategy_; Stats::IsolatedStoreImpl stats_store_; Event::MockDispatcher dispatcher_; - Event::MockTimer* timer_; + Event::MockTimer* flush_timer_; + Event::MockTimer* retry_timer_; std::unique_ptr logger_; envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig config_; }; @@ -87,8 +99,8 @@ TEST_F(FluentdAccessLoggerImplTest, NoWriteOnLogIfBufferLimitNotPassed) { } TEST_F(FluentdAccessLoggerImplTest, NoWriteOnLogIfDisconnectedByRemote) { - init(); - EXPECT_CALL(*timer_, disableTimer()); + init(1, 1); + EXPECT_CALL(*flush_timer_, disableTimer()); EXPECT_CALL(*async_client_, write(_, _)).Times(0); EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)); EXPECT_CALL(*async_client_, connect()).WillOnce(Invoke([this]() -> bool { @@ -100,8 +112,8 @@ TEST_F(FluentdAccessLoggerImplTest, NoWriteOnLogIfDisconnectedByRemote) { } TEST_F(FluentdAccessLoggerImplTest, NoWriteOnLogIfDisconnectedByLocal) { - init(); - EXPECT_CALL(*timer_, disableTimer()); + init(1, 1); + EXPECT_CALL(*flush_timer_, disableTimer()); EXPECT_CALL(*async_client_, write(_, _)).Times(0); EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)); EXPECT_CALL(*async_client_, connect()).WillOnce(Invoke([this]() -> bool { @@ -114,6 +126,8 @@ TEST_F(FluentdAccessLoggerImplTest, NoWriteOnLogIfDisconnectedByLocal) { TEST_F(FluentdAccessLoggerImplTest, LogSingleEntry) { init(); // Default buffer limit is 0 so single entry should be flushed immediately. + EXPECT_CALL(*backoff_strategy_, reset()); + EXPECT_CALL(*retry_timer_, disableTimer()); EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)).WillOnce(Return(true)); EXPECT_CALL(*async_client_, connect()).WillOnce(Invoke([this]() -> bool { logger_->onEvent(Network::ConnectionEvent::Connected); @@ -133,6 +147,8 @@ TEST_F(FluentdAccessLoggerImplTest, LogTwoEntries) { init(12); // First entry is 10 bytes, so first entry should not cause the logger to flush. // First log should not be flushed. + EXPECT_CALL(*backoff_strategy_, reset()); + EXPECT_CALL(*retry_timer_, disableTimer()); EXPECT_CALL(*async_client_, connected()).Times(0); EXPECT_CALL(*async_client_, write(_, _)).Times(0); logger_->log(std::make_unique(time_, std::move(data_))); @@ -164,11 +180,123 @@ TEST_F(FluentdAccessLoggerImplTest, CallbacksTest) { EXPECT_NO_THROW(logger_->onData(buffer, false)); } +TEST_F(FluentdAccessLoggerImplTest, SuccessfulReconnect) { + init(1, 2); + EXPECT_CALL(*backoff_strategy_, nextBackOffMs()).WillOnce(Return(1)); + EXPECT_CALL(*async_client_, write(_, _)).Times(0); + EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)).WillOnce(Return(true)); + EXPECT_CALL(*async_client_, connect()) + .WillOnce(Invoke([this]() -> bool { + EXPECT_CALL(*backoff_strategy_, reset()).Times(0); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(1), _)); + EXPECT_CALL(*retry_timer_, disableTimer()).Times(0); + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })) + .WillOnce(Invoke([this]() -> bool { + EXPECT_CALL(*backoff_strategy_, reset()); + EXPECT_CALL(*retry_timer_, enableTimer(_, _)).Times(0); + EXPECT_CALL(*retry_timer_, disableTimer()); + logger_->onEvent(Network::ConnectionEvent::Connected); + return true; + })); + EXPECT_CALL(*async_client_, write(_, _)) + .WillOnce(Invoke([&](Buffer::Instance& buffer, bool end_stream) { + EXPECT_FALSE(end_stream); + std::string expected_payload = getExpectedMsgpackPayload(1); + EXPECT_EQ(expected_payload, buffer.toString()); + })); + + logger_->log(std::make_unique(time_, std::move(data_))); + retry_timer_->invokeCallback(); +} + +TEST_F(FluentdAccessLoggerImplTest, ReconnectFailure) { + init(1, 2); + + EXPECT_CALL(*backoff_strategy_, nextBackOffMs()).WillOnce(Return(1)); + EXPECT_CALL(*backoff_strategy_, reset()).Times(0); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(1), _)); + EXPECT_CALL(*retry_timer_, disableTimer()).Times(0); + + EXPECT_CALL(*flush_timer_, disableTimer()); + EXPECT_CALL(*async_client_, write(_, _)).Times(0); + EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)); + EXPECT_CALL(*async_client_, connect()) + .WillOnce(Invoke([this]() -> bool { + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })) + .WillOnce(Invoke([this]() -> bool { + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })); + + logger_->log(std::make_unique(time_, std::move(data_))); + retry_timer_->invokeCallback(); +} + +TEST_F(FluentdAccessLoggerImplTest, TwoReconnects) { + init(1, 3); + + EXPECT_CALL(*backoff_strategy_, nextBackOffMs()).WillOnce(Return(1)).WillOnce(Return(1)); + EXPECT_CALL(*backoff_strategy_, reset()).Times(0); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(1), _)).Times(2); + EXPECT_CALL(*retry_timer_, disableTimer()).Times(0); + + EXPECT_CALL(*flush_timer_, disableTimer()); + EXPECT_CALL(*async_client_, write(_, _)).Times(0); + EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)); + EXPECT_CALL(*async_client_, connect()) + .WillOnce(Invoke([this]() -> bool { + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })) + .WillOnce(Invoke([this]() -> bool { + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })) + .WillOnce(Invoke([this]() -> bool { + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })); + + logger_->log(std::make_unique(time_, std::move(data_))); + retry_timer_->invokeCallback(); + retry_timer_->invokeCallback(); +} + +TEST_F(FluentdAccessLoggerImplTest, RetryOnNoHealthyUpstream) { + init(); + + EXPECT_CALL(*backoff_strategy_, nextBackOffMs()).WillOnce(Return(1)); + EXPECT_CALL(*backoff_strategy_, reset()).Times(0); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(1), _)); + EXPECT_CALL(*retry_timer_, disableTimer()).Times(0); + + EXPECT_CALL(*async_client_, write(_, _)).Times(0); + EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)); + EXPECT_CALL(*async_client_, connect()).WillOnce(Return(false)); + logger_->log(std::make_unique(time_, std::move(data_))); +} + +TEST_F(FluentdAccessLoggerImplTest, NoWriteOnBufferFull) { + // Setting the buffer to 0 so new log will be thrown. + init(0); + EXPECT_CALL(*async_client_, write(_, _)).Times(0); + EXPECT_CALL(*async_client_, connect()).Times(0); + EXPECT_CALL(*async_client_, connected()).Times(0); + logger_->log(std::make_unique(time_, std::move(data_))); +} + class FluentdAccessLoggerCacheImplTest : public testing::Test { public: - FluentdAccessLoggerCacheImplTest() : logger_cache_(cluster_manager_, scope_, tls_) {} - void init(bool second_logger = false) { + tls_.setDispatcher(&dispatcher_); + flush_timer_ = new Event::MockTimer(&dispatcher_); + retry_timer_ = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*flush_timer_, enableTimer(_, _)); + async_client1_ = new Tcp::AsyncClient::MockAsyncTcpClient(); EXPECT_CALL(*async_client1_, setAsyncTcpClientCallbacks(_)); @@ -176,9 +304,16 @@ class FluentdAccessLoggerCacheImplTest : public testing::Test { async_client2_ = new Tcp::AsyncClient::MockAsyncTcpClient(); EXPECT_CALL(*async_client2_, setAsyncTcpClientCallbacks(_)); } + + logger_cache_ = std::make_unique(cluster_manager_, scope_, tls_); } std::string cluster_name_ = "test_cluster"; + uint64_t time_ = 123; + std::vector data_ = {10, 20}; + Event::MockTimer* flush_timer_; + Event::MockTimer* retry_timer_; + Event::MockDispatcher dispatcher_; NiceMock cluster_; NiceMock cluster_manager_; Tcp::AsyncClient::MockAsyncTcpClient* async_client1_; @@ -186,7 +321,8 @@ class FluentdAccessLoggerCacheImplTest : public testing::Test { NiceMock store_; Stats::Scope& scope_{*store_.rootScope()}; NiceMock tls_; - FluentdAccessLoggerCacheImpl logger_cache_; + NiceMock random_; + std::unique_ptr logger_cache_; }; TEST_F(FluentdAccessLoggerCacheImplTest, CreateNonExistingLogger) { @@ -200,7 +336,8 @@ TEST_F(FluentdAccessLoggerCacheImplTest, CreateNonExistingLogger) { config.set_cluster(cluster_name_); config.set_tag("test.tag"); config.mutable_buffer_size_bytes()->set_value(123); - auto logger = logger_cache_.getOrCreateLogger(std::make_shared(config)); + auto logger = + logger_cache_->getOrCreateLogger(std::make_shared(config), random_); EXPECT_TRUE(logger != nullptr); } @@ -215,14 +352,16 @@ TEST_F(FluentdAccessLoggerCacheImplTest, CreateTwoLoggersSameHash) { config1.set_cluster(cluster_name_); config1.set_tag("test.tag"); config1.mutable_buffer_size_bytes()->set_value(123); - auto logger1 = logger_cache_.getOrCreateLogger(std::make_shared(config1)); + auto logger1 = + logger_cache_->getOrCreateLogger(std::make_shared(config1), random_); EXPECT_TRUE(logger1 != nullptr); envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig config2; config2.set_cluster(cluster_name_); // config hash will be different than config1 config2.set_tag("test.tag"); config2.mutable_buffer_size_bytes()->set_value(123); - auto logger2 = logger_cache_.getOrCreateLogger(std::make_shared(config2)); + auto logger2 = + logger_cache_->getOrCreateLogger(std::make_shared(config2), random_); EXPECT_TRUE(logger2 != nullptr); // Make sure we got the same logger @@ -243,20 +382,60 @@ TEST_F(FluentdAccessLoggerCacheImplTest, CreateTwoLoggersDifferentHash) { config1.set_cluster(cluster_name_); config1.set_tag("test.tag"); config1.mutable_buffer_size_bytes()->set_value(123); - auto logger1 = logger_cache_.getOrCreateLogger(std::make_shared(config1)); + auto logger1 = + logger_cache_->getOrCreateLogger(std::make_shared(config1), random_); EXPECT_TRUE(logger1 != nullptr); + Event::MockTimer* flush_timer2 = new Event::MockTimer(&dispatcher_); + Event::MockTimer* retry_timer2 = new Event::MockTimer(&dispatcher_); + UNREFERENCED_PARAMETER(retry_timer2); + EXPECT_CALL(*flush_timer2, enableTimer(_, _)); + envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig config2; config2.set_cluster("different_cluster"); // config hash will be different than config1 config2.set_tag("test.tag"); config2.mutable_buffer_size_bytes()->set_value(123); - auto logger2 = logger_cache_.getOrCreateLogger(std::make_shared(config2)); + auto logger2 = + logger_cache_->getOrCreateLogger(std::make_shared(config2), random_); EXPECT_TRUE(logger2 != nullptr); // Make sure we got two different loggers EXPECT_NE(logger1, logger2); } +TEST_F(FluentdAccessLoggerCacheImplTest, JitteredExponentialBackOffStrategyConfig) { + init(); + + EXPECT_CALL(cluster_manager_, getThreadLocalCluster(cluster_name_)).WillOnce(Return(&cluster_)); + EXPECT_CALL(*async_client1_, connected()).WillOnce(Return(false)); + EXPECT_CALL(*async_client1_, connect()).WillRepeatedly(Return(false)); + EXPECT_CALL(cluster_, tcpAsyncClient(_, _)).WillOnce(Invoke([&] { + return Tcp::AsyncTcpClientPtr{async_client1_}; + })); + + envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig config; + config.set_cluster(cluster_name_); + config.set_tag("test.tag"); + config.mutable_buffer_size_bytes()->set_value(1); + config.mutable_retry_options()->mutable_backoff_options()->mutable_base_interval()->set_nanos( + 7000000); + config.mutable_retry_options()->mutable_backoff_options()->mutable_max_interval()->set_nanos( + 20000000); + + auto logger = + logger_cache_->getOrCreateLogger(std::make_shared(config), random_); + ASSERT_TRUE(logger != nullptr); + + // Setting random so it doesn't add jitter + EXPECT_CALL(random_, random()).WillOnce(Return(6)).WillOnce(Return(13)).WillOnce(Return(19)); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(6), _)); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(13), _)); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(19), _)); + logger->log(std::make_unique(time_, std::move(data_))); + retry_timer_->invokeCallback(); + retry_timer_->invokeCallback(); +} + class MockFluentdAccessLogger : public FluentdAccessLogger { public: MOCK_METHOD(void, log, (EntryPtr &&)); @@ -265,7 +444,7 @@ class MockFluentdAccessLogger : public FluentdAccessLogger { class MockFluentdAccessLoggerCache : public FluentdAccessLoggerCache { public: MOCK_METHOD(FluentdAccessLoggerSharedPtr, getOrCreateLogger, - (const FluentdAccessLogConfigSharedPtr)); + (const FluentdAccessLogConfigSharedPtr, Random::RandomGenerator&)); }; class MockFluentdFormatter : public FluentdFormatter { @@ -280,31 +459,32 @@ using FilterPtr = Envoy::AccessLog::FilterPtr; class FluentdAccessLogTest : public testing::Test { public: - FluentdAccessLogTest() { - ON_CALL(*filter_, evaluate(_, _)).WillByDefault(Return(true)); - EXPECT_CALL(*logger_cache_, getOrCreateLogger(_)).WillOnce(Return(logger_)); - } + FluentdAccessLogTest() { ON_CALL(*filter_, evaluate(_, _)).WillByDefault(Return(true)); } AccessLog::MockFilter* filter_{new NiceMock()}; NiceMock tls_; + NiceMock random_; + NiceMock context_; envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig config_; - MockFluentdFormatter* formatter_{new NiceMock()}; - std::shared_ptr logger_{new MockFluentdAccessLogger()}; - std::shared_ptr logger_cache_{new MockFluentdAccessLoggerCache()}; }; TEST_F(FluentdAccessLogTest, CreateAndLog) { - auto access_log = - FluentdAccessLog(AccessLog::FilterPtr{filter_}, FluentdFormatterPtr{formatter_}, - std::make_shared(config_), tls_, logger_cache_); + auto* formatter = new NiceMock(); + auto logger = std::make_shared(); + auto logger_cache = std::make_shared(); + + EXPECT_CALL(*logger_cache, getOrCreateLogger(_, _)).WillOnce(Return(logger)); + auto access_log = FluentdAccessLog(AccessLog::FilterPtr{filter_}, FluentdFormatterPtr{formatter}, + std::make_shared(config_), tls_, + random_, logger_cache); MockTimeSystem time_system; EXPECT_CALL(time_system, systemTime).WillOnce(Return(SystemTime(std::chrono::seconds(200)))); NiceMock stream_info; EXPECT_CALL(stream_info, timeSource()).WillOnce(ReturnRef(time_system)); - EXPECT_CALL(*formatter_, format(_, _)).WillOnce(Return(std::vector{10, 20})); - EXPECT_CALL(*logger_, log(_)).WillOnce(Invoke([](EntryPtr&& entry) { + EXPECT_CALL(*formatter, format(_, _)).WillOnce(Return(std::vector{10, 20})); + EXPECT_CALL(*logger, log(_)).WillOnce(Invoke([](EntryPtr&& entry) { EXPECT_EQ(200, entry->time_); ASSERT_EQ(2, entry->record_.size()); EXPECT_EQ(uint8_t(10), entry->record_[0]); @@ -314,6 +494,44 @@ TEST_F(FluentdAccessLogTest, CreateAndLog) { access_log.log({}, stream_info); } +TEST_F(FluentdAccessLogTest, UnknownCluster) { + FluentdAccessLogFactory factory; + + config_.set_cluster("unknown"); + config_.set_tag("tag"); + config_.set_stat_prefix("prefix"); + auto* record = config_.mutable_record(); + (*record->mutable_fields())["Message"].set_string_value("SomeValue"); + + EXPECT_CALL(context_.server_factory_context_.cluster_manager_, + checkActiveStaticCluster("unknown")) + .WillOnce(Return(absl::InvalidArgumentError("no cluster"))); + + EXPECT_THROW_WITH_MESSAGE( + factory.createAccessLogInstance(config_, AccessLog::FilterPtr{filter_}, context_), + EnvoyException, "cluster 'unknown' was not found"); +} + +TEST_F(FluentdAccessLogTest, InvalidBackoffConfig) { + FluentdAccessLogFactory factory; + + config_.set_cluster("unknown"); + config_.set_tag("tag"); + config_.set_stat_prefix("prefix"); + auto* record = config_.mutable_record(); + (*record->mutable_fields())["Message"].set_string_value("SomeValue"); + auto* retry_options = config_.mutable_retry_options(); + retry_options->mutable_backoff_options()->mutable_base_interval()->set_seconds(3); + retry_options->mutable_backoff_options()->mutable_max_interval()->set_seconds(2); + + EXPECT_CALL(context_.server_factory_context_.cluster_manager_, checkActiveStaticCluster(_)) + .WillOnce(Return(absl::OkStatus())); + + EXPECT_THROW_WITH_MESSAGE( + factory.createAccessLogInstance(config_, AccessLog::FilterPtr{filter_}, context_), + EnvoyException, "max_backoff_interval must be greater or equal to base_backoff_interval"); +} + } // namespace } // namespace Fluentd } // namespace AccessLoggers diff --git a/test/extensions/access_loggers/fluentd/fluentd_access_log_integration_test.cc b/test/extensions/access_loggers/fluentd/fluentd_access_log_integration_test.cc index 933ac3fe1369..6c386c9b979b 100644 --- a/test/extensions/access_loggers/fluentd/fluentd_access_log_integration_test.cc +++ b/test/extensions/access_loggers/fluentd/fluentd_access_log_integration_test.cc @@ -32,7 +32,10 @@ class FluentdAccessLogIntegrationTest : public testing::Test, public BaseIntegra void init(const std::string cluster_name = default_cluster_name, bool flush_access_log_on_connected = false, - absl::optional buffer_size_bytes = absl::nullopt) { + absl::optional buffer_size_bytes = absl::nullopt, + absl::optional max_connect_attempts = 1, + absl::optional base_backoff_interval = absl::nullopt, + absl::optional max_backoff_interval = absl::nullopt) { setUpstreamCount(2); config_helper_.renameListener("tcp_proxy"); config_helper_.addConfigModifier( @@ -64,6 +67,25 @@ class FluentdAccessLogIntegrationTest : public testing::Test, public BaseIntegra access_log_config.mutable_buffer_size_bytes()->set_value(buffer_size_bytes.value()); } + if (max_connect_attempts.has_value()) { + access_log_config.mutable_retry_options()->mutable_max_connect_attempts()->set_value( + max_connect_attempts.value()); + } + + if (base_backoff_interval.has_value()) { + access_log_config.mutable_retry_options() + ->mutable_backoff_options() + ->mutable_base_interval() + ->set_nanos(base_backoff_interval.value() * 1000000); + } + + if (max_backoff_interval.has_value()) { + access_log_config.mutable_retry_options() + ->mutable_backoff_options() + ->mutable_max_interval() + ->set_nanos(max_backoff_interval.value() * 1000000); + } + auto* record = access_log_config.mutable_record(); (*record->mutable_fields())["Message"].set_string_value("SomeValue"); (*record->mutable_fields())["LogType"].set_string_value("%ACCESS_LOG_TYPE%"); @@ -137,6 +159,18 @@ TEST_F(FluentdAccessLogIntegrationTest, UnknownCluster) { EXPECT_DEATH(init("unknown_cluster"), ""); } +TEST_F(FluentdAccessLogIntegrationTest, InvalidBackoffConfig) { + // Invalid config: min interval set to 30, max interval is set to 20. + EXPECT_DEATH(init(default_cluster_name, false, 1, 1, 30, 20), ""); +} + +TEST_F(FluentdAccessLogIntegrationTest, LogLostOnBufferFull) { + init(default_cluster_name, false, /* max_buffer_size = */ 0); + sendBidirectionalData(); + + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.entries_lost", 1); +} + TEST_F(FluentdAccessLogIntegrationTest, SingleEntrySingleRecord) { init(); sendBidirectionalData(); @@ -145,6 +179,9 @@ TEST_F(FluentdAccessLogIntegrationTest, SingleEntrySingleRecord) { test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.events_sent", 1); ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 1); + test_server_->waitForGaugeEq("cluster.fluentd_cluster.upstream_cx_active", 1); + EXPECT_TRUE(fake_access_log_connection_->waitForData([&](const std::string& tcp_data) -> bool { bool validated = false; validateFluentdPayload(tcp_data, &validated, @@ -161,6 +198,9 @@ TEST_F(FluentdAccessLogIntegrationTest, SingleEntryTwoRecords) { test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.events_sent", 1); ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 1); + test_server_->waitForGaugeEq("cluster.fluentd_cluster.upstream_cx_active", 1); + EXPECT_TRUE(fake_access_log_connection_->waitForData([&](const std::string& tcp_data) -> bool { bool validated = false; validateFluentdPayload(tcp_data, &validated, @@ -171,13 +211,16 @@ TEST_F(FluentdAccessLogIntegrationTest, SingleEntryTwoRecords) { } TEST_F(FluentdAccessLogIntegrationTest, TwoEntries) { - init(default_cluster_name, /*flush_access_log_on_connected = */ true, /*buffer_size_bytes = */ 0); + init(default_cluster_name, /*flush_access_log_on_connected = */ true, /*buffer_size_bytes = */ 1); sendBidirectionalData(); test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.entries_buffered", 2); test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.events_sent", 2); ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 1); + test_server_->waitForGaugeEq("cluster.fluentd_cluster.upstream_cx_active", 1); + EXPECT_TRUE(fake_access_log_connection_->waitForData([&](const std::string& tcp_data) -> bool { bool validated = false; validateFluentdPayload(tcp_data, &validated, @@ -195,6 +238,9 @@ TEST_F(FluentdAccessLogIntegrationTest, UpstreamConnectionClosed) { test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.events_sent", 1); ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 1); + test_server_->waitForGaugeEq("cluster.fluentd_cluster.upstream_cx_active", 1); + EXPECT_TRUE(fake_access_log_connection_->waitForData([&](const std::string& tcp_data) -> bool { bool validated = false; validateFluentdPayload(tcp_data, &validated, @@ -204,11 +250,42 @@ TEST_F(FluentdAccessLogIntegrationTest, UpstreamConnectionClosed) { ASSERT_TRUE(fake_access_log_connection_->close()); test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.connections_closed", 1); + test_server_->waitForGaugeEq("cluster.fluentd_cluster.upstream_cx_active", 0); // New access log would be discarded because the connection is closed. sendBidirectionalData(); test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.entries_lost", 1); } +TEST_F(FluentdAccessLogIntegrationTest, UpstreamConnectionClosedWithMultipleReconnects) { + init(default_cluster_name, false, {}, /* max_reconnect_attempts = */ 3); + sendBidirectionalData(); + + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.entries_buffered", 1); + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.events_sent", 1); + + ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 1); + ASSERT_TRUE(fake_access_log_connection_->close()); + + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.connections_closed", 1); + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.reconnect_attempts", 1); + FakeRawConnectionPtr fake_access_log_connection_2; + ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_2)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 2); + ASSERT_TRUE(fake_access_log_connection_2->close()); + + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.connections_closed", 2); + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.reconnect_attempts", 2); + FakeRawConnectionPtr fake_access_log_connection_3; + ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_3)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 3); + ASSERT_TRUE(fake_access_log_connection_3->close()); + + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.connections_closed", 3); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_connect_attempts_exceeded", + 1); +} + } // namespace } // namespace Envoy diff --git a/test/mocks/common.h b/test/mocks/common.h index 455dc2f59523..246aec0f0068 100644 --- a/test/mocks/common.h +++ b/test/mocks/common.h @@ -2,6 +2,7 @@ #include +#include "envoy/common/backoff_strategy.h" #include "envoy/common/conn_pool.h" #include "envoy/common/key_value_store.h" #include "envoy/common/random_generator.h" @@ -67,6 +68,16 @@ class MockTimeSystem : public Event::TestTimeSystem { Event::TestRealTimeSystem real_time_; // NO_CHECK_FORMAT(real_time) }; +class MockBackOffStrategy : public BackOffStrategy { +public: + ~MockBackOffStrategy() override = default; + + MOCK_METHOD(uint64_t, nextBackOffMs, ()); + MOCK_METHOD(void, reset, ()); + MOCK_METHOD(void, reset, (uint64_t base_interval)); + MOCK_METHOD(bool, isOverTimeLimit, (uint64_t interval_ms), (const)); +}; + // Captures absl::string_view parameters into temp strings, for use // with gmock's SaveArg. Providing an absl::string_view compiles, // but fails because by the time you examine the saved value, its From 8365ca889dcaf5694e306e5a331835c3b6e4b383 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Tue, 19 Mar 2024 10:12:07 -0500 Subject: [PATCH 086/124] mobile: Clean up .bazelrc for Swift (#32971) Signed-off-by: Fredy Wijaya --- mobile/.bazelrc | 2 -- 1 file changed, 2 deletions(-) diff --git a/mobile/.bazelrc b/mobile/.bazelrc index 0fc992ca2e46..f60cad86caa8 100644 --- a/mobile/.bazelrc +++ b/mobile/.bazelrc @@ -295,9 +295,7 @@ build:mobile-remote-ci-macos-kotlin --@com_envoyproxy_protoc_gen_validate//bazel build:mobile-remote-ci-macos-swift --config=mobile-remote-ci-macos build:mobile-remote-ci-macos-swift --config=mobile-test-ios -build:mobile-remote-ci-macos-swift --config=mobile-remote-ci-macos build:mobile-remote-ci-macos-swift --define=envoy_mobile_request_compression=disabled -build:mobile-remote-ci-macos-swift --define=envoy_mobile_swift_cxx_interop=disabled build:mobile-remote-ci-macos-swift --define=google_grpc=disabled build:mobile-remote-ci-macos-swift --define=envoy_mobile_xds=disabled build:mobile-remote-ci-macos-swift --@envoy//bazel:http3=False From 04835afdae38f7e609ed37e489685634c83c5d1f Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 19 Mar 2024 12:27:03 -0400 Subject: [PATCH 087/124] Revert "Support upstream http filters with tcp tunneling (#27183)" (#32980) This reverts commit 50e9108c22c2e46d4ad04a977d24d89201aa0c62. Signed-off-by: Alyssa Wilk --- envoy/router/router.h | 11 - envoy/tcp/BUILD | 1 - envoy/tcp/upstream.h | 15 +- source/common/http/conn_manager_impl.h | 1 - source/common/http/filter_manager.cc | 12 +- source/common/http/filter_manager.h | 5 - source/common/router/router.cc | 10 +- source/common/router/upstream_request.cc | 12 +- source/common/router/upstream_request.h | 23 +- source/common/runtime/runtime_features.cc | 3 - source/common/runtime/runtime_features.h | 2 - source/common/tcp_proxy/BUILD | 7 - source/common/tcp_proxy/tcp_proxy.cc | 122 ++------ source/common/tcp_proxy/tcp_proxy.h | 118 +------- source/common/tcp_proxy/upstream.cc | 223 +-------------- source/common/tcp_proxy/upstream.h | 184 +----------- .../upstreams/http/http/upstream_request.h | 1 - .../upstreams/http/tcp/upstream_request.h | 1 - .../upstreams/http/udp/upstream_request.h | 1 - source/extensions/upstreams/tcp/generic/BUILD | 1 - .../upstreams/tcp/generic/config.cc | 4 +- .../extensions/upstreams/tcp/generic/config.h | 2 - test/common/router/upstream_request_test.cc | 5 +- test/common/tcp_proxy/BUILD | 4 - test/common/tcp_proxy/tcp_proxy_test.cc | 193 ++++--------- test/common/tcp_proxy/tcp_proxy_test_base.h | 5 +- test/common/tcp_proxy/upstream_test.cc | 264 ++---------------- test/extensions/filters/http/cache/BUILD | 1 - test/extensions/filters/http/csrf/BUILD | 1 - .../filters/http/custom_response/BUILD | 2 +- test/extensions/filters/http/fault/BUILD | 1 - .../filters/http/health_check/BUILD | 1 - test/extensions/filters/http/jwt_authn/BUILD | 2 +- test/extensions/filters/http/rbac/BUILD | 2 +- .../http/tcp/upstream_request_test.cc | 2 +- .../http/udp/upstream_request_test.cc | 1 - test/extensions/upstreams/tcp/generic/BUILD | 1 - .../upstreams/tcp/generic/config_test.cc | 46 ++- test/integration/BUILD | 13 +- test/integration/base_integration_test.cc | 1 - test/integration/http_protocol_integration.cc | 24 +- test/integration/http_protocol_integration.h | 4 - test/mocks/http/mocks.cc | 1 - test/mocks/http/mocks.h | 2 - test/mocks/router/BUILD | 9 - test/mocks/router/upstream_request.cc | 13 - test/mocks/router/upstream_request.h | 21 -- 47 files changed, 193 insertions(+), 1185 deletions(-) delete mode 100644 test/mocks/router/upstream_request.cc delete mode 100644 test/mocks/router/upstream_request.h diff --git a/envoy/router/router.h b/envoy/router/router.h index 8d559c280d33..299cb25c2960 100644 --- a/envoy/router/router.h +++ b/envoy/router/router.h @@ -1526,17 +1526,6 @@ class GenericUpstream { * @param trailers supplies the trailers to encode. */ virtual void encodeTrailers(const Http::RequestTrailerMap& trailers) PURE; - - // TODO(vikaschoudhary16): Remove this api. - // This api is only used to enable half-close semantics on the upstream connection. - // This ideally should be done via calling connection.enableHalfClose() but since TcpProxy - // does not have access to the upstream connection, it is done via this api for now. - /** - * Enable half-close semantics on the upstream connection. Reading a remote half-close - * will not fully close the connection. This is off by default. - * @param enabled Whether to set half-close semantics as enabled or disabled. - */ - virtual void enableHalfClose() PURE; /** * Enable/disable further data from this stream. */ diff --git a/envoy/tcp/BUILD b/envoy/tcp/BUILD index 5c3bdca0d9ef..86d70b1774c6 100644 --- a/envoy/tcp/BUILD +++ b/envoy/tcp/BUILD @@ -26,7 +26,6 @@ envoy_cc_library( "//envoy/http:header_evaluator", "//envoy/tcp:conn_pool_interface", "//envoy/upstream:upstream_interface", - "//source/common/router:router_lib", "@envoy_api//envoy/extensions/filters/network/tcp_proxy/v3:pkg_cc_proto", ], ) diff --git a/envoy/tcp/upstream.h b/envoy/tcp/upstream.h index f6191a27513b..200ec7fc9ea7 100644 --- a/envoy/tcp/upstream.h +++ b/envoy/tcp/upstream.h @@ -2,14 +2,11 @@ #include "envoy/buffer/buffer.h" #include "envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.pb.h" -#include "envoy/http/filter.h" #include "envoy/http/header_evaluator.h" #include "envoy/stream_info/stream_info.h" #include "envoy/tcp/conn_pool.h" #include "envoy/upstream/upstream.h" -#include "source/common/router/router.h" - namespace Envoy { namespace Upstream { @@ -51,17 +48,14 @@ class TunnelingConfigHelper { virtual void propagateResponseTrailers(Http::ResponseTrailerMapPtr&& trailers, const StreamInfo::FilterStateSharedPtr& filter_state) const PURE; - virtual const Envoy::Router::FilterConfig& routerFilterConfig() const PURE; - virtual Server::Configuration::ServerFactoryContext& serverFactoryContext() const PURE; }; using TunnelingConfigHelperOptConstRef = OptRef; // An API for wrapping either a TCP or an HTTP connection pool. -class GenericConnPool : public Event::DeferredDeletable, - public Logger::Loggable { +class GenericConnPool : public Logger::Loggable { public: - ~GenericConnPool() override = default; + virtual ~GenericConnPool() = default; /** * Called to create a TCP connection or HTTP stream for "CONNECT" streams. @@ -111,9 +105,9 @@ class GenericConnectionPoolCallbacks { // Interface for a generic Upstream, which can communicate with a TCP or HTTP // upstream. -class GenericUpstream : public Event::DeferredDeletable { +class GenericUpstream { public: - ~GenericUpstream() override = default; + virtual ~GenericUpstream() = default; /** * Enable/disable further data from this stream. @@ -181,7 +175,6 @@ class GenericConnPoolFactory : public Envoy::Config::TypedFactory { TunnelingConfigHelperOptConstRef config, Upstream::LoadBalancerContext* context, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, - Http::StreamDecoderFilterCallbacks& stream_decoder_callbacks, StreamInfo::StreamInfo& downstream_info) const PURE; }; diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index c8d731afdb3d..11151b157bf5 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -314,7 +314,6 @@ class ConnectionManagerImpl : Logger::Loggable, OptRef tracingConfig() const override; const ScopeTrackedObject& scope() override; OptRef downstreamCallbacks() override { return *this; } - bool isHalfCloseEnabled() override { return false; } // DownstreamStreamFilterCallbacks void setRoute(Router::RouteConstSharedPtr route) override; diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index 070440572660..187bb7b58561 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -901,17 +901,9 @@ FilterManager::commonEncodePrefix(ActiveStreamEncoderFilter* filter, bool end_st FilterIterationStartState filter_iteration_start_state) { // Only do base state setting on the initial call. Subsequent calls for filtering do not touch // the base state. - ENVOY_STREAM_LOG(trace, "commonEncodePrefix end_stream: {}, isHalfCloseEnabled: {}", *this, - end_stream, filter_manager_callbacks_.isHalfCloseEnabled()); if (filter == nullptr) { - // half close is enabled in case tcp proxying is done with http1 encoder. In this case, we - // should not set the local_complete_ flag to true when end_stream is true. - // setting local_complete_ to true will cause any data sent in the upstream direction to be - // dropped. - if (end_stream && !filter_manager_callbacks_.isHalfCloseEnabled()) { - ASSERT(!state_.local_complete_); - state_.local_complete_ = true; - } + ASSERT(!state_.local_complete_); + state_.local_complete_ = end_stream; return encoder_filters_.begin(); } diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index da5ba20033bd..6a671ab99e9b 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -545,11 +545,6 @@ class FilterManagerCallbacks { * Returns the DownstreamStreamFilterCallbacks for downstream HTTP filters. */ virtual OptRef downstreamCallbacks() { return {}; } - /** - * Returns if close from the upstream is to be handled with half-close semantics. - * This is used for HTTP/1.1 codec. - */ - virtual bool isHalfCloseEnabled() PURE; }; /** diff --git a/source/common/router/router.cc b/source/common/router/router.cc index c3baed7b839e..a41e1c3374dc 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -744,9 +744,8 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, // will never transition from false to true. bool can_use_http3 = !transport_socket_options_ || !transport_socket_options_->http11ProxyInfo().has_value(); - UpstreamRequestPtr upstream_request = - std::make_unique(*this, std::move(generic_conn_pool), can_send_early_data, - can_use_http3, false /*enable_half_close*/); + UpstreamRequestPtr upstream_request = std::make_unique( + *this, std::move(generic_conn_pool), can_send_early_data, can_use_http3); LinkedList::moveIntoList(std::move(upstream_request), upstream_requests_); upstream_requests_.front()->acceptHeadersFromRouter(end_stream); if (streaming_shadows_) { @@ -1988,9 +1987,8 @@ void Filter::doRetry(bool can_send_early_data, bool can_use_http3, TimeoutRetry cleanup(); return; } - UpstreamRequestPtr upstream_request = - std::make_unique(*this, std::move(generic_conn_pool), can_send_early_data, - can_use_http3, false /*enable_tcp_tunneling*/); + UpstreamRequestPtr upstream_request = std::make_unique( + *this, std::move(generic_conn_pool), can_send_early_data, can_use_http3); if (include_attempt_count_in_request_) { downstream_headers_->setEnvoyAttemptCount(attempt_count_); diff --git a/source/common/router/upstream_request.cc b/source/common/router/upstream_request.cc index bb0803df3be5..7c9079e58a44 100644 --- a/source/common/router/upstream_request.cc +++ b/source/common/router/upstream_request.cc @@ -80,8 +80,7 @@ class UpstreamFilterManager : public Http::FilterManager { UpstreamRequest::UpstreamRequest(RouterFilterInterface& parent, std::unique_ptr&& conn_pool, - bool can_send_early_data, bool can_use_http3, - bool enable_half_close) + bool can_send_early_data, bool can_use_http3) : parent_(parent), conn_pool_(std::move(conn_pool)), stream_info_(parent_.callbacks()->dispatcher().timeSource(), nullptr), start_time_(parent_.callbacks()->dispatcher().timeSource().monotonicTime()), @@ -94,8 +93,7 @@ UpstreamRequest::UpstreamRequest(RouterFilterInterface& parent, cleaned_up_(false), had_upstream_(false), stream_options_({can_send_early_data, can_use_http3}), grpc_rq_success_deferred_(false), upstream_wait_for_response_headers_before_disabling_read_(Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.upstream_wait_for_response_headers_before_disabling_read")), - enable_half_close_(enable_half_close) { + "envoy.reloadable_features.upstream_wait_for_response_headers_before_disabling_read")) { if (auto tracing_config = parent_.callbacks()->tracingConfig(); tracing_config.has_value()) { if (tracing_config->spawnUpstreamSpan() || parent_.config().start_child_span_) { span_ = parent_.callbacks()->activeSpan().spawnChild( @@ -260,8 +258,7 @@ void UpstreamRequest::decode1xxHeaders(Http::ResponseHeaderMapPtr&& headers) { // on to the router. void UpstreamRequest::decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) { ASSERT(headers.get()); - ENVOY_STREAM_LOG(trace, "end_stream: {}, upstream response headers:\n{}", *parent_.callbacks(), - end_stream, *headers); + ENVOY_STREAM_LOG(trace, "upstream response headers:\n{}", *parent_.callbacks(), *headers); ScopeTrackerScopeState scope(&parent_.callbacks()->scope(), parent_.callbacks()->dispatcher()); resetPerTryIdleTimer(); @@ -584,9 +581,6 @@ void UpstreamRequest::onPoolReady(std::unique_ptr&& upstream, had_upstream_ = true; // Have the upstream use the account of the downstream. upstream_->setAccount(parent_.callbacks()->account()); - if (enable_half_close_) { - upstream_->enableHalfClose(); - } host->outlierDetector().putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess); diff --git a/source/common/router/upstream_request.h b/source/common/router/upstream_request.h index c6e7a4ec4c94..9e88bfefda31 100644 --- a/source/common/router/upstream_request.h +++ b/source/common/router/upstream_request.h @@ -62,27 +62,29 @@ class UpstreamCodecFilter; * UpstreamCodecFilter. This is accomplished via the UpstreamStreamFilterCallbacks * interface, with the UpstreamFilterManager acting as intermediary. * + * UpstreamRequest is marked as final because no subclasses are expected. + * This class is intended to be used as-is without any specialized inheritance. */ -class UpstreamRequest : public Logger::Loggable, - public UpstreamToDownstream, - public LinkedObject, - public GenericConnectionPoolCallbacks, - public Event::DeferredDeletable { +class UpstreamRequest final : public Logger::Loggable, + public UpstreamToDownstream, + public LinkedObject, + public GenericConnectionPoolCallbacks, + public Event::DeferredDeletable { public: UpstreamRequest(RouterFilterInterface& parent, std::unique_ptr&& conn_pool, - bool can_send_early_data, bool can_use_http3, bool enable_half_close); + bool can_send_early_data, bool can_use_http3); ~UpstreamRequest() override; void deleteIsPending() override { cleanUp(); } // To be called from the destructor, or prior to deferred delete. void cleanUp(); - virtual void acceptHeadersFromRouter(bool end_stream); - virtual void acceptDataFromRouter(Buffer::Instance& data, bool end_stream); + void acceptHeadersFromRouter(bool end_stream); + void acceptDataFromRouter(Buffer::Instance& data, bool end_stream); void acceptTrailersFromRouter(Http::RequestTrailerMap& trailers); void acceptMetadataFromRouter(Http::MetadataMapPtr&& metadata_map_ptr); - virtual void resetStream(); + void resetStream(); void setupPerTryTimeout(); void maybeEndDecode(bool end_stream); void onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host, bool pool_success); @@ -256,7 +258,6 @@ class UpstreamRequest : public Logger::Loggable, Http::ConnectionPool::Instance::StreamOptions stream_options_; bool grpc_rq_success_deferred_ : 1; bool upstream_wait_for_response_headers_before_disabling_read_ : 1; - bool enable_half_close_ : 1; }; class UpstreamRequestFilterManagerCallbacks : public Http::FilterManagerCallbacks, @@ -370,7 +371,7 @@ class UpstreamRequestFilterManagerCallbacks : public Http::FilterManagerCallback void setUpstreamToDownstream(UpstreamToDownstream& upstream_to_downstream_interface) override { upstream_request_.upstream_interface_ = upstream_to_downstream_interface; } - bool isHalfCloseEnabled() override { return upstream_request_.enable_half_close_; } + Http::RequestTrailerMapPtr trailers_; Http::ResponseHeaderMapPtr informational_headers_; Http::ResponseHeaderMapPtr response_headers_; diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index ec9e7f6a40e3..40e526641425 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -130,9 +130,6 @@ FALSE_RUNTIME_GUARD(envoy_reloadable_features_runtime_initialized); FALSE_RUNTIME_GUARD(envoy_reloadable_features_always_use_v6); // TODO(wbpcode) complete remove this feature is no one use it. FALSE_RUNTIME_GUARD(envoy_reloadable_features_refresh_rtt_after_request); -// TODO(vikaschoudhary16) flip this to true only after all the -// TcpProxy::Filter::HttpStreamDecoderFilterCallbacks are implemented or commented as unnecessary -FALSE_RUNTIME_GUARD(envoy_restart_features_upstream_http_filters_with_tcp_proxy); // TODO(danzh) false deprecate it once QUICHE has its own enable/disable flag. FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_reject_all); // TODO(suniltheta): Once the newly added http async technique is stabilized move it under diff --git a/source/common/runtime/runtime_features.h b/source/common/runtime/runtime_features.h index e698064a5971..0c2fb2d9b9e7 100644 --- a/source/common/runtime/runtime_features.h +++ b/source/common/runtime/runtime_features.h @@ -26,8 +26,6 @@ void maybeSetRuntimeGuard(absl::string_view name, bool value); void maybeSetDeprecatedInts(absl::string_view name, uint32_t value); constexpr absl::string_view defer_processing_backedup_streams = "envoy.reloadable_features.defer_processing_backedup_streams"; -constexpr absl::string_view upstream_http_filters_with_tcp_proxy = - "envoy.restart_features.upstream_http_filters_with_tcp_proxy"; } // namespace Runtime } // namespace Envoy diff --git a/source/common/tcp_proxy/BUILD b/source/common/tcp_proxy/BUILD index c6883b43aec3..966088ed4a08 100644 --- a/source/common/tcp_proxy/BUILD +++ b/source/common/tcp_proxy/BUILD @@ -17,22 +17,15 @@ envoy_cc_library( "upstream.h", ], deps = [ - "//envoy/http:header_map_interface", - "//envoy/router:router_ratelimit_interface", "//envoy/tcp:conn_pool_interface", "//envoy/tcp:upstream_interface", "//envoy/upstream:cluster_manager_interface", "//envoy/upstream:load_balancer_interface", - "//source/common/http:async_client_lib", "//source/common/http:codec_client_lib", - "//source/common/http:hash_policy_lib", "//source/common/http:header_map_lib", "//source/common/http:headers_lib", "//source/common/http:utility_lib", - "//source/common/network:utility_lib", "//source/common/router:header_parser_lib", - "//source/common/router:router_lib", - "//source/common/router:shadow_writer_lib", ], ) diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index 21483aca5463..f60f31ec44db 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -32,10 +32,8 @@ #include "source/common/network/upstream_server_name.h" #include "source/common/network/upstream_socket_options_filter_state.h" #include "source/common/router/metadatamatchcriteria_impl.h" -#include "source/common/router/shadow_writer_impl.h" #include "source/common/stream_info/stream_id_provider_impl.h" #include "source/common/stream_info/uint64_accessor_impl.h" -#include "source/common/tracing/http_tracer_impl.h" namespace Envoy { namespace TcpProxy { @@ -112,7 +110,7 @@ Config::SharedConfig::SharedConfig( } if (config.has_tunneling_config()) { tunneling_config_helper_ = - std::make_unique(*stats_scope_.get(), config, context); + std::make_unique(config.tunneling_config(), context); } if (config.has_max_downstream_connection_duration()) { const uint64_t connection_duration = @@ -232,10 +230,8 @@ UpstreamDrainManager& Config::drainManager() { } Filter::Filter(ConfigSharedPtr config, Upstream::ClusterManager& cluster_manager) - : tracing_config_(Tracing::EgressConfig::get()), config_(config), - cluster_manager_(cluster_manager), downstream_callbacks_(*this), - upstream_callbacks_(new UpstreamCallbacks(this)), - upstream_decoder_filter_callbacks_(HttpStreamDecoderFilterCallbacks(this)) { + : config_(config), cluster_manager_(cluster_manager), downstream_callbacks_(*this), + upstream_callbacks_(new UpstreamCallbacks(this)) { ASSERT(config != nullptr); } @@ -293,11 +289,9 @@ void Filter::onInitFailure(UpstreamFailureReason reason) { // not have started attempting to connect to an upstream and there is no // connection pool callback latency to record. if (initial_upstream_connection_start_time_.has_value()) { - if (!getStreamInfo().upstreamInfo()->upstreamTiming().connectionPoolCallbackLatency()) { - getStreamInfo().upstreamInfo()->upstreamTiming().recordConnectionPoolCallbackLatency( - initial_upstream_connection_start_time_.value(), - read_callbacks_->connection().dispatcher().timeSource()); - } + getStreamInfo().upstreamInfo()->upstreamTiming().recordConnectionPoolCallbackLatency( + initial_upstream_connection_start_time_.value(), + read_callbacks_->connection().dispatcher().timeSource()); } read_callbacks_->connection().close( Network::ConnectionCloseType::NoFlush, @@ -558,16 +552,9 @@ bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster) { if (!factory) { return false; } - if (Runtime::runtimeFeatureEnabled( - "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { - // TODO(vikaschoudhary16): Initialize route_ once per cluster. - upstream_decoder_filter_callbacks_.route_ = std::make_shared( - cluster.info()->name(), - *std::unique_ptr{new Router::RetryPolicyImpl()}); - } - generic_conn_pool_ = factory->createGenericConnPool( - cluster, config_->tunnelingConfigHelper(), this, *upstream_callbacks_, - upstream_decoder_filter_callbacks_, getStreamInfo()); + + generic_conn_pool_ = factory->createGenericConnPool(cluster, config_->tunnelingConfigHelper(), + this, *upstream_callbacks_, getStreamInfo()); if (generic_conn_pool_) { connecting_ = true; connect_attempts_++; @@ -583,19 +570,7 @@ bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster) { void Filter::onGenericPoolFailure(ConnectionPool::PoolFailureReason reason, absl::string_view failure_reason, Upstream::HostDescriptionConstSharedPtr host) { - if (Runtime::runtimeFeatureEnabled( - "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { - // generic_conn_pool_ is an instance of TcpProxy::HttpConnPool. - // generic_conn_pool_->newStream() is called in maybeTunnel() which initializes an instance of - // Router::UpstreamRequest. If Router::UpstreamRequest receives headers from the upstream which - // results in end_stream=true, then via callbacks passed to Router::UpstreamRequest, - // TcpProxy::Filter::onGenericPoolFailure() gets invoked. If we do not do deferredDelete here, - // then the same instance of UpstreamRequest which is under execution will go out of scope. - read_callbacks_->connection().dispatcher().deferredDelete(std::move(generic_conn_pool_)); - } else { - generic_conn_pool_.reset(); - } - + generic_conn_pool_.reset(); read_callbacks_->upstreamHost(host); getStreamInfo().upstreamInfo()->setUpstreamHost(host); getStreamInfo().upstreamInfo()->setUpstreamTransportFailureReason(failure_reason); @@ -619,30 +594,23 @@ void Filter::onGenericPoolReady(StreamInfo::StreamInfo* info, Upstream::HostDescriptionConstSharedPtr& host, const Network::ConnectionInfoProvider& address_provider, Ssl::ConnectionInfoConstSharedPtr ssl_info) { - StreamInfo::UpstreamInfo& upstream_info = *getStreamInfo().upstreamInfo(); - if (!upstream_info.upstreamTiming().connectionPoolCallbackLatency()) { - upstream_info.upstreamTiming().recordConnectionPoolCallbackLatency( - initial_upstream_connection_start_time_.value(), - read_callbacks_->connection().dispatcher().timeSource()); - } upstream_ = std::move(upstream); generic_conn_pool_.reset(); read_callbacks_->upstreamHost(host); - // No need to set information using address_provider in case routing via Router::UpstreamRequest - // because in that case, information is already set by the - // Router::UpstreamRequest::onPoolReady() method before reaching here. - if (upstream_info.upstreamLocalAddress() == nullptr) { - upstream_info.setUpstreamLocalAddress(address_provider.localAddress()); - upstream_info.setUpstreamRemoteAddress(address_provider.remoteAddress()); - } + StreamInfo::UpstreamInfo& upstream_info = *getStreamInfo().upstreamInfo(); + upstream_info.upstreamTiming().recordConnectionPoolCallbackLatency( + initial_upstream_connection_start_time_.value(), + read_callbacks_->connection().dispatcher().timeSource()); upstream_info.setUpstreamHost(host); + upstream_info.setUpstreamLocalAddress(address_provider.localAddress()); + upstream_info.setUpstreamRemoteAddress(address_provider.remoteAddress()); upstream_info.setUpstreamSslConnection(ssl_info); onUpstreamConnection(); read_callbacks_->continueReading(); if (info) { upstream_info.setUpstreamFilterState(info->filterState()); } -} // namespace TcpProxy +} const Router::MetadataMatchCriteria* Filter::metadataMatchCriteria() { const Router::MetadataMatchCriteria* route_criteria = @@ -672,30 +640,14 @@ const std::string& TunnelResponseTrailers::key() { } TunnelingConfigHelperImpl::TunnelingConfigHelperImpl( - Stats::Scope& stats_scope, - const envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy& config_message, + const envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig& + config_message, Server::Configuration::FactoryContext& context) - : use_post_(config_message.tunneling_config().use_post()), - header_parser_(Envoy::Router::HeaderParser::configure( - config_message.tunneling_config().headers_to_add())), - propagate_response_headers_(config_message.tunneling_config().propagate_response_headers()), - propagate_response_trailers_(config_message.tunneling_config().propagate_response_trailers()), - post_path_(config_message.tunneling_config().post_path()), - route_stat_name_storage_("tcpproxy_tunneling", context.scope().symbolTable()), - // TODO(vikaschoudhary16): figure out which of the following router_config_ members are - // not required by tcp_proxy and move them to a different class - router_config_(route_stat_name_storage_.statName(), - context.serverFactoryContext().localInfo(), stats_scope, - context.serverFactoryContext().clusterManager(), - context.serverFactoryContext().runtime(), - context.serverFactoryContext().api().randomGenerator(), - std::make_unique( - context.serverFactoryContext().clusterManager()), - true, false, false, false, false, false, {}, - context.serverFactoryContext().api().timeSource(), - context.serverFactoryContext().httpContext(), - context.serverFactoryContext().routerContext()), - server_factory_context_(context.serverFactoryContext()) { + : use_post_(config_message.use_post()), + header_parser_(Envoy::Router::HeaderParser::configure(config_message.headers_to_add())), + propagate_response_headers_(config_message.propagate_response_headers()), + propagate_response_trailers_(config_message.propagate_response_trailers()), + post_path_(config_message.post_path()) { if (!post_path_.empty() && !use_post_) { throw EnvoyException("Can't set a post path when POST method isn't used"); } @@ -703,7 +655,7 @@ TunnelingConfigHelperImpl::TunnelingConfigHelperImpl( envoy::config::core::v3::SubstitutionFormatString substitution_format_config; substitution_format_config.mutable_text_format_source()->set_inline_string( - config_message.tunneling_config().hostname()); + config_message.hostname()); hostname_fmt_ = Formatter::SubstitutionFormatStringUtils::fromProtoConfig( substitution_format_config, context); } @@ -745,8 +697,8 @@ void Filter::onConnectTimeout() { } Network::FilterStatus Filter::onData(Buffer::Instance& data, bool end_stream) { - ENVOY_CONN_LOG(trace, "downstream connection received {} bytes, end_stream={}, has upstream {}", - read_callbacks_->connection(), data.length(), end_stream, upstream_ != nullptr); + ENVOY_CONN_LOG(trace, "downstream connection received {} bytes, end_stream={}", + read_callbacks_->connection(), data.length(), end_stream); getStreamInfo().getDownstreamBytesMeter()->addWireBytesReceived(data.length()); if (upstream_) { getStreamInfo().getUpstreamBytesMeter()->addWireBytesSent(data.length()); @@ -847,23 +799,14 @@ void Filter::onUpstreamEvent(Network::ConnectionEvent event) { if (event == Network::ConnectionEvent::RemoteClose || event == Network::ConnectionEvent::LocalClose) { - if (Runtime::runtimeFeatureEnabled( - "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { - read_callbacks_->connection().dispatcher().deferredDelete(std::move(upstream_)); - } else { - upstream_.reset(); - } + upstream_.reset(); disableIdleTimer(); if (connecting) { if (event == Network::ConnectionEvent::RemoteClose) { - getStreamInfo().setResponseFlag(StreamInfo::UpstreamConnectionFailure); - // upstreamHost can be nullptr if we received a disconnect from the upstream before - // receiving any response - if (read_callbacks_->upstreamHost() != nullptr) { - read_callbacks_->upstreamHost()->outlierDetector().putResult( - Upstream::Outlier::Result::LocalOriginConnectFailed); - } + getStreamInfo().setResponseFlag(StreamInfo::CoreResponseFlag::UpstreamConnectionFailure); + read_callbacks_->upstreamHost()->outlierDetector().putResult( + Upstream::Outlier::Result::LocalOriginConnectFailed); } if (!downstream_closed_) { route_ = pickRoute(); @@ -987,9 +930,6 @@ void Filter::disableIdleTimer() { } } -Filter::HttpStreamDecoderFilterCallbacks::HttpStreamDecoderFilterCallbacks(Filter* parent) - : parent_(parent), request_trailer_map_(Http::RequestTrailerMapImpl::create()) {} - UpstreamDrainManager::~UpstreamDrainManager() { // If connections aren't closed before they are destructed an ASSERT fires, // so cancel all pending drains, which causes the connections to be closed. diff --git a/source/common/tcp_proxy/tcp_proxy.h b/source/common/tcp_proxy/tcp_proxy.h index 4019ea1211ef..45f34b901405 100644 --- a/source/common/tcp_proxy/tcp_proxy.h +++ b/source/common/tcp_proxy/tcp_proxy.h @@ -10,7 +10,6 @@ #include "envoy/common/random_generator.h" #include "envoy/event/timer.h" #include "envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.pb.h" -#include "envoy/http/codec.h" #include "envoy/http/header_evaluator.h" #include "envoy/network/connection.h" #include "envoy/network/filter.h" @@ -23,7 +22,6 @@ #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/upstream.h" -#include "source/common/common/assert.h" #include "source/common/common/logger.h" #include "source/common/formatter/substitution_format_string.h" #include "source/common/http/header_map_impl.h" @@ -145,29 +143,24 @@ class TunnelResponseTrailers : public Http::TunnelResponseHeadersOrTrailersImpl private: const Http::ResponseTrailerMapPtr response_trailers_; }; -class Config; + class TunnelingConfigHelperImpl : public TunnelingConfigHelper, protected Logger::Loggable { public: TunnelingConfigHelperImpl( - Stats::Scope& scope, - const envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy& config_message, + const envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig& + config_message, Server::Configuration::FactoryContext& context); std::string host(const StreamInfo::StreamInfo& stream_info) const override; bool usePost() const override { return use_post_; } const std::string& postPath() const override { return post_path_; } Envoy::Http::HeaderEvaluator& headerEvaluator() const override { return *header_parser_; } - - const Envoy::Router::FilterConfig& routerFilterConfig() const override { return router_config_; } void propagateResponseHeaders(Http::ResponseHeaderMapPtr&& headers, const StreamInfo::FilterStateSharedPtr& filter_state) const override; void propagateResponseTrailers(Http::ResponseTrailerMapPtr&& trailers, const StreamInfo::FilterStateSharedPtr& filter_state) const override; - Server::Configuration::ServerFactoryContext& serverFactoryContext() const override { - return server_factory_context_; - } private: const bool use_post_; @@ -176,9 +169,6 @@ class TunnelingConfigHelperImpl : public TunnelingConfigHelper, const bool propagate_response_headers_; const bool propagate_response_trailers_; std::string post_path_; - Stats::StatNameManagedStorage route_stat_name_storage_; - const Router::FilterConfig router_config_; - Server::Configuration::ServerFactoryContext& server_factory_context_; }; /** @@ -471,107 +461,6 @@ class Filter : public Network::ReadFilter, }; StreamInfo::StreamInfo& getStreamInfo(); - class HttpStreamDecoderFilterCallbacks : public Http::StreamDecoderFilterCallbacks, - public ScopeTrackedObject { - public: - HttpStreamDecoderFilterCallbacks(Filter* parent); - // Http::StreamDecoderFilterCallbacks - OptRef connection() override { - return parent_->read_callbacks_->connection(); - } - StreamInfo::StreamInfo& streamInfo() override { return parent_->getStreamInfo(); } - const ScopeTrackedObject& scope() override { return *this; } - Event::Dispatcher& dispatcher() override { - return parent_->read_callbacks_->connection().dispatcher(); - } - void resetStream(Http::StreamResetReason, absl::string_view) override { - IS_ENVOY_BUG("Not implemented. Unexpected call to resetStream()"); - }; - Router::RouteConstSharedPtr route() override { return route_; } - Upstream::ClusterInfoConstSharedPtr clusterInfo() override { - return parent_->cluster_manager_.getThreadLocalCluster(parent_->route_->clusterName()) - ->info(); - } - uint64_t streamId() const override { - auto sip = parent_->getStreamInfo().getStreamIdProvider(); - if (sip) { - return sip->toInteger().value(); - } - return 0; - } - Tracing::Span& activeSpan() override { return parent_->active_span_; } - OptRef tracingConfig() const override { - return makeOptRef(parent_->tracing_config_); - } - void continueDecoding() override {} - void addDecodedData(Buffer::Instance&, bool) override {} - void injectDecodedDataToFilterChain(Buffer::Instance&, bool) override {} - Http::RequestTrailerMap& addDecodedTrailers() override { return *request_trailer_map_; } - Http::MetadataMapVector& addDecodedMetadata() override { - static Http::MetadataMapVector metadata_map_vector; - return metadata_map_vector; - } - const Buffer::Instance* decodingBuffer() override { return nullptr; } - void modifyDecodingBuffer(std::function) override {} - void sendLocalReply(Http::Code, absl::string_view, - std::function, - const absl::optional, - absl::string_view) override {} - void encode1xxHeaders(Http::ResponseHeaderMapPtr&&) override {} - Http::ResponseHeaderMapOptRef informationalHeaders() override { return {}; } - void encodeHeaders(Http::ResponseHeaderMapPtr&&, bool, absl::string_view) override {} - Http::ResponseHeaderMapOptRef responseHeaders() override { return {}; } - void encodeData(Buffer::Instance&, bool) override {} - Http::RequestHeaderMapOptRef requestHeaders() override { return {}; } - Http::RequestTrailerMapOptRef requestTrailers() override { return {}; } - void encodeTrailers(Http::ResponseTrailerMapPtr&&) override {} - Http::ResponseTrailerMapOptRef responseTrailers() override { return {}; } - void encodeMetadata(Http::MetadataMapPtr&&) override {} - // TODO(vikaschoudhary16): Implement watermark callbacks and test through flow control e2es. - void onDecoderFilterAboveWriteBufferHighWatermark() override {} - void onDecoderFilterBelowWriteBufferLowWatermark() override {} - void addDownstreamWatermarkCallbacks(Http::DownstreamWatermarkCallbacks&) override {} - void removeDownstreamWatermarkCallbacks(Http::DownstreamWatermarkCallbacks&) override {} - void setDecoderBufferLimit(uint32_t) override {} - uint32_t decoderBufferLimit() override { return 0; } - bool recreateStream(const Http::ResponseHeaderMap*) override { return false; } - void addUpstreamSocketOptions(const Network::Socket::OptionsSharedPtr&) override {} - Network::Socket::OptionsSharedPtr getUpstreamSocketOptions() const override { return nullptr; } - const Router::RouteSpecificFilterConfig* mostSpecificPerFilterConfig() const override { - return nullptr; - } - Buffer::BufferMemoryAccountSharedPtr account() const override { return nullptr; } - void setUpstreamOverrideHost(Upstream::LoadBalancerContext::OverrideHost) override {} - absl::optional - upstreamOverrideHost() const override { - return absl::nullopt; - } - void restoreContextOnContinue(ScopeTrackedObjectStack& tracked_object_stack) override { - tracked_object_stack.add(*this); - } - void traversePerFilterConfig( - std::function) const override {} - Http::Http1StreamEncoderOptionsOptRef http1StreamEncoderOptions() override { return {}; } - OptRef downstreamCallbacks() override { return {}; } - OptRef upstreamCallbacks() override { return {}; } - void resetIdleTimer() override {} - // absl::optional upstreamOverrideHost() const override { - // return absl::nullopt; - // } - absl::string_view filterConfigName() const override { return ""; } - - // ScopeTrackedObject - void dumpState(std::ostream& os, int indent_level) const override { - const char* spaces = spacesForLevel(indent_level); - os << spaces << "TcpProxy " << this << DUMP_MEMBER(streamId()) << "\n"; - DUMP_DETAILS(parent_->getStreamInfo().upstreamInfo()); - } - Filter* parent_{}; - Http::RequestTrailerMapPtr request_trailer_map_; - std::shared_ptr route_; - }; - Tracing::NullSpan active_span_; - const Tracing::Config& tracing_config_; protected: struct DownstreamCallbacks : public Network::ConnectionCallbacks { @@ -656,7 +545,6 @@ class Filter : public Network::ReadFilter, uint32_t connect_attempts_{}; bool connecting_{}; bool downstream_closed_{}; - HttpStreamDecoderFilterCallbacks upstream_decoder_filter_callbacks_; }; // This class deals with an upstream connection that needs to finish flushing, when the downstream diff --git a/source/common/tcp_proxy/upstream.cc b/source/common/tcp_proxy/upstream.cc index 01e99426b9f6..5e4eaa35338d 100644 --- a/source/common/tcp_proxy/upstream.cc +++ b/source/common/tcp_proxy/upstream.cc @@ -1,19 +1,16 @@ #include "source/common/tcp_proxy/upstream.h" -#include "envoy/http/header_map.h" #include "envoy/upstream/cluster_manager.h" #include "source/common/http/codec_client.h" #include "source/common/http/codes.h" #include "source/common/http/header_map_impl.h" #include "source/common/http/headers.h" -#include "source/common/http/null_route_impl.h" #include "source/common/http/utility.h" #include "source/common/runtime/runtime_features.h" namespace Envoy { namespace TcpProxy { - using TunnelingConfig = envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig; @@ -197,7 +194,6 @@ void HttpUpstream::resetEncoder(Network::ConnectionEvent event, bool inform_down conn_pool_callbacks_->onFailure(); return; } - if (inform_downstream) { upstream_callbacks_.onEvent(event); } @@ -233,7 +229,7 @@ TcpConnPool::~TcpConnPool() { void TcpConnPool::newStream(GenericConnectionPoolCallbacks& callbacks) { callbacks_ = &callbacks; - // Given this function is re-entrant, make sure we only reset the upstream_handle_ if given a + // Given this function is reentrant, make sure we only reset the upstream_handle_ if given a // valid connection handle. If newConnection fails inline it may result in attempting to // select a new host, and a recursive call to establishUpstreamConnection. In this case the // first call to newConnection will return null and the inner call will persist. @@ -274,67 +270,29 @@ HttpConnPool::HttpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, Upstream::LoadBalancerContext* context, const TunnelingConfigHelper& config, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, - Http::StreamDecoderFilterCallbacks& stream_decoder_callbacks, Http::CodecType type, StreamInfo::StreamInfo& downstream_info) - : config_(config), type_(type), decoder_filter_callbacks_(&stream_decoder_callbacks), - upstream_callbacks_(upstream_callbacks), downstream_info_(downstream_info) { + : config_(config), type_(type), upstream_callbacks_(upstream_callbacks), + downstream_info_(downstream_info) { absl::optional protocol; if (type_ == Http::CodecType::HTTP3) { protocol = Http::Protocol::Http3; } else if (type_ == Http::CodecType::HTTP2) { protocol = Http::Protocol::Http2; } - if (Runtime::runtimeFeatureEnabled( - "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { - absl::optional upstream_protocol = protocol; - generic_conn_pool_ = createConnPool(thread_local_cluster, context, upstream_protocol); - return; - } conn_pool_data_ = thread_local_cluster.httpConnPool(Upstream::ResourcePriority::Default, protocol, context); } -std::unique_ptr -HttpConnPool::createConnPool(Upstream::ThreadLocalCluster& cluster, - Upstream::LoadBalancerContext* context, - absl::optional protocol) { - Router::GenericConnPoolFactory* factory = nullptr; - factory = Envoy::Config::Utility::getFactoryByName( - "envoy.filters.connection_pools.http.generic"); - if (!factory) { - return nullptr; - } - - return factory->createGenericConnPool( - cluster, Envoy::Router::GenericConnPoolFactory::UpstreamProtocol::HTTP, - decoder_filter_callbacks_->route()->routeEntry()->priority(), protocol, context); -} - HttpConnPool::~HttpConnPool() { if (upstream_handle_ != nullptr) { // Because HTTP connections are generally shorter lived and have a higher probability of use // before going idle, they are closed with Default rather than CloseExcess. upstream_handle_->cancel(ConnectionPool::CancelPolicy::Default); } - if (combined_upstream_ != nullptr) { - combined_upstream_->onDownstreamEvent(Network::ConnectionEvent::LocalClose); - } } void HttpConnPool::newStream(GenericConnectionPoolCallbacks& callbacks) { callbacks_ = &callbacks; - if (Runtime::runtimeFeatureEnabled( - "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { - combined_upstream_ = std::make_unique( - *this, upstream_callbacks_, *decoder_filter_callbacks_, config_, downstream_info_); - RouterUpstreamRequestPtr upstream_request = std::make_unique( - *combined_upstream_, std::move(generic_conn_pool_), /*can_send_early_data_=*/false, - /*can_use_http3_=*/true, true /*enable_tcp_tunneling*/); - combined_upstream_->setRouterUpstreamRequest(std::move(upstream_request)); - combined_upstream_->newStream(callbacks); - return; - } - upstream_ = std::make_unique(upstream_callbacks_, config_, downstream_info_, type_); Tcp::ConnectionPool::Cancellable* handle = conn_pool_data_.value().newStream(upstream_->responseDecoder(), *this, @@ -352,15 +310,6 @@ void HttpConnPool::onPoolFailure(ConnectionPool::PoolFailureReason reason, callbacks_->onGenericPoolFailure(reason, failure_reason, host); } -void HttpConnPool::onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host, - bool pool_success) { - if (!pool_success) { - return; - } - combined_upstream_->setConnPoolCallbacks(std::make_unique( - *this, host, downstream_info_.downstreamAddressProvider().sslConnection())); -} - void HttpConnPool::onPoolReady(Http::RequestEncoder& request_encoder, Upstream::HostDescriptionConstSharedPtr host, StreamInfo::StreamInfo& info, absl::optional) { @@ -383,174 +332,8 @@ void HttpConnPool::onPoolReady(Http::RequestEncoder& request_encoder, void HttpConnPool::onGenericPoolReady(Upstream::HostDescriptionConstSharedPtr& host, const Network::ConnectionInfoProvider& address_provider, Ssl::ConnectionInfoConstSharedPtr ssl_info) { - if (Runtime::runtimeFeatureEnabled( - "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { - - callbacks_->onGenericPoolReady(nullptr, std::move(combined_upstream_), host, address_provider, - ssl_info); - return; - } callbacks_->onGenericPoolReady(nullptr, std::move(upstream_), host, address_provider, ssl_info); } -CombinedUpstream::CombinedUpstream(HttpConnPool& http_conn_pool, - Tcp::ConnectionPool::UpstreamCallbacks& callbacks, - Http::StreamDecoderFilterCallbacks& decoder_callbacks, - const TunnelingConfigHelper& config, - StreamInfo::StreamInfo& downstream_info) - : config_(config), downstream_info_(downstream_info), parent_(http_conn_pool), - decoder_filter_callbacks_(decoder_callbacks), response_decoder_(*this), - upstream_callbacks_(callbacks) { - auto is_ssl = downstream_info_.downstreamAddressProvider().sslConnection(); - downstream_headers_ = Http::createHeaderMap({ - {Http::Headers::get().Method, config_.usePost() ? "POST" : "CONNECT"}, - {Http::Headers::get().Host, config_.host(downstream_info_)}, - }); - - if (config_.usePost()) { - const std::string& scheme = - is_ssl ? Http::Headers::get().SchemeValues.Https : Http::Headers::get().SchemeValues.Http; - downstream_headers_->addReference(Http::Headers::get().Path, config_.postPath()); - downstream_headers_->addReference(Http::Headers::get().Scheme, scheme); - } - - config_.headerEvaluator().evaluateHeaders( - *downstream_headers_, {downstream_info_.getRequestHeaders()}, downstream_info_); -} - -void CombinedUpstream::setRouterUpstreamRequest( - Router::UpstreamRequestPtr router_upstream_request) { - LinkedList::moveIntoList(std::move(router_upstream_request), upstream_requests_); -} - -void CombinedUpstream::newStream(GenericConnectionPoolCallbacks&) { - upstream_requests_.front()->acceptHeadersFromRouter(false); -} - -void CombinedUpstream::encodeData(Buffer::Instance& data, bool end_stream) { - if (upstream_requests_.empty()) { - return; - } - upstream_requests_.front()->acceptDataFromRouter(data, end_stream); - if (end_stream) { - doneWriting(); - } -} - -bool CombinedUpstream::readDisable(bool disable) { - if (upstream_requests_.empty()) { - return false; - } - if (disable) { - upstream_requests_.front()->onAboveWriteBufferHighWatermark(); - } - return true; -} - -Tcp::ConnectionPool::ConnectionData* -CombinedUpstream::onDownstreamEvent(Network::ConnectionEvent event) { - if (upstream_requests_.empty()) { - return nullptr; - } - - if (event == Network::ConnectionEvent::LocalClose || - event == Network::ConnectionEvent::RemoteClose) { - upstream_requests_.front()->resetStream(); - } - return nullptr; -} - -bool CombinedUpstream::isValidResponse(const Http::ResponseHeaderMap& headers) { - switch (parent_.codecType()) { - case Http::CodecType::HTTP1: - // According to RFC7231 any 2xx response indicates that the connection is - // established. - // Any 'Content-Length' or 'Transfer-Encoding' header fields MUST be ignored. - // https://tools.ietf.org/html/rfc7231#section-4.3.6 - return Http::CodeUtility::is2xx(Http::Utility::getResponseStatus(headers)); - case Http::CodecType::HTTP2: - case Http::CodecType::HTTP3: - if (Http::Utility::getResponseStatus(headers) != 200) { - return false; - } - return true; - } - return true; -} - -void CombinedUpstream::onResetEncoder(Network::ConnectionEvent event, bool inform_downstream) { - if (event == Network::ConnectionEvent::LocalClose || - event == Network::ConnectionEvent::RemoteClose) { - if (!upstream_requests_.empty()) { - upstream_requests_.front()->resetStream(); - } - } - - // If we did not receive a valid CONNECT response yet we treat this as a pool - // failure, otherwise we forward the event downstream. - if (conn_pool_callbacks_ != nullptr) { - conn_pool_callbacks_->onFailure(); - return; - } - - if (inform_downstream) { - upstream_callbacks_.onEvent(event); - } -} - -// Router::RouterFilterInterface -void CombinedUpstream::onUpstreamHeaders([[maybe_unused]] uint64_t response_code, - Http::ResponseHeaderMapPtr&& headers, - [[maybe_unused]] UpstreamRequest& upstream_request, - bool end_stream) { - responseDecoder().decodeHeaders(std::move(headers), end_stream); -} - -void CombinedUpstream::onUpstreamData(Buffer::Instance& data, - [[maybe_unused]] UpstreamRequest& upstream_request, - bool end_stream) { - responseDecoder().decodeData(data, end_stream); -} - -void CombinedUpstream::onUpstreamTrailers(Http::ResponseTrailerMapPtr&& trailers, - UpstreamRequest&) { - responseDecoder().decodeTrailers(std::move(trailers)); -} - -Http::RequestHeaderMap* CombinedUpstream::downstreamHeaders() { return downstream_headers_.get(); } - -void CombinedUpstream::doneReading() { - read_half_closed_ = true; - if (write_half_closed_) { - onResetEncoder(Network::ConnectionEvent::LocalClose); - } -} - -void CombinedUpstream::onUpstreamReset(Http::StreamResetReason, absl::string_view, - UpstreamRequest&) { - upstream_callbacks_.onEvent(Network::ConnectionEvent::RemoteClose); -} - -void CombinedUpstream::doneWriting() { - write_half_closed_ = true; - if (read_half_closed_) { - onResetEncoder(Network::ConnectionEvent::LocalClose); - } -} - -void CombinedUpstream::onResetStream(Http::StreamResetReason, absl::string_view) { - read_half_closed_ = true; - write_half_closed_ = true; - onResetEncoder(Network::ConnectionEvent::LocalClose); -} - -void CombinedUpstream::onAboveWriteBufferHighWatermark() { - upstream_callbacks_.onAboveWriteBufferHighWatermark(); -} - -void CombinedUpstream::onBelowWriteBufferLowWatermark() { - upstream_callbacks_.onBelowWriteBufferLowWatermark(); -} - } // namespace TcpProxy } // namespace Envoy diff --git a/source/common/tcp_proxy/upstream.h b/source/common/tcp_proxy/upstream.h index 23af532b37cb..d16126547cc5 100644 --- a/source/common/tcp_proxy/upstream.h +++ b/source/common/tcp_proxy/upstream.h @@ -1,11 +1,7 @@ #pragma once -#include - #include "envoy/http/conn_pool.h" -#include "envoy/http/header_map.h" #include "envoy/network/connection.h" -#include "envoy/router/router_ratelimit.h" #include "envoy/tcp/conn_pool.h" #include "envoy/tcp/upstream.h" #include "envoy/upstream/load_balancer.h" @@ -15,13 +11,7 @@ #include "source/common/buffer/buffer_impl.h" #include "source/common/common/dump_state_utils.h" #include "source/common/http/codec_client.h" -#include "source/common/http/hash_policy.h" -#include "source/common/http/null_route_impl.h" -#include "source/common/network/utility.h" -#include "source/common/router/config_impl.h" #include "source/common/router/header_parser.h" -#include "source/common/router/router.h" -#include "source/extensions/early_data/default_early_data_policy.h" namespace Envoy { namespace TcpProxy { @@ -57,25 +47,16 @@ class TcpConnPool : public GenericConnPool, public Tcp::ConnectionPool::Callback }; class HttpUpstream; -class CombinedUpstream; class HttpConnPool : public GenericConnPool, public Http::ConnectionPool::Callbacks { public: HttpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, Upstream::LoadBalancerContext* context, const TunnelingConfigHelper& config, - Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, - Http::StreamDecoderFilterCallbacks&, Http::CodecType type, + Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, Http::CodecType type, StreamInfo::StreamInfo& downstream_info); - - using RouterUpstreamRequest = Router::UpstreamRequest; - using RouterUpstreamRequestPtr = std::unique_ptr; ~HttpConnPool() override; - bool valid() const { return conn_pool_data_.has_value() || generic_conn_pool_; } - Http::CodecType codecType() const { return type_; } - std::unique_ptr createConnPool(Upstream::ThreadLocalCluster&, - Upstream::LoadBalancerContext* context, - absl::optional protocol); + bool valid() const { return conn_pool_data_.has_value(); } // GenericConnPool void newStream(GenericConnectionPoolCallbacks& callbacks) override; @@ -88,32 +69,16 @@ class HttpConnPool : public GenericConnPool, public Http::ConnectionPool::Callba Upstream::HostDescriptionConstSharedPtr host, StreamInfo::StreamInfo& info, absl::optional) override; - void onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr, bool); - void onHttpPoolReady(Upstream::HostDescriptionConstSharedPtr& host, - Ssl::ConnectionInfoConstSharedPtr ssl_info); - class Callbacks { public: Callbacks(HttpConnPool& conn_pool, Upstream::HostDescriptionConstSharedPtr host, Ssl::ConnectionInfoConstSharedPtr ssl_info) : conn_pool_(&conn_pool), host_(host), ssl_info_(ssl_info) {} virtual ~Callbacks() = default; - virtual void onSuccess(Http::RequestEncoder* request_encoder) { + virtual void onSuccess(Http::RequestEncoder& request_encoder) { ASSERT(conn_pool_ != nullptr); - if (!Runtime::runtimeFeatureEnabled( - "envoy.restart_features.upstream_http_filters_with_tcp_proxy")) { - ASSERT(request_encoder != nullptr); - conn_pool_->onGenericPoolReady(host_, request_encoder->getStream().connectionInfoProvider(), - ssl_info_); - return; - } - - Network::ConnectionInfoProviderSharedPtr local_connection_info_provider( - std::make_shared( - Network::Utility::getCanonicalIpv4LoopbackAddress(), - Network::Utility::getCanonicalIpv4LoopbackAddress())); - - conn_pool_->onGenericPoolReady(host_, *local_connection_info_provider.get(), ssl_info_); + conn_pool_->onGenericPoolReady(host_, request_encoder.getStream().connectionInfoProvider(), + ssl_info_); } virtual void onFailure() { ASSERT(conn_pool_ != nullptr); @@ -139,12 +104,9 @@ class HttpConnPool : public GenericConnPool, public Http::ConnectionPool::Callba absl::optional conn_pool_data_{}; Http::ConnectionPool::Cancellable* upstream_handle_{}; GenericConnectionPoolCallbacks* callbacks_{}; - Http::StreamDecoderFilterCallbacks* decoder_filter_callbacks_; Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks_; std::unique_ptr upstream_; - std::unique_ptr combined_upstream_; StreamInfo::StreamInfo& downstream_info_; - std::unique_ptr generic_conn_pool_; }; class TcpUpstream : public GenericUpstream { @@ -168,7 +130,6 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { public: using TunnelingConfig = envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig; - HttpUpstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, const TunnelingConfigHelper& config, StreamInfo::StreamInfo& downstream_info, Http::CodecType type); @@ -194,7 +155,7 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { void onAboveWriteBufferHighWatermark() override; void onBelowWriteBufferLowWatermark() override; - virtual void setRequestEncoder(Http::RequestEncoder& request_encoder, bool is_ssl); + void setRequestEncoder(Http::RequestEncoder& request_encoder, bool is_ssl); void setConnPoolCallbacks(std::unique_ptr&& callbacks) { conn_pool_callbacks_ = std::move(callbacks); } @@ -209,13 +170,12 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { const TunnelingConfigHelper& config_; // The downstream info that is owned by the downstream connection. StreamInfo::StreamInfo& downstream_info_; - std::unique_ptr downstream_headers_; private: - Upstream::ClusterInfoConstSharedPtr cluster_; class DecoderShim : public Http::ResponseDecoder { public: DecoderShim(HttpUpstream& parent) : parent_(parent) {} + // Http::ResponseDecoder void decode1xxHeaders(Http::ResponseHeaderMapPtr&&) override {} void decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) override { bool is_valid_response = parent_.isValidResponse(*headers); @@ -224,7 +184,7 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { if (!is_valid_response || end_stream) { parent_.resetEncoder(Network::ConnectionEvent::LocalClose); } else if (parent_.conn_pool_callbacks_ != nullptr) { - parent_.conn_pool_callbacks_->onSuccess(parent_.request_encoder_); + parent_.conn_pool_callbacks_->onSuccess(*parent_.request_encoder_); parent_.conn_pool_callbacks_.reset(); } } @@ -264,133 +224,5 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { std::unique_ptr conn_pool_callbacks_; }; -class CombinedUpstream : public GenericUpstream, - protected Http::StreamCallbacks, - public Envoy::Router::RouterFilterInterface { -public: - CombinedUpstream(HttpConnPool& http_conn_pool, Tcp::ConnectionPool::UpstreamCallbacks& callbacks, - Http::StreamDecoderFilterCallbacks& decoder_callbacks, - const TunnelingConfigHelper& config, StreamInfo::StreamInfo& downstream_info); - ~CombinedUpstream() override = default; - using UpstreamRequest = Router::UpstreamRequest; - Http::ResponseDecoder& responseDecoder() { return response_decoder_; } - void doneReading(); - void doneWriting(); - using UpstreamRequestPtr = std::unique_ptr; - void setRouterUpstreamRequest(UpstreamRequestPtr); - void newStream(GenericConnectionPoolCallbacks& callbacks); - void encodeData(Buffer::Instance& data, bool end_stream) override; - Tcp::ConnectionPool::ConnectionData* onDownstreamEvent(Network::ConnectionEvent event) override; - bool isValidResponse(const Http::ResponseHeaderMap&); - bool readDisable(bool disable) override; - void setConnPoolCallbacks(std::unique_ptr&& callbacks) { - conn_pool_callbacks_ = std::move(callbacks); - } - void addBytesSentCallback(Network::Connection::BytesSentCb) override{}; - // HTTP upstream must not implement converting upstream transport - // socket from non-secure to secure mode. - bool startUpstreamSecureTransport() override { return false; } - Ssl::ConnectionInfoConstSharedPtr getUpstreamConnectionSslInfo() override { return nullptr; } - - // Http::StreamCallbacks - void onResetStream(Http::StreamResetReason reason, - absl::string_view transport_failure_reason) override; - void onAboveWriteBufferHighWatermark() override; - void onBelowWriteBufferLowWatermark() override; - - // Router::RouterFilterInterface - void onUpstreamHeaders(uint64_t response_code, Http::ResponseHeaderMapPtr&& headers, - UpstreamRequest& upstream_request, bool end_stream) override; - void onUpstreamData(Buffer::Instance& data, UpstreamRequest& upstream_request, - bool end_stream) override; - void onUpstream1xxHeaders(Http::ResponseHeaderMapPtr&&, UpstreamRequest&) override {} - void onUpstreamTrailers(Http::ResponseTrailerMapPtr&&, UpstreamRequest&) override; - void onUpstreamMetadata(Http::MetadataMapPtr&&) override {} - void onUpstreamReset(Http::StreamResetReason stream_reset_reason, - absl::string_view transport_failure_reason, UpstreamRequest&) override; - void onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host, - bool pool_success) override { - parent_.onUpstreamHostSelected(host, pool_success); - } - void onPerTryTimeout(UpstreamRequest&) override {} - void onPerTryIdleTimeout(UpstreamRequest&) override {} - void onStreamMaxDurationReached(UpstreamRequest&) override {} - Http::StreamDecoderFilterCallbacks* callbacks() override { return &decoder_filter_callbacks_; } - Upstream::ClusterInfoConstSharedPtr cluster() override { - return decoder_filter_callbacks_.clusterInfo(); - } - Router::FilterConfig& config() override { - return const_cast(config_.routerFilterConfig()); - } - Router::TimeoutData timeout() override { return {}; } - absl::optional dynamicMaxStreamDuration() const override { - return absl::nullopt; - } - Http::RequestHeaderMap* downstreamHeaders() override; - Http::RequestTrailerMap* downstreamTrailers() override { return nullptr; } - bool downstreamResponseStarted() const override { return false; } - bool downstreamEndStream() const override { return false; } - uint32_t attemptCount() const override { return 0; } - -protected: - void onResetEncoder(Network::ConnectionEvent event, bool inform_downstream = true); - - // The config object that is owned by the downstream network filter chain factory. - const TunnelingConfigHelper& config_; - // The downstream info that is owned by the downstream connection. - StreamInfo::StreamInfo& downstream_info_; - std::list upstream_requests_; - std::unique_ptr downstream_headers_; - HttpConnPool& parent_; - -private: - Http::StreamDecoderFilterCallbacks& decoder_filter_callbacks_; - class DecoderShim : public Http::ResponseDecoder { - public: - DecoderShim(CombinedUpstream& parent) : parent_(parent) {} - // Http::ResponseDecoder - void decode1xxHeaders(Http::ResponseHeaderMapPtr&&) override {} - void decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) override { - bool is_valid_response = parent_.isValidResponse(*headers); - parent_.config_.propagateResponseHeaders(std::move(headers), - parent_.downstream_info_.filterState()); - if (!is_valid_response || end_stream) { - parent_.onResetEncoder(Network::ConnectionEvent::LocalClose); - } else if (parent_.conn_pool_callbacks_ != nullptr) { - parent_.conn_pool_callbacks_->onSuccess(nullptr /*parent_.request_encoder_*/); - parent_.conn_pool_callbacks_.reset(); - } - } - void decodeData(Buffer::Instance& data, bool end_stream) override { - parent_.upstream_callbacks_.onUpstreamData(data, end_stream); - if (end_stream) { - parent_.doneReading(); - } - } - void decodeTrailers(Http::ResponseTrailerMapPtr&& trailers) override { - parent_.config_.propagateResponseTrailers(std::move(trailers), - parent_.downstream_info_.filterState()); - if (Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.tcp_tunneling_send_downstream_fin_on_upstream_trailers")) { - Buffer::OwnedImpl data; - parent_.upstream_callbacks_.onUpstreamData(data, /* end_stream = */ true); - } - parent_.doneReading(); - } - void decodeMetadata(Http::MetadataMapPtr&&) override {} - void dumpState(std::ostream& os, int indent_level) const override { - DUMP_STATE_UNIMPLEMENTED(DecoderShim); - } - - private: - CombinedUpstream& parent_; - }; - DecoderShim response_decoder_; - Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks_; - std::unique_ptr conn_pool_callbacks_; - bool read_half_closed_{}; - bool write_half_closed_{}; -}; - } // namespace TcpProxy } // namespace Envoy diff --git a/source/extensions/upstreams/http/http/upstream_request.h b/source/extensions/upstreams/http/http/upstream_request.h index db8adfc2cc14..d7b29d7c8d53 100644 --- a/source/extensions/upstreams/http/http/upstream_request.h +++ b/source/extensions/upstreams/http/http/upstream_request.h @@ -73,7 +73,6 @@ class HttpUpstream : public Router::GenericUpstream, public Envoy::Http::StreamC void encodeTrailers(const Envoy::Http::RequestTrailerMap& trailers) override { request_encoder_->encodeTrailers(trailers); } - void enableHalfClose() override { request_encoder_->enableTcpTunneling(); } void readDisable(bool disable) override { request_encoder_->getStream().readDisable(disable); } diff --git a/source/extensions/upstreams/http/tcp/upstream_request.h b/source/extensions/upstreams/http/tcp/upstream_request.h index 8c7431250c1e..87e9431fbd30 100644 --- a/source/extensions/upstreams/http/tcp/upstream_request.h +++ b/source/extensions/upstreams/http/tcp/upstream_request.h @@ -72,7 +72,6 @@ class TcpUpstream : public Router::GenericUpstream, void encodeMetadata(const Envoy::Http::MetadataMapVector&) override {} Envoy::Http::Status encodeHeaders(const Envoy::Http::RequestHeaderMap&, bool end_stream) override; void encodeTrailers(const Envoy::Http::RequestTrailerMap&) override; - void enableHalfClose() override {} void readDisable(bool disable) override; void resetStream() override; void setAccount(Buffer::BufferMemoryAccountSharedPtr) override {} diff --git a/source/extensions/upstreams/http/udp/upstream_request.h b/source/extensions/upstreams/http/udp/upstream_request.h index b5f6e25e6540..4d4a132411e1 100644 --- a/source/extensions/upstreams/http/udp/upstream_request.h +++ b/source/extensions/upstreams/http/udp/upstream_request.h @@ -77,7 +77,6 @@ class UdpUpstream : public Router::GenericUpstream, void encodeTrailers(const Envoy::Http::RequestTrailerMap&) override {} void readDisable(bool) override {} void resetStream() override; - void enableHalfClose() override {} void setAccount(Buffer::BufferMemoryAccountSharedPtr) override {} const StreamInfo::BytesMeterSharedPtr& bytesMeter() override { return bytes_meter_; } diff --git a/source/extensions/upstreams/tcp/generic/BUILD b/source/extensions/upstreams/tcp/generic/BUILD index ea02233167e3..a29fa7133934 100644 --- a/source/extensions/upstreams/tcp/generic/BUILD +++ b/source/extensions/upstreams/tcp/generic/BUILD @@ -18,7 +18,6 @@ envoy_cc_extension( ], visibility = ["//visibility:public"], deps = [ - "//envoy/http:filter_interface", "//envoy/stream_info:bool_accessor_interface", "//envoy/stream_info:filter_state_interface", "//source/common/http:codec_client_lib", diff --git a/source/extensions/upstreams/tcp/generic/config.cc b/source/extensions/upstreams/tcp/generic/config.cc index d3e33fdb83f8..e688ab84a510 100644 --- a/source/extensions/upstreams/tcp/generic/config.cc +++ b/source/extensions/upstreams/tcp/generic/config.cc @@ -18,7 +18,6 @@ TcpProxy::GenericConnPoolPtr GenericConnPoolFactory::createGenericConnPool( Upstream::ThreadLocalCluster& thread_local_cluster, TcpProxy::TunnelingConfigHelperOptConstRef config, Upstream::LoadBalancerContext* context, Envoy::Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, - Http::StreamDecoderFilterCallbacks& stream_decoder_callbacks, StreamInfo::StreamInfo& downstream_info) const { if (config.has_value() && !disableTunnelingByFilterState(downstream_info)) { Http::CodecType pool_type; @@ -31,8 +30,7 @@ TcpProxy::GenericConnPoolPtr GenericConnPoolFactory::createGenericConnPool( pool_type = Http::CodecType::HTTP1; } auto ret = std::make_unique( - thread_local_cluster, context, *config, upstream_callbacks, stream_decoder_callbacks, - pool_type, downstream_info); + thread_local_cluster, context, *config, upstream_callbacks, pool_type, downstream_info); return (ret->valid() ? std::move(ret) : nullptr); } auto ret = std::make_unique(thread_local_cluster, context, diff --git a/source/extensions/upstreams/tcp/generic/config.h b/source/extensions/upstreams/tcp/generic/config.h index e7e87a0145c3..c4fee935ef96 100644 --- a/source/extensions/upstreams/tcp/generic/config.h +++ b/source/extensions/upstreams/tcp/generic/config.h @@ -1,7 +1,6 @@ #pragma once #include "envoy/extensions/upstreams/tcp/generic/v3/generic_connection_pool.pb.h" -#include "envoy/http/filter.h" #include "envoy/registry/registry.h" #include "envoy/tcp/upstream.h" @@ -23,7 +22,6 @@ class GenericConnPoolFactory : public TcpProxy::GenericConnPoolFactory { TcpProxy::TunnelingConfigHelperOptConstRef config, Upstream::LoadBalancerContext* context, Envoy::Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, - Http::StreamDecoderFilterCallbacks& stream_decoder_callbacks, StreamInfo::StreamInfo& downstream_info) const override; ProtobufTypes::MessagePtr createEmptyConfigProto() override { diff --git a/test/common/router/upstream_request_test.cc b/test/common/router/upstream_request_test.cc index bb9a8185d4b4..67a6b003cd36 100644 --- a/test/common/router/upstream_request_test.cc +++ b/test/common/router/upstream_request_test.cc @@ -33,9 +33,8 @@ class UpstreamRequestTest : public testing::Test { void initialize() { auto conn_pool = std::make_unique>(); conn_pool_ = conn_pool.get(); - upstream_request_ = - std::make_unique(router_filter_interface_, std::move(conn_pool), false, - true, false /*enable_tcp_tunneling*/); + upstream_request_ = std::make_unique(router_filter_interface_, + std::move(conn_pool), false, true); } Http::FilterFactoryCb createDecoderFilterFactoryCb(Http::StreamDecoderFilterSharedPtr filter) { return [filter](Http::FilterChainFactoryCallbacks& callbacks) { diff --git a/test/common/tcp_proxy/BUILD b/test/common/tcp_proxy/BUILD index d6c47b8a9cbc..0630b106fd5c 100644 --- a/test/common/tcp_proxy/BUILD +++ b/test/common/tcp_proxy/BUILD @@ -79,13 +79,9 @@ envoy_cc_test( deps = [ "//source/common/tcp_proxy", "//test/mocks/http:http_mocks", - "//test/mocks/router:router_filter_interface", - "//test/mocks/router:upstream_request", "//test/mocks/server:factory_context_mocks", - "//test/mocks/stats:stats_mocks", "//test/mocks/tcp:tcp_mocks", "//test/mocks/upstream:cluster_manager_mocks", - "//test/mocks/upstream:load_balancer_context_mock", "//test/test_common:test_runtime_lib", ], ) diff --git a/test/common/tcp_proxy/tcp_proxy_test.cc b/test/common/tcp_proxy/tcp_proxy_test.cc index 144e50f2fbbc..41ad163dba41 100644 --- a/test/common/tcp_proxy/tcp_proxy_test.cc +++ b/test/common/tcp_proxy/tcp_proxy_test.cc @@ -171,7 +171,7 @@ class TcpProxyTest : public TcpProxyTestBase { std::shared_ptr> mock_access_logger_; }; -TEST_P(TcpProxyTest, ExplicitCluster) { +TEST_F(TcpProxyTest, ExplicitCluster) { configure(defaultConfig()); NiceMock connection; @@ -179,7 +179,7 @@ TEST_P(TcpProxyTest, ExplicitCluster) { } // Tests that half-closes are proxied and don't themselves cause any connection to be closed. -TEST_P(TcpProxyTest, HalfCloseProxy) { +TEST_F(TcpProxyTest, HalfCloseProxy) { setup(1); EXPECT_CALL(filter_callbacks_.connection_, close(_)).Times(0); @@ -200,7 +200,7 @@ TEST_P(TcpProxyTest, HalfCloseProxy) { } // Test with an explicitly configured upstream. -TEST_P(TcpProxyTest, ExplicitFactory) { +TEST_F(TcpProxyTest, ExplicitFactory) { // Explicitly configure an HTTP upstream, to test factory creation. auto& info = factory_context_.server_factory_context_.cluster_manager_.thread_local_cluster_ .cluster_.info_; @@ -224,7 +224,7 @@ TEST_P(TcpProxyTest, ExplicitFactory) { } // Test nothing bad happens if an invalid factory is configured. -TEST_P(TcpProxyTest, BadFactory) { +TEST_F(TcpProxyTest, BadFactory) { auto& info = factory_context_.server_factory_context_.cluster_manager_.thread_local_cluster_ .cluster_.info_; info->upstream_config_ = std::make_unique(); @@ -262,7 +262,7 @@ TEST_P(TcpProxyTest, BadFactory) { } // Test that downstream is closed after an upstream LocalClose. -TEST_P(TcpProxyTest, UpstreamLocalDisconnect) { +TEST_F(TcpProxyTest, UpstreamLocalDisconnect) { setup(1); raiseEventUpstreamConnected(0); @@ -280,7 +280,7 @@ TEST_P(TcpProxyTest, UpstreamLocalDisconnect) { } // Test that downstream is closed after an upstream RemoteClose. -TEST_P(TcpProxyTest, UpstreamRemoteDisconnect) { +TEST_F(TcpProxyTest, UpstreamRemoteDisconnect) { setup(1); timeSystem().advanceTimeWait(std::chrono::microseconds(20)); @@ -304,7 +304,7 @@ TEST_P(TcpProxyTest, UpstreamRemoteDisconnect) { } // Test that reconnect is attempted after a local connect failure -TEST_P(TcpProxyTest, ConnectAttemptsUpstreamLocalFail) { +TEST_F(TcpProxyTest, ConnectAttemptsUpstreamLocalFail) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_max_connect_attempts()->set_value(2); @@ -326,7 +326,7 @@ TEST_P(TcpProxyTest, ConnectAttemptsUpstreamLocalFail) { } // Make sure that the tcp proxy code handles reentrant calls to onPoolFailure. -TEST_P(TcpProxyTest, ConnectAttemptsUpstreamLocalFailReentrant) { +TEST_F(TcpProxyTest, ConnectAttemptsUpstreamLocalFailReentrant) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_max_connect_attempts()->set_value(2); @@ -350,7 +350,7 @@ TEST_P(TcpProxyTest, ConnectAttemptsUpstreamLocalFailReentrant) { } // Test that reconnect is attempted after a remote connect failure -TEST_P(TcpProxyTest, ConnectAttemptsUpstreamRemoteFail) { +TEST_F(TcpProxyTest, ConnectAttemptsUpstreamRemoteFail) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_max_connect_attempts()->set_value(2); setup(2, config); @@ -364,7 +364,7 @@ TEST_P(TcpProxyTest, ConnectAttemptsUpstreamRemoteFail) { } // Test that reconnect is attempted after a connect timeout. -TEST_P(TcpProxyTest, ConnectAttemptsUpstreamTimeout) { +TEST_F(TcpProxyTest, ConnectAttemptsUpstreamTimeout) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_max_connect_attempts()->set_value(2); setup(2, config); @@ -378,7 +378,7 @@ TEST_P(TcpProxyTest, ConnectAttemptsUpstreamTimeout) { } // Test that only the configured number of connect attempts occur -TEST_P(TcpProxyTest, ConnectAttemptsLimit) { +TEST_F(TcpProxyTest, ConnectAttemptsLimit) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = accessLogConfig("%RESPONSE_FLAGS%"); config.mutable_max_connect_attempts()->set_value(3); @@ -409,7 +409,7 @@ TEST_P(TcpProxyTest, ConnectAttemptsLimit) { EXPECT_EQ(access_log_data_, "UF,URX"); } -TEST_P(TcpProxyTest, ConnectedNoOp) { +TEST_F(TcpProxyTest, ConnectedNoOp) { setup(1); raiseEventUpstreamConnected(0); @@ -419,7 +419,7 @@ TEST_P(TcpProxyTest, ConnectedNoOp) { } // Test that the tcp proxy sends the correct notifications to the outlier detector -TEST_P(TcpProxyTest, OutlierDetection) { +TEST_F(TcpProxyTest, OutlierDetection) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_max_connect_attempts()->set_value(3); setup(3, config); @@ -437,7 +437,7 @@ TEST_P(TcpProxyTest, OutlierDetection) { raiseEventUpstreamConnected(2); } -TEST_P(TcpProxyTest, UpstreamDisconnectDownstreamFlowControl) { +TEST_F(TcpProxyTest, UpstreamDisconnectDownstreamFlowControl) { setup(1); raiseEventUpstreamConnected(0); @@ -459,7 +459,7 @@ TEST_P(TcpProxyTest, UpstreamDisconnectDownstreamFlowControl) { filter_callbacks_.connection_.runLowWatermarkCallbacks(); } -TEST_P(TcpProxyTest, DownstreamDisconnectRemote) { +TEST_F(TcpProxyTest, DownstreamDisconnectRemote) { setup(1); raiseEventUpstreamConnected(0); @@ -476,7 +476,7 @@ TEST_P(TcpProxyTest, DownstreamDisconnectRemote) { filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); } -TEST_P(TcpProxyTest, DownstreamDisconnectLocal) { +TEST_F(TcpProxyTest, DownstreamDisconnectLocal) { setup(1); raiseEventUpstreamConnected(0); @@ -493,7 +493,7 @@ TEST_P(TcpProxyTest, DownstreamDisconnectLocal) { filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); } -TEST_P(TcpProxyTest, UpstreamConnectTimeout) { +TEST_F(TcpProxyTest, UpstreamConnectTimeout) { setup(1, accessLogConfig("%RESPONSE_FLAGS%")); EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); @@ -503,7 +503,7 @@ TEST_P(TcpProxyTest, UpstreamConnectTimeout) { EXPECT_EQ(access_log_data_, "UF,URX"); } -TEST_P(TcpProxyTest, UpstreamClusterNotFound) { +TEST_F(TcpProxyTest, UpstreamClusterNotFound) { setup(0, accessLogConfig("%RESPONSE_FLAGS%")); EXPECT_CALL(factory_context_.server_factory_context_.cluster_manager_, getThreadLocalCluster(_)) @@ -514,7 +514,7 @@ TEST_P(TcpProxyTest, UpstreamClusterNotFound) { EXPECT_EQ(access_log_data_.value(), "NC"); } -TEST_P(TcpProxyTest, NoHost) { +TEST_F(TcpProxyTest, NoHost) { EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); setup(0, accessLogConfig("%RESPONSE_FLAGS%")); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); @@ -522,78 +522,7 @@ TEST_P(TcpProxyTest, NoHost) { EXPECT_EQ(access_log_data_, "UH"); } -// Tests StreamDecoderFilterCallbacks interface implementation -TEST_P(TcpProxyTest, StreamDecoderFilterCallbacks) { - envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = - accessLogConfig("%RESPONSE_FLAGS%"); - config.mutable_tunneling_config()->set_hostname("www.example.com"); - configure(config); - NiceMock thread_local_cluster_; - auto cluster_info = std::make_shared>(); - // EXPECT_CALL(factory_context_.serverFactoryContext().clusterManager(), getThreadLocalCluster(_)) - // .WillRepeatedly(Return(&thread_local_cluster_)); - EXPECT_CALL(thread_local_cluster_, info()).WillRepeatedly(Return(cluster_info)); - filter_ = - std::make_unique(config_, factory_context_.serverFactoryContext().clusterManager()); - filter_->initializeReadFilterCallbacks(filter_callbacks_); - auto stream_decoder_callbacks = Filter::HttpStreamDecoderFilterCallbacks(filter_.get()); - EXPECT_NO_THROW(stream_decoder_callbacks.streamId()); - EXPECT_NO_THROW(stream_decoder_callbacks.connection()); - EXPECT_NO_THROW(stream_decoder_callbacks.dispatcher()); - EXPECT_ENVOY_BUG( - { stream_decoder_callbacks.resetStream(Http::StreamResetReason::RemoteReset, ""); }, - "Not implemented"); - EXPECT_NO_THROW(stream_decoder_callbacks.streamInfo()); - EXPECT_NO_THROW(stream_decoder_callbacks.scope()); - EXPECT_NO_THROW(stream_decoder_callbacks.route()); - EXPECT_NO_THROW(stream_decoder_callbacks.continueDecoding()); - EXPECT_NO_THROW(stream_decoder_callbacks.requestHeaders()); - EXPECT_NO_THROW(stream_decoder_callbacks.requestTrailers()); - EXPECT_NO_THROW(stream_decoder_callbacks.responseHeaders()); - EXPECT_NO_THROW(stream_decoder_callbacks.responseTrailers()); - EXPECT_NO_THROW(stream_decoder_callbacks.encodeMetadata(nullptr)); - EXPECT_NO_THROW(stream_decoder_callbacks.onDecoderFilterAboveWriteBufferHighWatermark()); - EXPECT_NO_THROW(stream_decoder_callbacks.onDecoderFilterBelowWriteBufferLowWatermark()); - EXPECT_NO_THROW(stream_decoder_callbacks.setDecoderBufferLimit(uint32_t{0})); - EXPECT_NO_THROW(stream_decoder_callbacks.decoderBufferLimit()); - EXPECT_NO_THROW(stream_decoder_callbacks.recreateStream(nullptr)); - EXPECT_NO_THROW(stream_decoder_callbacks.getUpstreamSocketOptions()); - Network::Socket::OptionsSharedPtr sock_options = - Network::SocketOptionFactory::buildIpTransparentOptions(); - EXPECT_NO_THROW(stream_decoder_callbacks.addUpstreamSocketOptions(sock_options)); - EXPECT_NO_THROW(stream_decoder_callbacks.mostSpecificPerFilterConfig()); - EXPECT_NO_THROW(stream_decoder_callbacks.account()); - EXPECT_NO_THROW(stream_decoder_callbacks.setUpstreamOverrideHost( - Upstream::LoadBalancerContext::OverrideHost(std::make_pair("foo", true)))); - EXPECT_NO_THROW(stream_decoder_callbacks.http1StreamEncoderOptions()); - EXPECT_NO_THROW(stream_decoder_callbacks.downstreamCallbacks()); - EXPECT_NO_THROW(stream_decoder_callbacks.upstreamCallbacks()); - EXPECT_NO_THROW(stream_decoder_callbacks.upstreamOverrideHost()); - EXPECT_NO_THROW(stream_decoder_callbacks.resetIdleTimer()); - EXPECT_NO_THROW(stream_decoder_callbacks.filterConfigName()); - EXPECT_NO_THROW(stream_decoder_callbacks.activeSpan()); - EXPECT_NO_THROW(stream_decoder_callbacks.tracingConfig()); - Buffer::OwnedImpl inject_data; - EXPECT_NO_THROW(stream_decoder_callbacks.addDecodedData(inject_data, false)); - EXPECT_NO_THROW(stream_decoder_callbacks.injectDecodedDataToFilterChain(inject_data, false)); - EXPECT_NO_THROW(stream_decoder_callbacks.addDecodedData(inject_data, false)); - EXPECT_NO_THROW(stream_decoder_callbacks.addDecodedTrailers()); - EXPECT_NO_THROW(stream_decoder_callbacks.addDecodedMetadata()); - EXPECT_NO_THROW(stream_decoder_callbacks.decodingBuffer()); - auto func = [](Buffer::Instance&) {}; - EXPECT_NO_THROW(stream_decoder_callbacks.modifyDecodingBuffer(func)); - EXPECT_NO_THROW(stream_decoder_callbacks.encode1xxHeaders(nullptr)); - EXPECT_NO_THROW(stream_decoder_callbacks.informationalHeaders()); - EXPECT_NO_THROW(stream_decoder_callbacks.encodeHeaders(nullptr, false, "")); - EXPECT_NO_THROW(stream_decoder_callbacks.encodeData(inject_data, false)); - EXPECT_NO_THROW(stream_decoder_callbacks.encodeTrailers(nullptr)); - EXPECT_NO_THROW(stream_decoder_callbacks.setDecoderBufferLimit(0)); - std::array buffer; - OutputBufferStream ostream{buffer.data(), buffer.size()}; - EXPECT_NO_THROW(stream_decoder_callbacks.dumpState(ostream, 0)); -} - -TEST_P(TcpProxyTest, RouteWithMetadataMatch) { +TEST_F(TcpProxyTest, RouteWithMetadataMatch) { auto v1 = ProtobufWkt::Value(); v1.set_string_value("v1"); auto v2 = ProtobufWkt::Value(); @@ -633,7 +562,7 @@ TEST_P(TcpProxyTest, RouteWithMetadataMatch) { // Tests that the endpoint selector of a weighted cluster gets included into the // LoadBalancerContext. -TEST_P(TcpProxyTest, WeightedClusterWithMetadataMatch) { +TEST_F(TcpProxyTest, WeightedClusterWithMetadataMatch) { const std::string yaml = R"EOF( stat_prefix: name weighted_clusters: @@ -730,7 +659,7 @@ TEST_P(TcpProxyTest, WeightedClusterWithMetadataMatch) { } // Test that metadata match criteria provided on the StreamInfo is used. -TEST_P(TcpProxyTest, StreamInfoDynamicMetadata) { +TEST_F(TcpProxyTest, StreamInfoDynamicMetadata) { configure(defaultConfig()); ProtobufWkt::Value val; @@ -768,7 +697,7 @@ TEST_P(TcpProxyTest, StreamInfoDynamicMetadata) { // Test that if both streamInfo and configuration add metadata match criteria, they // are merged. -TEST_P(TcpProxyTest, StreamInfoDynamicMetadataAndConfigMerged) { +TEST_F(TcpProxyTest, StreamInfoDynamicMetadataAndConfigMerged) { const std::string yaml = R"EOF( stat_prefix: name weighted_clusters: @@ -829,7 +758,7 @@ TEST_P(TcpProxyTest, StreamInfoDynamicMetadataAndConfigMerged) { EXPECT_EQ(hv2, effective_criterions[2]->value()); } -TEST_P(TcpProxyTest, DisconnectBeforeData) { +TEST_F(TcpProxyTest, DisconnectBeforeData) { configure(defaultConfig()); filter_ = std::make_unique(config_, factory_context_.server_factory_context_.cluster_manager_); @@ -840,7 +769,7 @@ TEST_P(TcpProxyTest, DisconnectBeforeData) { // Test that if the downstream connection is closed before the upstream connection // is established, the upstream connection is cancelled. -TEST_P(TcpProxyTest, RemoteClosedBeforeUpstreamConnected) { +TEST_F(TcpProxyTest, RemoteClosedBeforeUpstreamConnected) { setup(1); EXPECT_CALL(*conn_pool_handles_.at(0), cancel(Tcp::ConnectionPool::CancelPolicy::CloseExcess)); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); @@ -848,13 +777,13 @@ TEST_P(TcpProxyTest, RemoteClosedBeforeUpstreamConnected) { // Test that if the downstream connection is closed before the upstream connection // is established, the upstream connection is cancelled. -TEST_P(TcpProxyTest, LocalClosedBeforeUpstreamConnected) { +TEST_F(TcpProxyTest, LocalClosedBeforeUpstreamConnected) { setup(1); EXPECT_CALL(*conn_pool_handles_.at(0), cancel(Tcp::ConnectionPool::CancelPolicy::CloseExcess)); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); } -TEST_P(TcpProxyTest, UpstreamConnectFailure) { +TEST_F(TcpProxyTest, UpstreamConnectFailure) { setup(1, accessLogConfig("%RESPONSE_FLAGS%")); EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); @@ -870,7 +799,7 @@ TEST_P(TcpProxyTest, UpstreamConnectFailure) { EXPECT_EQ(access_log_data_, "UF,URX"); } -TEST_P(TcpProxyTest, UpstreamConnectionLimit) { +TEST_F(TcpProxyTest, UpstreamConnectionLimit) { configure(accessLogConfig("%RESPONSE_FLAGS%")); factory_context_.server_factory_context_.cluster_manager_.thread_local_cluster_.cluster_.info_ ->resetResourceManager(0, 0, 0, 0, 0); @@ -887,7 +816,7 @@ TEST_P(TcpProxyTest, UpstreamConnectionLimit) { EXPECT_EQ(access_log_data_, "UO"); } -TEST_P(TcpProxyTest, IdleTimeoutObjectFactory) { +TEST_F(TcpProxyTest, IdleTimeoutObjectFactory) { const std::string name = "envoy.tcp_proxy.per_connection_idle_timeout_ms"; auto* factory = Registry::FactoryRegistry::getFactory(name); @@ -899,7 +828,7 @@ TEST_P(TcpProxyTest, IdleTimeoutObjectFactory) { EXPECT_EQ(duration_in_milliseconds, object->serializeAsString()); } -TEST_P(TcpProxyTest, InvalidIdleTimeoutObjectFactory) { +TEST_F(TcpProxyTest, InvalidIdleTimeoutObjectFactory) { const std::string name = "envoy.tcp_proxy.per_connection_idle_timeout_ms"; auto* factory = Registry::FactoryRegistry::getFactory(name); @@ -908,7 +837,7 @@ TEST_P(TcpProxyTest, InvalidIdleTimeoutObjectFactory) { ASSERT_EQ(nullptr, factory->createFromBytes("not_a_number")); } -TEST_P(TcpProxyTest, IdleTimeoutWithFilterStateOverride) { +TEST_F(TcpProxyTest, IdleTimeoutWithFilterStateOverride) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -948,7 +877,7 @@ TEST_P(TcpProxyTest, IdleTimeoutWithFilterStateOverride) { // Tests that the idle timer closes both connections, and gets updated when either // connection has activity. -TEST_P(TcpProxyTest, IdleTimeout) { +TEST_F(TcpProxyTest, IdleTimeout) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -978,7 +907,7 @@ TEST_P(TcpProxyTest, IdleTimeout) { } // Tests that the idle timer is disabled when the downstream connection is closed. -TEST_P(TcpProxyTest, IdleTimerDisabledDownstreamClose) { +TEST_F(TcpProxyTest, IdleTimerDisabledDownstreamClose) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -992,7 +921,7 @@ TEST_P(TcpProxyTest, IdleTimerDisabledDownstreamClose) { } // Tests that the idle timer is disabled when the upstream connection is closed. -TEST_P(TcpProxyTest, IdleTimerDisabledUpstreamClose) { +TEST_F(TcpProxyTest, IdleTimerDisabledUpstreamClose) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -1006,7 +935,7 @@ TEST_P(TcpProxyTest, IdleTimerDisabledUpstreamClose) { } // Tests that flushing data during an idle timeout doesn't cause problems. -TEST_P(TcpProxyTest, IdleTimeoutWithOutstandingDataFlushed) { +TEST_F(TcpProxyTest, IdleTimeoutWithOutstandingDataFlushed) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -1058,7 +987,7 @@ TEST_P(TcpProxyTest, IdleTimeoutWithOutstandingDataFlushed) { // Checks that %UPSTREAM_WIRE_BYTES_SENT%, %UPSTREAM_WIRE_BYTES_RECEIVED%, // %DOWNSTREAM_WIRE_BYTES_SENT%, and %DOWNSTREAM_WIRE_BYTES_RECEIVED% are // correctly logged. -TEST_P(TcpProxyTest, AccessLogBytesMeterData) { +TEST_F(TcpProxyTest, AccessLogBytesMeterData) { setup(1, accessLogConfig("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " "%DOWNSTREAM_WIRE_BYTES_SENT% %DOWNSTREAM_WIRE_BYTES_RECEIVED%")); raiseEventUpstreamConnected(0); @@ -1076,7 +1005,7 @@ TEST_P(TcpProxyTest, AccessLogBytesMeterData) { // Test that access log fields %UPSTREAM_HOST% and %UPSTREAM_CLUSTER% are correctly logged with the // observability name. -TEST_P(TcpProxyTest, AccessLogUpstreamHost) { +TEST_F(TcpProxyTest, AccessLogUpstreamHost) { setup(1, accessLogConfig("%UPSTREAM_HOST% %UPSTREAM_CLUSTER%")); raiseEventUpstreamConnected(0); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); @@ -1085,7 +1014,7 @@ TEST_P(TcpProxyTest, AccessLogUpstreamHost) { } // Test that access log field %UPSTREAM_LOCAL_ADDRESS% is correctly logged. -TEST_P(TcpProxyTest, AccessLogUpstreamLocalAddress) { +TEST_F(TcpProxyTest, AccessLogUpstreamLocalAddress) { setup(1, accessLogConfig("%UPSTREAM_LOCAL_ADDRESS%")); raiseEventUpstreamConnected(0); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); @@ -1094,7 +1023,7 @@ TEST_P(TcpProxyTest, AccessLogUpstreamLocalAddress) { } // Test that access log fields %DOWNSTREAM_PEER_URI_SAN% is correctly logged. -TEST_P(TcpProxyTest, AccessLogPeerUriSan) { +TEST_F(TcpProxyTest, AccessLogPeerUriSan) { filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress( Network::Utility::resolveUrl("tcp://1.1.1.2:20000")); filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress( @@ -1112,7 +1041,7 @@ TEST_P(TcpProxyTest, AccessLogPeerUriSan) { } // Test that access log fields %DOWNSTREAM_TLS_SESSION_ID% is correctly logged. -TEST_P(TcpProxyTest, AccessLogTlsSessionId) { +TEST_F(TcpProxyTest, AccessLogTlsSessionId) { filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress( Network::Utility::resolveUrl("tcp://1.1.1.2:20000")); filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress( @@ -1132,7 +1061,7 @@ TEST_P(TcpProxyTest, AccessLogTlsSessionId) { // Test that access log fields %DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% and // %DOWNSTREAM_LOCAL_ADDRESS% are correctly logged. -TEST_P(TcpProxyTest, AccessLogDownstreamAddress) { +TEST_F(TcpProxyTest, AccessLogDownstreamAddress) { filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress( Network::Utility::resolveUrl("tcp://1.1.1.2:20000")); filter_callbacks_.connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress( @@ -1144,7 +1073,7 @@ TEST_P(TcpProxyTest, AccessLogDownstreamAddress) { } // Test that intermediate log entry by field %ACCESS_LOG_TYPE%. -TEST_P(TcpProxyTest, IntermediateLogEntry) { +TEST_F(TcpProxyTest, IntermediateLogEntry) { auto config = accessLogConfig("%ACCESS_LOG_TYPE%"); config.mutable_access_log_options()->mutable_access_log_flush_interval()->set_seconds(1); config.mutable_idle_timeout()->set_seconds(0); @@ -1200,7 +1129,7 @@ TEST_P(TcpProxyTest, IntermediateLogEntry) { AccessLogType_Name(AccessLog::AccessLogType::TcpConnectionEnd)); } -TEST_P(TcpProxyTest, TestAccessLogOnUpstreamConnected) { +TEST_F(TcpProxyTest, TestAccessLogOnUpstreamConnected) { auto config = accessLogConfig("%UPSTREAM_HOST% %ACCESS_LOG_TYPE%"); config.mutable_access_log_options()->set_flush_access_log_on_connected(true); @@ -1222,7 +1151,7 @@ TEST_P(TcpProxyTest, TestAccessLogOnUpstreamConnected) { AccessLogType_Name(AccessLog::AccessLogType::TcpConnectionEnd))); } -TEST_P(TcpProxyTest, AccessLogUpstreamSSLConnection) { +TEST_F(TcpProxyTest, AccessLogUpstreamSSLConnection) { setup(1); NiceMock stream_info; @@ -1239,7 +1168,7 @@ TEST_P(TcpProxyTest, AccessLogUpstreamSSLConnection) { } // Tests that upstream flush works properly with no idle timeout configured. -TEST_P(TcpProxyTest, UpstreamFlushNoTimeout) { +TEST_F(TcpProxyTest, UpstreamFlushNoTimeout) { setup(1); raiseEventUpstreamConnected(0); @@ -1263,7 +1192,7 @@ TEST_P(TcpProxyTest, UpstreamFlushNoTimeout) { // Tests that upstream flush works with an idle timeout configured, but the connection // finishes draining before the timer expires. -TEST_P(TcpProxyTest, UpstreamFlushTimeoutConfigured) { +TEST_F(TcpProxyTest, UpstreamFlushTimeoutConfigured) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -1294,7 +1223,7 @@ TEST_P(TcpProxyTest, UpstreamFlushTimeoutConfigured) { } // Tests that upstream flush closes the connection when the idle timeout fires. -TEST_P(TcpProxyTest, UpstreamFlushTimeoutExpired) { +TEST_F(TcpProxyTest, UpstreamFlushTimeoutExpired) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); config.mutable_idle_timeout()->set_seconds(1); setup(1, config); @@ -1322,7 +1251,7 @@ TEST_P(TcpProxyTest, UpstreamFlushTimeoutExpired) { // Tests that upstream flush will close a connection if it reads data from the upstream // connection after the downstream connection is closed (nowhere to send it). -TEST_P(TcpProxyTest, UpstreamFlushReceiveUpstreamData) { +TEST_F(TcpProxyTest, UpstreamFlushReceiveUpstreamData) { setup(1); raiseEventUpstreamConnected(0); @@ -1341,13 +1270,13 @@ TEST_P(TcpProxyTest, UpstreamFlushReceiveUpstreamData) { upstream_callbacks_->onUpstreamData(buffer, false); } -TEST_P(TcpProxyTest, UpstreamSocketOptionsReturnedEmpty) { +TEST_F(TcpProxyTest, UpstreamSocketOptionsReturnedEmpty) { setup(1); auto options = filter_->upstreamSocketOptions(); EXPECT_EQ(options, nullptr); } -TEST_P(TcpProxyTest, TcpProxySetRedirectRecordsToUpstream) { +TEST_F(TcpProxyTest, TcpProxySetRedirectRecordsToUpstream) { setup(1, true); EXPECT_TRUE(filter_->upstreamSocketOptions()); auto iterator = std::find_if( @@ -1367,7 +1296,7 @@ TEST_P(TcpProxyTest, TcpProxySetRedirectRecordsToUpstream) { } // Tests that downstream connection can access upstream connections filter state. -TEST_P(TcpProxyTest, ShareFilterState) { +TEST_F(TcpProxyTest, ShareFilterState) { setup(1); upstream_connections_.at(0)->streamInfo().filterState()->setData( @@ -1383,7 +1312,7 @@ TEST_P(TcpProxyTest, ShareFilterState) { } // Tests that filter callback can access downstream and upstream address and ssl properties. -TEST_P(TcpProxyTest, AccessDownstreamAndUpstreamProperties) { +TEST_F(TcpProxyTest, AccessDownstreamAndUpstreamProperties) { setup(1); raiseEventUpstreamConnected(0); @@ -1396,7 +1325,7 @@ TEST_P(TcpProxyTest, AccessDownstreamAndUpstreamProperties) { upstream_connections_.at(0)->streamInfo().downstreamAddressProvider().sslConnection()); } -TEST_P(TcpProxyTest, PickClusterOnUpstreamFailure) { +TEST_F(TcpProxyTest, PickClusterOnUpstreamFailure) { auto config = defaultConfig(); set2Cluster(config); config.mutable_max_connect_attempts()->set_value(2); @@ -1425,7 +1354,7 @@ TEST_P(TcpProxyTest, PickClusterOnUpstreamFailure) { } // Verify that odcds callback does not re-pick cluster. Upstream connect failure does. -TEST_P(TcpProxyTest, OnDemandCallbackStickToTheSelectedCluster) { +TEST_F(TcpProxyTest, OnDemandCallbackStickToTheSelectedCluster) { auto config = onDemandConfig(); set2Cluster(config); config.mutable_max_connect_attempts()->set_value(2); @@ -1484,7 +1413,7 @@ TEST_P(TcpProxyTest, OnDemandCallbackStickToTheSelectedCluster) { } // Verify the on demand api is not invoked when the target thread local cluster is present. -TEST_P(TcpProxyTest, OdcdsIsIgnoredIfClusterExists) { +TEST_F(TcpProxyTest, OdcdsIsIgnoredIfClusterExists) { auto config = onDemandConfig(); setup(1, config); @@ -1503,7 +1432,7 @@ TEST_P(TcpProxyTest, OdcdsIsIgnoredIfClusterExists) { } // Verify the on demand request is cancelled if the tcp downstream connection is closed. -TEST_P(TcpProxyTest, OdcdsCancelIfConnectionClose) { +TEST_F(TcpProxyTest, OdcdsCancelIfConnectionClose) { auto config = onDemandConfig(); mock_odcds_api_handle_ = Upstream::MockOdCdsApiHandle::create().release(); @@ -1525,7 +1454,7 @@ TEST_P(TcpProxyTest, OdcdsCancelIfConnectionClose) { } // Verify a request can be served after a successful on demand cluster request. -TEST_P(TcpProxyTest, OdcdsBasicDownstreamLocalClose) { +TEST_F(TcpProxyTest, OdcdsBasicDownstreamLocalClose) { auto config = onDemandConfig(); mock_odcds_api_handle_ = Upstream::MockOdCdsApiHandle::create().release(); @@ -1570,7 +1499,7 @@ TEST_P(TcpProxyTest, OdcdsBasicDownstreamLocalClose) { } // Verify the connection is closed after the cluster missing callback is triggered. -TEST_P(TcpProxyTest, OdcdsClusterMissingCauseConnectionClose) { +TEST_F(TcpProxyTest, OdcdsClusterMissingCauseConnectionClose) { auto config = onDemandConfig(); mock_odcds_api_handle_ = Upstream::MockOdCdsApiHandle::create().release(); @@ -1599,7 +1528,7 @@ TEST_P(TcpProxyTest, OdcdsClusterMissingCauseConnectionClose) { } // Test that upstream transport failure message is reflected in access logs. -TEST_P(TcpProxyTest, UpstreamConnectFailureStreamInfoAccessLog) { +TEST_F(TcpProxyTest, UpstreamConnectFailureStreamInfoAccessLog) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); setup(1, accessLogConfig("%UPSTREAM_TRANSPORT_FAILURE_REASON%")); @@ -1616,7 +1545,7 @@ TEST_P(TcpProxyTest, UpstreamConnectFailureStreamInfoAccessLog) { // Test that call to tcp_proxy filter's startUpstreamSecureTransport results // in upstream's startUpstreamSecureTransport call. -TEST_P(TcpProxyTest, UpstreamStartSecureTransport) { +TEST_F(TcpProxyTest, UpstreamStartSecureTransport) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); setup(1, config); @@ -1625,8 +1554,6 @@ TEST_P(TcpProxyTest, UpstreamStartSecureTransport) { filter_->startUpstreamSecureTransport(); } -INSTANTIATE_TEST_SUITE_P(WithOrWithoutUpstream, TcpProxyTest, ::testing::Bool()); - TEST(PerConnectionCluster, ObjectFactory) { const std::string name = "envoy.tcp_proxy.cluster"; auto* factory = diff --git a/test/common/tcp_proxy/tcp_proxy_test_base.h b/test/common/tcp_proxy/tcp_proxy_test_base.h index a7a7770d218a..9816df3871d9 100644 --- a/test/common/tcp_proxy/tcp_proxy_test_base.h +++ b/test/common/tcp_proxy/tcp_proxy_test_base.h @@ -57,11 +57,9 @@ inline Config constructConfigFromYaml(const std::string& yaml, return {tcp_proxy, context}; } -class TcpProxyTestBase : public testing::TestWithParam { +class TcpProxyTestBase : public testing::Test { public: TcpProxyTestBase() { - scoped_runtime_.mergeValues({{"envoy.restart_features.upstream_http_filters_with_tcp_proxy", - GetParam() ? "true" : "false"}}); ON_CALL(*factory_context_.server_factory_context_.access_log_manager_.file_, write(_)) .WillByDefault(SaveArg<0>(&access_log_data_)); ON_CALL(filter_callbacks_.connection_.stream_info_, setUpstreamClusterInfo(_)) @@ -177,7 +175,6 @@ class TcpProxyTestBase : public testing::TestWithParam { Upstream::HostDescriptionConstSharedPtr upstream_host_{}; Upstream::ClusterInfoConstSharedPtr upstream_cluster_{}; std::string redirect_records_data_ = "some data"; - TestScopedRuntime scoped_runtime_; }; } // namespace TcpProxy diff --git a/test/common/tcp_proxy/upstream_test.cc b/test/common/tcp_proxy/upstream_test.cc index feca11eb4a60..d48d9690c602 100644 --- a/test/common/tcp_proxy/upstream_test.cc +++ b/test/common/tcp_proxy/upstream_test.cc @@ -6,11 +6,8 @@ #include "test/mocks/buffer/mocks.h" #include "test/mocks/http/mocks.h" #include "test/mocks/http/stream_encoder.h" -#include "test/mocks/router/router_filter_interface.h" -#include "test/mocks/router/upstream_request.h" #include "test/mocks/server/factory_context.h" #include "test/mocks/tcp/mocks.h" -#include "test/mocks/upstream/load_balancer_context.h" #include "test/test_common/environment.h" #include "test/test_common/network_utility.h" #include "test/test_common/test_runtime.h" @@ -26,7 +23,7 @@ using testing::Return; namespace Envoy { namespace TcpProxy { namespace { -using envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy; +using envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig; class HttpUpstreamTest : public testing::TestWithParam { public: @@ -40,11 +37,11 @@ class HttpUpstreamTest : public testing::TestWithParam { .WillByDefault(Return(Http::Http1StreamEncoderOptionsOptRef(stream_encoder_options_))); } EXPECT_CALL(stream_encoder_options_, enableHalfClose()).Times(AnyNumber()); - tcp_proxy_.mutable_tunneling_config()->set_hostname("default.host.com:443"); + config_message_.set_hostname("default.host.com:443"); } void setupUpstream() { - config_ = std::make_unique(scope_, tcp_proxy_, context_); + config_ = std::make_unique(config_message_, context_); upstream_ = std::make_unique(callbacks_, *this->config_, downstream_stream_info_, GetParam()); upstream_->setRequestEncoder(encoder_, true); @@ -54,9 +51,7 @@ class HttpUpstreamTest : public testing::TestWithParam { Http::MockRequestEncoder encoder_; Http::MockHttp1StreamEncoderOptions stream_encoder_options_; NiceMock callbacks_; - TcpProxy tcp_proxy_; - NiceMock store_; - Stats::MockScope& scope_{store_.mockScope()}; + TcpProxy_TunnelingConfig config_message_; std::unique_ptr config_; std::unique_ptr upstream_; NiceMock context_; @@ -151,7 +146,7 @@ TEST_P(HttpUpstreamTest, UpstreamWatermarks) { class MockHttpConnPoolCallbacks : public HttpConnPool::Callbacks { public: - MOCK_METHOD(void, onSuccess, (Http::RequestEncoder * request_encoder)); + MOCK_METHOD(void, onSuccess, (Http::RequestEncoder & request_encoder)); MOCK_METHOD(void, onFailure, ()); }; @@ -240,11 +235,11 @@ class HttpUpstreamRequestEncoderTest : public testing::TestWithParamset_hostname("default.host.com:443"); + config_message_.set_hostname("default.host.com:443"); } void setupUpstream() { - config_ = std::make_unique(scope_, tcp_proxy_, context_); + config_ = std::make_unique(config_message_, context_); upstream_ = std::make_unique(callbacks_, *this->config_, this->downstream_stream_info_, GetParam()); } @@ -264,9 +259,7 @@ class HttpUpstreamRequestEncoderTest : public testing::TestWithParam context_; std::unique_ptr upstream_; - TcpProxy tcp_proxy_; - NiceMock store_; - Stats::MockScope& scope_{store_.mockScope()}; + TcpProxy_TunnelingConfig config_message_; std::unique_ptr config_; bool is_http2_ = true; }; @@ -288,7 +281,7 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoder) { } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderUsePost) { - this->tcp_proxy_.mutable_tunneling_config()->set_use_post(true); + this->config_message_.set_use_post(true); this->setupUpstream(); std::unique_ptr expected_headers; expected_headers = Http::createHeaderMap({ @@ -307,8 +300,8 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderUsePost) { } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderUsePostWithCustomPath) { - this->tcp_proxy_.mutable_tunneling_config()->set_use_post(true); - this->tcp_proxy_.mutable_tunneling_config()->set_post_path("/test"); + this->config_message_.set_use_post(true); + this->config_message_.set_post_path("/test"); this->setupUpstream(); std::unique_ptr expected_headers; expected_headers = Http::createHeaderMap({ @@ -327,25 +320,25 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderUsePostWithCustomPath) { } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderConnectWithCustomPath) { - this->tcp_proxy_.mutable_tunneling_config()->set_use_post(false); - this->tcp_proxy_.mutable_tunneling_config()->set_post_path("/test"); + this->config_message_.set_use_post(false); + this->config_message_.set_post_path("/test"); EXPECT_THROW_WITH_MESSAGE(this->setupUpstream(), EnvoyException, "Can't set a post path when POST method isn't used"); } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHeaders) { - auto* header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); + auto* header = this->config_message_.add_headers_to_add(); auto* hdr = header->mutable_header(); hdr->set_key("header0"); hdr->set_value("value0"); - header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); + header = this->config_message_.add_headers_to_add(); hdr = header->mutable_header(); hdr->set_key("header1"); hdr->set_value("value1"); header->set_append_action(envoy::config::core::v3::HeaderValueOption::APPEND_IF_EXISTS_OR_ADD); - header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); + header = this->config_message_.add_headers_to_add(); hdr = header->mutable_header(); hdr->set_key("header1"); hdr->set_value("value2"); @@ -367,13 +360,13 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHeaders) { } TEST_P(HttpUpstreamRequestEncoderTest, ConfigReuse) { - auto* header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); + auto* header = this->config_message_.add_headers_to_add(); auto* hdr = header->mutable_header(); hdr->set_key("key"); hdr->set_value("value1"); header->set_append_action(envoy::config::core::v3::HeaderValueOption::APPEND_IF_EXISTS_OR_ADD); - header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); + header = this->config_message_.add_headers_to_add(); hdr = header->mutable_header(); hdr->set_key("key"); hdr->set_value("value2"); @@ -411,12 +404,12 @@ TEST_P(HttpUpstreamRequestEncoderTest, ConfigReuse) { } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHeadersWithDownstreamInfo) { - auto* header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); + auto* header = this->config_message_.add_headers_to_add(); auto* hdr = header->mutable_header(); hdr->set_key("header0"); hdr->set_value("value0"); - header = this->tcp_proxy_.mutable_tunneling_config()->add_headers_to_add(); + header = this->config_message_.add_headers_to_add(); hdr = header->mutable_header(); hdr->set_key("downstream_local_port"); hdr->set_value("%DOWNSTREAM_LOCAL_PORT%"); @@ -445,7 +438,7 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHeadersWithDownstreamInfo) TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHostnameWithDownstreamInfoRequestedServerName) { - this->tcp_proxy_.mutable_tunneling_config()->set_hostname("%REQUESTED_SERVER_NAME%:443"); + this->config_message_.set_hostname("%REQUESTED_SERVER_NAME%:443"); this->setupUpstream(); std::unique_ptr expected_headers; @@ -469,8 +462,7 @@ TEST_P(HttpUpstreamRequestEncoderTest, } TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHostnameWithDownstreamInfoDynamicMetadata) { - this->tcp_proxy_.mutable_tunneling_config()->set_hostname( - "%DYNAMIC_METADATA(tunnel:address)%:443"); + this->config_message_.set_hostname("%DYNAMIC_METADATA(tunnel:address)%:443"); this->setupUpstream(); std::unique_ptr expected_headers; @@ -490,218 +482,6 @@ TEST_P(HttpUpstreamRequestEncoderTest, RequestEncoderHostnameWithDownstreamInfoD EXPECT_CALL(this->encoder_, encodeHeaders(HeaderMapEqualRef(expected_headers.get()), false)); this->upstream_->setRequestEncoder(this->encoder_, false); } - -class CombinedUpstreamTest : public testing::Test { -public: - CombinedUpstreamTest() { - EXPECT_CALL(encoder_, getStream()).Times(AnyNumber()); - EXPECT_CALL(encoder_, http1StreamEncoderOptions()).Times(AnyNumber()); - EXPECT_CALL(encoder_, enableTcpTunneling()).Times(AnyNumber()); - EXPECT_CALL(stream_encoder_options_, enableHalfClose()).Times(AnyNumber()); - tcp_proxy_.mutable_tunneling_config()->set_hostname("default.host.com:443"); - } - - void setup() { - tunnel_config_ = std::make_unique(scope_, tcp_proxy_, context_); - conn_pool_ = std::make_unique(cluster_, &lb_context_, *tunnel_config_, callbacks_, - decoder_callbacks_, Http::CodecType::HTTP2, - downstream_stream_info_); - upstream_ = std::make_unique(*conn_pool_, callbacks_, decoder_callbacks_, - *tunnel_config_, downstream_stream_info_); - auto mock_conn_pool = std::make_unique>(); - std::unique_ptr generic_conn_pool = std::move(mock_conn_pool); - config_ = std::make_shared(tcp_proxy_, factory_context_); - filter_ = - std::make_unique(config_, factory_context_.serverFactoryContext().clusterManager()); - filter_->initializeReadFilterCallbacks(filter_callbacks_); - auto mock_upst = std::make_unique>( - *upstream_, std::move(generic_conn_pool)); - mock_router_upstream_request_ = mock_upst.get(); - upstream_->setRouterUpstreamRequest(std::move(mock_upst)); - EXPECT_CALL(*mock_router_upstream_request_, acceptHeadersFromRouter(false)); - EXPECT_NO_THROW(tunnel_config_->serverFactoryContext()); - upstream_->newStream(*filter_); - } - - Router::MockUpstreamRequest* mock_router_upstream_request_{}; - NiceMock router_filter_interface_; - NiceMock factory_context_; - ConfigSharedPtr config_; - NiceMock filter_callbacks_; - std::unique_ptr filter_; - - NiceMock downstream_stream_info_; - Http::MockRequestEncoder encoder_; - Http::MockHttp1StreamEncoderOptions stream_encoder_options_; - NiceMock callbacks_; - TcpProxy tcp_proxy_; - NiceMock decoder_callbacks_; - NiceMock cluster_; - NiceMock lb_context_; - std::unique_ptr conn_pool_; - NiceMock store_; - Stats::MockScope& scope_{store_.mockScope()}; - std::unique_ptr tunnel_config_; - std::unique_ptr upstream_; - NiceMock context_; -}; -TEST_F(CombinedUpstreamTest, RouterFilterInterface) { - this->setup(); - EXPECT_EQ(this->upstream_->startUpstreamSecureTransport(), false); - EXPECT_EQ(this->upstream_->getUpstreamConnectionSslInfo(), nullptr); - auto mock_conn_pool = std::make_unique>(); - std::unique_ptr generic_conn_pool = std::move(mock_conn_pool); - auto mock_upst = std::make_unique>( - *this->upstream_, std::move(generic_conn_pool)); - EXPECT_NO_THROW(this->upstream_->onUpstream1xxHeaders(nullptr, *mock_upst.get())); - EXPECT_NO_THROW(this->upstream_->onUpstreamMetadata(nullptr)); - EXPECT_NO_THROW(this->upstream_->onPerTryTimeout(*mock_upst.get())); - EXPECT_NO_THROW(this->upstream_->onPerTryIdleTimeout(*mock_upst.get())); - EXPECT_NO_THROW(this->upstream_->onStreamMaxDurationReached(*mock_upst.get())); - EXPECT_EQ(this->upstream_->dynamicMaxStreamDuration(), absl::nullopt); - EXPECT_EQ(this->upstream_->downstreamTrailers(), nullptr); - EXPECT_EQ(this->upstream_->downstreamResponseStarted(), false); - EXPECT_EQ(this->upstream_->downstreamEndStream(), false); - EXPECT_EQ(this->upstream_->attemptCount(), 0); -} - -TEST_F(CombinedUpstreamTest, WriteUpstream) { - this->setup(); - EXPECT_CALL(*this->mock_router_upstream_request_, - acceptDataFromRouter(BufferStringEqual("foo"), false /*end_stream*/)); - Buffer::OwnedImpl buffer1("foo"); - this->upstream_->encodeData(buffer1, false); - - EXPECT_CALL(*this->mock_router_upstream_request_, - acceptDataFromRouter(BufferStringEqual("bar"), true /*end_stream*/)); - Buffer::OwnedImpl buffer2("bar"); - this->upstream_->encodeData(buffer2, true); - - // New upstream with no encoder. - this->upstream_ = std::make_unique(*conn_pool_, callbacks_, decoder_callbacks_, - *tunnel_config_, downstream_stream_info_); - this->upstream_->encodeData(buffer2, true); -} - -TEST_F(CombinedUpstreamTest, WriteDownstream) { - this->setup(); - EXPECT_CALL(this->callbacks_, onUpstreamData(BufferStringEqual("foo"), false)); - Buffer::OwnedImpl buffer1("foo"); - this->upstream_->responseDecoder().decodeData(buffer1, false); - - EXPECT_CALL(this->callbacks_, onUpstreamData(BufferStringEqual("bar"), true)); - Buffer::OwnedImpl buffer2("bar"); - this->upstream_->responseDecoder().decodeData(buffer2, true); -} - -TEST_F(CombinedUpstreamTest, InvalidUpgradeWithEarlyFin) { - this->setup(); - EXPECT_CALL(this->callbacks_, onEvent(_)); - Http::ResponseHeaderMapPtr headers{new Http::TestResponseHeaderMapImpl{{":status", "200"}}}; - this->upstream_->responseDecoder().decodeHeaders(std::move(headers), true); -} - -TEST_F(CombinedUpstreamTest, InvalidUpgradeWithNon200) { - this->setup(); - EXPECT_CALL(this->callbacks_, onEvent(_)); - Http::ResponseHeaderMapPtr headers{new Http::TestResponseHeaderMapImpl{{":status", "301"}}}; - this->upstream_->responseDecoder().decodeHeaders(std::move(headers), false); -} - -TEST_F(CombinedUpstreamTest, ReadDisable) { - this->setup(); - EXPECT_CALL(*this->mock_router_upstream_request_, onAboveWriteBufferHighWatermark()); - EXPECT_TRUE(this->upstream_->readDisable(true)); - - EXPECT_CALL(*this->mock_router_upstream_request_, onAboveWriteBufferHighWatermark()).Times(0); - EXPECT_TRUE(this->upstream_->readDisable(false)); - - // New upstream with no encoder. - this->upstream_ = std::make_unique(*conn_pool_, callbacks_, decoder_callbacks_, - *tunnel_config_, downstream_stream_info_); - EXPECT_FALSE(this->upstream_->readDisable(true)); -} - -TEST_F(CombinedUpstreamTest, AddBytesSentCallbackForCoverage) { - this->setup(); - this->upstream_->addBytesSentCallback([&](uint64_t) { return true; }); -} - -TEST_F(CombinedUpstreamTest, DownstreamDisconnect) { - this->setup(); - EXPECT_CALL(*this->mock_router_upstream_request_, resetStream()); - EXPECT_CALL(this->callbacks_, onEvent(_)).Times(0); - EXPECT_TRUE(this->upstream_->onDownstreamEvent(Network::ConnectionEvent::LocalClose) == nullptr); -} - -TEST_F(CombinedUpstreamTest, UpstreamReset) { - this->setup(); - EXPECT_CALL(*this->mock_router_upstream_request_, resetStream()); - EXPECT_CALL(this->callbacks_, onEvent(_)); - this->upstream_->onResetStream(Http::StreamResetReason::ConnectionTermination, ""); -} - -TEST_F(CombinedUpstreamTest, UpstreamWatermarks) { - this->setup(); - EXPECT_CALL(this->callbacks_, onAboveWriteBufferHighWatermark()); - this->upstream_->onAboveWriteBufferHighWatermark(); - - EXPECT_CALL(this->callbacks_, onBelowWriteBufferLowWatermark()); - this->upstream_->onBelowWriteBufferLowWatermark(); -} - -TEST_F(CombinedUpstreamTest, OnSuccessCalledOnValidResponse) { - this->setup(); - auto conn_pool_callbacks = std::make_unique(); - auto conn_pool_callbacks_raw = conn_pool_callbacks.get(); - this->upstream_->setConnPoolCallbacks(std::move(conn_pool_callbacks)); - EXPECT_CALL(*conn_pool_callbacks_raw, onFailure()).Times(0); - EXPECT_CALL(*conn_pool_callbacks_raw, onSuccess(_)); - Http::ResponseHeaderMapPtr headers{new Http::TestResponseHeaderMapImpl{{":status", "200"}}}; - this->upstream_->responseDecoder().decodeHeaders(std::move(headers), false); -} - -TEST_F(CombinedUpstreamTest, OnFailureCalledOnInvalidResponse) { - this->setup(); - auto conn_pool_callbacks = std::make_unique(); - auto conn_pool_callbacks_raw = conn_pool_callbacks.get(); - this->upstream_->setConnPoolCallbacks(std::move(conn_pool_callbacks)); - EXPECT_CALL(*conn_pool_callbacks_raw, onFailure()); - EXPECT_CALL(*conn_pool_callbacks_raw, onSuccess(_)).Times(0); - Http::ResponseHeaderMapPtr headers{new Http::TestResponseHeaderMapImpl{{":status", "404"}}}; - this->upstream_->responseDecoder().decodeHeaders(std::move(headers), false); -} - -TEST_F(CombinedUpstreamTest, DumpsResponseDecoderWithoutAllocatingMemory) { - std::array buffer; - OutputBufferStream ostream{buffer.data(), buffer.size()}; - this->setup(); - - Stats::TestUtil::MemoryTest memory_test; - this->upstream_->responseDecoder().dumpState(ostream, 1); - EXPECT_EQ(memory_test.consumedBytes(), 0); - EXPECT_THAT(ostream.contents(), EndsWith("has not implemented dumpState\n")); -} -TEST_F(CombinedUpstreamTest, UpstreamTrailersMarksDoneReading) { - this->setup(); - EXPECT_CALL(*this->mock_router_upstream_request_, resetStream()); - this->upstream_->doneWriting(); - Http::ResponseTrailerMapPtr trailers{new Http::TestResponseTrailerMapImpl{{"key", "value"}}}; - this->upstream_->responseDecoder().decodeTrailers(std::move(trailers)); -} - -TEST_F(CombinedUpstreamTest, UpstreamTrailersDontPropagateFinDownstreamWhenFeatureDisabled) { - TestScopedRuntime scoped_runtime; - scoped_runtime.mergeValues( - {{"envoy.reloadable_features.tcp_tunneling_send_downstream_fin_on_upstream_trailers", - "false"}}); - this->setup(); - EXPECT_CALL(*this->mock_router_upstream_request_, resetStream()); - upstream_->doneWriting(); - EXPECT_CALL(callbacks_, onUpstreamData(_, _)).Times(0); - Http::ResponseTrailerMapPtr trailers{new Http::TestResponseTrailerMapImpl{{"key", "value"}}}; - upstream_->responseDecoder().decodeTrailers(std::move(trailers)); -} } // namespace } // namespace TcpProxy } // namespace Envoy diff --git a/test/extensions/filters/http/cache/BUILD b/test/extensions/filters/http/cache/BUILD index ce6100368be6..1d1f5f0ab270 100644 --- a/test/extensions/filters/http/cache/BUILD +++ b/test/extensions/filters/http/cache/BUILD @@ -124,7 +124,6 @@ envoy_extension_cc_test( "cache_filter_integration_test.cc", ], extension_names = ["envoy.filters.http.cache"], - shard_count = 2, deps = [ "//source/extensions/filters/http/cache:config", "//source/extensions/filters/http/cache:http_cache_lib", diff --git a/test/extensions/filters/http/csrf/BUILD b/test/extensions/filters/http/csrf/BUILD index 2a3b5ef8c3a4..5a958bce60e8 100644 --- a/test/extensions/filters/http/csrf/BUILD +++ b/test/extensions/filters/http/csrf/BUILD @@ -33,7 +33,6 @@ envoy_extension_cc_test( size = "large", srcs = ["csrf_filter_integration_test.cc"], extension_names = ["envoy.filters.http.csrf"], - shard_count = 2, deps = [ "//source/extensions/filters/http/csrf:config", "//test/config:utility_lib", diff --git a/test/extensions/filters/http/custom_response/BUILD b/test/extensions/filters/http/custom_response/BUILD index 01c7daba1d5c..704c20d197f4 100644 --- a/test/extensions/filters/http/custom_response/BUILD +++ b/test/extensions/filters/http/custom_response/BUILD @@ -81,7 +81,7 @@ envoy_extension_cc_test( "custom_response_integration_test.cc", ], extension_names = ["envoy.filters.http.custom_response"], - shard_count = 4, + shard_count = 2, tags = [ "cpu:3", ], diff --git a/test/extensions/filters/http/fault/BUILD b/test/extensions/filters/http/fault/BUILD index 55c19ce038bf..a7413da5feaf 100644 --- a/test/extensions/filters/http/fault/BUILD +++ b/test/extensions/filters/http/fault/BUILD @@ -55,7 +55,6 @@ envoy_extension_cc_test( size = "large", srcs = ["fault_filter_integration_test.cc"], extension_names = ["envoy.filters.http.fault"], - shard_count = 4, deps = [ "//source/extensions/filters/http/fault:config", "//test/integration:http_protocol_integration_lib", diff --git a/test/extensions/filters/http/health_check/BUILD b/test/extensions/filters/http/health_check/BUILD index 7023b7b02bf6..8d7a622ebf1d 100644 --- a/test/extensions/filters/http/health_check/BUILD +++ b/test/extensions/filters/http/health_check/BUILD @@ -45,7 +45,6 @@ envoy_extension_cc_test( "health_check_integration_test.cc", ], extension_names = ["envoy.filters.http.health_check"], - shard_count = 2, deps = [ "//source/extensions/filters/http/buffer:config", "//source/extensions/filters/http/health_check:config", diff --git a/test/extensions/filters/http/jwt_authn/BUILD b/test/extensions/filters/http/jwt_authn/BUILD index 321ebccf2c83..9b84849c432f 100644 --- a/test/extensions/filters/http/jwt_authn/BUILD +++ b/test/extensions/filters/http/jwt_authn/BUILD @@ -162,7 +162,7 @@ envoy_extension_cc_test( "envoy.filters.http.jwt_authn", "envoy.filters.http.set_filter_state", ], - shard_count = 8, + shard_count = 4, tags = [ "cpu:3", ], diff --git a/test/extensions/filters/http/rbac/BUILD b/test/extensions/filters/http/rbac/BUILD index 33538419eebd..4c397d1e0389 100644 --- a/test/extensions/filters/http/rbac/BUILD +++ b/test/extensions/filters/http/rbac/BUILD @@ -58,7 +58,7 @@ envoy_extension_cc_test( size = "large", srcs = ["rbac_filter_integration_test.cc"], extension_names = ["envoy.filters.http.rbac"], - shard_count = 10, + shard_count = 3, tags = ["skip_on_windows"], deps = [ "//source/extensions/clusters/dynamic_forward_proxy:cluster", diff --git a/test/extensions/upstreams/http/tcp/upstream_request_test.cc b/test/extensions/upstreams/http/tcp/upstream_request_test.cc index 9bacca7d18af..e48d6a4ad7e4 100644 --- a/test/extensions/upstreams/http/tcp/upstream_request_test.cc +++ b/test/extensions/upstreams/http/tcp/upstream_request_test.cc @@ -114,7 +114,7 @@ class TcpUpstreamTest : public ::testing::Test { EXPECT_CALL(mock_router_filter_, callbacks()).Times(AnyNumber()); upstream_request_ = std::make_unique( mock_router_filter_, std::make_unique>(), false, - false, false /*enable_tcp_tunneling*/); + false); auto data = std::make_unique>(); EXPECT_CALL(*data, connection()).Times(AnyNumber()).WillRepeatedly(ReturnRef(connection())); tcp_upstream_ = std::make_unique(upstream_request_.get(), std::move(data)); diff --git a/test/extensions/upstreams/http/udp/upstream_request_test.cc b/test/extensions/upstreams/http/udp/upstream_request_test.cc index c6020febfea6..423b21589589 100644 --- a/test/extensions/upstreams/http/udp/upstream_request_test.cc +++ b/test/extensions/upstreams/http/udp/upstream_request_test.cc @@ -46,7 +46,6 @@ class UdpUpstreamTest : public ::testing::Test { udp_upstream_ = std::make_unique(&mock_upstream_to_downstream_, std::move(mock_socket), std::move(mock_host), mock_dispatcher_); - EXPECT_NO_THROW(udp_upstream_->enableHalfClose()); } protected: diff --git a/test/extensions/upstreams/tcp/generic/BUILD b/test/extensions/upstreams/tcp/generic/BUILD index 57cc2e3fcc9a..2a9375294a53 100644 --- a/test/extensions/upstreams/tcp/generic/BUILD +++ b/test/extensions/upstreams/tcp/generic/BUILD @@ -17,6 +17,5 @@ envoy_cc_test( "//source/extensions/upstreams/tcp/generic:config", "//test/mocks/server:factory_context_mocks", "//test/mocks/upstream:upstream_mocks", - "@envoy_api//envoy/extensions/filters/network/tcp_proxy/v3:pkg_cc_proto", ], ) diff --git a/test/extensions/upstreams/tcp/generic/config_test.cc b/test/extensions/upstreams/tcp/generic/config_test.cc index 08d2ff0ead46..f2c62b449174 100644 --- a/test/extensions/upstreams/tcp/generic/config_test.cc +++ b/test/extensions/upstreams/tcp/generic/config_test.cc @@ -1,10 +1,7 @@ -#include "envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.pb.h" - #include "source/common/stream_info/bool_accessor_impl.h" #include "source/common/tcp_proxy/tcp_proxy.h" #include "source/extensions/upstreams/tcp/generic/config.h" -#include "test/mocks/http/mocks.h" #include "test/mocks/server/factory_context.h" #include "test/mocks/tcp/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -17,13 +14,13 @@ using testing::_; using testing::AnyNumber; using testing::NiceMock; using testing::Return; -using testing::ReturnRef; namespace Envoy { namespace Extensions { namespace Upstreams { namespace Tcp { namespace Generic { + class TcpConnPoolTest : public ::testing::Test { public: TcpConnPoolTest() { @@ -35,10 +32,6 @@ class TcpConnPoolTest : public ::testing::Test { NiceMock downstream_stream_info_; NiceMock connection_; Upstream::MockLoadBalancerContext lb_context_; - envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy tcp_proxy_; - NiceMock store_; - Stats::MockScope& scope_{store_.mockScope()}; - NiceMock decoder_callbacks_; NiceMock context_; }; @@ -46,13 +39,13 @@ TEST_F(TcpConnPoolTest, TestNoTunnelingConfig) { EXPECT_CALL(thread_local_cluster_, tcpConnPool(_, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(), - &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, downstream_stream_info_)); } TEST_F(TcpConnPoolTest, TestTunnelingDisabledByFilterState) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; - tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); - const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); + config_proto.set_hostname("host"); + const TcpProxy::TunnelingConfigHelperImpl config(config_proto, context_); downstream_stream_info_.filterState()->setData( TcpProxy::DisableTunnelingFilterStateKey, @@ -62,13 +55,13 @@ TEST_F(TcpConnPoolTest, TestTunnelingDisabledByFilterState) { EXPECT_CALL(thread_local_cluster_, tcpConnPool(_, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), - &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, downstream_stream_info_)); } TEST_F(TcpConnPoolTest, TestTunnelingNotDisabledIfFilterStateHasFalseValue) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; - tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); - const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); + config_proto.set_hostname("host"); + const TcpProxy::TunnelingConfigHelperImpl config(config_proto, context_); downstream_stream_info_.filterState()->setData( TcpProxy::DisableTunnelingFilterStateKey, @@ -78,47 +71,46 @@ TEST_F(TcpConnPoolTest, TestTunnelingNotDisabledIfFilterStateHasFalseValue) { EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), - &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, downstream_stream_info_)); } TEST_F(TcpConnPoolTest, TestNoConnPool) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; - tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); - const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); + config_proto.set_hostname("host"); + const TcpProxy::TunnelingConfigHelperImpl config(config_proto, context_); EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), - &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, downstream_stream_info_)); } TEST_F(TcpConnPoolTest, Http2Config) { auto info = std::make_shared(); - const std::string fake_cluster_name = "fake_cluster"; - + EXPECT_CALL(*info, features()).WillOnce(Return(Upstream::ClusterInfo::Features::HTTP2)); + EXPECT_CALL(thread_local_cluster_, info).WillOnce(Return(info)); envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; - tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); - const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); + config_proto.set_hostname("host"); + const TcpProxy::TunnelingConfigHelperImpl config(config_proto, context_); EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), - &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, downstream_stream_info_)); } TEST_F(TcpConnPoolTest, Http3Config) { auto info = std::make_shared(); - const std::string fake_cluster_name = "fake_cluster"; EXPECT_CALL(*info, features()) .Times(AnyNumber()) .WillRepeatedly(Return(Upstream::ClusterInfo::Features::HTTP3)); EXPECT_CALL(thread_local_cluster_, info).Times(AnyNumber()).WillRepeatedly(Return(info)); envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; - tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); - const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); + config_proto.set_hostname("host"); + const TcpProxy::TunnelingConfigHelperImpl config(config_proto, context_); EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), - &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); + &lb_context_, callbacks_, downstream_stream_info_)); } TEST(DisableTunnelingObjectFactory, CreateFromBytes) { diff --git a/test/integration/BUILD b/test/integration/BUILD index 72636c49d709..fce0e38d657d 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -329,7 +329,6 @@ envoy_cc_test( srcs = envoy_select_admin_functionality([ "drain_close_integration_test.cc", ]), - shard_count = 6, tags = [ "cpu:3", ], @@ -615,7 +614,6 @@ envoy_cc_test( srcs = [ "buffer_accounting_integration_test.cc", ], - shard_count = 2, tags = [ "cpu:3", ], @@ -987,7 +985,7 @@ envoy_cc_test( srcs = ["idle_timeout_integration_test.cc"], # As this test has many pauses for idle timeouts, it takes a while to run. # Shard it enough to bring the run time in line with other integration tests. - shard_count = 16, + shard_count = 4, tags = [ "cpu:3", ], @@ -1357,7 +1355,7 @@ envoy_cc_test( srcs = [ "redirect_integration_test.cc", ], - shard_count = 4, + shard_count = 2, tags = [ "cpu:3", "nofips", @@ -1393,7 +1391,6 @@ envoy_cc_test( name = "websocket_integration_test", size = "large", srcs = ["websocket_integration_test.cc"], - shard_count = 2, tags = [ "cpu:3", ], @@ -1540,7 +1537,7 @@ envoy_cc_test( name = "overload_integration_test", size = "large", srcs = ["overload_integration_test.cc"], - shard_count = 10, + shard_count = 4, tags = [ "cpu:3", ], @@ -1814,7 +1811,7 @@ envoy_cc_test( data = [ "//test/config/integration/certs", ], - shard_count = 30, + shard_count = 8, tags = [ "cpu:3", ], @@ -1840,7 +1837,6 @@ envoy_cc_test( data = [ "//test/config/integration/certs", ], - shard_count = 4, deps = [ ":http_integration_lib", ":http_protocol_integration_lib", @@ -2285,7 +2281,6 @@ envoy_cc_test( srcs = [ "local_reply_integration_test.cc", ], - shard_count = 2, tags = [ "cpu:2", ], diff --git a/test/integration/base_integration_test.cc b/test/integration/base_integration_test.cc index baab4dd3be91..13295850af25 100644 --- a/test/integration/base_integration_test.cc +++ b/test/integration/base_integration_test.cc @@ -427,7 +427,6 @@ void BaseIntegrationTest::registerTestServerPorts(const std::vector const auto admin_addr = test_server->server().admin()->socket().connectionInfoProvider().localAddress(); if (admin_addr->type() == Network::Address::Type::Ip) { - ENVOY_LOG(debug, "registered 'admin' as port {}.", admin_addr->ip()->port()); registerPort("admin", admin_addr->ip()->port()); } } diff --git a/test/integration/http_protocol_integration.cc b/test/integration/http_protocol_integration.cc index 914fffefcd99..50f79fdf6366 100644 --- a/test/integration/http_protocol_integration.cc +++ b/test/integration/http_protocol_integration.cc @@ -39,17 +39,15 @@ std::vector HttpProtocolIntegrationTest::getProtocolTest #else use_header_validator_values.push_back(false); #endif - for (const bool tunneling_with_upstream_filters : {false, true}) { - for (Http1ParserImpl http1_implementation : http1_implementations) { - for (Http2Impl http2_implementation : http2_implementations) { - for (bool defer_processing : http2_bool_values) { - for (bool deprecate_callback_visitor : http2_bool_values) { - for (bool use_header_validator : use_header_validator_values) { - ret.push_back(HttpProtocolTestParams{ - ip_version, downstream_protocol, upstream_protocol, http1_implementation, - http2_implementation, defer_processing, use_header_validator, - deprecate_callback_visitor, tunneling_with_upstream_filters}); - } + for (Http1ParserImpl http1_implementation : http1_implementations) { + for (Http2Impl http2_implementation : http2_implementations) { + for (bool defer_processing : http2_bool_values) { + for (bool deprecate_callback_visitor : http2_bool_values) { + for (bool use_header_validator : use_header_validator_values) { + ret.push_back(HttpProtocolTestParams{ + ip_version, downstream_protocol, upstream_protocol, http1_implementation, + http2_implementation, defer_processing, use_header_validator, + deprecate_callback_visitor}); } } } @@ -104,11 +102,9 @@ std::string HttpProtocolIntegrationTest::protocolTestParamsToString( http2ImplementationToString(params.param.http2_implementation), params.param.defer_processing_backedup_streams ? "WithDeferredProcessing" : "NoDeferredProcessing", - params.param.use_universal_header_validator ? "Uhv" : "Legacy", params.param.deprecate_callback_visitor ? "WithCallbackVisitor" : "NoCallbackVisitor", - params.param.tunneling_with_upstream_filters ? "WithUpstreamHttpFilters" - : "WithoutUpstreamHttpFilters"); + params.param.use_universal_header_validator ? "Uhv" : "Legacy"); } void HttpProtocolIntegrationTest::setUpstreamOverrideStreamErrorOnInvalidHttpMessage() { diff --git a/test/integration/http_protocol_integration.h b/test/integration/http_protocol_integration.h index 1fa57f3d1992..0c0dd6c1bbc0 100644 --- a/test/integration/http_protocol_integration.h +++ b/test/integration/http_protocol_integration.h @@ -15,7 +15,6 @@ struct HttpProtocolTestParams { Http2Impl http2_implementation; bool defer_processing_backedup_streams; bool use_universal_header_validator; - bool tunneling_with_upstream_filters; bool deprecate_callback_visitor; }; @@ -87,9 +86,6 @@ class HttpProtocolIntegrationTest : public testing::TestWithParam static void initializeMockStreamFilterCallbacks(T& callbacks) MockStreamDecoderFilterCallbacks::MockStreamDecoderFilterCallbacks() { initializeMockStreamFilterCallbacks(*this); - ON_CALL(*this, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); ON_CALL(*this, decodingBuffer()).WillByDefault(Invoke(&buffer_, &Buffer::InstancePtr::get)); ON_CALL(*this, addDownstreamWatermarkCallbacks(_)) diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index f7770ac37b96..3ec3f7c5eb71 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -109,7 +109,6 @@ class MockFilterManagerCallbacks : public FilterManagerCallbacks { MOCK_METHOD(OptRef, tracingConfig, (), (const)); MOCK_METHOD(const ScopeTrackedObject&, scope, ()); MOCK_METHOD(void, restoreContextOnContinue, (ScopeTrackedObjectStack&)); - MOCK_METHOD(bool, isHalfCloseEnabled, ()); ResponseHeaderMapPtr informational_headers_; ResponseHeaderMapPtr response_headers_; @@ -331,7 +330,6 @@ class MockStreamDecoderFilterCallbacks : public StreamDecoderFilterCallbacks, bool is_grpc_request_{}; bool is_head_request_{false}; bool stream_destroyed_{}; - NiceMock dispatcher_; }; class MockStreamEncoderFilterCallbacks : public StreamEncoderFilterCallbacks, diff --git a/test/mocks/router/BUILD b/test/mocks/router/BUILD index 2d1364e40bc1..79a35cb2b25d 100644 --- a/test/mocks/router/BUILD +++ b/test/mocks/router/BUILD @@ -49,12 +49,3 @@ envoy_cc_mock( "//test/test_common:test_time_lib", ], ) - -envoy_cc_mock( - name = "upstream_request", - srcs = ["upstream_request.cc"], - hdrs = ["upstream_request.h"], - deps = [ - "//source/common/router:router_lib", - ], -) diff --git a/test/mocks/router/upstream_request.cc b/test/mocks/router/upstream_request.cc deleted file mode 100644 index aabf4baf8a05..000000000000 --- a/test/mocks/router/upstream_request.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include "test/mocks/router/upstream_request.h" - -namespace Envoy { -namespace Router { - -MockUpstreamRequest::MockUpstreamRequest(RouterFilterInterface& router_interface, - std::unique_ptr&& conn_pool) - : UpstreamRequest(router_interface, std::move(conn_pool), false, true, true) {} - -MockUpstreamRequest::~MockUpstreamRequest() = default; - -} // namespace Router -} // namespace Envoy diff --git a/test/mocks/router/upstream_request.h b/test/mocks/router/upstream_request.h deleted file mode 100644 index 10688fdde5d1..000000000000 --- a/test/mocks/router/upstream_request.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "source/common/router/upstream_request.h" - -#include "gmock/gmock.h" - -namespace Envoy { -namespace Router { - -class MockUpstreamRequest : public UpstreamRequest { -public: - MockUpstreamRequest(RouterFilterInterface& router_interface, std::unique_ptr&&); - ~MockUpstreamRequest() override; - MOCK_METHOD(void, acceptHeadersFromRouter, (bool end_stream), (override)); - MOCK_METHOD(void, acceptDataFromRouter, (Buffer::Instance & data, bool end_stream), (override)); - MOCK_METHOD(void, onAboveWriteBufferHighWatermark, (), (override)); - MOCK_METHOD(void, resetStream, (), (override)); -}; - -} // namespace Router -} // namespace Envoy From 9904c0b30a6313736d929bedc1ce077108b95f7f Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Tue, 19 Mar 2024 15:11:15 -0400 Subject: [PATCH 088/124] Shard file_system_buffer_integration_test to avoid bazel timeouts (#32983) --------- Signed-off-by: Yan Avlasov --- test/extensions/filters/http/file_system_buffer/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/test/extensions/filters/http/file_system_buffer/BUILD b/test/extensions/filters/http/file_system_buffer/BUILD index fb28ba6f0b58..661945a57506 100644 --- a/test/extensions/filters/http/file_system_buffer/BUILD +++ b/test/extensions/filters/http/file_system_buffer/BUILD @@ -47,6 +47,7 @@ envoy_extension_cc_test( "filter_integration_test.cc", ], extension_names = ["envoy.filters.http.file_system_buffer"], + shard_count = 4, tags = [ "cpu:3", "skip_on_windows", From 002e7f9cd0224800e5b318cd7d84c49fb65b9565 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 19 Mar 2024 12:36:19 -0700 Subject: [PATCH 089/124] test: Enable METADATA tests in protocol_integration_test.cc for HTTP/3 (#32957) Enable METADATA tests in protocol_integration_test.cc for HTTP/3 Signed-off-by: Ryan Hamilton --- test/integration/protocol_integration_test.cc | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 70ecf10ac2dd..fec8f47df756 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -3110,17 +3110,23 @@ TEST_P(DownstreamProtocolIntegrationTest, LocalReplyWithMetadata) { } TEST_P(ProtocolIntegrationTest, ContinueAllFromDecodeMetadata) { - if (downstream_protocol_ != Http::CodecType::HTTP2 || - upstreamProtocol() != Http::CodecType::HTTP2) { - GTEST_SKIP() << "Metadata is not enabled for non HTTP2 protocols."; + if (downstream_protocol_ == Http::CodecType::HTTP1 || + upstreamProtocol() == Http::CodecType::HTTP1) { + GTEST_SKIP() << "Metadata is not enabled for HTTP1 protocols."; } config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { RELEASE_ASSERT(bootstrap.mutable_static_resources()->clusters_size() >= 1, ""); ConfigHelper::HttpProtocolOptions protocol_options; - protocol_options.mutable_explicit_http_config() - ->mutable_http2_protocol_options() - ->set_allow_metadata(true); + if (upstreamProtocol() == Http::CodecType::HTTP3) { + protocol_options.mutable_explicit_http_config() + ->mutable_http3_protocol_options() + ->set_allow_metadata(true); + } else { + protocol_options.mutable_explicit_http_config() + ->mutable_http2_protocol_options() + ->set_allow_metadata(true); + } ConfigHelper::setProtocolOptions(*bootstrap.mutable_static_resources()->mutable_clusters(0), protocol_options); }); @@ -3161,17 +3167,23 @@ TEST_P(ProtocolIntegrationTest, ContinueAllFromDecodeMetadata) { } TEST_P(DownstreamProtocolIntegrationTest, ContinueAllFromDecodeMetadataFollowedByLocalReply) { - if (downstream_protocol_ != Http::CodecType::HTTP2 || - upstreamProtocol() != Http::CodecType::HTTP2) { - GTEST_SKIP() << "Metadata is not enabled for non HTTP2 protocols."; + if (downstream_protocol_ == Http::CodecType::HTTP1 || + upstreamProtocol() == Http::CodecType::HTTP1) { + GTEST_SKIP() << "Metadata is not enabled for HTTP1 protocols."; } config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { RELEASE_ASSERT(bootstrap.mutable_static_resources()->clusters_size() >= 1, ""); ConfigHelper::HttpProtocolOptions protocol_options; - protocol_options.mutable_explicit_http_config() - ->mutable_http2_protocol_options() - ->set_allow_metadata(true); + if (upstreamProtocol() == Http::CodecType::HTTP3) { + protocol_options.mutable_explicit_http_config() + ->mutable_http3_protocol_options() + ->set_allow_metadata(true); + } else { + protocol_options.mutable_explicit_http_config() + ->mutable_http2_protocol_options() + ->set_allow_metadata(true); + } ConfigHelper::setProtocolOptions(*bootstrap.mutable_static_resources()->mutable_clusters(0), protocol_options); }); @@ -3205,16 +3217,22 @@ TEST_P(DownstreamProtocolIntegrationTest, ContinueAllFromDecodeMetadataFollowedB } TEST_P(ProtocolIntegrationTest, ContinueAllFromEncodeMetadata) { - if (upstreamProtocol() != Http::CodecType::HTTP2 || - downstream_protocol_ != Http::CodecType::HTTP2) { - GTEST_SKIP() << "Metadata is not enabled for non HTTP2 protocols."; + if (downstream_protocol_ == Http::CodecType::HTTP1 || + upstreamProtocol() == Http::CodecType::HTTP1) { + GTEST_SKIP() << "Metadata is not enabled for HTTP1 protocols."; } config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { RELEASE_ASSERT(bootstrap.mutable_static_resources()->clusters_size() >= 1, ""); ConfigHelper::HttpProtocolOptions protocol_options; - protocol_options.mutable_explicit_http_config() - ->mutable_http2_protocol_options() - ->set_allow_metadata(true); + if (upstreamProtocol() == Http::CodecType::HTTP3) { + protocol_options.mutable_explicit_http_config() + ->mutable_http3_protocol_options() + ->set_allow_metadata(true); + } else { + protocol_options.mutable_explicit_http_config() + ->mutable_http2_protocol_options() + ->set_allow_metadata(true); + } ConfigHelper::setProtocolOptions(*bootstrap.mutable_static_resources()->mutable_clusters(0), protocol_options); }); From 8d64f2dd17c038292100c93a4c7fc90edc4fdcab Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Tue, 19 Mar 2024 13:57:02 -0700 Subject: [PATCH 090/124] tests: reduce integration test http permutations (#32984) --- test/integration/http_protocol_integration.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/integration/http_protocol_integration.cc b/test/integration/http_protocol_integration.cc index 50f79fdf6366..a6856e1f1dd5 100644 --- a/test/integration/http_protocol_integration.cc +++ b/test/integration/http_protocol_integration.cc @@ -8,6 +8,8 @@ std::vector HttpProtocolIntegrationTest::getProtocolTest const std::vector& upstream_protocols) { std::vector ret; + bool handled_http2_special_cases_downstream = false; + bool handled_http2_special_cases_upstream = false; for (auto ip_version : TestEnvironment::getIpVersionsForTest()) { for (auto downstream_protocol : downstream_protocols) { for (auto upstream_protocol : upstream_protocols) { @@ -27,10 +29,19 @@ std::vector HttpProtocolIntegrationTest::getProtocolTest std::vector http2_implementations = {Http2Impl::Nghttp2}; std::vector http2_bool_values = {false}; - if (downstream_protocol == Http::CodecType::HTTP2 || - upstream_protocol == Http::CodecType::HTTP2) { + if ((!handled_http2_special_cases_downstream && + downstream_protocol == Http::CodecType::HTTP2) || + (!handled_http2_special_cases_upstream && + upstream_protocol == Http::CodecType::HTTP2)) { http2_implementations.push_back(Http2Impl::Oghttp2); http2_bool_values.push_back(true); + + if (downstream_protocol == Http::CodecType::HTTP2) { + handled_http2_special_cases_downstream = true; + } + if (upstream_protocol == Http::CodecType::HTTP2) { + handled_http2_special_cases_upstream = true; + } } std::vector use_header_validator_values; From 0c6cf47221a27b91ca8f7754d4814d981908f690 Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Tue, 19 Mar 2024 18:05:58 -0400 Subject: [PATCH 091/124] Make test independent of BoringSSL version (#32964) Signed-off-by: Yan Avlasov --- test/common/quic/envoy_quic_proof_source_test.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/common/quic/envoy_quic_proof_source_test.cc b/test/common/quic/envoy_quic_proof_source_test.cc index afe1e521378e..a6c6bcb2d4f3 100644 --- a/test/common/quic/envoy_quic_proof_source_test.cc +++ b/test/common/quic/envoy_quic_proof_source_test.cc @@ -18,6 +18,7 @@ #include "quiche/quic/core/crypto/certificate_view.h" #include "quiche/quic/test_tools/test_certificates.h" +using testing::HasSubstr; using testing::Invoke; using testing::Return; using testing::ReturnRef; @@ -309,10 +310,10 @@ f/lOd5zz2e7Tu2pUtx1sX1tlKph1D0ANpJwxRV78R2hjmynLSl7h4Ual9NMubqkD x96rVeUbRJ/qU4//nNM/XQa9vIAIcTZ0jFhmb0c3R4rmoqqC3vkSDwtaE5yuS5T4 GUy+n0vQNB0cXGzgcGI= -----END CERTIFICATE-----)"}; - EXPECT_THROW_WITH_MESSAGE(expectCertChainAndPrivateKey(cert_with_rsa_1024, false, true), - EnvoyException, - "Failed to load certificate chain from , only RSA certificates with " - "2048-bit or larger keys are supported"); + EXPECT_THAT_THROWS_MESSAGE( + expectCertChainAndPrivateKey(cert_with_rsa_1024, false, true), EnvoyException, + HasSubstr("Failed to load certificate chain from , only RSA certificates with " + "2048-bit")); } TEST_F(EnvoyQuicProofSourceTest, ComputeSignatureFailNoFilterChain) { From e4bd0e6f70749a50dd122d5d7006e8913bb9e84c Mon Sep 17 00:00:00 2001 From: htuch Date: Tue, 19 Mar 2024 18:06:26 -0400 Subject: [PATCH 092/124] test: bump shard count for idle_timeout_integration_test (#32990) Attempt to reduce number of timeout flakes we're witnessing. Signed-off-by: Harvey Tuch --- test/integration/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/BUILD b/test/integration/BUILD index fce0e38d657d..06afa7651b01 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -985,7 +985,7 @@ envoy_cc_test( srcs = ["idle_timeout_integration_test.cc"], # As this test has many pauses for idle timeouts, it takes a while to run. # Shard it enough to bring the run time in line with other integration tests. - shard_count = 4, + shard_count = 8, tags = [ "cpu:3", ], From f4a813b12145d1563d0efce648373ea044047e39 Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Tue, 19 Mar 2024 19:39:16 -0400 Subject: [PATCH 093/124] logs: Static format validation for logging macros with tags (#32966) fmt::runtime does not do compile time validation of format string. This change brings back compile time format validation to logging macros with tags and reduces memory allocations needed to log these messages. It also avoids logging errors if tags contain the {} sequence. Signed-off-by: Yan Avlasov --- source/common/common/logger.h | 36 ++++++++----------- .../quic/envoy_quic_client_connection.cc | 2 +- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/source/common/common/logger.h b/source/common/common/logger.h index 4f0e7375f459..7fb16e1ae1d8 100644 --- a/source/common/common/logger.h +++ b/source/common/common/logger.h @@ -560,9 +560,8 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { #define ENVOY_TAGGED_LOG_TO_LOGGER(LOGGER, LEVEL, TAGS, FORMAT, ...) \ do { \ if (ENVOY_LOG_COMP_LEVEL(LOGGER, LEVEL)) { \ - ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, \ - fmt::runtime(::Envoy::Logger::Utility::serializeLogTags(TAGS) + FORMAT), \ - ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(TAGS), ##__VA_ARGS__); \ } \ } while (0) @@ -571,10 +570,8 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { if (ENVOY_LOG_COMP_LEVEL(LOGGER, LEVEL)) { \ std::map log_tags = TAGS; \ log_tags.emplace("ConnectionId", std::to_string((CONNECTION).id())); \ - ENVOY_LOG_TO_LOGGER( \ - LOGGER, LEVEL, \ - fmt::runtime(::Envoy::Logger::Utility::serializeLogTags(log_tags) + FORMAT), \ - ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ } \ } while (0) @@ -585,10 +582,8 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { log_tags.emplace("ConnectionId", \ (STREAM).connection() ? std::to_string((STREAM).connection()->id()) : "0"); \ log_tags.emplace("StreamId", std::to_string((STREAM).streamId())); \ - ENVOY_LOG_TO_LOGGER( \ - LOGGER, LEVEL, \ - fmt::runtime(::Envoy::Logger::Utility::serializeLogTags(log_tags) + FORMAT), \ - ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ } \ } while (0) @@ -597,10 +592,8 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { if (ENVOY_LOG_COMP_LEVEL(LOGGER, LEVEL)) { \ std::map log_tags; \ log_tags.emplace("ConnectionId", std::to_string((CONNECTION).id())); \ - ENVOY_LOG_TO_LOGGER( \ - LOGGER, LEVEL, \ - fmt::runtime(::Envoy::Logger::Utility::serializeLogTags(log_tags) + FORMAT), \ - ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ } \ } while (0) @@ -611,10 +604,8 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { log_tags.emplace("ConnectionId", \ (STREAM).connection() ? std::to_string((STREAM).connection()->id()) : "0"); \ log_tags.emplace("StreamId", std::to_string((STREAM).streamId())); \ - ENVOY_LOG_TO_LOGGER( \ - LOGGER, LEVEL, \ - fmt::runtime(::Envoy::Logger::Utility::serializeLogTags(log_tags) + FORMAT), \ - ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ } \ } while (0) @@ -675,10 +666,11 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { if (ENVOY_LOG_COMP_LEVEL(ENVOY_LOGGER(), LEVEL)) { \ std::map log_tags; \ log_tags.emplace("ConnectionId", std::to_string((CONNECTION).id())); \ - const auto combined_format = ::Envoy::Logger::Utility::serializeLogTags(log_tags) + FORMAT; \ - ENVOY_LOG_TO_LOGGER(ENVOY_LOGGER(), LEVEL, fmt::runtime(combined_format), ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(ENVOY_LOGGER(), LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ ::Envoy::Logger::Registry::getSink()->logWithStableName( \ - EVENT_NAME, #LEVEL, (ENVOY_LOGGER()).name(), combined_format, ##__VA_ARGS__); \ + EVENT_NAME, #LEVEL, (ENVOY_LOGGER()).name(), "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ } \ } while (0) diff --git a/source/common/quic/envoy_quic_client_connection.cc b/source/common/quic/envoy_quic_client_connection.cc index 633f6822068e..7b379f05ca84 100644 --- a/source/common/quic/envoy_quic_client_connection.cc +++ b/source/common/quic/envoy_quic_client_connection.cc @@ -85,7 +85,7 @@ void EnvoyQuicClientConnection::processPacket( (peer_address->ip()->version() == Network::Address::IpVersion::v4 ? "v4" : "v6"), (HasPendingPathValidation() ? "" : " no"), (self_address().IsInitialized() ? "" : " not")); - ENVOY_CONN_LOG(error, error_message, *this); + ENVOY_CONN_LOG(error, "{}", *this, error_message); if (num_packets_with_unknown_dst_address_ > 10) { // If too many packets are without destination addresses, close the connection. CloseConnection(quic::QUIC_PACKET_READ_ERROR, error_message, From 77c698c1cfe6e99d73e24cc8aaf9e5c95285e924 Mon Sep 17 00:00:00 2001 From: birenroy Date: Tue, 19 Mar 2024 20:39:26 -0400 Subject: [PATCH 094/124] deps: Update QUICHE from 70bc4dfde to 10053f8f1 (#32988) * Update QUICHE from 70bc4dfde to 10053f8f1 https://github.com/google/quiche/compare/70bc4dfde..10053f8f1 ``` $ git log 70bc4dfde..10053f8f1 --date=short --no-merges --format="%ad %al %s" 2024-03-19 birenroy Updates the callback struct in line with the latest version of nghttp2. 2024-03-19 rch Add a 3rd flag count for quic_enable_http3_metadata_decoding. 2024-03-18 rch Automated g4 rollback of changelist 616801281. 2024-03-18 wub Deprecate --gfe2_reloadable_flag_quic_bbr2_fix_spurious_loss_bytes_counting. 2024-03-18 awillia Update ParseQuicTag to only use hex decoding if valid 2024-03-16 asedeno Add missing includes to metadata_decoder.h and metadata_decoder.cc 2024-03-15 bnc Only export flags with `export_to_quiche` set to true publicly. 2024-03-15 danzh Add FLAG_COUNT for flag --gfe2_reloadable_flag_quic_no_write_control_frame_upon_connection_close. 2024-03-13 rch Incrementally process HTTP/3 METADATA frames instead of buffering the full payload. ``` Signed-off-by: Biren Roy * Adds a library target for the new QUIC metadata decoder. Signed-off-by: Biren Roy * Fixing format. Signed-off-by: Biren Roy --------- Signed-off-by: Biren Roy --- bazel/external/quiche.BUILD | 13 +++++++++++++ bazel/repository_locations.bzl | 6 +++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/bazel/external/quiche.BUILD b/bazel/external/quiche.BUILD index 03f9b143274a..fed3bfc9f04e 100644 --- a/bazel/external/quiche.BUILD +++ b/bazel/external/quiche.BUILD @@ -3126,6 +3126,18 @@ envoy_quic_cc_library( ], ) +envoy_quic_cc_library( + name = "quic_core_http_metadata_decoder_lib", + srcs = ["quiche/quic/core/http/metadata_decoder.cc"], + hdrs = ["quiche/quic/core/http/metadata_decoder.h"], + deps = [ + ":quic_core_error_codes_lib", + ":quic_core_http_header_list_lib", + ":quic_core_qpack_qpack_decoded_headers_accumulator_lib", + ":quic_core_qpack_qpack_decoder_lib", + ], +) + envoy_quic_cc_library( name = "quic_core_http_server_initiated_spdy_stream_lib", srcs = ["quiche/quic/core/http/quic_server_initiated_spdy_stream.cc"], @@ -3167,6 +3179,7 @@ envoy_quic_cc_library( ":quic_core_http_http_constants_lib", ":quic_core_http_http_decoder_lib", ":quic_core_http_http_encoder_lib", + ":quic_core_http_metadata_decoder_lib", ":quic_core_http_spdy_stream_body_manager_lib", ":quic_core_http_spdy_utils_lib", ":quic_core_packets_lib", diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index f4026bfe6818..0d399a004e86 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -1192,12 +1192,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "QUICHE", project_desc = "QUICHE (QUIC, HTTP/2, Etc) is Google‘s implementation of QUIC and related protocols", project_url = "https://github.com/google/quiche", - version = "70bc4dfde98dd4feb7de4ce4b68b220e8cdfee9b", - sha256 = "33c6a54f605eb5348004f66af4682ae050216e1da5601d860ec634b255256d6e", + version = "10053f8f199aa6d6ad7727b3112db9e62884bb8c", + sha256 = "ab6f41a72fda9f731b157840293d1a22a0b7a495ac208a8c8f4c727ded9c7e1d", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["controlplane", "dataplane_core"], - release_date = "2024-03-13", + release_date = "2024-03-19", cpe = "N/A", license = "BSD-3-Clause", license_url = "https://github.com/google/quiche/blob/{version}/LICENSE", From eee9327cba8a1cc801f363c35699536afb11257e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20B=C3=A9ky?= Date: Tue, 19 Mar 2024 20:41:04 -0400 Subject: [PATCH 095/124] Use GetQuiche*Flag() instead of GetQuic*Flag() in runtime_impl_test.cc. (#32814) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GetQuic*Flag() is just an alias anyway, see https://github.com/google/quiche/blob/2337058d22637344f6b3e772426420dcef9e543d/quiche/quic/platform/api/quic_flags.h Include the appropriate header. source/common/quic/envoy_quic_utils.h does not declare any flag accessors. Signed-off-by: Bence Béky Co-authored-by: Bence Béky --- test/common/runtime/runtime_impl_test.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/common/runtime/runtime_impl_test.cc b/test/common/runtime/runtime_impl_test.cc index 54cebe55ecfb..aabbd377ba4c 100644 --- a/test/common/runtime/runtime_impl_test.cc +++ b/test/common/runtime/runtime_impl_test.cc @@ -29,7 +29,7 @@ #include "gtest/gtest.h" #ifdef ENVOY_ENABLE_QUIC -#include "source/common/quic/envoy_quic_utils.h" +#include "quiche/common/platform/api/quiche_flags.h" #endif using testing::_; @@ -563,13 +563,13 @@ TEST_F(StaticLoaderImplTest, All) { #ifdef ENVOY_ENABLE_QUIC TEST_F(StaticLoaderImplTest, QuicheReloadableFlags) { - EXPECT_TRUE(GetQuicReloadableFlag(quic_testonly_default_true)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_false)); + EXPECT_TRUE(GetQuicheReloadableFlag(quic_testonly_default_true)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_false)); SetQuicheReloadableFlag(quic_testonly_default_true, false); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_true)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_false)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_true)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_false)); // Test that Quiche flags can be overwritten via Envoy runtime config. base_ = TestUtility::parseYaml( @@ -577,8 +577,8 @@ TEST_F(StaticLoaderImplTest, QuicheReloadableFlags) { "true"); setup(); - EXPECT_TRUE(GetQuicReloadableFlag(quic_testonly_default_true)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_false)); + EXPECT_TRUE(GetQuicheReloadableFlag(quic_testonly_default_true)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_false)); // Test that Quiche flags can be overwritten again. base_ = TestUtility::parseYaml( @@ -586,8 +586,8 @@ TEST_F(StaticLoaderImplTest, QuicheReloadableFlags) { "false"); setup(); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_true)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_false)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_true)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_false)); } #endif From de6ba838d592b1192e425ef88fbc6bba456eefea Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Tue, 19 Mar 2024 18:44:27 -0700 Subject: [PATCH 096/124] Stringmatcher: factory context for aws signer (#32965) Signed-off-by: Greg Greenway --- envoy/grpc/google_grpc_creds.h | 4 +- .../common/grpc/async_client_manager_impl.cc | 26 ++++----- .../common/grpc/async_client_manager_impl.h | 14 ++--- .../common/grpc/google_async_client_impl.cc | 5 +- source/common/grpc/google_async_client_impl.h | 3 +- source/common/grpc/google_grpc_creds_impl.cc | 4 +- source/common/grpc/google_grpc_utils.cc | 10 ++-- source/common/grpc/google_grpc_utils.h | 5 +- .../common/upstream/cluster_manager_impl.cc | 17 +++--- source/common/upstream/cluster_manager_impl.h | 3 +- .../extensions/common/aws/signer_base_impl.h | 24 +++++--- .../extensions/common/aws/sigv4_signer_impl.h | 5 +- .../common/aws/sigv4a_signer_impl.h | 5 +- .../filters/http/aws_lambda/config.cc | 3 +- .../http/aws_request_signing/config.cc | 14 ++--- .../grpc_credentials/aws_iam/config.cc | 9 +-- .../grpc_credentials/aws_iam/config.h | 2 +- .../grpc_credentials/example/config.cc | 5 +- .../grpc_credentials/example/config.h | 2 +- .../file_based_metadata/config.cc | 10 ++-- .../file_based_metadata/config.h | 2 +- .../extensions/tracers/opencensus/config.cc | 3 +- .../opencensus/opencensus_tracer_impl.cc | 8 +-- .../opencensus/opencensus_tracer_impl.h | 5 +- .../config_validation/cluster_manager.cc | 2 +- source/server/server.cc | 2 +- test/common/grpc/BUILD | 3 + .../grpc/async_client_manager_benchmark.cc | 23 ++++---- .../grpc/async_client_manager_impl_test.cc | 14 +++-- .../grpc/google_async_client_impl_test.cc | 7 ++- .../grpc_client_integration_test_harness.h | 7 ++- .../upstream/cluster_manager_impl_test.cc | 55 ++++++++++--------- .../deferred_cluster_initialization_test.cc | 7 ++- test/common/upstream/test_cluster_manager.h | 32 ++++++----- .../clusters/aggregate/cluster_update_test.cc | 13 +++-- test/extensions/common/aws/BUILD | 2 + .../common/aws/sigv4_signer_impl_test.cc | 14 +++-- .../common/aws/sigv4a_signer_impl_test.cc | 8 ++- .../filters/http/ext_authz/config_test.cc | 11 ++-- test/extensions/tracers/opencensus/BUILD | 2 +- .../tracers/opencensus/tracer_test.cc | 17 +++--- test/mocks/server/server_factory_context.cc | 1 + test/mocks/server/server_factory_context.h | 2 +- test/per_file_coverage.sh | 2 +- 44 files changed, 227 insertions(+), 185 deletions(-) diff --git a/envoy/grpc/google_grpc_creds.h b/envoy/grpc/google_grpc_creds.h index 32dd71efdd75..1cc5188fb09f 100644 --- a/envoy/grpc/google_grpc_creds.h +++ b/envoy/grpc/google_grpc_creds.h @@ -2,10 +2,10 @@ #include -#include "envoy/api/api.h" #include "envoy/common/pure.h" #include "envoy/config/core/v3/grpc_service.pb.h" #include "envoy/config/typed_config.h" +#include "envoy/server/factory_context.h" #include "grpcpp/grpcpp.h" @@ -33,7 +33,7 @@ class GoogleGrpcCredentialsFactory : public Config::UntypedFactory { */ virtual std::shared_ptr getChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, - Api::Api& api) PURE; + Server::Configuration::CommonFactoryContext& context) PURE; std::string category() const override { return "envoy.grpc_credentials"; } }; diff --git a/source/common/grpc/async_client_manager_impl.cc b/source/common/grpc/async_client_manager_impl.cc index f2875e30466e..9d3bf65d132c 100644 --- a/source/common/grpc/async_client_manager_impl.cc +++ b/source/common/grpc/async_client_manager_impl.cc @@ -56,11 +56,11 @@ AsyncClientFactoryImpl::AsyncClientFactoryImpl(Upstream::ClusterManager& cm, } AsyncClientManagerImpl::AsyncClientManagerImpl( - Upstream::ClusterManager& cm, ThreadLocal::Instance& tls, TimeSource& time_source, - Api::Api& api, const StatNames& stat_names, + Upstream::ClusterManager& cm, ThreadLocal::Instance& tls, + Server::Configuration::CommonFactoryContext& context, const StatNames& stat_names, const envoy::config::bootstrap::v3::Bootstrap::GrpcAsyncClientManagerConfig& config) - : cm_(cm), tls_(tls), time_source_(time_source), api_(api), stat_names_(stat_names), - raw_async_client_cache_(tls_) { + : tls_(tls), cm_(cm), context_(context), stat_names_(stat_names), + raw_async_client_cache_(context.threadLocal()) { const auto max_cached_entry_idle_duration = std::chrono::milliseconds( PROTOBUF_GET_MS_OR_DEFAULT(config, max_cached_entry_idle_duration, DefaultEntryIdleDuration)); @@ -69,11 +69,10 @@ AsyncClientManagerImpl::AsyncClientManagerImpl( return std::make_shared(dispatcher, max_cached_entry_idle_duration); }); #ifdef ENVOY_GOOGLE_GRPC - google_tls_slot_ = tls.allocateSlot(); + google_tls_slot_ = context_.threadLocal().allocateSlot(); + Api::Api& api = context_.api(); google_tls_slot_->set( [&api](Event::Dispatcher&) { return std::make_shared(api); }); -#else - UNREFERENCED_PARAMETER(api_); #endif } @@ -83,17 +82,18 @@ RawAsyncClientPtr AsyncClientFactoryImpl::createUncachedRawAsyncClient() { GoogleAsyncClientFactoryImpl::GoogleAsyncClientFactoryImpl( ThreadLocal::Instance& tls, ThreadLocal::Slot* google_tls_slot, Stats::Scope& scope, - const envoy::config::core::v3::GrpcService& config, Api::Api& api, const StatNames& stat_names, + const envoy::config::core::v3::GrpcService& config, + Server::Configuration::CommonFactoryContext& context, const StatNames& stat_names, absl::Status& creation_status) : tls_(tls), google_tls_slot_(google_tls_slot), scope_(scope.createScope(fmt::format("grpc.{}.", config.google_grpc().stat_prefix()))), - config_(config), api_(api), stat_names_(stat_names) { + config_(config), factory_context_(context), stat_names_(stat_names) { #ifndef ENVOY_GOOGLE_GRPC UNREFERENCED_PARAMETER(tls_); UNREFERENCED_PARAMETER(google_tls_slot_); UNREFERENCED_PARAMETER(scope_); UNREFERENCED_PARAMETER(config_); - UNREFERENCED_PARAMETER(api_); + UNREFERENCED_PARAMETER(factory_context_); UNREFERENCED_PARAMETER(stat_names_); creation_status = absl::InvalidArgumentError("Google C++ gRPC client is not linked"); return; @@ -128,7 +128,7 @@ RawAsyncClientPtr GoogleAsyncClientFactoryImpl::createUncachedRawAsyncClient() { GoogleGenericStubFactory stub_factory; return std::make_unique( tls_.dispatcher(), google_tls_slot_->getTyped(), stub_factory, - scope_, config_, api_, stat_names_); + scope_, config_, factory_context_, stat_names_); #else return nullptr; #endif @@ -142,11 +142,11 @@ AsyncClientManagerImpl::factoryForGrpcService(const envoy::config::core::v3::Grp switch (config.target_specifier_case()) { case envoy::config::core::v3::GrpcService::TargetSpecifierCase::kEnvoyGrpc: factory = std::make_unique(cm_, config, skip_cluster_check, - time_source_, creation_status); + context_.timeSource(), creation_status); break; case envoy::config::core::v3::GrpcService::TargetSpecifierCase::kGoogleGrpc: factory = std::make_unique( - tls_, google_tls_slot_.get(), scope, config, api_, stat_names_, creation_status); + tls_, google_tls_slot_.get(), scope, config, context_, stat_names_, creation_status); break; case envoy::config::core::v3::GrpcService::TargetSpecifierCase::TARGET_SPECIFIER_NOT_SET: PANIC_DUE_TO_PROTO_UNSET; diff --git a/source/common/grpc/async_client_manager_impl.h b/source/common/grpc/async_client_manager_impl.h index 54c3cf6b2b00..c1591dd19395 100644 --- a/source/common/grpc/async_client_manager_impl.h +++ b/source/common/grpc/async_client_manager_impl.h @@ -33,7 +33,8 @@ class GoogleAsyncClientFactoryImpl : public AsyncClientFactory { public: GoogleAsyncClientFactoryImpl(ThreadLocal::Instance& tls, ThreadLocal::Slot* google_tls_slot, Stats::Scope& scope, - const envoy::config::core::v3::GrpcService& config, Api::Api& api, + const envoy::config::core::v3::GrpcService& config, + Server::Configuration::CommonFactoryContext& context, const StatNames& stat_names, absl::Status& creation_status); RawAsyncClientPtr createUncachedRawAsyncClient() override; @@ -42,15 +43,15 @@ class GoogleAsyncClientFactoryImpl : public AsyncClientFactory { ThreadLocal::Slot* google_tls_slot_; Stats::ScopeSharedPtr scope_; const envoy::config::core::v3::GrpcService config_; - Api::Api& api_; + Server::Configuration::CommonFactoryContext& factory_context_; const StatNames& stat_names_; }; class AsyncClientManagerImpl : public AsyncClientManager { public: AsyncClientManagerImpl( - Upstream::ClusterManager& cm, ThreadLocal::Instance& tls, TimeSource& time_source, - Api::Api& api, const StatNames& stat_names, + Upstream::ClusterManager& cm, ThreadLocal::Instance& tls, + Server::Configuration::CommonFactoryContext& context, const StatNames& stat_names, const envoy::config::bootstrap::v3::Bootstrap::GrpcAsyncClientManagerConfig& config); absl::StatusOr getOrCreateRawAsyncClient(const envoy::config::core::v3::GrpcService& config, Stats::Scope& scope, @@ -92,11 +93,10 @@ class AsyncClientManagerImpl : public AsyncClientManager { }; private: - Upstream::ClusterManager& cm_; ThreadLocal::Instance& tls_; + Upstream::ClusterManager& cm_; // Need to track outside of `context_` due to startup ordering. + Server::Configuration::CommonFactoryContext& context_; ThreadLocal::SlotPtr google_tls_slot_; - TimeSource& time_source_; - Api::Api& api_; const StatNames& stat_names_; ThreadLocal::TypedSlot raw_async_client_cache_; }; diff --git a/source/common/grpc/google_async_client_impl.cc b/source/common/grpc/google_async_client_impl.cc index eea658811b45..8c0e9a6d7e36 100644 --- a/source/common/grpc/google_async_client_impl.cc +++ b/source/common/grpc/google_async_client_impl.cc @@ -82,7 +82,8 @@ GoogleAsyncClientImpl::GoogleAsyncClientImpl(Event::Dispatcher& dispatcher, GoogleStubFactory& stub_factory, Stats::ScopeSharedPtr scope, const envoy::config::core::v3::GrpcService& config, - Api::Api& api, const StatNames& stat_names) + Server::Configuration::CommonFactoryContext& context, + const StatNames& stat_names) : dispatcher_(dispatcher), tls_(tls), stat_prefix_(config.google_grpc().stat_prefix()), target_uri_(config.google_grpc().target_uri()), scope_(scope), per_stream_buffer_limit_bytes_(PROTOBUF_GET_WRAPPED_OR_DEFAULT( @@ -94,7 +95,7 @@ GoogleAsyncClientImpl::GoogleAsyncClientImpl(Event::Dispatcher& dispatcher, // smart enough to do connection pooling and reuse with identical channel args, so this should // have comparable overhead to what we are doing in Grpc::AsyncClientImpl, i.e. no expensive // new connection implied. - std::shared_ptr channel = GoogleGrpcUtils::createChannel(config, api); + std::shared_ptr channel = GoogleGrpcUtils::createChannel(config, context); // Get state with try_to_connect = true to try connection at channel creation. // This is for initializing gRPC channel at channel creation. This GetState(true) is used to poke // the gRPC lb at channel creation, it doesn't have any effect no matter it succeeds or fails. But diff --git a/source/common/grpc/google_async_client_impl.h b/source/common/grpc/google_async_client_impl.h index 9fb4ad022fd8..45eceae18434 100644 --- a/source/common/grpc/google_async_client_impl.h +++ b/source/common/grpc/google_async_client_impl.h @@ -175,7 +175,8 @@ class GoogleAsyncClientImpl final : public RawAsyncClient, Logger::Loggable getChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, - Api::Api& api) override { - return CredsUtility::defaultChannelCredentials(grpc_service_config, api); + Server::Configuration::CommonFactoryContext& context) override { + return CredsUtility::defaultChannelCredentials(grpc_service_config, context.api()); } std::string name() const override { return "envoy.grpc_credentials.default"; } diff --git a/source/common/grpc/google_grpc_utils.cc b/source/common/grpc/google_grpc_utils.cc index fbe891306692..9ad8a0f5999c 100644 --- a/source/common/grpc/google_grpc_utils.cc +++ b/source/common/grpc/google_grpc_utils.cc @@ -26,7 +26,7 @@ namespace { std::shared_ptr getGoogleGrpcChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service, - Api::Api& api) { + Server::Configuration::CommonFactoryContext& context) { GoogleGrpcCredentialsFactory* credentials_factory = nullptr; const std::string& google_grpc_credentials_factory_name = grpc_service.google_grpc().credentials_factory_name(); @@ -41,7 +41,7 @@ getGoogleGrpcChannelCredentials(const envoy::config::core::v3::GrpcService& grpc throw EnvoyException(absl::StrCat("Unknown google grpc credentials factory: ", google_grpc_credentials_factory_name)); } - return credentials_factory->getChannelCredentials(grpc_service, api); + return credentials_factory->getChannelCredentials(grpc_service, context); } } // namespace @@ -133,8 +133,10 @@ GoogleGrpcUtils::channelArgsFromConfig(const envoy::config::core::v3::GrpcServic } std::shared_ptr -GoogleGrpcUtils::createChannel(const envoy::config::core::v3::GrpcService& config, Api::Api& api) { - std::shared_ptr creds = getGoogleGrpcChannelCredentials(config, api); +GoogleGrpcUtils::createChannel(const envoy::config::core::v3::GrpcService& config, + Server::Configuration::CommonFactoryContext& context) { + std::shared_ptr creds = + getGoogleGrpcChannelCredentials(config, context); const grpc::ChannelArguments args = channelArgsFromConfig(config); return CreateCustomChannel(config.google_grpc().target_uri(), creds, args); } diff --git a/source/common/grpc/google_grpc_utils.h b/source/common/grpc/google_grpc_utils.h index 859a61ccfff9..102bc0c3f25d 100644 --- a/source/common/grpc/google_grpc_utils.h +++ b/source/common/grpc/google_grpc_utils.h @@ -3,10 +3,10 @@ #include #include -#include "envoy/api/api.h" #include "envoy/buffer/buffer.h" #include "envoy/common/platform.h" #include "envoy/config/core/v3/grpc_service.pb.h" +#include "envoy/server/factory_context.h" #include "grpcpp/grpcpp.h" @@ -46,7 +46,8 @@ class GoogleGrpcUtils { * @return static std::shared_ptr a gRPC channel. */ static std::shared_ptr - createChannel(const envoy::config::core::v3::GrpcService& config, Api::Api& api); + createChannel(const envoy::config::core::v3::GrpcService& config, + Server::Configuration::CommonFactoryContext& context); }; } // namespace Grpc diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 4a2a4f076afa..41e490ae0271 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -295,12 +295,12 @@ void ClusterManagerInitHelper::setPrimaryClustersInitializedCb( ClusterManagerImpl::ClusterManagerImpl( 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, OptRef admin, - ProtobufMessage::ValidationContext& validation_context, Api::Api& api, - Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, - const Server::Instance& server) + Server::Configuration::CommonFactoryContext& context, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, + OptRef admin, ProtobufMessage::ValidationContext& validation_context, + Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, + Router::Context& router_context, const Server::Instance& server) : server_(server), factory_(factory), runtime_(runtime), stats_(stats), tls_(tls), random_(api.randomGenerator()), deferred_cluster_creation_(bootstrap.cluster_manager().enable_deferred_cluster_creation()), @@ -331,8 +331,7 @@ ClusterManagerImpl::ClusterManagerImpl( }); } async_client_manager_ = std::make_unique( - *this, tls, time_source_, api, grpc_context.statNames(), - bootstrap.grpc_async_client_manager_config()); + *this, tls, context, grpc_context.statNames(), bootstrap.grpc_async_client_manager_config()); const auto& cm_config = bootstrap.cluster_manager(); if (cm_config.has_outlier_detection()) { const std::string event_log_file_path = cm_config.outlier_detection().event_log_path(); @@ -2222,7 +2221,7 @@ void ClusterManagerImpl::ThreadLocalClusterManagerImpl::tcpConnPoolIsIdle( ClusterManagerPtr ProdClusterManagerFactory::clusterManagerFromProto( const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto cluster_manager_impl = std::unique_ptr{new ClusterManagerImpl( - bootstrap, *this, stats_, tls_, context_.runtime(), context_.localInfo(), + bootstrap, *this, context_, stats_, tls_, context_.runtime(), context_.localInfo(), context_.accessLogManager(), context_.mainThreadDispatcher(), context_.admin(), context_.messageValidationContext(), context_.api(), http_context_, context_.grpcContext(), context_.routerContext(), server_)}; diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index 0c0a0c857c87..9a093d7561dc 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -386,7 +386,8 @@ class ClusterManagerImpl : public ClusterManager, // ClusterManagerImpl's constructor should not be invoked directly; create instances from the // clusterManagerFromProto() static method. The init() method must be called after construction. ClusterManagerImpl(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, - ClusterManagerFactory& factory, Stats::Store& stats, + ClusterManagerFactory& factory, + Server::Configuration::CommonFactoryContext& context, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, diff --git a/source/extensions/common/aws/signer_base_impl.h b/source/extensions/common/aws/signer_base_impl.h index 7dff4a6aed2b..a8bb754facf2 100644 --- a/source/extensions/common/aws/signer_base_impl.h +++ b/source/extensions/common/aws/signer_base_impl.h @@ -63,18 +63,22 @@ using AwsSigningHeaderExclusionVector = std::vector { public: SignerBaseImpl(absl::string_view service_name, absl::string_view region, - const CredentialsProviderSharedPtr& credentials_provider, TimeSource& time_source, + const CredentialsProviderSharedPtr& credentials_provider, + Server::Configuration::CommonFactoryContext& context, const AwsSigningHeaderExclusionVector& matcher_config, const bool query_string = false, const uint16_t expiration_time = SignatureQueryParameterValues::DefaultExpiration) - : service_name_(service_name), region_(region), credentials_provider_(credentials_provider), - query_string_(query_string), expiration_time_(expiration_time), time_source_(time_source), + : service_name_(service_name), region_(region), + excluded_header_matchers_(defaultMatchers(context)), + credentials_provider_(credentials_provider), query_string_(query_string), + expiration_time_(expiration_time), time_source_(context.timeSource()), long_date_formatter_(std::string(SignatureConstants::LongDateFormat)), short_date_formatter_(std::string(SignatureConstants::ShortDateFormat)) { for (const auto& matcher : matcher_config) { excluded_header_matchers_.emplace_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); } } @@ -128,14 +132,16 @@ class SignerBaseImpl : public Signer, public Logger::Loggable { const std::map& signed_headers, const uint16_t expiration_time) const; - std::vector defaultMatchers() const { + std::vector + defaultMatchers(Server::Configuration::CommonFactoryContext& context) const { std::vector matcher_ptrs{}; for (const auto& header : default_excluded_headers_) { envoy::type::matcher::v3::StringMatcher m; m.set_exact(header); matcher_ptrs.emplace_back( - std::make_unique>( - m)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + m, context)); } return matcher_ptrs; } @@ -145,7 +151,7 @@ class SignerBaseImpl : public Signer, public Logger::Loggable { const std::vector default_excluded_headers_ = { Http::Headers::get().ForwardedFor.get(), Http::Headers::get().ForwardedProto.get(), "x-amzn-trace-id"}; - std::vector excluded_header_matchers_ = defaultMatchers(); + std::vector excluded_header_matchers_; CredentialsProviderSharedPtr credentials_provider_; const bool query_string_; const uint16_t expiration_time_; diff --git a/source/extensions/common/aws/sigv4_signer_impl.h b/source/extensions/common/aws/sigv4_signer_impl.h index f9e047ec7754..68b78dd8037f 100644 --- a/source/extensions/common/aws/sigv4_signer_impl.h +++ b/source/extensions/common/aws/sigv4_signer_impl.h @@ -41,11 +41,12 @@ using AwsSigningHeaderExclusionVector = std::vector AwsLambdaFilterFactory::createFilterFactor Extensions::Common::Aws::Utility::fetchMetadata); auto signer = std::make_shared( - service_name, region, std::move(credentials_provider), - server_context.mainThreadDispatcher().timeSource(), + service_name, region, std::move(credentials_provider), server_context, // TODO: extend API to allow specifying header exclusion. ref: // https://github.com/envoyproxy/envoy/pull/18998 Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}); diff --git a/source/extensions/filters/http/aws_request_signing/config.cc b/source/extensions/filters/http/aws_request_signing/config.cc index 4727c0133f10..c0de8b785e58 100644 --- a/source/extensions/filters/http/aws_request_signing/config.cc +++ b/source/extensions/filters/http/aws_request_signing/config.cc @@ -71,9 +71,8 @@ AwsRequestSigningFilterFactory::createFilterFactoryFromProtoTyped( if (config.signing_algorithm() == AwsRequestSigning_SigningAlgorithm_AWS_SIGV4A) { signer = std::make_unique( - config.service_name(), region, credentials_provider, - server_context.mainThreadDispatcher().timeSource(), matcher_config, query_string, - expiration_time); + config.service_name(), region, credentials_provider, server_context, matcher_config, + query_string, expiration_time); } else { // Verify that we have not specified a region set when using sigv4 algorithm if (isARegionSet(region)) { @@ -81,9 +80,8 @@ AwsRequestSigningFilterFactory::createFilterFactoryFromProtoTyped( "can be specified when using signing_algorithm: AWS_SIGV4A."); } signer = std::make_unique( - config.service_name(), region, credentials_provider, - server_context.mainThreadDispatcher().timeSource(), matcher_config, query_string, - expiration_time); + config.service_name(), region, credentials_provider, server_context, matcher_config, + query_string, expiration_time); } auto filter_config = @@ -137,7 +135,7 @@ AwsRequestSigningFilterFactory::createRouteSpecificFilterConfigTyped( AwsRequestSigning_SigningAlgorithm_AWS_SIGV4A) { signer = std::make_unique( per_route_config.aws_request_signing().service_name(), region, credentials_provider, - context.mainThreadDispatcher().timeSource(), matcher_config, query_string, expiration_time); + context, matcher_config, query_string, expiration_time); } else { // Verify that we have not specified a region set when using sigv4 algorithm if (isARegionSet(region)) { @@ -146,7 +144,7 @@ AwsRequestSigningFilterFactory::createRouteSpecificFilterConfigTyped( } signer = std::make_unique( per_route_config.aws_request_signing().service_name(), region, credentials_provider, - context.mainThreadDispatcher().timeSource(), matcher_config, query_string, expiration_time); + context, matcher_config, query_string, expiration_time); } return std::make_shared( diff --git a/source/extensions/grpc_credentials/aws_iam/config.cc b/source/extensions/grpc_credentials/aws_iam/config.cc index 4e8127a19749..fc9c11a68dfb 100644 --- a/source/extensions/grpc_credentials/aws_iam/config.cc +++ b/source/extensions/grpc_credentials/aws_iam/config.cc @@ -22,11 +22,12 @@ namespace GrpcCredentials { namespace AwsIam { std::shared_ptr AwsIamGrpcCredentialsFactory::getChannelCredentials( - const envoy::config::core::v3::GrpcService& grpc_service_config, Api::Api& api) { + const envoy::config::core::v3::GrpcService& grpc_service_config, + Server::Configuration::CommonFactoryContext& context) { const auto& google_grpc = grpc_service_config.google_grpc(); std::shared_ptr creds = - Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, api); + Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, context.api()); std::shared_ptr call_creds; for (const auto& credential : google_grpc.call_credentials()) { @@ -66,10 +67,10 @@ std::shared_ptr AwsIamGrpcCredentialsFactory::getChann // usage of AWS credentials common utils. Until then we are setting nullopt for server // factory context. auto credentials_provider = std::make_shared( - api, absl::nullopt /*Empty factory context*/, region, + context.api(), absl::nullopt /*Empty factory context*/, region, Common::Aws::Utility::fetchMetadata); auto signer = std::make_unique( - config.service_name(), region, credentials_provider, api.timeSource(), + config.service_name(), region, credentials_provider, context, // TODO: extend API to allow specifying header exclusion. ref: // https://github.com/envoyproxy/envoy/pull/18998 Common::Aws::AwsSigningHeaderExclusionVector{}); diff --git a/source/extensions/grpc_credentials/aws_iam/config.h b/source/extensions/grpc_credentials/aws_iam/config.h index 5997f61903de..16efbcead7c6 100644 --- a/source/extensions/grpc_credentials/aws_iam/config.h +++ b/source/extensions/grpc_credentials/aws_iam/config.h @@ -20,7 +20,7 @@ class AwsIamGrpcCredentialsFactory : public Grpc::GoogleGrpcCredentialsFactory { public: std::shared_ptr getChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, - Api::Api& api) override; + Server::Configuration::CommonFactoryContext& context) override; Envoy::ProtobufTypes::MessagePtr createEmptyConfigProto() { return std::make_unique(); diff --git a/source/extensions/grpc_credentials/example/config.cc b/source/extensions/grpc_credentials/example/config.cc index 2645fb07deea..18cebbe34ef0 100644 --- a/source/extensions/grpc_credentials/example/config.cc +++ b/source/extensions/grpc_credentials/example/config.cc @@ -13,10 +13,11 @@ namespace Example { std::shared_ptr AccessTokenExampleGrpcCredentialsFactory::getChannelCredentials( - const envoy::config::core::v3::GrpcService& grpc_service_config, Api::Api& api) { + const envoy::config::core::v3::GrpcService& grpc_service_config, + Server::Configuration::CommonFactoryContext& context) { const auto& google_grpc = grpc_service_config.google_grpc(); std::shared_ptr creds = - Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, api); + Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, context.api()); std::shared_ptr call_creds = nullptr; for (const auto& credential : google_grpc.call_credentials()) { switch (credential.credential_specifier_case()) { diff --git a/source/extensions/grpc_credentials/example/config.h b/source/extensions/grpc_credentials/example/config.h index 33cd85e6f425..28c860adcfaf 100644 --- a/source/extensions/grpc_credentials/example/config.h +++ b/source/extensions/grpc_credentials/example/config.h @@ -28,7 +28,7 @@ class AccessTokenExampleGrpcCredentialsFactory : public Grpc::GoogleGrpcCredenti public: std::shared_ptr getChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, - Api::Api& api) override; + Server::Configuration::CommonFactoryContext& context) override; std::string name() const override { return "envoy.grpc_credentials.access_token_example"; } }; diff --git a/source/extensions/grpc_credentials/file_based_metadata/config.cc b/source/extensions/grpc_credentials/file_based_metadata/config.cc index 266adbf96443..ba02ec399fd1 100644 --- a/source/extensions/grpc_credentials/file_based_metadata/config.cc +++ b/source/extensions/grpc_credentials/file_based_metadata/config.cc @@ -19,10 +19,11 @@ namespace FileBasedMetadata { std::shared_ptr FileBasedMetadataGrpcCredentialsFactory::getChannelCredentials( - const envoy::config::core::v3::GrpcService& grpc_service_config, Api::Api& api) { + const envoy::config::core::v3::GrpcService& grpc_service_config, + Server::Configuration::CommonFactoryContext& context) { const auto& google_grpc = grpc_service_config.google_grpc(); std::shared_ptr creds = - Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, api); + Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, context.api()); std::shared_ptr call_creds = nullptr; for (const auto& credential : google_grpc.call_credentials()) { switch (credential.credential_specifier_case()) { @@ -39,8 +40,9 @@ FileBasedMetadataGrpcCredentialsFactory::getChannelCredentials( const auto& file_based_metadata_config = Envoy::MessageUtil::downcastAndValidate< const envoy::config::grpc_credential::v3::FileBasedMetadataConfig&>( *file_based_metadata_config_message, ProtobufMessage::getNullValidationVisitor()); - std::shared_ptr new_call_creds = grpc::MetadataCredentialsFromPlugin( - std::make_unique(file_based_metadata_config, api)); + std::shared_ptr new_call_creds = + grpc::MetadataCredentialsFromPlugin(std::make_unique( + file_based_metadata_config, context.api())); if (call_creds == nullptr) { call_creds = new_call_creds; } else { diff --git a/source/extensions/grpc_credentials/file_based_metadata/config.h b/source/extensions/grpc_credentials/file_based_metadata/config.h index 2ff1c54a161f..778ad34db9a1 100644 --- a/source/extensions/grpc_credentials/file_based_metadata/config.h +++ b/source/extensions/grpc_credentials/file_based_metadata/config.h @@ -24,7 +24,7 @@ class FileBasedMetadataGrpcCredentialsFactory : public Grpc::GoogleGrpcCredentia public: std::shared_ptr getChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, - Api::Api& api) override; + Server::Configuration::CommonFactoryContext& context) override; Envoy::ProtobufTypes::MessagePtr createEmptyConfigProto() { return std::make_unique(); diff --git a/source/extensions/tracers/opencensus/config.cc b/source/extensions/tracers/opencensus/config.cc index bf33e76de248..d8f5283068a2 100644 --- a/source/extensions/tracers/opencensus/config.cc +++ b/source/extensions/tracers/opencensus/config.cc @@ -26,8 +26,7 @@ Tracing::DriverSharedPtr OpenCensusTracerFactory::createTracerDriverTyped( } } - driver_ = std::make_shared(proto_config, context.serverFactoryContext().localInfo(), - context.serverFactoryContext().api()); + driver_ = std::make_shared(proto_config, context.serverFactoryContext()); config_ = proto_config; return driver_; } diff --git a/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc b/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc index 5c4ea0b4dfba..8bde7883a704 100644 --- a/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc +++ b/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc @@ -253,8 +253,8 @@ void Span::setSampled(bool sampled) { span_.AddAnnotation("setSampled", {{"sampl } // namespace Driver::Driver(const envoy::config::trace::v3::OpenCensusConfig& oc_config, - const LocalInfo::LocalInfo& localinfo, Api::Api& api) - : oc_config_(oc_config), local_info_(localinfo) { + Server::Configuration::CommonFactoryContext& context) + : oc_config_(oc_config), local_info_(context.localInfo()) { // To give user a chance to correct initially invalid configuration and try to apply it once again // without a need to restart Envoy, validation checks must be done prior to any side effects. if (oc_config.stackdriver_exporter_enabled() && oc_config.has_stackdriver_grpc_service() && @@ -289,7 +289,7 @@ Driver::Driver(const envoy::config::trace::v3::OpenCensusConfig& oc_config, // address will be used. stackdriver_service.mutable_google_grpc()->set_target_uri(GoogleStackdriverTraceAddress); } - auto channel = Envoy::Grpc::GoogleGrpcUtils::createChannel(stackdriver_service, api); + auto channel = Envoy::Grpc::GoogleGrpcUtils::createChannel(stackdriver_service, context); // TODO(bianpengyuan): add tests for trace_service_stub and initial_metadata options with mock // stubs. opts.trace_service_stub = ::google::devtools::cloudtrace::v2::TraceService::NewStub(channel); @@ -322,7 +322,7 @@ Driver::Driver(const envoy::config::trace::v3::OpenCensusConfig& oc_config, #ifdef ENVOY_GOOGLE_GRPC const envoy::config::core::v3::GrpcService& ocagent_service = oc_config.ocagent_grpc_service(); - auto channel = Envoy::Grpc::GoogleGrpcUtils::createChannel(ocagent_service, api); + auto channel = Envoy::Grpc::GoogleGrpcUtils::createChannel(ocagent_service, context); opts.trace_service_stub = ::opencensus::proto::agent::trace::v1::TraceService::NewStub(channel); #else diff --git a/source/extensions/tracers/opencensus/opencensus_tracer_impl.h b/source/extensions/tracers/opencensus/opencensus_tracer_impl.h index db3da7856a86..3ca29cb71d30 100644 --- a/source/extensions/tracers/opencensus/opencensus_tracer_impl.h +++ b/source/extensions/tracers/opencensus/opencensus_tracer_impl.h @@ -1,8 +1,7 @@ #pragma once -#include "envoy/api/api.h" #include "envoy/config/trace/v3/opencensus.pb.h" -#include "envoy/local_info/local_info.h" +#include "envoy/server/factory_context.h" #include "envoy/tracing/trace_driver.h" #include "source/common/common/logger.h" @@ -18,7 +17,7 @@ namespace OpenCensus { class Driver : public Tracing::Driver, Logger::Loggable { public: Driver(const envoy::config::trace::v3::OpenCensusConfig& oc_config, - const LocalInfo::LocalInfo& localinfo, Api::Api& api); + Server::Configuration::CommonFactoryContext& context); // Tracing::Driver Tracing::SpanPtr startSpan(const Tracing::Config& config, Tracing::TraceContext& trace_context, diff --git a/source/server/config_validation/cluster_manager.cc b/source/server/config_validation/cluster_manager.cc index 719919f25874..cc1834be90a9 100644 --- a/source/server/config_validation/cluster_manager.cc +++ b/source/server/config_validation/cluster_manager.cc @@ -11,7 +11,7 @@ namespace Upstream { ClusterManagerPtr ValidationClusterManagerFactory::clusterManagerFromProto( const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto cluster_manager = std::unique_ptr{new ValidationClusterManager( - bootstrap, *this, stats_, tls_, context_.runtime(), context_.localInfo(), + bootstrap, *this, context_, stats_, tls_, context_.runtime(), context_.localInfo(), context_.accessLogManager(), context_.mainThreadDispatcher(), context_.admin(), context_.messageValidationContext(), context_.api(), http_context_, context_.grpcContext(), context_.routerContext(), server_)}; diff --git a/source/server/server.cc b/source/server/server.cc index a570ccf16cce..fc3a50beeed8 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -826,7 +826,7 @@ void InstanceBase::onRuntimeReady() { if (bootstrap_.has_hds_config()) { const auto& hds_config = bootstrap_.hds_config(); async_client_manager_ = std::make_unique( - *config_.clusterManager(), thread_local_, time_source_, *api_, grpc_context_.statNames(), + *config_.clusterManager(), thread_local_, server_contexts_, grpc_context_.statNames(), bootstrap_.grpc_async_client_manager_config()); TRY_ASSERT_MAIN_THREAD { THROW_IF_NOT_OK(Config::Utility::checkTransportVersion(hds_config)); diff --git a/test/common/grpc/BUILD b/test/common/grpc/BUILD index cbf20b2ab3c4..ec28b9cea693 100644 --- a/test/common/grpc/BUILD +++ b/test/common/grpc/BUILD @@ -34,6 +34,7 @@ envoy_cc_test( deps = [ "//source/common/api:api_lib", "//source/common/grpc:async_client_manager_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/stats:stats_mocks", "//test/mocks/thread_local:thread_local_mocks", "//test/mocks/upstream:cluster_manager_mocks", @@ -113,6 +114,7 @@ envoy_cc_test( "//source/common/stats:stats_lib", "//source/common/tracing:http_tracer_lib", "//test/mocks/grpc:grpc_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/tracing:tracing_mocks", "//test/proto:helloworld_proto_cc_proto", "//test/test_common:test_time_lib", @@ -227,6 +229,7 @@ envoy_cc_benchmark_binary( deps = [ "//source/common/api:api_lib", "//source/common/grpc:async_client_manager_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/stats:stats_mocks", "//test/mocks/thread_local:thread_local_mocks", "//test/mocks/upstream:cluster_manager_mocks", diff --git a/test/common/grpc/async_client_manager_benchmark.cc b/test/common/grpc/async_client_manager_benchmark.cc index 5b62f6c270b5..05c2b74b829a 100644 --- a/test/common/grpc/async_client_manager_benchmark.cc +++ b/test/common/grpc/async_client_manager_benchmark.cc @@ -9,6 +9,7 @@ #include "source/common/grpc/async_client_manager_impl.h" #include "test/benchmark/main.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stats/mocks.h" #include "test/mocks/thread_local/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -27,20 +28,20 @@ namespace { class AsyncClientManagerImplTest { public: - AsyncClientManagerImplTest() - : api_(Api::createApiForTest()), stat_names_(scope_.symbolTable()), - async_client_manager_( - cm_, tls_, test_time_.timeSystem(), *api_, stat_names_, - envoy::config::bootstrap::v3::Bootstrap::GrpcAsyncClientManagerConfig()) {} + AsyncClientManagerImplTest() : api_(Api::createApiForTest()), stat_names_(scope_.symbolTable()) { + ON_CALL(context_, api()).WillByDefault(testing::ReturnRef(*api_)); + async_client_manager_ = std::make_unique( + cm_, context_.threadLocal(), context_, stat_names_, + envoy::config::bootstrap::v3::Bootstrap::GrpcAsyncClientManagerConfig()); + } Upstream::MockClusterManager cm_; - NiceMock tls_; + NiceMock context_; Stats::MockStore store_; Stats::MockScope& scope_{store_.mockScope()}; - DangerousDeprecatedTestTime test_time_; Api::ApiPtr api_; StatNames stat_names_; - AsyncClientManagerImpl async_client_manager_; + std::unique_ptr async_client_manager_; }; void testGetOrCreateAsyncClientWithConfig(::benchmark::State& state) { @@ -54,7 +55,7 @@ void testGetOrCreateAsyncClientWithConfig(::benchmark::State& state) { for (int i = 0; i < 1000; i++) { RawAsyncClientSharedPtr foo_client0 = async_client_man_test.async_client_manager_ - .getOrCreateRawAsyncClient(grpc_service, async_client_man_test.scope_, true) + ->getOrCreateRawAsyncClient(grpc_service, async_client_man_test.scope_, true) .value(); } } @@ -72,8 +73,8 @@ void testGetOrCreateAsyncClientWithHashConfig(::benchmark::State& state) { for (int i = 0; i < 1000; i++) { RawAsyncClientSharedPtr foo_client0 = async_client_man_test.async_client_manager_ - .getOrCreateRawAsyncClientWithHashKey(config_with_hash_key_a, - async_client_man_test.scope_, true) + ->getOrCreateRawAsyncClientWithHashKey(config_with_hash_key_a, + async_client_man_test.scope_, true) .value(); } } diff --git a/test/common/grpc/async_client_manager_impl_test.cc b/test/common/grpc/async_client_manager_impl_test.cc index 8cbcd82c5c5d..13581c6571f5 100644 --- a/test/common/grpc/async_client_manager_impl_test.cc +++ b/test/common/grpc/async_client_manager_impl_test.cc @@ -9,6 +9,7 @@ #include "source/common/event/dispatcher_impl.h" #include "source/common/grpc/async_client_manager_impl.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stats/mocks.h" #include "test/mocks/thread_local/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -202,21 +203,26 @@ class AsyncClientManagerImplTest : public testing::Test { : api_(Api::createApiForTest(time_system_)), dispatcher_(api_->allocateDispatcher("test_grpc_manager")), stat_names_(scope_.symbolTable()) { - tls_.setDispatcher(dispatcher_.get()); + context_.thread_local_.setDispatcher(dispatcher_.get()); } void initialize(absl::optional config = absl::nullopt) { + ON_CALL(context_, clusterManager()).WillByDefault(testing::ReturnRef(cm_)); + ON_CALL(context_, mainThreadDispatcher()).WillByDefault(testing::ReturnRef(*dispatcher_)); + ON_CALL(context_, timeSource()).WillByDefault(testing::ReturnRef(time_system_)); + ON_CALL(context_, api()).WillByDefault(testing::ReturnRef(*api_)); if (config.has_value()) { async_client_manager_ = std::make_unique( - cm_, tls_, time_system_, *api_, stat_names_, config.value()); + cm_, context_.threadLocal(), context_, stat_names_, config.value()); } else { async_client_manager_ = std::make_unique( - cm_, tls_, time_system_, *api_, stat_names_, Bootstrap::GrpcAsyncClientManagerConfig()); + cm_, context_.threadLocal(), context_, stat_names_, + Bootstrap::GrpcAsyncClientManagerConfig()); } } + NiceMock context_; Upstream::MockClusterManager cm_; - NiceMock tls_; Stats::MockStore store_; Stats::MockScope& scope_{store_.mockScope()}; Event::SimulatedTimeSystem time_system_; diff --git a/test/common/grpc/google_async_client_impl_test.cc b/test/common/grpc/google_async_client_impl_test.cc index d0d3f7d5e67b..7e8f48655393 100644 --- a/test/common/grpc/google_async_client_impl_test.cc +++ b/test/common/grpc/google_async_client_impl_test.cc @@ -9,6 +9,7 @@ #include "source/common/stream_info/stream_info_impl.h" #include "test/mocks/grpc/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/tracing/mocks.h" #include "test/proto/helloworld.pb.h" #include "test/test_common/test_time.h" @@ -57,6 +58,7 @@ class EnvoyGoogleAsyncClientImplTest : public testing::Test { method_descriptor_(helloworld::Greeter::descriptor()->FindMethodByName("SayHello")), stat_names_(scope_->symbolTable()) { + ON_CALL(context_, api()).WillByDefault(testing::ReturnRef(*api_)); auto* google_grpc = config_.mutable_google_grpc(); google_grpc->set_target_uri("fake_address"); google_grpc->set_stat_prefix("test_cluster"); @@ -70,7 +72,7 @@ class EnvoyGoogleAsyncClientImplTest : public testing::Test { virtual void initialize() { grpc_client_ = std::make_unique(*dispatcher_, *tls_, stub_factory_, - scope_, config_, *api_, stat_names_); + scope_, config_, context_, stat_names_); } envoy::config::core::v3::GrpcService config_; @@ -78,6 +80,7 @@ class EnvoyGoogleAsyncClientImplTest : public testing::Test { Stats::IsolatedStoreImpl stats_store_; Api::ApiPtr api_; Event::DispatcherPtr dispatcher_; + NiceMock context_; Stats::ScopeSharedPtr scope_; GoogleAsyncClientThreadLocalPtr tls_; MockStubFactory stub_factory_; @@ -180,7 +183,7 @@ class EnvoyGoogleLessMockedAsyncClientImplTest : public EnvoyGoogleAsyncClientIm public: void initialize() override { grpc_client_ = std::make_unique(*dispatcher_, *tls_, real_stub_factory_, - scope_, config_, *api_, stat_names_); + scope_, config_, context_, stat_names_); } GoogleGenericStubFactory real_stub_factory_; diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 0b52d9c55e82..f6a3363eb24b 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -368,9 +368,9 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { #ifdef ENVOY_GOOGLE_GRPC google_tls_ = std::make_unique(*api_); GoogleGenericStubFactory stub_factory; - return std::make_unique(*dispatcher_, *google_tls_, stub_factory, - stats_scope_, createGoogleGrpcConfig(), *api_, - google_grpc_stat_names_); + return std::make_unique( + *dispatcher_, *google_tls_, stub_factory, stats_scope_, createGoogleGrpcConfig(), + server_factory_context_, google_grpc_stat_names_); #else PANIC("reached unexpected code"); #endif @@ -533,6 +533,7 @@ class GrpcSslClientIntegrationTest : public GrpcClientIntegrationTest { public: GrpcSslClientIntegrationTest() { ON_CALL(factory_context_.server_context_, api()).WillByDefault(ReturnRef(*api_)); + ON_CALL(server_factory_context_, api()).WillByDefault(ReturnRef(*api_)); } void TearDown() override { // Reset some state in the superclass before we destruct context_manager_ in our destructor, it diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 04b65bd5c1db..17644f883a1c 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -105,20 +105,18 @@ class MockedUpdatedClusterManagerImpl : public TestClusterManagerImpl { public: using TestClusterManagerImpl::TestClusterManagerImpl; - 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, - Router::Context& router_context, Server::Instance& server) - : TestClusterManagerImpl(bootstrap, factory, stats, tls, runtime, local_info, log_manager, - main_thread_dispatcher, admin, validation_context, api, http_context, - grpc_context, router_context, server), + MockedUpdatedClusterManagerImpl( + const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, + Server::Configuration::CommonFactoryContext& factory_context, 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, Router::Context& router_context, + Server::Instance& server) + : TestClusterManagerImpl(bootstrap, factory, factory_context, stats, tls, runtime, local_info, + log_manager, main_thread_dispatcher, admin, validation_context, api, + http_context, grpc_context, router_context, server), local_cluster_update_(local_cluster_update), local_hosts_removed_(local_hosts_removed) {} protected: @@ -161,6 +159,7 @@ class MockGrpcMuxFactory : public Config::MuxFactory { class UpdateOverrideClusterManagerImpl : public TestClusterManagerImpl { public: UpdateOverrideClusterManagerImpl(const Bootstrap& bootstrap, ClusterManagerFactory& factory, + Server::Configuration::CommonFactoryContext& factory_context, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, @@ -169,9 +168,9 @@ class UpdateOverrideClusterManagerImpl : public TestClusterManagerImpl { Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, Server::Instance& server) - : TestClusterManagerImpl(bootstrap, factory, stats, tls, runtime, local_info, log_manager, - main_thread_dispatcher, admin, validation_context, api, http_context, - grpc_context, router_context, server) {} + : TestClusterManagerImpl(bootstrap, factory, factory_context, stats, tls, runtime, local_info, + log_manager, main_thread_dispatcher, admin, validation_context, api, + http_context, grpc_context, router_context, server) {} protected: void postThreadLocalClusterUpdate(ClusterManagerCluster& cluster, @@ -208,9 +207,10 @@ class ClusterManagerImplTest : public testing::Test { virtual void create(const Bootstrap& bootstrap) { cluster_manager_ = TestClusterManagerImpl::createAndInit( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *factory_.api_, http_context_, grpc_context_, router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, http_context_, grpc_context_, router_context_, + server_); cluster_manager_->setPrimaryClustersInitializedCb([this, bootstrap]() { THROW_IF_NOT_OK(cluster_manager_->initializeSecondaryClusters(bootstrap)); }); @@ -275,18 +275,19 @@ class ClusterManagerImplTest : public testing::Test { const auto& bootstrap = parseBootstrapFromV3Yaml(yaml); cluster_manager_ = std::make_unique( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *factory_.api_, local_cluster_update_, local_hosts_removed_, http_context_, grpc_context_, - router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, local_cluster_update_, local_hosts_removed_, + http_context_, grpc_context_, router_context_, server_); THROW_IF_NOT_OK(cluster_manager_->init(bootstrap)); } void createWithUpdateOverrideClusterManager(const Bootstrap& bootstrap) { cluster_manager_ = std::make_unique( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *factory_.api_, http_context_, grpc_context_, router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, http_context_, grpc_context_, router_context_, + server_); THROW_IF_NOT_OK(cluster_manager_->init(bootstrap)); } diff --git a/test/common/upstream/deferred_cluster_initialization_test.cc b/test/common/upstream/deferred_cluster_initialization_test.cc index a9701fc77c25..12fb3373e7cc 100644 --- a/test/common/upstream/deferred_cluster_initialization_test.cc +++ b/test/common/upstream/deferred_cluster_initialization_test.cc @@ -63,9 +63,10 @@ class DeferredClusterInitializationTest : public testing::TestWithParam { void create(const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { cluster_manager_ = TestClusterManagerImpl::createAndInit( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *factory_.api_, http_context_, grpc_context_, router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, http_context_, grpc_context_, router_context_, + server_); cluster_manager_->setPrimaryClustersInitializedCb([this, bootstrap]() { THROW_IF_NOT_OK(cluster_manager_->initializeSecondaryClusters(bootstrap)); }); diff --git a/test/common/upstream/test_cluster_manager.h b/test/common/upstream/test_cluster_manager.h index a8e232eacb9e..d05bfc493376 100644 --- a/test/common/upstream/test_cluster_manager.h +++ b/test/common/upstream/test_cluster_manager.h @@ -162,17 +162,18 @@ class TestClusterManagerFactory : public ClusterManagerFactory { // clusters, which is necessary in order to call updateHosts on the priority set. class TestClusterManagerImpl : public ClusterManagerImpl { public: - static std::unique_ptr - createAndInit(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, Http::Context& http_context, Grpc::Context& grpc_context, - Router::Context& router_context, Server::Instance& server) { - auto cluster_manager = std::unique_ptr{new TestClusterManagerImpl( - bootstrap, factory, stats, tls, runtime, local_info, log_manager, main_thread_dispatcher, - admin, validation_context, api, http_context, grpc_context, router_context, server)}; + static std::unique_ptr createAndInit( + const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, + Server::Configuration::CommonFactoryContext& context, 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, + Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, + Server::Instance& server) { + auto cluster_manager = std::unique_ptr{ + new TestClusterManagerImpl(bootstrap, factory, context, stats, tls, runtime, local_info, + log_manager, main_thread_dispatcher, admin, validation_context, + api, http_context, grpc_context, router_context, server)}; THROW_IF_NOT_OK(cluster_manager->init(bootstrap)); return cluster_manager; } @@ -205,7 +206,8 @@ class TestClusterManagerImpl : public ClusterManagerImpl { using ClusterManagerImpl::ClusterManagerImpl; TestClusterManagerImpl(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, - ClusterManagerFactory& factory, Stats::Store& stats, + ClusterManagerFactory& factory, + Server::Configuration::CommonFactoryContext& context, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, @@ -213,9 +215,9 @@ class TestClusterManagerImpl : public ClusterManagerImpl { ProtobufMessage::ValidationContext& validation_context, Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, Server::Instance& server) - : ClusterManagerImpl(bootstrap, factory, stats, tls, runtime, local_info, log_manager, - main_thread_dispatcher, admin, validation_context, api, http_context, - grpc_context, router_context, server) {} + : ClusterManagerImpl(bootstrap, factory, context, stats, tls, runtime, local_info, + log_manager, main_thread_dispatcher, admin, validation_context, api, + http_context, grpc_context, router_context, server) {} }; } // namespace Upstream diff --git a/test/extensions/clusters/aggregate/cluster_update_test.cc b/test/extensions/clusters/aggregate/cluster_update_test.cc index 281cd49b87e5..a432877c001b 100644 --- a/test/extensions/clusters/aggregate/cluster_update_test.cc +++ b/test/extensions/clusters/aggregate/cluster_update_test.cc @@ -43,9 +43,10 @@ class AggregateClusterUpdateTest : public Event::TestUsingSimulatedTime, const bool use_deferred_cluster = GetParam(); bootstrap.mutable_cluster_manager()->set_enable_deferred_cluster_creation(use_deferred_cluster); cluster_manager_ = Upstream::TestClusterManagerImpl::createAndInit( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *factory_.api_, http_context_, grpc_context_, router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, http_context_, grpc_context_, router_context_, + server_); ASSERT_TRUE(cluster_manager_->initializeSecondaryClusters(bootstrap).ok()); EXPECT_EQ(cluster_manager_->activeClusters().size(), 1); cluster_ = cluster_manager_->getThreadLocalCluster("aggregate_cluster"); @@ -278,9 +279,9 @@ TEST_P(AggregateClusterUpdateTest, InitializeAggregateClusterAfterOtherClusters) auto bootstrap = parseBootstrapFromV2Yaml(config); cluster_manager_ = Upstream::TestClusterManagerImpl::createAndInit( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.local_info_, - log_manager_, factory_.dispatcher_, admin_, validation_context_, *factory_.api_, - http_context_, grpc_context_, router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, http_context_, grpc_context_, router_context_, server_); ASSERT_TRUE(cluster_manager_->initializeSecondaryClusters(bootstrap).ok()); EXPECT_EQ(cluster_manager_->activeClusters().size(), 2); cluster_ = cluster_manager_->getThreadLocalCluster("aggregate_cluster"); diff --git a/test/extensions/common/aws/BUILD b/test/extensions/common/aws/BUILD index 69b7469b8ae1..0923c214b656 100644 --- a/test/extensions/common/aws/BUILD +++ b/test/extensions/common/aws/BUILD @@ -31,6 +31,7 @@ envoy_cc_test( "//source/common/http:message_lib", "//source/extensions/common/aws:sigv4_signer_impl_lib", "//test/extensions/common/aws:aws_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:simulated_time_system_lib", "//test/test_common:utility_lib", ], @@ -45,6 +46,7 @@ envoy_cc_test( "//source/extensions/common/aws:sigv4a_key_derivation_lib", "//source/extensions/common/aws:sigv4a_signer_impl_lib", "//test/extensions/common/aws:aws_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:simulated_time_system_lib", "//test/test_common:utility_lib", ], diff --git a/test/extensions/common/aws/sigv4_signer_impl_test.cc b/test/extensions/common/aws/sigv4_signer_impl_test.cc index cab15cb91833..eb555a153ef3 100644 --- a/test/extensions/common/aws/sigv4_signer_impl_test.cc +++ b/test/extensions/common/aws/sigv4_signer_impl_test.cc @@ -4,11 +4,13 @@ #include "source/extensions/common/aws/utility.h" #include "test/extensions/common/aws/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" using testing::NiceMock; using testing::Return; +using testing::ReturnRef; namespace Envoy { namespace Extensions { @@ -21,11 +23,12 @@ class SigV4SignerImplTest : public testing::Test { SigV4SignerImplTest() : credentials_provider_(new NiceMock()), message_(new Http::RequestMessageImpl()), - signer_("service", "region", CredentialsProviderSharedPtr{credentials_provider_}, - time_system_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}), + signer_("service", "region", CredentialsProviderSharedPtr{credentials_provider_}, context_, + Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}), credentials_("akid", "secret"), token_credentials_("akid", "secret", "token") { // 20180102T030405Z time_system_.setSystemTime(std::chrono::milliseconds(1514862245000)); + ON_CALL(context_, timeSystem()).WillByDefault(ReturnRef(time_system_)); } void addMethod(const std::string& method) { message_->headers().setMethod(method); } @@ -49,7 +52,7 @@ class SigV4SignerImplTest : public testing::Test { headers.addCopy(Http::LowerCaseString("host"), "www.example.com"); SigV4SignerImpl signer(service_name, "region", - CredentialsProviderSharedPtr{credentials_provider}, time_system_, + CredentialsProviderSharedPtr{credentials_provider}, context_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}, false, 5); if (use_unsigned_payload) { signer.signUnsignedPayload(headers, override_region); @@ -79,7 +82,7 @@ class SigV4SignerImplTest : public testing::Test { } SigV4SignerImpl signer(service_name, "region", - CredentialsProviderSharedPtr{credentials_provider}, time_system_, + CredentialsProviderSharedPtr{credentials_provider}, context_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}, true, 5); signer.signUnsignedPayload(extra_headers, override_region); @@ -90,6 +93,7 @@ class SigV4SignerImplTest : public testing::Test { NiceMock* credentials_provider_; Event::SimulatedTimeSystem time_system_; + NiceMock context_; Http::RequestMessagePtr message_; SigV4SignerImpl signer_; Credentials credentials_; @@ -268,7 +272,7 @@ TEST_F(SigV4SignerImplTest, QueryStringDefault5s) { headers.addCopy(Http::LowerCaseString("host"), "example.service.zz"); headers.addCopy("testheader", "value1"); SigV4SignerImpl querysigner("service", "region", - CredentialsProviderSharedPtr{credentials_provider}, time_system_, + CredentialsProviderSharedPtr{credentials_provider}, context_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}, true); querysigner.signUnsignedPayload(headers); diff --git a/test/extensions/common/aws/sigv4a_signer_impl_test.cc b/test/extensions/common/aws/sigv4a_signer_impl_test.cc index 5f92d3e969f8..b6e9e2f2984c 100644 --- a/test/extensions/common/aws/sigv4a_signer_impl_test.cc +++ b/test/extensions/common/aws/sigv4a_signer_impl_test.cc @@ -8,11 +8,13 @@ #include "source/extensions/common/aws/utility.h" #include "test/extensions/common/aws/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" using testing::NiceMock; using testing::Return; +using testing::ReturnRef; namespace Envoy { namespace Extensions { @@ -28,6 +30,7 @@ class SigV4ASignerImplTest : public testing::Test { token_credentials_("akid", "secret", "token") { // 20180102T030405Z time_system_.setSystemTime(std::chrono::milliseconds(1514862245000)); + ON_CALL(context_, timeSystem()).WillByDefault(ReturnRef(time_system_)); } void addMethod(const std::string& method) { message_->headers().setMethod(method); } @@ -54,7 +57,7 @@ class SigV4ASignerImplTest : public testing::Test { return SigV4ASignerImpl{"service", "region", getTestCredentialsProvider(), - time_system_, + context_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}, query_string, expiration_time}; @@ -133,6 +136,7 @@ class SigV4ASignerImplTest : public testing::Test { } NiceMock* credentials_provider_; Event::SimulatedTimeSystem time_system_; + NiceMock context_; Http::RequestMessagePtr message_; Credentials credentials_; Credentials token_credentials_; @@ -478,7 +482,7 @@ TEST_F(SigV4ASignerImplTest, QueryStringDefault5s) { headers.setPath("/example/path"); headers.addCopy(Http::LowerCaseString("host"), "example.service.zz"); headers.addCopy("testheader", "value1"); - SigV4ASignerImpl querysigner("service", "region", getTestCredentialsProvider(), time_system_, + SigV4ASignerImpl querysigner("service", "region", getTestCredentialsProvider(), context_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}, true); querysigner.signUnsignedPayload(headers); diff --git a/test/extensions/filters/http/ext_authz/config_test.cc b/test/extensions/filters/http/ext_authz/config_test.cc index 62909863a5ec..dcdfc7f81ad5 100644 --- a/test/extensions/filters/http/ext_authz/config_test.cc +++ b/test/extensions/filters/http/ext_authz/config_test.cc @@ -31,10 +31,10 @@ namespace ExtAuthz { class TestAsyncClientManagerImpl : public Grpc::AsyncClientManagerImpl { public: TestAsyncClientManagerImpl(Upstream::ClusterManager& cm, ThreadLocal::Instance& tls, - TimeSource& time_source, Api::Api& api, + Server::Configuration::CommonFactoryContext& context, const Grpc::StatNames& stat_names, const Bootstrap::GrpcAsyncClientManagerConfig& config) - : Grpc::AsyncClientManagerImpl(cm, tls, time_source, api, stat_names, config) {} + : Grpc::AsyncClientManagerImpl(cm, tls, context, stat_names, config) {} absl::StatusOr factoryForGrpcService(const envoy::config::core::v3::GrpcService&, Stats::Scope&, bool) override { return std::make_unique>(); @@ -46,10 +46,13 @@ class ExtAuthzFilterTest : public Event::TestUsingSimulatedTime, public testing::Test { public: ExtAuthzFilterTest() : RealThreadsTestHelper(5), stat_names_(symbol_table_) { + ON_CALL(context_.server_factory_context_, threadLocal()) + .WillByDefault(testing::ReturnRef(tls())); + ON_CALL(context_.server_factory_context_, api()).WillByDefault(testing::ReturnRef(api())); runOnMainBlocking([&]() { async_client_manager_ = std::make_unique( - context_.server_factory_context_.cluster_manager_, tls(), api().timeSource(), api(), - stat_names_, Bootstrap::GrpcAsyncClientManagerConfig()); + context_.server_factory_context_.cluster_manager_, tls(), + context_.server_factory_context_, stat_names_, Bootstrap::GrpcAsyncClientManagerConfig()); }); } diff --git a/test/extensions/tracers/opencensus/BUILD b/test/extensions/tracers/opencensus/BUILD index 64e7311cec58..53cf89ab6677 100644 --- a/test/extensions/tracers/opencensus/BUILD +++ b/test/extensions/tracers/opencensus/BUILD @@ -20,7 +20,7 @@ envoy_extension_cc_test( deps = [ "//source/extensions/tracers/opencensus:opencensus_tracer_impl", "//test/mocks/http:http_mocks", - "//test/mocks/local_info:local_info_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/stream_info:stream_info_mocks", "//test/mocks/tracing:tracing_mocks", "@envoy_api//envoy/config/trace/v3:pkg_cc_proto", diff --git a/test/extensions/tracers/opencensus/tracer_test.cc b/test/extensions/tracers/opencensus/tracer_test.cc index 0548b99bc569..367420d5bf73 100644 --- a/test/extensions/tracers/opencensus/tracer_test.cc +++ b/test/extensions/tracers/opencensus/tracer_test.cc @@ -11,7 +11,7 @@ #include "source/extensions/tracers/opencensus/opencensus_tracer_impl.h" #include "test/mocks/http/mocks.h" -#include "test/mocks/local_info/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/tracing/mocks.h" @@ -102,9 +102,8 @@ void registerSpanCatcher() { TEST(OpenCensusTracerTest, Span) { registerSpanCatcher(); OpenCensusConfig oc_config; - NiceMock local_info; - std::unique_ptr driver( - new OpenCensus::Driver(oc_config, local_info, *Api::createApiForTest())); + NiceMock context; + std::unique_ptr driver(new OpenCensus::Driver(oc_config, context)); NiceMock config; Tracing::TestTraceContextImpl request_headers{ @@ -189,7 +188,6 @@ void testIncomingHeaders( const std::initializer_list>& headers) { registerSpanCatcher(); OpenCensusConfig oc_config; - NiceMock local_info; oc_config.add_incoming_trace_context(OpenCensusConfig::NONE); oc_config.add_incoming_trace_context(OpenCensusConfig::B3); oc_config.add_incoming_trace_context(OpenCensusConfig::TRACE_CONTEXT); @@ -200,8 +198,8 @@ void testIncomingHeaders( oc_config.add_outgoing_trace_context(OpenCensusConfig::TRACE_CONTEXT); oc_config.add_outgoing_trace_context(OpenCensusConfig::GRPC_TRACE_BIN); oc_config.add_outgoing_trace_context(OpenCensusConfig::CLOUD_TRACE_CONTEXT); - std::unique_ptr driver( - new OpenCensus::Driver(oc_config, local_info, *Api::createApiForTest())); + NiceMock context; + std::unique_ptr driver(new OpenCensus::Driver(oc_config, context)); NiceMock config; Tracing::TestTraceContextImpl request_headers{ {":path", "/"}, @@ -291,9 +289,8 @@ namespace { // the exporter (either zero or one). int samplerTestHelper(const OpenCensusConfig& oc_config) { registerSpanCatcher(); - NiceMock local_info; - std::unique_ptr driver( - new OpenCensus::Driver(oc_config, local_info, *Api::createApiForTest())); + NiceMock context; + std::unique_ptr driver(new OpenCensus::Driver(oc_config, context)); auto span = ::opencensus::trace::Span::StartSpan("test_span"); span.End(); // Retrieve SpanData from the OpenCensus trace exporter. diff --git a/test/mocks/server/server_factory_context.cc b/test/mocks/server/server_factory_context.cc index 6de698d5c0a2..fb12fbe4f8b8 100644 --- a/test/mocks/server/server_factory_context.cc +++ b/test/mocks/server/server_factory_context.cc @@ -23,6 +23,7 @@ MockServerFactoryContext::MockServerFactoryContext() ON_CALL(*this, admin()).WillByDefault(Return(OptRef{admin_})); ON_CALL(*this, api()).WillByDefault(ReturnRef(api_)); ON_CALL(*this, timeSource()).WillByDefault(ReturnRef(time_system_)); + ON_CALL(*this, timeSystem()).WillByDefault(ReturnRef(time_system_)); ON_CALL(*this, messageValidationContext()).WillByDefault(ReturnRef(validation_context_)); ON_CALL(*this, messageValidationVisitor()) .WillByDefault(ReturnRef(ProtobufMessage::getStrictValidationVisitor())); diff --git a/test/mocks/server/server_factory_context.h b/test/mocks/server/server_factory_context.h index 128afe221c26..d9670dead628 100644 --- a/test/mocks/server/server_factory_context.h +++ b/test/mocks/server/server_factory_context.h @@ -67,7 +67,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(ThreadLocal::Instance&, threadLocal, ()); MOCK_METHOD(OptRef, admin, ()); MOCK_METHOD(TimeSource&, timeSource, ()); - Event::TestTimeSystem& timeSystem() { return time_system_; } + MOCK_METHOD(Event::TestTimeSystem&, timeSystem, ()); MOCK_METHOD(ProtobufMessage::ValidationContext&, messageValidationContext, ()); MOCK_METHOD(ProtobufMessage::ValidationVisitor&, messageValidationVisitor, ()); MOCK_METHOD(Api::Api&, api, ()); diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index 5ee457fe70b3..7f9b5e00b4a6 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -48,7 +48,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/tracers:96.4" "source/extensions/tracers/common:74.8" "source/extensions/tracers/common/ot:72.9" -"source/extensions/tracers/opencensus:94.0" +"source/extensions/tracers/opencensus:93.9" "source/extensions/tracers/zipkin:95.8" "source/extensions/transport_sockets:97.4" "source/common/tls:94.9" From c2515764e395040def6fdd4e5e7df9c99a31ae44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 06:59:39 +0000 Subject: [PATCH 097/124] build(deps): bump postgres from `4784d9b` to `6b841c8` in /examples/shared/postgres (#32950) build(deps): bump postgres in /examples/shared/postgres Bumps postgres from `4784d9b` to `6b841c8`. --- updated-dependencies: - dependency-name: postgres dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/shared/postgres/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shared/postgres/Dockerfile b/examples/shared/postgres/Dockerfile index b01031b6cadb..b00d7aa9bb9d 100644 --- a/examples/shared/postgres/Dockerfile +++ b/examples/shared/postgres/Dockerfile @@ -1,3 +1,3 @@ -FROM postgres:latest@sha256:4784d9bcd8e603179d03732c9d2cd145c3c90e42f29847a95b5317fb37ac4765 +FROM postgres:latest@sha256:6b841c8f6a819884207402f1209a8116844365df15fca8cf556fc54a24c70800 COPY docker-healthcheck.sh /usr/local/bin/ HEALTHCHECK CMD ["docker-healthcheck.sh"] From 479f165bfc0edb7f086c3b3abcfcd8bf74fcafcd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 06:59:55 +0000 Subject: [PATCH 098/124] build(deps): bump node from `54836e4` to `ab9d878` in /examples/shared/node (#32952) build(deps): bump node in /examples/shared/node Bumps node from `54836e4` to `ab9d878`. --- updated-dependencies: - dependency-name: node dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/shared/node/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shared/node/Dockerfile b/examples/shared/node/Dockerfile index 4ef89898f771..22d2a68c7995 100644 --- a/examples/shared/node/Dockerfile +++ b/examples/shared/node/Dockerfile @@ -1,4 +1,4 @@ -FROM node:21.7-bookworm-slim@sha256:54836e4f9d2442d9dd01a220ba5f4d56d7afcfc86e7c129f9a79a5e4cdbb96b9 as node-base +FROM node:21.7-bookworm-slim@sha256:ab9d8781cb3fe8b6429d68c6ce19512a4ff526a6688240249ca15069b8124626 as node-base FROM node-base as node-http-auth From 9cf5ec2e9d9eb64e866442965357307e823eb38a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 07:48:15 +0000 Subject: [PATCH 099/124] build(deps): bump icalendar from 5.0.11 to 5.0.12 in /tools/base (#32994) Bumps [icalendar](https://github.com/collective/icalendar) from 5.0.11 to 5.0.12. - [Release notes](https://github.com/collective/icalendar/releases) - [Changelog](https://github.com/collective/icalendar/blob/master/CHANGES.rst) - [Commits](https://github.com/collective/icalendar/compare/v5.0.11...v5.0.12) --- updated-dependencies: - dependency-name: icalendar dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 542b6bd88dcd..58b2065b1dab 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -722,9 +722,9 @@ humanfriendly==10.0 \ --hash=sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477 \ --hash=sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc # via coloredlogs -icalendar==5.0.11 \ - --hash=sha256:7a298bb864526589d0de81f4b736eeb6ff9e539fefb405f7977aa5c1e201ca00 \ - --hash=sha256:81864971ac43a1b7d0a555dc1b667836ce59fc719a7f845a96f2f03205fb83b9 +icalendar==5.0.12 \ + --hash=sha256:73f9be68477722c98320621400943705dcfdbbc6c2b565253f72d3f87e514db8 \ + --hash=sha256:d873bb859df9c6d0e597b16d247436e0f83f7ac1b90a06429b8393fe8afeba40 # via -r requirements.in idna==3.6 \ --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \ From fab65ac1b58e871d407c82dfcb29e37a02c0fa9d Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Wed, 20 Mar 2024 08:04:28 -0500 Subject: [PATCH 100/124] mobile: Remove envoy_engine_callbacks C wrapper (#32915) This PR removes envoy_engine_callbacks C wrapper, which is the first part of removing the C wrappers from Envoy Mobile code. Risk Level: low Testing: CI Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a --- mobile/library/cc/BUILD | 3 +- mobile/library/cc/engine_builder.cc | 8 +- mobile/library/cc/engine_builder.h | 7 +- mobile/library/cc/engine_callbacks.cc | 31 --- mobile/library/cc/engine_callbacks.h | 23 -- mobile/library/common/BUILD | 9 + mobile/library/common/internal_engine.cc | 19 +- mobile/library/common/internal_engine.h | 9 +- mobile/library/common/internal_engine_types.h | 23 ++ mobile/library/common/types/c_types.h | 23 -- mobile/library/jni/jni_impl.cc | 55 ++--- mobile/library/objective-c/EnvoyEngineImpl.mm | 42 ++-- .../EnvoyCxxSwiftInterop/cxx_swift_interop.h | 2 + mobile/test/cc/unit/envoy_config_test.cc | 14 +- mobile/test/common/internal_engine_test.cc | 215 ++++-------------- 15 files changed, 157 insertions(+), 326 deletions(-) delete mode 100644 mobile/library/cc/engine_callbacks.cc delete mode 100644 mobile/library/cc/engine_callbacks.h create mode 100644 mobile/library/common/internal_engine_types.h diff --git a/mobile/library/cc/BUILD b/mobile/library/cc/BUILD index 516f8c372a5e..be0f83254934 100644 --- a/mobile/library/cc/BUILD +++ b/mobile/library/cc/BUILD @@ -41,6 +41,7 @@ envoy_cc_library( "@envoy_api//envoy/extensions/transport_sockets/http_11_proxy/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/transport_sockets/quic/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/transport_sockets/raw_buffer/v3:pkg_cc_proto", + "@envoy_mobile//library/common:internal_engine_types_lib", "@envoy_mobile//library/common/config:certificates_lib", "@envoy_mobile//library/common/extensions/cert_validator/platform_bridge:platform_bridge_cc_proto", "@envoy_mobile//library/common/extensions/filters/http/local_error:filter_cc_proto", @@ -60,7 +61,6 @@ envoy_cc_library( srcs = [ "bridge_utility.cc", "engine.cc", - "engine_callbacks.cc", "headers.cc", "headers_builder.cc", "key_value_store.cc", @@ -85,7 +85,6 @@ envoy_cc_library( "bridge_utility.h", "direct_response_testing.h", "engine.h", - "engine_callbacks.h", "envoy_error.h", "headers.h", "headers_builder.h", diff --git a/mobile/library/cc/engine_builder.cc b/mobile/library/cc/engine_builder.cc index 8152ccef64a4..d925cf6c66b0 100644 --- a/mobile/library/cc/engine_builder.cc +++ b/mobile/library/cc/engine_builder.cc @@ -138,7 +138,7 @@ void XdsBuilder::build(envoy::config::bootstrap::v3::Bootstrap& bootstrap) const } #endif -EngineBuilder::EngineBuilder() : callbacks_(std::make_shared()) { +EngineBuilder::EngineBuilder() : callbacks_(std::make_unique()) { #ifndef ENVOY_ENABLE_QUIC enable_http3_ = false; #endif @@ -899,9 +899,9 @@ EngineSharedPtr EngineBuilder::build() { envoy_event_tracker null_tracker{}; - Envoy::InternalEngine* envoy_engine = new Envoy::InternalEngine( - callbacks_->asEnvoyEngineCallbacks(), - (envoy_logger_.has_value()) ? *envoy_logger_ : null_logger, null_tracker); + InternalEngine* envoy_engine = + new InternalEngine(std::move(callbacks_), + (envoy_logger_.has_value()) ? *envoy_logger_ : null_logger, null_tracker); for (const auto& [name, store] : key_value_stores_) { // TODO(goaway): This leaks, but it's tied to the life of the engine. diff --git a/mobile/library/cc/engine_builder.h b/mobile/library/cc/engine_builder.h index 746d32f6f901..1a3e0ab5c859 100644 --- a/mobile/library/cc/engine_builder.h +++ b/mobile/library/cc/engine_builder.h @@ -14,10 +14,10 @@ #include "absl/types/optional.h" #include "direct_response_testing.h" #include "library/cc/engine.h" -#include "library/cc/engine_callbacks.h" #include "library/cc/key_value_store.h" #include "library/cc/log_level.h" #include "library/cc/string_accessor.h" +#include "library/common/internal_engine_types.h" #include "library/common/types/matcher_data.h" namespace Envoy { @@ -123,7 +123,8 @@ class XdsBuilder final { class EngineBuilder { public: EngineBuilder(); - virtual ~EngineBuilder() {} + EngineBuilder(EngineBuilder&&) = default; + virtual ~EngineBuilder() = default; EngineBuilder& addLogLevel(LogLevel log_level); EngineBuilder& setLogger(envoy_logger envoy_logger); @@ -215,7 +216,7 @@ class EngineBuilder { LogLevel log_level_ = LogLevel::info; absl::optional envoy_logger_; - EngineCallbacksSharedPtr callbacks_; + std::unique_ptr callbacks_; int connect_timeout_seconds_ = 30; int dns_refresh_seconds_ = 60; diff --git a/mobile/library/cc/engine_callbacks.cc b/mobile/library/cc/engine_callbacks.cc deleted file mode 100644 index ba570a8d1fe6..000000000000 --- a/mobile/library/cc/engine_callbacks.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include "library/cc/engine_callbacks.h" - -namespace Envoy { -namespace Platform { - -namespace { - -void c_on_engine_running(void* context) { - auto engine_callbacks = *static_cast(context); - std::function on_engine_running_cb = std::move(engine_callbacks->on_engine_running); - engine_callbacks->on_engine_running = {}; - on_engine_running_cb(); -} - -void c_on_exit(void* context) { - auto engine_callbacks_ptr = static_cast(context); - delete engine_callbacks_ptr; -} - -} // namespace - -envoy_engine_callbacks EngineCallbacks::asEnvoyEngineCallbacks() { - return envoy_engine_callbacks{ - &c_on_engine_running, - &c_on_exit, - new EngineCallbacksSharedPtr(shared_from_this()), - }; -} - -} // namespace Platform -} // namespace Envoy diff --git a/mobile/library/cc/engine_callbacks.h b/mobile/library/cc/engine_callbacks.h deleted file mode 100644 index ee82028c84f4..000000000000 --- a/mobile/library/cc/engine_callbacks.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -#include "library/cc/engine.h" -#include "library/common/types/c_types.h" - -namespace Envoy { -namespace Platform { - -struct EngineCallbacks : public std::enable_shared_from_this { - std::function on_engine_running; - // unused: - // std::function on_exit; - - envoy_engine_callbacks asEnvoyEngineCallbacks(); -}; - -using EngineCallbacksSharedPtr = std::shared_ptr; - -} // namespace Platform -} // namespace Envoy diff --git a/mobile/library/common/BUILD b/mobile/library/common/BUILD index f2c47473ceaa..69f9a7b288c2 100644 --- a/mobile/library/common/BUILD +++ b/mobile/library/common/BUILD @@ -24,6 +24,7 @@ envoy_cc_library( repository = "@envoy", deps = [ ":engine_common_lib", + ":internal_engine_types_lib", "//library/common/bridge:utility_lib", "//library/common/data:utility_lib", "//library/common/event:provisional_dispatcher_lib", @@ -73,3 +74,11 @@ envoy_cc_library( "@envoy//source/exe:envoy_stripped_main_base_lib", ], ) + +envoy_cc_library( + name = "internal_engine_types_lib", + hdrs = [ + "internal_engine_types.h", + ], + repository = "@envoy", +) diff --git a/mobile/library/common/internal_engine.cc b/mobile/library/common/internal_engine.cc index 9269ba676678..c185777fefdd 100644 --- a/mobile/library/common/internal_engine.cc +++ b/mobile/library/common/internal_engine.cc @@ -15,10 +15,10 @@ static std::atomic current_stream_handle_{0}; envoy_stream_t InternalEngine::initStream() { return current_stream_handle_++; } -InternalEngine::InternalEngine(envoy_engine_callbacks callbacks, envoy_logger logger, - envoy_event_tracker event_tracker, +InternalEngine::InternalEngine(std::unique_ptr callbacks, + envoy_logger logger, envoy_event_tracker event_tracker, Thread::PosixThreadFactoryPtr thread_factory) - : thread_factory_(std::move(thread_factory)), callbacks_(callbacks), logger_(logger), + : thread_factory_(std::move(thread_factory)), callbacks_(std::move(callbacks)), logger_(logger), event_tracker_(event_tracker), dispatcher_(std::make_unique()) { ExtensionRegistry::registerFactories(); @@ -33,9 +33,10 @@ InternalEngine::InternalEngine(envoy_engine_callbacks callbacks, envoy_logger lo Runtime::maybeSetRuntimeGuard("envoy.reloadable_features.dfp_mixed_scheme", true); } -InternalEngine::InternalEngine(envoy_engine_callbacks callbacks, envoy_logger logger, - envoy_event_tracker event_tracker) - : InternalEngine(callbacks, logger, event_tracker, Thread::PosixThreadFactory::create()) {} +InternalEngine::InternalEngine(std::unique_ptr callbacks, + envoy_logger logger, envoy_event_tracker event_tracker) + : InternalEngine(std::move(callbacks), logger, event_tracker, + Thread::PosixThreadFactory::create()) {} envoy_status_t InternalEngine::run(const std::string& config, const std::string& log_level) { // Start the Envoy on the dedicated thread. @@ -128,9 +129,7 @@ envoy_status_t InternalEngine::main(std::shared_ptr opti server_->serverFactoryContext().scope(), server_->api().randomGenerator()); dispatcher_->drain(server_->dispatcher()); - if (callbacks_.on_engine_running != nullptr) { - callbacks_.on_engine_running(callbacks_.context); - } + callbacks_->on_engine_running(); }); } // mutex_ @@ -147,7 +146,7 @@ envoy_status_t InternalEngine::main(std::shared_ptr opti bug_handler_registration_.reset(nullptr); assert_handler_registration_.reset(nullptr); - callbacks_.on_exit(callbacks_.context); + callbacks_->on_exit(); return run_success ? ENVOY_SUCCESS : ENVOY_FAILURE; } diff --git a/mobile/library/common/internal_engine.h b/mobile/library/common/internal_engine.h index 3ac09ced5d07..9adeb99280ee 100644 --- a/mobile/library/common/internal_engine.h +++ b/mobile/library/common/internal_engine.h @@ -1,17 +1,16 @@ #pragma once #include "envoy/server/lifecycle_notifier.h" -#include "envoy/stats/store.h" #include "source/common/common/logger.h" #include "source/common/common/macros.h" #include "source/common/common/posix/thread_impl.h" #include "source/common/common/thread.h" -#include "absl/base/call_once.h" #include "extension_registry.h" #include "library/common/engine_common.h" #include "library/common/http/client.h" +#include "library/common/internal_engine_types.h" #include "library/common/logger/logger_delegate.h" #include "library/common/network/connectivity_manager.h" #include "library/common/types/c_types.h" @@ -26,7 +25,7 @@ class InternalEngine : public Logger::Loggable { * @param logger, the callbacks to use for engine logging. * @param event_tracker, the event tracker to use for the emission of events. */ - InternalEngine(envoy_engine_callbacks callbacks, envoy_logger logger, + InternalEngine(std::unique_ptr callbacks, envoy_logger logger, envoy_event_tracker event_tracker); /** @@ -123,7 +122,7 @@ class InternalEngine : public Logger::Loggable { private: GTEST_FRIEND_CLASS(InternalEngineTest, ThreadCreationFailed); - InternalEngine(envoy_engine_callbacks callbacks, envoy_logger logger, + InternalEngine(std::unique_ptr callbacks, envoy_logger logger, envoy_event_tracker event_tracker, Thread::PosixThreadFactoryPtr thread_factory); envoy_status_t main(std::shared_ptr options); @@ -134,7 +133,7 @@ class InternalEngine : public Logger::Loggable { Event::Dispatcher* event_dispatcher_{}; Stats::ScopeSharedPtr client_scope_; Stats::StatNameSetPtr stat_name_set_; - envoy_engine_callbacks callbacks_; + std::unique_ptr callbacks_; envoy_logger logger_; envoy_event_tracker event_tracker_; Assert::ActionRegistrationPtr assert_handler_registration_; diff --git a/mobile/library/common/internal_engine_types.h b/mobile/library/common/internal_engine_types.h new file mode 100644 index 000000000000..7058127e1b65 --- /dev/null +++ b/mobile/library/common/internal_engine_types.h @@ -0,0 +1,23 @@ +#pragma once + +//================================================================================================== +// READ THIS BEFORE UPDATING THIS FILE +//================================================================================================== +// Keep the code here (including the includes) as simple as possible given that this file will be +// directly included by Swift and the Swift/C++ interop is far from complete. Including headers or +// having code that is not supported by Swift may lead into weird compilation errors that can be +// difficult to debug. +// For more information, see +// https://github.com/apple/swift/blob/swift-5.7.3-RELEASE/docs/CppInteroperability/CppInteroperabilityStatus.md + +#include + +namespace Envoy { + +/** The callbacks for the `InternalEngine`. */ +struct InternalEngineCallbacks { + std::function on_engine_running = [] {}; + std::function on_exit = [] {}; +}; + +} // namespace Envoy diff --git a/mobile/library/common/types/c_types.h b/mobile/library/common/types/c_types.h index c942e11d298f..a75e5f036db4 100644 --- a/mobile/library/common/types/c_types.h +++ b/mobile/library/common/types/c_types.h @@ -407,19 +407,6 @@ typedef void (*envoy_on_complete_f)(envoy_stream_intel stream_intel, typedef void (*envoy_on_cancel_f)(envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context); -/** - * Called when the envoy engine is exiting. - */ -typedef void (*envoy_on_exit_f)(void* context); - -/** - * Called when the envoy has finished its async setup and returned post-init callbacks. - * - * @param context, contains the necessary state to carry out platform-specific dispatch and - * execution. - */ -typedef void (*envoy_on_engine_running_f)(void* context); - /** * Called when envoy's logger logs data. * @@ -482,16 +469,6 @@ typedef struct { void* context; } envoy_http_callbacks; -/** - * Interface that can handle engine callbacks. - */ -typedef struct { - envoy_on_engine_running_f on_engine_running; - envoy_on_exit_f on_exit; - // Context passed through to callbacks to provide dispatch and execution state. - void* context; -} envoy_engine_callbacks; - /** * Interface for logging. */ diff --git a/mobile/library/jni/jni_impl.cc b/mobile/library/jni/jni_impl.cc index a2ec47ba23f4..fd86a5c7cae4 100644 --- a/mobile/library/jni/jni_impl.cc +++ b/mobile/library/jni/jni_impl.cc @@ -33,25 +33,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*reserved*/) { // JniLibrary -static void jvm_on_engine_running(void* context) { - if (context == nullptr) { - return; - } - - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); - jobject j_context = static_cast(context); - Envoy::JNI::LocalRefUniquePtr jcls_JvmonEngineRunningContext = - jni_helper.getObjectClass(j_context); - jmethodID jmid_onEngineRunning = jni_helper.getMethodId( - jcls_JvmonEngineRunningContext.get(), "invokeOnEngineRunning", "()Ljava/lang/Object;"); - Envoy::JNI::LocalRefUniquePtr unused = - jni_helper.callObjectMethod(j_context, jmid_onEngineRunning); - - // TODO(goaway): This isn't re-used by other engine callbacks, so it's safe to delete here. - // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332 - jni_helper.getEnv()->DeleteGlobalRef(j_context); -} - static void jvm_on_log(envoy_log_level log_level, envoy_data data, const void* context) { if (context == nullptr) { return; @@ -70,14 +51,6 @@ static void jvm_on_log(envoy_log_level log_level, envoy_data data, const void* c release_envoy_data(data); } -static void jvm_on_exit(void*) { - // Note that this is not dispatched because the thread that - // needs to be detached is the engine thread. - // This function is called from the context of the engine's - // thread due to it being posted to the engine's event dispatcher. - Envoy::JNI::JavaVirtualMachine::detachCurrentThread(); -} - static void jvm_on_track(envoy_map events, const void* context) { if (context == nullptr) { return; @@ -106,10 +79,32 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_setLogLevel(JNIEnv* /*env*/, jc extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_initEngine( JNIEnv* env, jclass, jobject on_start_context, jobject envoy_logger_context, jobject j_event_tracker) { + std::unique_ptr callbacks = + std::make_unique(); + jobject retained_on_start_context = env->NewGlobalRef(on_start_context); // Required to keep context in memory - envoy_engine_callbacks native_callbacks = {jvm_on_engine_running, jvm_on_exit, - retained_on_start_context}; + callbacks->on_engine_running = [retained_on_start_context] { + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); + Envoy::JNI::LocalRefUniquePtr jcls_JvmonEngineRunningContext = + jni_helper.getObjectClass(retained_on_start_context); + jmethodID jmid_onEngineRunning = jni_helper.getMethodId( + jcls_JvmonEngineRunningContext.get(), "invokeOnEngineRunning", "()Ljava/lang/Object;"); + Envoy::JNI::LocalRefUniquePtr unused = + jni_helper.callObjectMethod(retained_on_start_context, jmid_onEngineRunning); + + // TODO(goaway): This isn't re-used by other engine callbacks, so it's safe to delete here. + // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332 + jni_helper.getEnv()->DeleteGlobalRef(retained_on_start_context); + }; + + callbacks->on_exit = [] { + // Note that this is not dispatched because the thread that + // needs to be detached is the engine thread. + // This function is called from the context of the engine's + // thread due to it being posted to the engine's event dispatcher. + Envoy::JNI::JavaVirtualMachine::detachCurrentThread(); + }; const jobject retained_logger_context = env->NewGlobalRef(envoy_logger_context); envoy_logger logger = {nullptr, nullptr, nullptr}; @@ -127,7 +122,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr } return reinterpret_cast( - new Envoy::InternalEngine(native_callbacks, logger, event_tracker)); + new Envoy::InternalEngine(std::move(callbacks), logger, event_tracker)); } extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_runEngine( diff --git a/mobile/library/objective-c/EnvoyEngineImpl.mm b/mobile/library/objective-c/EnvoyEngineImpl.mm index 57f9f3fb2b9f..4680e71d1884 100644 --- a/mobile/library/objective-c/EnvoyEngineImpl.mm +++ b/mobile/library/objective-c/EnvoyEngineImpl.mm @@ -20,25 +20,6 @@ @interface EnvoyConfiguration (CXX) - (std::unique_ptr)generateBootstrap; @end -static void ios_on_engine_running(void *context) { - // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block - // is necessary to act as a breaker for any Objective-C allocation that happens. - @autoreleasepool { - EnvoyEngineImpl *engineImpl = (__bridge EnvoyEngineImpl *)context; - if (engineImpl.onEngineRunning) { - engineImpl.onEngineRunning(); - } - } -} - -static void ios_on_exit(void *context) { - // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block - // is necessary to act as a breaker for any Objective-C allocation that happens. - @autoreleasepool { - NSLog(@"[Envoy] library is exiting"); - } -} - static void ios_on_log(envoy_log_level log_level, envoy_data data, const void *context) { // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block // is necessary to act as a breaker for any Objective-C allocation that happens. @@ -414,8 +395,24 @@ - (instancetype)initWithRunningCallback:(nullable void (^)())onEngineRunning } self.onEngineRunning = onEngineRunning; - envoy_engine_callbacks native_callbacks = {ios_on_engine_running, ios_on_exit, - (__bridge void *)(self)}; + std::unique_ptr native_callbacks = + std::make_unique(); + native_callbacks->on_engine_running = [self] { + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool + // block is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + if (self.onEngineRunning) { + self.onEngineRunning(); + } + } + }; + native_callbacks->on_exit = [] { + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool + // block is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + NSLog(@"[Envoy] library is exiting"); + } + }; envoy_logger native_logger = {NULL, NULL, NULL}; if (logger) { @@ -435,7 +432,8 @@ - (instancetype)initWithRunningCallback:(nullable void (^)())onEngineRunning native_event_tracker.context = CFBridgingRetain(objcEventTracker); } - _engine = new Envoy::InternalEngine(native_callbacks, native_logger, native_event_tracker); + _engine = + new Envoy::InternalEngine(std::move(native_callbacks), native_logger, native_event_tracker); _engineHandle = reinterpret_cast(_engine); if (networkMonitoringMode == 1) { diff --git a/mobile/library/swift/EnvoyCxxSwiftInterop/cxx_swift_interop.h b/mobile/library/swift/EnvoyCxxSwiftInterop/cxx_swift_interop.h index 04a63aaa4655..d4ff534773f9 100644 --- a/mobile/library/swift/EnvoyCxxSwiftInterop/cxx_swift_interop.h +++ b/mobile/library/swift/EnvoyCxxSwiftInterop/cxx_swift_interop.h @@ -2,10 +2,12 @@ #include "library/cc/bridge_utility.h" #include "library/cc/direct_response_testing.h" +#include "library/cc/engine.h" #include "library/cc/engine_builder.h" #include "library/common/data/utility.h" #include "library/common/extensions/filters/http/platform_bridge/c_types.h" #include "library/common/stats/utility.h" +#include "library/common/types/c_types.h" // This file exists in order to expose headers for Envoy's C++ libraries // to Envoy Mobile's Swift implementation. diff --git a/mobile/test/cc/unit/envoy_config_test.cc b/mobile/test/cc/unit/envoy_config_test.cc index 38eaffc6e108..989bd9643322 100644 --- a/mobile/test/cc/unit/envoy_config_test.cc +++ b/mobile/test/cc/unit/envoy_config_test.cc @@ -379,7 +379,7 @@ TEST(TestConfig, XdsConfig) { "com.google.envoymobile.io.myapp"); } -TEST(TestConfig, CopyConstructor) { +TEST(TestConfig, MoveConstructor) { EngineBuilder engine_builder; engine_builder.setRuntimeGuard("test_feature_false", true).enableGzipDecompression(false); @@ -388,18 +388,18 @@ TEST(TestConfig, CopyConstructor) { EXPECT_THAT(bootstrap_str, HasSubstr("\"test_feature_false\" value { bool_value: true }")); EXPECT_THAT(bootstrap_str, Not(HasSubstr("envoy.filters.http.decompressor"))); - EngineBuilder engine_builder_copy(engine_builder); - engine_builder_copy.enableGzipDecompression(true); + EngineBuilder engine_builder_move1(std::move(engine_builder)); + engine_builder_move1.enableGzipDecompression(true); XdsBuilder xdsBuilder("FAKE_XDS_SERVER", 0); xdsBuilder.addClusterDiscoveryService(); - engine_builder_copy.setXds(xdsBuilder); - bootstrap_str = engine_builder_copy.generateBootstrap()->ShortDebugString(); + engine_builder_move1.setXds(xdsBuilder); + bootstrap_str = engine_builder_move1.generateBootstrap()->ShortDebugString(); EXPECT_THAT(bootstrap_str, HasSubstr("\"test_feature_false\" value { bool_value: true }")); EXPECT_THAT(bootstrap_str, HasSubstr("envoy.filters.http.decompressor")); EXPECT_THAT(bootstrap_str, HasSubstr("FAKE_XDS_SERVER")); - EngineBuilder engine_builder_copy2(engine_builder_copy); - bootstrap_str = engine_builder_copy2.generateBootstrap()->ShortDebugString(); + EngineBuilder engine_builder_move2(std::move(engine_builder_move1)); + bootstrap_str = engine_builder_move2.generateBootstrap()->ShortDebugString(); EXPECT_THAT(bootstrap_str, HasSubstr("\"test_feature_false\" value { bool_value: true }")); EXPECT_THAT(bootstrap_str, HasSubstr("envoy.filters.http.decompressor")); EXPECT_THAT(bootstrap_str, HasSubstr("FAKE_XDS_SERVER")); diff --git a/mobile/test/common/internal_engine_test.cc b/mobile/test/common/internal_engine_test.cc index 93fc7b440a98..80c62b55df7d 100644 --- a/mobile/test/common/internal_engine_test.cc +++ b/mobile/test/common/internal_engine_test.cc @@ -116,8 +116,8 @@ struct EngineTestContext { // between the main thread and the engine thread both writing to the // Envoy::Logger::current_log_context global. struct TestEngine { - TestEngine(envoy_engine_callbacks callbacks, const std::string& level) { - engine_.reset(new Envoy::InternalEngine(callbacks, {}, {})); + TestEngine(std::unique_ptr callbacks, const std::string& level) { + engine_.reset(new Envoy::InternalEngine(std::move(callbacks), {}, {})); Platform::EngineBuilder builder; auto bootstrap = builder.generateBootstrap(); std::string yaml = Envoy::MessageUtil::getYamlStringFromMessage(*bootstrap); @@ -133,6 +133,15 @@ struct TestEngine { std::unique_ptr engine_; }; +std::unique_ptr +createDefaultEngineCallbacks(EngineTestContext& test_context) { + std::unique_ptr engine_callbacks = + std::make_unique(); + engine_callbacks->on_engine_running = [&] { test_context.on_engine_running.Notify(); }; + engine_callbacks->on_exit = [&] { test_context.on_exit.Notify(); }; + return engine_callbacks; +} + // Transform C map to C++ map. [[maybe_unused]] static inline std::map toMap(envoy_map map) { std::map new_map; @@ -175,21 +184,8 @@ class InternalEngineTest : public testing::Test { }; TEST_F(InternalEngineTest, EarlyExit) { - const std::string level = "debug"; - EngineTestContext test_context{}; - envoy_engine_callbacks callbacks{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; - - engine_ = std::make_unique(callbacks, level); + engine_ = std::make_unique(createDefaultEngineCallbacks(test_context), LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); ASSERT_EQ(engine_->terminate(), ENVOY_SUCCESS); @@ -202,17 +198,8 @@ TEST_F(InternalEngineTest, EarlyExit) { } TEST_F(InternalEngineTest, AccessEngineAfterInitialization) { - const std::string level = "debug"; - EngineTestContext test_context{}; - envoy_engine_callbacks callbacks{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void*) -> void {} /*on_exit*/, &test_context /*context*/}; - - engine_ = std::make_unique(callbacks, level); + engine_ = std::make_unique(createDefaultEngineCallbacks(test_context), LEVEL_DEBUG); engine_->handle(); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); @@ -231,17 +218,8 @@ TEST_F(InternalEngineTest, AccessEngineAfterInitialization) { TEST_F(InternalEngineTest, RecordCounter) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -253,15 +231,6 @@ TEST_F(InternalEngineTest, RecordCounter) { TEST_F(InternalEngineTest, Logger) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; envoy_logger logger{[](envoy_log_level, envoy_data data, const void* context) -> void { auto* test_context = @@ -277,7 +246,8 @@ TEST_F(InternalEngineTest, Logger) { test_context->on_logger_release.Notify(); } /* release */, &test_context}; - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, logger, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), logger, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -291,17 +261,9 @@ TEST_F(InternalEngineTest, Logger) { TEST_F(InternalEngineTest, EventTrackerRegistersDefaultAPI) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); // A default event tracker is registered in external API registry. @@ -324,15 +286,7 @@ TEST_F(InternalEngineTest, EventTrackerRegistersDefaultAPI) { TEST_F(InternalEngineTest, EventTrackerRegistersAPI) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; + envoy_event_tracker event_tracker{[](envoy_map map, const void* context) -> void { const auto new_map = toMap(map); if (new_map.count("foo") && new_map.at("foo") == "bar") { @@ -343,8 +297,8 @@ TEST_F(InternalEngineTest, EventTrackerRegistersAPI) { } /*track*/, &test_context /*context*/}; - std::unique_ptr engine( - new Envoy::InternalEngine(engine_cbs, {}, event_tracker)); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, event_tracker)); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -364,15 +318,6 @@ TEST_F(InternalEngineTest, EventTrackerRegistersAPI) { TEST_F(InternalEngineTest, EventTrackerRegistersAssertionFailureRecordAction) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; envoy_event_tracker event_tracker{ [](envoy_map map, const void* context) -> void { @@ -385,8 +330,8 @@ TEST_F(InternalEngineTest, EventTrackerRegistersAssertionFailureRecordAction) { } /*track*/, &test_context /*context*/}; - std::unique_ptr engine( - new Envoy::InternalEngine(engine_cbs, {}, event_tracker)); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, event_tracker)); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -403,15 +348,6 @@ TEST_F(InternalEngineTest, EventTrackerRegistersAssertionFailureRecordAction) { TEST_F(InternalEngineTest, EventTrackerRegistersEnvoyBugRecordAction) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; envoy_event_tracker event_tracker{[](envoy_map map, const void* context) -> void { const auto new_map = toMap(map); @@ -424,8 +360,8 @@ TEST_F(InternalEngineTest, EventTrackerRegistersEnvoyBugRecordAction) { } /*track*/, &test_context /*context*/}; - std::unique_ptr engine( - new Envoy::InternalEngine(engine_cbs, {}, event_tracker)); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, event_tracker)); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -441,23 +377,12 @@ TEST_F(InternalEngineTest, EventTrackerRegistersEnvoyBugRecordAction) { } TEST_F(InternalEngineTest, BasicStream) { - const std::string level = "debug"; - EngineTestContext engine_cbs_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &engine_cbs_context /*context*/}; - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); - engine->run(BUFFERED_TEST_CONFIG, level); - - ASSERT_TRUE( - engine_cbs_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); + EngineTestContext test_context{}; + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); + engine->run(BUFFERED_TEST_CONFIG, LEVEL_DEBUG); + + ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); absl::Notification on_complete_notification; envoy_http_callbacks stream_cbs{ @@ -499,29 +424,18 @@ TEST_F(InternalEngineTest, BasicStream) { engine->terminate(); - ASSERT_TRUE(engine_cbs_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); + ASSERT_TRUE(test_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); } TEST_F(InternalEngineTest, ResetStream) { - EngineTestContext engine_cbs_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &engine_cbs_context /*context*/}; - + EngineTestContext test_context{}; // There is nothing functional about the config used to run the engine, as the created stream is // immediately reset. - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); - ASSERT_TRUE( - engine_cbs_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); + ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); absl::Notification on_cancel_notification; envoy_http_callbacks stream_cbs{ @@ -548,50 +462,30 @@ TEST_F(InternalEngineTest, ResetStream) { engine->terminate(); - ASSERT_TRUE(engine_cbs_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); + ASSERT_TRUE(test_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); } TEST_F(InternalEngineTest, RegisterPlatformApi) { - EngineTestContext engine_cbs_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &engine_cbs_context /*context*/}; - + EngineTestContext test_context{}; // Using the minimal envoy mobile config that allows for running the engine. - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); - ASSERT_TRUE( - engine_cbs_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); + ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); uint64_t fake_api; Envoy::Api::External::registerApi("api", &fake_api); engine->terminate(); - ASSERT_TRUE(engine_cbs_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); + ASSERT_TRUE(test_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); } TEST_F(InternalEngineTest, ResetConnectivityState) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -664,23 +558,12 @@ TEST_F(InternalEngineTest, SetLogger) { } TEST_F(InternalEngineTest, ThreadCreationFailed) { - const std::string level = "debug"; - EngineTestContext engine_cbs_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &engine_cbs_context /*context*/}; + EngineTestContext test_context{}; auto thread_factory = std::make_unique(); EXPECT_CALL(*thread_factory, createThread(_, _, false)).WillOnce(Return(ByMove(nullptr))); - std::unique_ptr engine( - new Envoy::InternalEngine(engine_cbs, {}, {}, std::move(thread_factory))); - envoy_status_t status = engine->run(BUFFERED_TEST_CONFIG, level); + std::unique_ptr engine(new InternalEngine( + createDefaultEngineCallbacks(test_context), {}, {}, std::move(thread_factory))); + envoy_status_t status = engine->run(BUFFERED_TEST_CONFIG, LEVEL_DEBUG); EXPECT_EQ(status, ENVOY_FAILURE); // Calling `terminate()` should not crash. EXPECT_EQ(engine->terminate(), ENVOY_FAILURE); From e697a93204ca78e559ba5a779cd0ac94eeb1f2c6 Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Wed, 20 Mar 2024 21:05:55 +0800 Subject: [PATCH 101/124] deps: Update ipp-crypto to 2021.11.1 (#32838) Signed-off-by: Xie Zhihao --- bazel/foreign_cc/ipp-crypto-bn2lebinpad.patch | 17 ------- .../ipp-crypto-skip-dynamic-lib.patch | 46 ------------------- bazel/repositories.bzl | 27 ----------- bazel/repository_locations.bzl | 6 +-- .../private_key_providers/source/BUILD | 8 ++-- 5 files changed, 7 insertions(+), 97 deletions(-) delete mode 100644 bazel/foreign_cc/ipp-crypto-bn2lebinpad.patch delete mode 100644 bazel/foreign_cc/ipp-crypto-skip-dynamic-lib.patch diff --git a/bazel/foreign_cc/ipp-crypto-bn2lebinpad.patch b/bazel/foreign_cc/ipp-crypto-bn2lebinpad.patch deleted file mode 100644 index da1546a1afcd..000000000000 --- a/bazel/foreign_cc/ipp-crypto-bn2lebinpad.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/sources/ippcp/crypto_mb/src/common/ifma_cvt52.c b/sources/ippcp/crypto_mb/src/common/ifma_cvt52.c -index 1099518..7526fdc 100644 ---- a/sources/ippcp/crypto_mb/src/common/ifma_cvt52.c -+++ b/sources/ippcp/crypto_mb/src/common/ifma_cvt52.c -@@ -168,12 +168,6 @@ __INLINE void transform_8sb_to_mb8(U64 out_mb8[], int bitLen, int8u *inp[8], int - } - } - --#ifdef OPENSSL_IS_BORINGSSL --static int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen) { -- return BN_bn2le_padded(to, tolen, a); --} --#endif -- - #ifndef BN_OPENSSL_DISABLE - // Convert BIGNUM into MB8(Radix=2^52) format - // Returns bitmask of succesfully converted values diff --git a/bazel/foreign_cc/ipp-crypto-skip-dynamic-lib.patch b/bazel/foreign_cc/ipp-crypto-skip-dynamic-lib.patch deleted file mode 100644 index acc43511f2a2..000000000000 --- a/bazel/foreign_cc/ipp-crypto-skip-dynamic-lib.patch +++ /dev/null @@ -1,46 +0,0 @@ -diff --git a/sources/ippcp/crypto_mb/src/CMakeLists.txt b/sources/ippcp/crypto_mb/src/CMakeLists.txt -index f75f448..043a0a2 100644 ---- a/sources/ippcp/crypto_mb/src/CMakeLists.txt -+++ b/sources/ippcp/crypto_mb/src/CMakeLists.txt -@@ -90,41 +90,6 @@ if(CMAKE_C_COMPILER_VERSION VERSION_LESS 20.2.3) - COMPILE_FLAGS "${AVX512_CFLAGS} ${CMAKE_C_FLAGS_SECURITY}") - endif() - --# Create shared library --if(DYNAMIC_LIB OR MB_STANDALONE) -- if(WIN32) -- add_library(${MB_DYN_LIB_TARGET} SHARED ${CRYPTO_MB_HEADERS} ${CRYPTO_MB_SOURCES} ${CPU_FEATURES_FILE} ${WIN_RESOURCE_FILE}) -- else() -- add_library(${MB_DYN_LIB_TARGET} SHARED ${CRYPTO_MB_HEADERS} ${CRYPTO_MB_SOURCES} ${CPU_FEATURES_FILE}) -- endif() -- -- set_target_properties(${MB_DYN_LIB_TARGET} PROPERTIES C_VISIBILITY_PRESET hidden -- VISIBILITY_INLINES_HIDDEN ON -- LINK_FLAGS "${LINK_FLAGS_DYNAMIC} ${LINK_FLAG_SECURITY}" -- PUBLIC_HEADER "${PUBLIC_HEADERS}" -- ) -- -- if(UNIX) -- set_target_properties(${MB_DYN_LIB_TARGET} PROPERTIES VERSION ${MBX_INTERFACE_VERSION} -- SOVERSION ${MBX_INTERFACE_VERSION_MAJOR}) -- endif() -- -- target_link_libraries(${MB_DYN_LIB_TARGET} OpenSSL::Crypto) --endif(DYNAMIC_LIB OR MB_STANDALONE) -- --# Installation of the shared library --if (MB_STANDALONE) # standalone crypto_mb's cmake run -- install(TARGETS ${MB_DYN_LIB_TARGET} -- LIBRARY DESTINATION "lib" -- RUNTIME DESTINATION "lib" -- PUBLIC_HEADER DESTINATION "include/crypto_mb") --elseif (DYNAMIC_LIB) # build from ippcp's cmake -- install(TARGETS ${MB_DYN_LIB_TARGET} -- LIBRARY DESTINATION "lib/intel64" -- RUNTIME DESTINATION "lib/intel64" -- PUBLIC_HEADER DESTINATION "include/crypto_mb") --endif() -- - # Static library - if(WIN32) - add_library(${MB_STATIC_LIB_TARGET} STATIC ${CRYPTO_MB_HEADERS} ${CRYPTO_MB_SOURCES} ${CPU_FEATURES_FILE} ${WIN_RESOURCE_FILE}) diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 82922ec78b69..4756615a3ab8 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -310,7 +310,6 @@ def envoy_dependencies(skip_targets = []): _com_github_rules_proto_grpc() _com_github_unicode_org_icu() _com_github_intel_ipp_crypto_crypto_mb() - _com_github_intel_ipp_crypto_crypto_mb_fips() _com_github_intel_qatlib() _com_github_intel_qatzip() _com_github_qat_zstd() @@ -543,33 +542,7 @@ def _com_github_unicode_org_icu(): def _com_github_intel_ipp_crypto_crypto_mb(): external_http_archive( name = "com_github_intel_ipp_crypto_crypto_mb", - # Patch removes from CMakeLists.txt instructions to - # to create dynamic *.so library target. Linker fails when linking - # with boringssl_fips library. Envoy uses only static library - # anyways, so created dynamic library would not be used anyways. - patches = [ - "@envoy//bazel/foreign_cc:ipp-crypto-skip-dynamic-lib.patch", - "@envoy//bazel/foreign_cc:ipp-crypto-bn2lebinpad.patch", - ], - patch_args = ["-p1"], - build_file_content = BUILD_ALL_CONTENT, - ) - -def _com_github_intel_ipp_crypto_crypto_mb_fips(): - # Temporary fix for building ipp-crypto when boringssl-fips is used. - # Build will fail if bn2lebinpad patch is applied. Remove this archive - # when upstream dependency fixes this issue. - external_http_archive( - name = "com_github_intel_ipp_crypto_crypto_mb_fips", - # Patch removes from CMakeLists.txt instructions to - # to create dynamic *.so library target. Linker fails when linking - # with boringssl_fips library. Envoy uses only static library - # anyways, so created dynamic library would not be used anyways. - patches = ["@envoy//bazel/foreign_cc:ipp-crypto-skip-dynamic-lib.patch"], - patch_args = ["-p1"], build_file_content = BUILD_ALL_CONTENT, - # Use existing ipp-crypto repository location name to avoid redefinition. - location_name = "com_github_intel_ipp_crypto_crypto_mb", ) def _com_github_intel_qatlib(): diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 0d399a004e86..76571cb1f65f 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -425,11 +425,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "libipp-crypto", project_desc = "Intel® Integrated Performance Primitives Cryptography", project_url = "https://github.com/intel/ipp-crypto", - version = "2021.6", - sha256 = "632cc5ba54413eeab575682619c05d247e9b7f2fc58ea3e5f4a02bdcab3e6b78", + version = "2021.11.1", + sha256 = "d785fd8d5245ada79068588e5cc4d721d35c50e7d26fc268306f4aaae28ec6d6", strip_prefix = "ipp-crypto-ippcp_{version}", urls = ["https://github.com/intel/ipp-crypto/archive/ippcp_{version}.tar.gz"], - release_date = "2022-08-09", + release_date = "2024-02-28", use_category = ["dataplane_ext"], extensions = ["envoy.tls.key_providers.cryptomb"], cpe = "cpe:2.3:a:intel:cryptography_for_intel_integrated_performance_primitives:*", diff --git a/contrib/cryptomb/private_key_providers/source/BUILD b/contrib/cryptomb/private_key_providers/source/BUILD index 610b66513373..55381c6b5f80 100644 --- a/contrib/cryptomb/private_key_providers/source/BUILD +++ b/contrib/cryptomb/private_key_providers/source/BUILD @@ -18,14 +18,14 @@ envoy_cmake( name = "ipp-crypto", cache_entries = { "BORINGSSL": "on", + "DYNAMIC_LIB": "off", + "MB_STANDALONE": "off", }, defines = [ "OPENSSL_USE_STATIC_LIBS=TRUE", ], - lib_source = select({ - "//bazel:boringssl_fips": "@com_github_intel_ipp_crypto_crypto_mb_fips//:all", - "//conditions:default": "@com_github_intel_ipp_crypto_crypto_mb//:all", - }), + lib_source = "@com_github_intel_ipp_crypto_crypto_mb//:all", + out_lib_dir = "lib/intel64", out_static_libs = ["libcrypto_mb.a"], tags = ["skip_on_windows"], target_compatible_with = envoy_contrib_linux_x86_64_constraints(), From 716353e26ca36b738a5e4e61230856e0d3fa7f07 Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Wed, 20 Mar 2024 06:13:04 -0700 Subject: [PATCH 102/124] config: preserve presence of connect_config field (#32939) * config: preserve presence of connect_config field When converting a Route config proto into a RouteEntry, if there is an UpgradeConfig with an upgrade_type of CONNECT or CONNECT-UDP, the RouteEntry member variable connect_config_ is populated. When this happens, the presence information associated with the connect_config proto field is lost. That is, even if the connect_config field was not present in the config proto, the connect_config_ member variable will still be populated (with the default ConnectConfig value). This is significant because, according to the documentation at https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/http/upgrades#connect-support the presence of the connect_config field should control whether CONNECT requests are proxied to the upstream or terminated by Envoy. To preserve this distinction, tweak the existing logic so that the connect_config_ member variable is populated only when the connect_config field is present. Risk Level: low Testing: unit tests Docs Changes: n/a Release Notes: added (please let me know if config is not the appropriate area) Platform Specific Features: n/a Fixes #32938 Signed-off-by: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> --- changelogs/current.yaml | 10 ++++ source/common/router/config_impl.cc | 9 ++- source/common/runtime/runtime_features.cc | 1 + test/common/router/config_impl_test.cc | 73 +++++++++++++++++++++++ test/config/utility.cc | 11 ++-- 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index e95e090f0ed4..dc0280318732 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -60,6 +60,16 @@ minor_behavior_changes: change: | Enable obsolete line folding in BalsaParser (for behavior parity with http-parser, the previously used HTTP/1 parser). +- area: http + change: | + When the HTTP CONNECT request method is enabled using :ref:`RouteAction.UpgradeConfig + `, CONNECT requests will now be + proxied to the upstream, unless the :ref:`connect_config + ` field is also + set. (Previously Envoy would terminate CONNECT requests even when the ``connect_config`` field + was unset.) The updated behavior should now be consistent with the existing documentation. This + change can be reverted by setting + ``envoy.reloadable_features.http_route_connect_proxy_by_default`` to ``false``. - area: proxy status change: | Add more conversion in the proxy status utility. It can be disabled by the runtime guard diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 43dfaccb33e5..52b262a05e13 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -659,7 +659,14 @@ RouteEntryImplBase::RouteEntryImplBase(const CommonVirtualHostSharedPtr& vhost, (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.enable_connect_udp_support") && absl::EqualsIgnoreCase(upgrade_config.upgrade_type(), Http::Headers::get().UpgradeValues.ConnectUdp))) { - connect_config_ = std::make_unique(upgrade_config.connect_config()); + if (Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.http_route_connect_proxy_by_default")) { + if (upgrade_config.has_connect_config()) { + connect_config_ = std::make_unique(upgrade_config.connect_config()); + } + } else { + connect_config_ = std::make_unique(upgrade_config.connect_config()); + } } else if (upgrade_config.has_connect_config()) { throwEnvoyExceptionOrPanic(absl::StrCat("Non-CONNECT upgrade type ", upgrade_config.upgrade_type(), " has ConnectConfig")); diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 40e526641425..e63003e29196 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -64,6 +64,7 @@ RUNTIME_GUARD(envoy_reloadable_features_http_allow_partial_urls_in_referer); RUNTIME_GUARD(envoy_reloadable_features_http_filter_avoid_reentrant_local_reply); // Delay deprecation and decommission until UHV is enabled. RUNTIME_GUARD(envoy_reloadable_features_http_reject_path_with_fragment); +RUNTIME_GUARD(envoy_reloadable_features_http_route_connect_proxy_by_default); RUNTIME_GUARD(envoy_reloadable_features_immediate_response_use_filter_mutation_rule); RUNTIME_GUARD(envoy_reloadable_features_locality_routing_use_new_routing_logic); RUNTIME_GUARD(envoy_reloadable_features_lowercase_scheme); diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index e867650b01e9..22a22de713e0 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -9180,6 +9180,79 @@ TEST_F(RouteConfigurationV2, BadConnectConfig) { "Non-CONNECT upgrade type Websocket has ConnectConfig"); } +TEST_F(RouteConfigurationV2, ConnectProxy) { + const std::string yaml = R"EOF( +virtual_hosts: + - name: connect-proxy + domains: ["*"] + routes: + - match: + connect_matcher: {} + route: + cluster: some-cluster + upgrade_configs: + - upgrade_type: CONNECT + )EOF"; + + factory_context_.cluster_manager_.initializeClusters({"some-cluster"}, {}); + Http::TestRequestHeaderMapImpl headers = genPathlessHeaders("example.com", "CONNECT"); + + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.http_route_connect_proxy_by_default", "true"}}); + + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, true); + EXPECT_FALSE(config.route(headers, 0)->routeEntry()->connectConfig()); + } + + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.http_route_connect_proxy_by_default", "false"}}); + + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, true); + EXPECT_TRUE(config.route(headers, 0)->routeEntry()->connectConfig()); + } +} + +TEST_F(RouteConfigurationV2, ConnectTerminate) { + const std::string yaml = R"EOF( +virtual_hosts: + - name: connect-terminate + domains: ["*"] + routes: + - match: + connect_matcher: {} + route: + cluster: some-cluster + upgrade_configs: + - upgrade_type: CONNECT + connect_config: {} + )EOF"; + + factory_context_.cluster_manager_.initializeClusters({"some-cluster"}, {}); + Http::TestRequestHeaderMapImpl headers = genPathlessHeaders("example.com", "CONNECT"); + + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.http_route_connect_proxy_by_default", "true"}}); + + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, true); + EXPECT_TRUE(config.route(headers, 0)->routeEntry()->connectConfig()); + } + + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.http_route_connect_proxy_by_default", "false"}}); + + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, true); + EXPECT_TRUE(config.route(headers, 0)->routeEntry()->connectConfig()); + } +} + // Verifies that we're creating a new instance of the retry plugins on each call instead of // always returning the same one. TEST_F(RouteConfigurationV2, RetryPluginsAreNotReused) { diff --git a/test/config/utility.cc b/test/config/utility.cc index 43f40bd93aef..5c7b4fcac21f 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -832,9 +832,10 @@ void ConfigHelper::setConnectConfig( match->mutable_connect_matcher(); } + auto* upgrade = route->mutable_route()->add_upgrade_configs(); + upgrade->set_upgrade_type("CONNECT"); + if (terminate_connect) { - auto* upgrade = route->mutable_route()->add_upgrade_configs(); - upgrade->set_upgrade_type("CONNECT"); auto* config = upgrade->mutable_connect_config(); if (allow_post) { config->set_allow_post(true); @@ -862,9 +863,11 @@ void ConfigHelper::setConnectUdpConfig( match->Clear(); match->mutable_connect_matcher(); + auto* upgrade = route->mutable_route()->add_upgrade_configs(); + upgrade->set_upgrade_type("connect-udp"); + if (terminate_connect) { - auto* upgrade = route->mutable_route()->add_upgrade_configs(); - upgrade->set_upgrade_type("connect-udp"); + upgrade->mutable_connect_config(); } hcm.add_upgrade_configs()->set_upgrade_type("connect-udp"); From 4bfb167b25e97a647186fd03ec49968bcd5bff59 Mon Sep 17 00:00:00 2001 From: phlax Date: Wed, 20 Mar 2024 13:51:32 +0000 Subject: [PATCH 103/124] repo: Sync version histories (#32979) Signed-off-by: Ryan Northey --- changelogs/1.29.2.yaml | 20 ++++++++++++++++++++ docs/inventories/v1.29/objects.inv | Bin 167998 -> 168026 bytes docs/versions.yaml | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 changelogs/1.29.2.yaml diff --git a/changelogs/1.29.2.yaml b/changelogs/1.29.2.yaml new file mode 100644 index 000000000000..7e9eaf3c2ed8 --- /dev/null +++ b/changelogs/1.29.2.yaml @@ -0,0 +1,20 @@ +date: March 7, 2024 + +behavior_changes: +- area: http2 + change: | + Changes the default value of ``envoy.reloadable_features.http2_use_oghttp2`` to ``false``. This changes the codec used for HTTP/2 + requests and responses. A number of users have reported issues with oghttp2 including issue 32611 and issue 32401 This behavior + can be reverted by setting the feature to ``true``. + +bug_fixes: +- area: jwt_authn + change: | + Fixed JWT extractor, which concatenated headers with a comma, resultig in invalid tokens. + +new_features: +- area: google_grpc + change: | + Added an off-by-default runtime flag + ``envoy.reloadable_features.google_grpc_disable_tls_13`` to disable TLSv1.3 + usage by gRPC SDK for ``google_grpc`` services. diff --git a/docs/inventories/v1.29/objects.inv b/docs/inventories/v1.29/objects.inv index 70feb4b0ae53595ba3d2aec2a78939b1eac7ac07..47a8dd464a41ddfedd40e3a963df6f9d7df04a25 100644 GIT binary patch delta 20617 zcmV)KK)S!apbFZc3XnqqGJ!;eL;DAXU5w85d9Eplp_{Kg$VJ=Gu&^IeSh4mHUfh) z8Vh}cp6ga3NNzzm_fprGpZ9iZP<=;#)}7Auo7%S9&r4haYSP!&Kk^Bin@m1C>V+(S zQ#+)r@?dUn50@LbJWg`GZ=v;4Z$FkzO3xnb)G?0U*`p-N(k!>&0kXK0!oMW(*!q$%&4sw*fyl6WvyE0J!Xun1gLLC zAWq*?5kw%pIwHyn`mnO(u9}BKSBt=ZFy>RSQ-HYw%;N)?ge5d{Zp%itIz+Kv!;tsq zU}`~g0~%mU1PzIf-1EIHM2L~%g&5uxtC#Mt^N5&Ch`hjn#YHA$eUfqGj{Ge<7v*ye zZXiZ(FoI-o6@u`{K7anMmnI}DmoRPhf(zNAtYT#h+6K@#25ROvlZ!&hElQAoQYU}I z>hI6eA3-==Y|iY6D1L{glKtAQdVXpIDZ~l@^biLu;uL|^hfhNejP#HNt`^sTF^-Ly z#1=O2hB$1G@&${%IC`dQVt%%%Wm^rF0~FiCU7-*8nP|-1cWzzgno%G#ZFP9u=(%-Z ziT5r!+*>s;%^Nnym<;i;RwB56ucA1xh+tfZ=+LT~U3=U;qdxi>yM&vj4EXo0+qN%P zWzz{e6CIFC!a!pHAY!8`3;>U zFw6&*{Wr3QPtHPH><-)VGZVLw{Q%m&oP?Z`wkF)xG&mWP}iE&-jMbT7u!l34g*MN6|E@=-darg{oBSk4Sm?p zwzS0CReI)Oc&jpVAQ0s(6PM{UORe@-CJ;oVC?EW8!E}8hDKNpJziksZ;dncuMTtS} zEo}9j$Zlwi)pEUQ!%G;PguzkXvxu^mG|Q$*WUJmht?>Xg5ucxb#^dm+J&Iq?#S8GF zyt>tAEPWD;S@@tXN~9F8@0^?vY09S`{;|`a!L@FLS3it2F$I2;lOeL!S3e)>T>dsD z{c#noi)9kqgqOc{>qQ{WZ4rJ+ds+Y#BEor)+$YzgdLEitoBp!cY?^Z8+q+1hjm4R@ zh<>++%i!}KD!^xd{je`~)e06T+!xJSbwt_RoYtv4wUb;sQC-^*=W^Y~Xa7vA#b}<3 zb=9um>O+JMcsGGLGizj&&9g7R`}X=PKR5aER?t>l@1K*SMuEAr>__!G`}XH+KP|h& zX6+G{OK+%WJ|}5yvN%rVJ`|XK2ye{9Vyyl=;4vg94fVMFN&9M%1#sqcgS&{P&`%3W(2M-)7xinSJ_y_BwlSM*Xt4+Ixs{TX*-*!WbMs zHDTC$gX;(|{_DDZEKlk$2Y)#iSxDDg->T@xk;sW_CLhzQSPN1DpNpXdqlJ=y{jgs( zC5C6+!sAWN!Dr$=X03uE!A|(iySHyLYSyx{(BJtdrQF}+lWr^wE4B}^65Bl~E8_Nh zf{=WF(|SM8^~ie!`m`TK`pKU$I(&2iY@7mwkD!GP-+g!eKL5AtUw+OG^7f&wM37nk zr6&7~@Hq?ppw;>#&h{g+?a1xGjI&R#gueGZ0@Dd2~97yjcZmC`EE{Q|#CG)w;VA{>ah39E@yn1Za4Q&D`3lHOX<-t*r5i zV|SNG*qAA<<|u)tG|3S+#_)K1Tc)YIC=+Cczxb>aR)o>>Y$|rpVZGn0^5EZ;RtLt;vcOI5Gi0*n|{Nj13v=IHCDBH6M?lBsL4_pb4*uxLF;rET!pL zSSy^UGzV;&(jr%&F?OKL^yKn?PcB9X6k~&Xj?(DxcP+VCDZm&lXqMbo<#sJ(IxSzH z#?s~CgeDF)ZO~6nm~iOBuh}VWvx)Vow9yB%T}D8g5=5ZOM^Ybpd0ZB>uavC(qm>tN zcLIpYHR-t+Ilx_8_9!hA;3_9vIQ(&QS~_bqJN0HOOb)yI@+O^|G_u-%aq@Q>H$aRq zJ5eDE365;FJ{mp6rW?}1^0V%05e%#nAx_K*cXBqBepmOQSK>LaLUO6xtszg5&dt}V z(#4oo2Z%(oNgdMUu?YjS<4wp$U3pXOtKM-71>J!aDcBO6UE!W7y@$0G5gYm97@%$#6Tw6HOHxFOC4 zzDQ#^v{hSh%&Ek2cC}Nk=MkfQOnZ-HdZc&czbya}VNpX_ohK1dCb;P}X@Flt#R%;M z#PHs)(>NI`q-75H)L^{{g2vdHwd-w@)^(?b$dMfLX_}?&fF5CgNTCwSJ*3JH63W z_=BQ(wL3?~Dr(U19GkB+)F+gRCx`h-%^PZH7$-MtfGxL4)BbiW+O)Y@WrFPR7oV5H ziZS|Q6kA<jMh7Y6$iThf+(urL9QIb@7fn%^II?ofAwrx~$yoj)dp$%mIG2=zUO6^a z%UFqGrSG19x-!D#U}T;)#$=U>mD)RFS%hVMyt%2x1z4%dBUBsh}U_NI7~ z`@n3}QF73wB`n;_$OQnsui4)v@~C_EtQ$fikj*q>k1 z-NTniyE7=KjKUkLsRQwbT4XHVQ2M==t?BZB5o%7jkt1_d2O6GA;cQK#cU0CgCvH~n zt2sN%t7<#`R4-I<(V1Qy*!%P)g3hvP=V+dA>7XveNs9+f*z)25VXeh5n)r{+lS>{hIT z8R^xKEu+)2p`#WgJpemng>egfcijYk`sE1S+r&(5dz;=PV&8+a>uk$_*2b(V*m4O~ zxt35<{wD6L?oQSm;=17l_=73SyVuK2X8;%i@jM`mZ* zOhxX+pJ9fz>dGqX+<-dhp(b5ZuFHK_fr>pSs8r#YEh8JFyHbUP`IRhPkA!+FRk7oc z!hM`KMO&`JZ8bkZhleDxxE&PjL$$$RCrSAc=iDLA7c1JvU|R|?X2;#2TT*g#prVZV zB5+VZRMMKLzXf%nskw@p9pQw3YFX~E2V*Mm!F+Bih!Lm4QV|~+jl{&c4=to6KBm>J zp8W;^1cL~+m{V_%vg*e#Qf`q(bWn4sT9~OReHe=KI8n13XfL`&!_bQM9En@ptBMqU0kdr$u}fp$(7J*SwZ)jxJR%f1PW(i8+=(KUVdz zJvp}{2l_3cryiS2Ij}sR-H#RpA z5VH1Tu|1Zg3l+Nw%D&g+z6WnmAvyNQnl{4Mu~d- zoQ?gE_(JwvC>uD^ZvE!QuMK-``SG*1EsM6}?gjVdYv2Uio%F;hIYI}4P2#@n?265{ z{CH|Ul(iblfM2J7FxL$Dl$&C|u6L(WR@Q3b1Ad)CSu;qW3`cj2t$<$E#sLyH2Pbid zqdWIbK`;IKxQ8#-O*~Y+Hv_#3J&j`yPs#+C=ZX+N8z2f_p8U=#YpnnA*D1U;gDGX% zI$YgBcV5XuwR$vKKm@MwdSln{K(YQPv1A2l9N8a2=Fm-l(xk{Wrf#L?8Z$sr1#}nH z{&4JCSZZH2^&Wyg$gfRP6*ut_GD~yd>dfUzMVOSALpR*!MfuB>znu8XEwxa_RCf!H zaYUr4N^|tg4?*|R40I0A<|srNbJ{b$dpOAdR>hwE1cnaItOL6w!Jc6~0Cd-9qvsRL zgKUEz4qIq{5kU&>Ry9vSGV=p6vHodFD?1Oek=^qNZC4v-V^wYij4CU{XD&9Hs>I9z zQynu%u4OYpcDV6g$JGR%^DYx6xl?X1L>(hC{gaSKE)4prpVHRHW>x;t_g-~H6c^^* z-^*T4LiI9a-L^P~1KSr*V7rH|5~n6fSv~ul_cyYCYIaN;XL-e;Xo{V?KiS zWXE|bEyT$+PMnqr!=Of1o|T!2+F+pg{&SG)s>8XUza`W_9eahX_^ghq+ z)BFHl6!hiJFR8wYtNHu9Foc{!*kp_eG@#Y`$ zllw=1d_E0yoMdN$UH(%v!!4VXM)~>^?u%J~#Y0*-EIKN7+zP>R}!ADa~*_Y-u&eeOK*D zam$n}_mLd?!9lmtxwH&u>-^R)VeZfkQG;NA<;(Rps`uEMAC@U6B${SdJw{EKaZG94PVy{#n?BU}wL3vHHawxhx^}^y;rYm=cE$pbI2kuIL zeVQ%srQYE)YL08ZC+h9u_kL zIIFuNKIMctq}E!WtoIhU4!?TVG{_Ika&49?n;hU`%-9o3Lt-aRBMoVWCXdXxU$uUD zJXY&CJCpnjlH+5R|A)D#(LPy!c_D&}gC2vBUB?RFm34i;w?e9>`fKClMt;uResCv_ z^GY}$x@I%p^WH~JS#mO{s;0X#u7EquRBzi?a&VMJ9_?~uz#&}iVbB8>-swx}yeBTy zl=SFiS}u&az2wYH*F1>3x*e=)tD%yW??j=mF-n(oa|TJzCblR-IzAbH&WUuELo&L6 z4P4~PMXop{mHssQ;q)ebjtDKyGxv>i9 z?WZ(GR!l}<3VD*sj7>9=Vg&Ukl8EgBEFehS!Vaq_jPu-H>F#fIRP9rM~El>G7 zjUu2r*fqrpIKk&yR?(q%0Fz?dK&1`BE4R6E8vxeAD|N@iw#ixWT9BmZi;*T?GC-xu?Xr`EQHe0oE+NEVlQ>Lo4D_v+g__KWby3b|v)L z^xPSDx$>1e=VeWf+7A!+#rA`53V-}g^~pKBh;u)KPffvlpCKU!6&?EVeyr-7j@>bh2fYPS;rK4g~&AqiV4^L>Uo(+Z>H}R@k;FGk5GZ2dUM87p2`n z&6paI+h`}uL34HR25fhbJ>@C)-JnEj%*SeLowWE?Q=-!VS02}C`LOeV(U@1L@K=D9 zP|Fy&qm&avqZeofEY(?YTR__<_)SHvKy;NWqrf=jl<%gkPv$CZ#SKiJK8h_ca4KVv$1~enG4=^`SQuqFkpMVo{>_#}Te3k)lUg%)!sr5TZV?CVfYQ z4cD=oM6$My$qkJt`-%Qol&iEb-<{KMty`0SY{X5zzr*i|?5I4y^{RM)% z_Ehf=e7iZX4-nNF_$L-l@~lrQnm|=6>c zdKPHuHUfNK?3!nRM#oX`b_YM6iGX$&f4Pvn%q4Mv=aWUK4HiMmjZ4Y{XCm%$AM;xHT6bAGmD;o7^ z)U$#dNto8$-g5n~gZpqJ>-^hF=l14UZP#Zv)xK!V0u2H>ylMbT35TU*Q+Anuxj1lF zgZf9?Zax0YtZJ!@siV9SA399zRtF_yrr@fuj<`F zz9h=8DdkEAa$O#pa@C7V5)^O3zEHz67$nO`vNpRb_Uo;D5)RyQy(z7a#xPl3?1?l64Lw%W$!&P6po~EL&qrP{CKN zav_$lmar{Tx9yFvTkB`VvF5`ZzOqYyr9e#sRQ~$@{oAv7rqZ!E5fwL9?+B+hJN56Rtw zQq>gux)Zk`fEJ}SH5m}uJ>csl8#A@6A>r~E`qS+}d>p7}I^hP%9C@t|u=NLWD?hFO znSZaM!IbO#Ei{egEs!;3v4%J6YYipqPgqfvLo=vQtVr7%KU{@>-bGIsIg%(gKVa)g zRwqSfZDsDEY-BnD&rW_0vSN*?x>`XFrQpEl>ew{pe)S;qc2)u&OgwDg?Em+_|EJ5| zz01HRvo^AqaVU!h-s77xgAbRJ#=2@j$4soMW_7GOaU=WL9~{*$(`<5w?hEWQ==7OR zpPBSdPTL!CDE8HVN_2O!9N*R3b%f2-K=Y6u6ud)wy0$Lr8hd7EG|s5M^;pkK&NzAR8OQ@gC#6xAMFJUGcWmBr$JSC!UuYP0WuczZoXgW6oDDA(e)lwD&-iHKX6yjQC`Q)G=VxEJtMuttR4 zln}YiAr>T==Q#&apiNV$zJD;YG~MX&LKk}AM!uQNaSyNz{Nr8}!-d5@27{D}!_#=0qgK_3{qm$F!^yn$wj__)x^QHR4_ z&Jm_Blk|Q#dsS0sR&Ib!n610M0SlQkkxq1CV)I1wLx%dpE!ym zf^DdO0g;qel%l0lT;?jZ`F7*LBnQvA_WZr7%alle z2wce2g%_K0FPmoNX#1NvtNXa{Q>N@X9LFlR&iHTix?-cxrEai~hOkhBkR*s@Tc%N1S?pgqQgR3|kfBH3~Qoai%XR#6QUgj)%4bX-vgn z?k?m*ALQ2HZBt^~xYN|Wg?3ewRl8zSw$?v>@TwcOdz1c+{vTW4y}L;dY~&M@+s2S_ zIvb8KJ^@*TNqfm+#CyQx2wt!!V_-L`Up*)(S=%@EXH&9#SrKjVK6 zgjKP+E1l85$2geM2ZI+)IPW)bpc_0c$hv7GP_?Oo97tgO=9hQZ0f4r-jR43LUI4Iu zbN%jnKft;^LMzy@dteNtQmO^8K0umAmSi|kZHeqlTvD)m|KoL5?r~3{eDl6?5z^b> z)N3!sOWG!^^gg&rpAOoG`9ZdS_Pq~!Xqro;Z`GMm=i^J$8bxmm&bRF;oS}kkALPet z$0RIaJcN5!F1E5CAc|vm_j{P7P3EH`6XeRzcc1js2OQQ-^}FU_U3}#)fNEK-Qc;<7 zAjh0WiG9q}g4*8a_qg`CKlYPp^$}&+SCe6hF_hE9hpiIlDsgW7{~Mmo z)LAp@?`M}ni^pd~#LvKg5hi{Ntirx~@Ra?(<6qe{_~0y5IOx-qFk%HwaHp2U7}ag# zqk$2kY-+0@@gqzK+S7J^=*u79U4P}|3EK+R*!EL{tD$Du*)+MI9_%VmDb*Nk_Gj${ z29c(1P9M3ddl17RwAfvA8U=aEpYBN3> z()LIB&s$5$1Y)9N6NQ^f*8~Vm6CTGsx&N|rb9@^iwVK$h=hpy~@dbs|NAN{u= zu%~|vHr#pb0Ui4LMIODkNB-*LP`BNtDI-lSlgfu}y}VUFf*A*KOB^v`l>9!i54!h; zWjuRlj$Oz%#VQPcic*?BB(Q#0%eMHRG5qH@E0{Y{eM8x-{qo(Pzj^ajmSaP~1zxVJ zmesGqlwKBa(e$g{!9n0o<6@Ux)iiYs5{0=`N-r`=gQE{+rVgNjF(LgNELwRT`bMzC zxHv?b2`X_8i9zn$$7Pf(yT;=Tj^#~(QFb`1dP~22Biqh@U&VQk@Rf#b@n$k-iIH&|NZa( zX-9n~J4Sk$v&yYe<`FZtqc8#lR6kkxa8xE1 zU&|7&nj|oPy$t1EddWhp{^k0u}dY>*+g5;;<#n-cW`m7)l=H%*Y)#af%C|X!W%*{bl;`+{K1HL?_vxIq81)3uNkI0h_g0U%vm**_Q{cKza7ykR5P9=Qe48M4u>w5%eGI z(T{qzQ_o|1*i2R^xOmt#3T_7A1F*00-N$WnFt4V=>SkYF|NO&Oet0aHv!@`DD^H^@ z+=uevP*jat=qa1gcc#u=*E^WmE%)m~UAbp}rYObvlTn=81_SdAne31sgUbXyV6gq0 ztd+BGi>}x-#V&hm6iUAQ+qZ9B4+eJBvO0CR^>*d$T;gTz=U0&9eDi<)kH2GgU*5Qu zs7)|UGB2w2^J{sdmZG`tsY^9%jHTtmJV4sRf2sfcwao7lBZ4EZdQrgozvLh5xvT4c zEq^vyrN+s7CFG@+U8pse$D8B6I}SaYsVW?t!?pQ%eh8>{d!Wz`7@fI|98}B{6%OWM zN<0rk4$vu6Up6?M!RZk-)%ow#>O3wrw~>pQ8V%^w(!QRW8W&JY8NAeRQ%gZ5V7dGP zmM)SiIgyPeOt0};X0HwDc1`VCgo1y6`(+HBZ3?{z1}HQ?hN~$U25Pm%H~J_7(!m-9 zdHY*Fj5X0f_BafMa_{Y30Yg9HrtI#^a&J}&vB5)56rF2zF~dLuI)$zjBLpWAA=#`Z zmWs+>w7AGm`s|DJfRv|Gu;DB%JPaJAQZP%p9>K%77?IJdzuv&=uNIU6Onb_I^OWdO zlnp)7rBt4(K)VzcUYbizBrAOsL$L|_1y*5)3%18a@A7QHq)Qj*?~1+bH&{_w4y4`qcvpL@)1vCM@i#w95rTJ{%jiSq21FpYa17 z53r}L47&{zM4pPWDA`CG1Xi z+P6%sp^?TjCo??xB5lQM3^=k@w(nDhO8wNy3AiIbHj->MtxiA#GY(>X0|Q*eO$8Ir zXKF+ph7LE3H!WnVKLTp@O`l^B7#yYH4~`W4FZ2`x9RzzY3}l-I-P`GZCXqd6fI1{E z*hcylvOP72S>HHcH}cERL7JNAZJ+um3N%woIErFZiG3~zE2UY^5_ zvTCO^DI`XwnuHn=>*E8^!EibO zGMuJNxmndg?}+tFQ7T~&Nw{i4?j+SgC^gX;b9_~;h}z+ zQA5I<<4{n$(9&;(wbP$ra~Qxh0-yS;Q}YnVR{HyhzrX!63l5g;rh|=oaluzAUcZD)tr2q~nJKt_ zj{=>^2%vcl_2w{tvyb5$x{o1BL1{#-$r>ysg@NISf;?O9*Rsl#-;=LlFI)nRewsa* zNWYlaM+H+PkkPwmaP!P*oT!H&HJUJiA&kk)ukmWGxNj&B0$I_IinJi_> z1d@Y3*&}Y~)K8G^H2KM#PrkVmHwlarZ!&%XP9q7XVPcSfB7sZ?;n;EO^ht!Qd61M% z#V6LD#HS6=2xD*R8rI@`Yz}PT-bAUsh09#jXuf)#oTNi>KaW4Vpf%-D54*_5EH4 zu#x@WVRWnn@?5|E9C1DHvRU6qC!khv+)w=_XD;Y1kfosA6J+FZkBsm7)GVeyxa0oA zUYe)oMVt9~>I6{^6A?9*f$sb2qXFbKPM7Us0si5A8WiJB%E>T$M_vQ`9K@6cn8z547lw7!3#ao66% zFbGUi#wVs`h|$h@>+@j>>wSw#N!uf5j=^K5M{j<_L&yeLsZ z_RX&{9H$hZpcgajJK8gx;!f{lzQ+y7r?VQkA*0doJ-auXn}<5mNkr?W+EmzyX-{TJ zedw`9eFk*Ux@up<>w&RLnE*qzJ3^{F#!o+gDK_x_J;obMX4q8gQh*TkW9V5YJvTUo z0@ulmI7`f8_B{{pOt8;IKm_~|ZuBTJoXqUWG+s?rbDDrLd(FgnL+Qh`+NuTlO4hu2 z+=H$}LE2Jzga%$3o8!$m^uh_|liCvW2Gpe_HhqYYQwjAR6sP~1L=s6MB+M@F6X{rg zdvb&N4;9%$9`opyB4HD9tCA=_@2uF!^+;?a;8otrZI#eK*6DS{qK(&OSFYk822B|x zdslFk{|-+qO@B^{?wiwLd~QE{VxZ9eX3TXbq2-}*SLJyPN9aQxB|~Ce9=7!ZEQS@` z!vQqLMkl85Q4`btHD@zC>jfMO$wk0_Nb=#+>}sQy^Do(LubcHRxZ8J4H069u#v)!} zB4Z^l2aoI^pE%{>LpJ3UrGY)KH^a!wgN%P0MQm5kqQ7Pzp+0^wRV1C7PxMNM5~aZv zyW!Qj+v*m@NDCuxJ9RKgVis)PqWWloGLxlkPwD7-;;w3OTl>V{D1q|!cXcv<`>Q&s zecj&`?qC;=%R`BNBUK=i3FnJ9+Kj_{^}fKwd4DZe4kvG<`?iiS-8c3NI1R$Dh0zdp z-0F#`4%xIWSM_?bqc;r>mFniXb#?yI1bTv<&#TN_aFBp~oAR>Ol6({i*tkhtEnebQ zSF3)X>Ai-1k{A8%1EF@qLql$VET%e?qqbH3ZnHC1ics$?SEqC3jI5d6m0f{z3Im@( zynZ{3+g4-ii}}d$Px}1OsU!lAW1@?Ec*xBKvqolplohp7NFFW4bnTKGExi2VPju-e z)cnPISMBA^&-d`4i$)!&a9y{>s-&)Dd$+4@VNPrK#q=b9(c^Rdne3;2 zQwp`&Rz7c9?iSV&?oiI6lI>g_Sed%GZN=@j*!WnP%Q~D7Xf-PV&Jxi7TAWLvL`f+JB9Au^(S707sf^w!BT5{Go72jyotsO z;QO`Nz8$t3r7=_co{u|!u^lcZOTq2M7tGzf97NX!VBg+e=1$!pY+!S*Z{Kdp2HYTa zY~yZlJMP3=zpdMchkx~C5BwbXXo}lg7*CP!-DoJypUUosfRmX%NxTYt=Y*{fy;XEy zmwwTo&GBsBTQjQJF`|GvwS63Ju6*P{7~OGuERt~(22-yJYUn?I5>M@=M^9Wxaq}Q{ zMf>6Dpz|B~i3g7<6hI|Jen>Xs=E7r%fZtsCO`&7rempH1>-7{WI0OImPs&C(k?s>8 zTeZhICQ+{2C*-8P=HN9*1-Dl{5?J{N!l+qCRhO$!l#fCr?s9a;2CqLnA$0vA3Ve8q zDhb1e#4Dsb*#vEWs!syL=?F6V_6TIa>%t zz6)W{*{3iV#Ywrym5b0wfS8U1xX`9!0TfbR&3hlLL4rw?Sr_WKGfy$8y}OT3A;dkX zy{zlaW3h7U=vV!NBF3sGI<7zgjNNpoA@a63#u%vI@d%=8&_TF5luivRN0*X<>Fc@i zGwyAY;lap%xS=jJgW*F{9mhvlJ-6Fb!KUhH*RA+a%0_tIIii-1LN}7Nq>T}zlMZs} zAh!r>|9AKG*V*;A*G7f4Eny;YBo&9}%sdia ze+#i4G%FQgR|S!6)!dFMZA9q7cZ)+J4_|1?#D8vo{O82K){uPDCj&WaO!k6|rauvz z3GfTh?t*pDT2sLcfwKnF@5}90;9BL0y+%dnHdvE&evRjC$R;zs{=%=;YFCDQ@h3Y4 zhhmcD`o%CH2ptJ>L~yeqKVC%Ri1ywMVyA|Q)zxXRF=ZN+F~yoFwpLxN=}l0!Dh0D6jtkr&SV%uRVlC z0|D4J?66O#y#B1V9m->eQ~3eQC)3h_WOnv{!dw2;SI1RzxKh#m6Qh@^7dSN|NBW%x zCOXKmd=o!Rfw-0m{Quq8Syy%2^8b6`4jUN)Ab}-K{77d^$dHddXyh;|q$}cM(2t1- zISBpe3hNm3V!}QS;yZ?7Hio=SVmq>eH=YoSF-U?L%8?nSF*WL9I<;|~$B4%04YC-2 zLLWwAOo+A^37;6kbVXY9#Ze3dM+`!YS6Lu6xwhQywDYxFzCz#E>sZ;$Co`Hy-@;P^P7_|SRo5TUi~o+J+TYJ(Fhq(E)1XZ zJ}9BbXCIPq7^LO)wIFe=LkS4OKP?N5+io7sc@e1B{3Ga|F~2g6Z&n zG>{)FCSSyl?$iYeu(lEGJpOF_ zI(DEsXYjco#7J7mOO=L;3;7#|p7Y0+n?yOm;9UXVn838UAuaTZO}r-AJg($_rxFLH zBY|Cl7>*b)oOpEpkS#&9VPF?m>=s9~jgZIAu9FEzXbA>gi2#hEvqYk=c*2FmFib>| zczhK{NRW_o6EP$l8^javBjlLI1erwr@CNe;dK^Ujpiw<0DXaZrE|_i1Nn-6@-r4%T z&2F%o3AH;(1Q8Dei61f!6XFVg0TOg&LWHPTAY>TOVb$*&bB+xPGU7HGSR`X^U*H)X zADA%!CrmsHzxP3a66M~+w$Mg;cus>H5mZ5qKz7BdZa*c0XrmzrOex3@&@b2jIfA&? zB_R#NSWQ55!O|c&l;RJ^D7jED>?|hZ<8p*Z0Ct6dlD;j zCcEd^?${j*H3aY_%(Q(48^6o_Z4EmMR@(};`;t|PIQc{LHzRz7_VARnbM4d1UJIJ3 z=H)aC!JF&}S$h(HS@881M1s~pblX+1j0&i#;IM60bb5SkS3_X*mp8?)7y>aO=&!II|JYkg>L5n#(ve}7|zVV z7*WW&t%8SCY##@n6Gz|lKL={rZU(Y4c`P=%fIl7x|KMZ-$Y`)~FhJ=B#C5fGTRsVJ zZk!ncnvXtz?IGfA946o^A8)pxxYak#VfMMLP%gy_4Ju;Ykq-Xxo5%U?!=Y%~`}HFstQ+&g1Gg_b`C)z}n0BK6^8mrrv(ZsL9m#mn z|7$EqWc0u$dhJ>nsSWkS%}^0#B)bregFX~qph%;Cg<`#5pE$!)EO-AC)r)~wM}>N- z_uR$wUVR$sj4r2#$bVVzee(}~wrY9CgR_5%z@s_=R^A@+V zs6_xh%5nXaD1ZBwqI_JK2?+h46?+)n(Be?#9~ZiOoJ|r>&nV=N3o|95|0DjjtVVx^ zu(6we$anR=im@3do}3tSv3tm^_jhKeyEy|~aLt@)iD}v%ua&YEBq`e)%17U1?{^;>+vu(3QIz`Bu#4|B}sL z%`U!jLCo+^1VAOSTxWhqQLZML;R+UC{w#|aQeC_XQGKQU#t?L}p6;uVx0h^xJ!ibZ z-yE81o1G)!*KWcpv(dY&ll6pWA#ZWQHlZbqclqCr+lOp6V-SAHraU7MGkH&r8`*7t z&Snb^$ear@Cm{3e%U@Pq4Ta?@1Tt5DV-Px7f9vNVZ}D4i6I#k(WGOFZ49@YpZxa%f z8F`I3cjhJg-pmP#y#3#nD_yU=;=nApFbe{+$iDmuH$q}Tn!gG|EV&R%0s^emh}Q_yJCMVmKq~lz2aV`nYC8=gvckPOcaz#+C`nP0%hV7X7 z%#E^Ce`6RrS^s7)L*C+VwoPa`FKs&}cvp zAPBs-+E}um?2BB)rwe{DO;fL2xd(Z<{f`>luJ{t^tt!iv%T#y+7 zp?$1yyV6+CxiE7AGv}5fW4z#^EC|YiSBi}3l8dn<7|VoG6fdVXAwf8Qw}TGzWeDU_ z{f$BBWPMS+40($eRh!UKzNDR#@QpFXGcL-EpvbcNeReHld|_)sH}iZ@e*`aWQ5DW0r<7 z=VHtW#ykyU!Nph*3|0YuGKNbo!jd2)7NB@L)g~k$7xb0JLH+sM4f9(4je+Q7eK~y{ z@)j?rHlZc`x*vF6gzk&6oN*y$1Y*X#`Nnp^1z8Y~Me5Cu7bKgIz}U8(uCpT;=Uj|) zf?)9m{Wyd5lG;!Emjjmx6Xm;tQ`AYqNjd|C}daAEN-d?i( z^?YB^CTOTCv$iuX$c%u@CbMNRm^l|_PGIKT(r&F6T$lxcv2D=#1qWlv#aI%IC8x9- z!*edeIYBV*(~Omg3ogV3fw)M5xa2}y5{OG(QO{=~h?)8u1JKF(_iYyP7JuJtLQ8ly zXh+W(oL{n8&j`wYjQu7U>p2%@PGD>^Xnx7TSa2~G1Y^N@6O7@Ki?AdJ=3SdDIS}Vu zh;ssQZi?wS2jYSYaX}z1XgB{V1f#xM(Fd-R^>4#GxzqVMNqEj zSBdqayyn8ZCNQr}8(e?+JQxf0H-?~-_0`}aZ3I)nvt!G*aXFc-ECVHL4UF2*InxMZ~%jO7&< z;)+0A*#-maeSFQucug=~kGjJijHUV;L(s|k>UJ4_@)oadZ9>a{~SBn`P|sCKU4`i9x8m#y+;PW$7hCFJp-il_eB@^Y;G!yZ4^^oO{nb zpL5QAo_o&a_8#c^*8u7SR9?d{y+8qPyg}6^<<-DOX=M}Q88_3xdf`RzTvT<5=8=H5 zI0h0f)O58ohZv}W;dxEEeny~**G~@!=(_PpxMPeEOCAHW&X+p{K0kO33VzXYR*OF( zc`TL<_9$)P)CM>IG09u8~iY zc&ivtJ1|TBX;RV{a6vLA{H;$qp|n*6lwVN=QPTTowzj~`o5wlBvfJdyT)2eyae$MWtVS9hI==>R{fD5%sliwk&+K%-t zUN##Xmo`<_(*G_su#9~0S~>W|L3AVA@3h{&P`D6vBcZW9eH=f)fU9w0b%xaVdh?Q1 zWn_bHsWF9>7_u0vlh2e{7}i$g^=zW$SO(XQ+$s0y_0uO0ueytlC>cQ=eCf@4ikY#+^>x#xUz4$5GD#c%Wj2PX@&w;zm$;hhydz<- z-f+b;95Ygs$L$2W^M2kX471&KJrUjablUR4(<`3%+0qdgY)*pgUktZDFK2ZNT>hb% zByhfm*K+mTL`)^z>KCC)J74}jDUua-g5;AaQX;hPgRWhR4qcc}47s)1Q7T|h%VtM( z8atws%XFwitA85YFf8Z#qvFRu=KE;QwVJ!B(VEt%J3`NYr4@y>NvmmbPp6P5vbMMy zI^oO|(oOB)(9pLiF-$;`14Xezg$402ThhZPFD0qchVWq7Bo^mr zF3Fn&MP;^e=0kM7Zf@~nJ=E~k8=%#^%4d?)O#^tS>|GX+U+&odcPAXjdoNBCHZ!mz zF9LMCSNkt=Oho{<;*o#|dd2`0-e&y{j?=pbY2c+`A|^xus!z4@AXmm|msCLw^JPI$R9{=W7QYMa*-{7;HPWWn0!2C(%Z5Wy zAK7hQr1SUlm%)e)7Z#BW@vqvEKMgI7`1QMVE)s#h!8Fgnx96T2;xQ3!M&MYTDQR3}-PDd=VwG0s0VjMnak)uE8 zuh(QMG7yO!FGNyd+t*Ke(5_LDoRYs!`eA`Mis}J{g`Wb^f@*PK1j4lt!&wTWHBgZ+ zQ0RUVD5&SX7y>6M4b?os9neNB6X+WLZCTBnbr=CsylvaSVw# zy5ryh2*FhpsL|L$0)e{~3u)p7Db;I81JI0Wi$Si^MJ*Ez6QMW8$elw` z+o7;9L1z9L<8)||dOIhLo3cny5x`m-<4co4%YckIii+WZ1&J^Tst^;frMznlnZ~U< zm@9A=@XoH*RpKlV;wd@{ETw~E5{^n^T=r8JpnpJ%jNA3{Ar@X*WdhjqSHlYmn+Avh zqbA@iMHUD5OHFs!BWRJwzpME#nkF8{zB0oXB!iX!xkwasf(I5X0&-F%S%8OiTx0%S z>_fXOAX`-JqQqG(#B-f22Y44(Q)MQaQkf{K#!*S@AB5Q4m3Fu^pa~3|a%KdQenh7%Yq|!gTs>;{d+z<;r3UFnB!J z5sbL$%Ha&AC^4>L0AohAg%Ve3wyB>G7UzU7u;Um~cs`MgR8>NcjB$*N5%z_!#STEJ zevP0I-J!H;rwxEEkrdTJyrTv?!4SBN+&sR{U@M&(M3JD47nuX5Io~ zV2CFek>grOgjiIC_o@NL1J%mT+{(^swcg5@d2FzuMXd<=RH*XUOR$6}7VM25+5mmeD>Id%N*{v-BS236wLV}(8Qbi* zN=-^4Q;{#uquEoI)a?e@EX4$u4M1B!xxWTfd!9q^y=5K7?1|wQr zT``ls=Kro`$l{LV{2x)_1PmrthB|pNl~ERJFes$CQZkp&^_l zCb*_W(e#p^r4rlE#;U_>0+4k3`;1qqOSRs5{#@rpx${Ir-EWLgiPmRhFJ)x0$(S?d zHw`zJl_k4lv{SgQ*XCZLs}yBNka;-xVu|7udaqY^E0wqt7jtoA?rjdBTx93ZCY>X& zNfYjJjZ|XNnb=EkcQ&V_1i4{UWPsq2PyOlYbBTXZ)74r^l8&!Rq1S;v%$VBqz@)(~ z-VQkpCmCpQc(c?-?&C-RojBH7wEpQ&WD09b$LKKV*XGao;`vek+^ zG+~cQ1QQPz*D}_M`0L(IOwMnSS$rLed%`^x`55A1_S$$)Q97+}B5r1jypTp$*eiWj zaq=_quxkE9B37wUUvAk?)Z?V~6#QOyZ`%PzAAZ z>@dzz)L}ETt|INp*x^&hqjsB-J6#twb!9+mBM+w+;dQg<2< zy4H5>+x?fzxlo@5f>OTx<5?*$KehYJ;Ck(_Ww)gCyp#Q8+{+6(O>jE_P&QI8UX$3To3}EQ>KwnhI!~{6?YTOy(zC|1oN6!)ZqyQ&G_&C= zuf4kV+$HzP6@#4Hk0_=Y?f^liTaj7y-C=v(U-74{Q{UZpW3n}qK9gCg-i<{HR#z-1 zc_c$r?9zC0*S|%DrY@4}ca7?oCxm{4@fZ0R)F0w?sO-%9@_oapkdLmI(-w}QUos~A zzD<7ko!YPZ(S8IU)Oe@6{K6ZL`fCr}Ul81XMgAaX*!XQr6n6(FtyZm1w_bkiCEwH9 zkZXG<_i1zuq)ET|c3*Idl@Q1KvjP9isnebHmM?{n9_fI{ws9M3qjZ6(8JK(<<)5dL+p5;nyehA1A-y0haJYiZO| zLUGr@pm#O?t)-1Gj4vxOmQs>L)>PWX{o?%&N+{cBN>yPwzr#BFF;_$>XAfj={qUC^ z4E@T15sUh@@IH0ftWMFE8 z|EU#=b%SiwMTa9d#6fm(>uZfRhREc=Q!MzvAKt%^^o?&Q(Gc7*1dH4bnGR?q)YbtKmw0VhGZ`~ zN#Y!{%w+nSDsoIdOnt82I^{E7x3CQLwuTx_t4|H!pmD z`iHuM*R<^y`ooJqI9A*~@Jh5-^nGE-yn$RV(OF$kobEBSjEM*e7y9z8#OeH~*)BA% zDJ-y(+*Yob{8`h9HX#|jYZ<7~J3x&hM^>)t^xgV$Z?73+Q=|UvK=h4zKwi2y`Pttd z(q0_UY=(Hn337rjVudXU{cl=lpbEaA3XnqqF@Z#dL;$e?KO%rJ?@<1oY$>)56u zpG#Ap70v2SK+K1?1dUU6X#^TdP6!-Thg2M$>O_+ipKEaS5nNUtj;oCv0vq`5dViwM^}Q zsItm~xxGDHZs77b$@RX4R!zPAST-p=ey~%=II3rlk|;~F+=2(l;tmV{Qr#!1@&Q9V z`Y4m8yl<+md}b~NCBWvlW3{5itXd1~t9ql$Im#ON5D8=6ke-&cYMu9(F|HDzz7>J^ zeNROYf%K}0C@bjO%96Wk9u8eC0>hhsPsL6F<_a*64{Q>a(EPbA8`&BW#d-}x;h%%4 z1HmC_qGs$Mv50=cvGxia=z4%5P5+Ci;GOi`Xu9q9{F2#I?Cr7^gxW< zUIC7DgZ}(oGEGQUE@9g01sAe4RmI8}v<;xq4AjhTCKrX0Ta+NBPX2~}RpFnd zpMr3>2%XtMQTz@~CHuQw_59QbQiv4*=%Ei-+$jR95}$?~80n!5Tt%({V;mbZxh-r! z4sqBX#Qbbi%eERU6DYQayFwrGGtrni@!Y!1HKRag+UoGQQFZIU67OAd zxVLJ+nm3$|u^QrIutbPoMR#C-5y5y7(IHng>-M;NMt$@%c?ma78Sw90w{2gp%BB-` zhB`(Wm7G9-Shmq%y_Gz(ZS`V<9dn3BTI7J*&x^-6!tx9DWQnX%K0*UyV05OMQo9>l z9L)(fW!xafnL<+g-UPdytt>gKNblMV?`s<#Hw~vEOqpI* zZs8bApJSA{_Z=W`U$nb_px!m9y&>%xF2a>I90rikDqT|&yfvEg`?rm88v3xGZE1Q-!m)QmixPv{TiEJ5 zk=@W3tL1vrhL<)t34^1&XAxyBX_ifs$X301TKfTNB0fKj$Kh3fdz8SQix=QUd3CGL zSo$Owv+zM(lt?LF-#IxU(v(j>{9~s@gKOXjuYMS5Vha2wCqrbduYR7?xh!r>`r|5E z7t18J2`_)^){8)#+ammu_Ot*fM1=DoxlgV~^*l7QX8mQc*)-+Gx2KUn8;dh*5&dot zm&fNlRDjR=VPEWjsue6+xG$Qu>WH$rIjvK9YA3mNqPn&r&gHs|&;FTKi_t#AMB z)rSZj@NNQjX4c3ko@ZZv_wDspes1#Rt)Q*A-ajWtjRJFL*^laX_U+Htep+_v&DtX< zk=R5W@v#eMzkl;|rhOsnpG~L$qsy+L(ZiQWG z8X_iL<>*;0;DWh)U0J?p}+G_O1ZzsC*4>WR%{<+CAND~R>bZ11R?pR z^?sgz>yh^e^l3ke^pihhbol53*f@m=A3+NpzWeU_eg1FPzxST@rr59Vt95rL{1K&nIT+dE2+;5po4K`9Ym(!vTUp~3$L=nX zurX6y%~1kPX_6yujN$S2woFrfQ6|U?fALu%G|4^&yRrHbPGN zH=ovepCe+78SLI}k7-&h#;9LPr%&*I=^5AU8?GB(iGT#OKZD8>f&9Hr6W?^<%PQh+g9&@8#F%I#XnbXwLvjit-O z2~8Yq+Mu7DFyYXLU$ax%W)tgEX`>HjyNrM~C5S_pkEA~I^0+K$UnyDnM=LMl?gS8< zYtnNua)7(G>`_`Kz*SDTaQNfqv~<>JcIwSmm>hQZE1?Ez@TSJ~wotv*!tBWzM z4iKSclRC!Po8fwCd&K5g9dd&kVW?`I6U)Nb7}$=_u{oatcR)LGJ& z*W~(8ZMPy6SIx(c8UjIodd#K?MmEY?gekO*jz!`}*BIF*nK{9xXklaYa6_C8h>^x} zXsfp1m{W=4>}sc6&m%_rnD!pY^hoc>e_H?|!lH(! zr*Se?NXs1Xslj>^1dXvXYuDQ*t?Nz=ks~?g(=bUSH-DzflM{5h`7gHvZNr0j{p#kG*w8bmYx!>k%l1pG0VA zin<7cGi;hxdO&fAFy?HX9Ps4sqa1MZY^59r_)z=Qt35~`b^Hk^yYY2sZUtNH84h3L z%qYkjE)4c3wxlinU~8KFIkp+#H>hn6qI(NN^r`Wnjka!o(}+j5Ao^J+v?`~pPi$FE zS%+JfbVK&12fKPLnfK6KPic`eN!*nLmF2=<-)-ARyr=_ftcU4|Be`XDR{~TeN+>{5=nK-nQX1gulQzyJF&DflOx8e8yi;{kN;r>wEekjCEQ-4U$ z6Sc*mcygS=9_@0?flGN;ws)ry21{FV`cvtQkwTpBiTw&g`iTbV4RwCE!tmeK`^|}; zPGXANv-`so%4o)}P<9XPZ`%{;_NGX;KTM(vW^B5Ly6rZyA#)nzp|vg5!_z6`jU}7^ z$>sWg5kuC7Y)UCk8Mw9-6UVMCuki4-rG#QSCp~v!+RO4JKWsGPKWP;I+WmGSsa~k!qBFfZ;P>fE1f6Bo&e1&K(m`E_lNJw}aOTAW#!h_d>^c16(D(FoLI$;e zby^Pl2IrGx>=BJwor`{*2!s4dZ^(f7DN7*?)7UElQS186UaS=wVW{(~>rmj4x*&{oR z6rw#CNLQ;tS~ZKrWsRehPr@2K*&*I!){2OWb*)-$D$LKzUGZsG#n--$%+9obnTp(t zKf?@b)sh5I;f zind&b+iHG-4i8CWaXTp5hiZetPLlE?&bdRJFIKdT!L}4)%#OQ3x1{9gKt&nzMc|-- zsH8Pfe+%kDQ*#wTJHiRovfN>R55`pBgZbQ45F<{7r6N8u8i|Q>A6iIDd`zobJ^Kv; z2nG>sF{j=jWz~;gq}(En=%D6MwJ=jt`Y;sdaiV57(ztMXB6C&43&9*YbT7=Tl{s|8 zhYaKriVZG0c|`t3K1}f?r;_tz>?65RaX8029}_oLDggzTr$=tHRH_Pp7`bz_$&#_@ za=58Pe$!RFI)K#oU-@9=>iD;D;!&s1wng*;Roke6F3EC#lILE*~M#lVwV#iX9FK|LK_~buX!!q99^nj{yNum6LTzoeyr+advb0^ z4)j|>Pdzr5#@&fIY>Qj@zBV<4UX)Fac!0ra9w#nvZftz=vH1jlhdDaTHPLBKA!O~x zVtXt}7b=z?YePCsyd|s<&=xI8cgaBt$H8xkZLwd0xDhrDDtYHkq-x!7jS}_vIUD;S z@rCTUP&RO+-TKXqUmNz?^5bW1TNZ7{-3#u^*T4z3JL!p2a)b^7o5X$D*%g~@`SH|z zC~Gy80l!XRt{L!uDL2J_UGGk%tgO|<2mCsPvSyG#8IJB4TLHbSjRPcZ4o>0@M|bX> zf?oRdaSvavn|P>tZw7i7dK$+Zo|FkN&lMqlHb4}!}JuTywy22;wib-22N z?!1zRYV~NefCyaU^~SE@fnxnpV#x~9II=&4%%Pj4Ns()ROx;S&HD-XM3g|AX{o&ZP zu++Y4>OBN~kYAgoDsJKw4&TWX<~Q0~j;jei=UpaDa;My2h&o1O`X?ceTp09KKc%gY&8qyP@4f1ZC@##qzn8t9 zgz9C;x@~a|2evPsz;+K^B~DF}vU>J8?{8$)?3gxx&hm;w(G)v(f4q~m>6xxu$&T|> zT8NWtoH#8LhCz+2JS#I3wZTB~$#qR3d#ay@oyFqN(^Cnb<>u+J`joq;>3yEtr}-t^ zKO8G<#J<@5p)azcks}QT>3@d0J*86Dg#YNTsiw^QkZRIUIR6PqBMk=WZI~ILXcgyZonUhFdl%jqqbr{ZY*n2_0*u*zOwi6+Tb@()P%ptH9`2_7vrP!qtQw zjyW-;Pkmj>sV6-cJvz&}*?oSjeQx-1vXxLdkFuep)WbUJQ<~v=*wSi_`>xuR;+82{ z?jt$&gM)6Pb7>jS*7>bp!rY-7q6Wdrm+NhRRPWiL8ltVwq_rK6kiSF39aV?IxOjqs>Ti8)a58RddG+W?* zOTEKq)Ew7*Puzv_0glwWir*h&I6UxI`pV3S9)m5+p=lOwSelPY+>N(Q+&1?3Hw~A<-yX6 z@TFj^Ho#ozGG^^_ZJ#TW)2tEI;B~3OCqu}z^3x$&%G{=BV5;PV4#CnxWQU1=d5*{q zo*-~wTTE8D5?0wWIwy-gK4&WPU=F0$zO7_sF=6e#yEogEoAQ(Vy{YzP z`*Ks)n=RCaQF-ry9e(w!X^n6W36hQv;sMjFx#O&*zXziR#Rc&yfO zb|(25B*(`r{||FfqkXdSLIf9o2R#NMyN(sUE9?4xZ-rD%_1DJ9jr^Rs{oqa<=aq0i zbj@bG=e>`dvgBk?RZVwgTmg5Qsou7)9wwbbK91(twY{9Bgy(0mOf!_MCE?EH+dqWjt6ph5x zU5Gd^&>IeLdS032hJlBFb7L1!g4AqvTR}5$dz2qVrU0SdFF)~;u0*Ij*#(oh?C;WbEh5TGV=LL1Nq}jVl+uPsaBSWX6F=ZjEwH#7!F3 z`S<(M)kxDWI^HgSznveU+di;9GuHMSVNS#d!MS8j9THUO-JSL%+3ZIiR$wIE9!$~x2(D|zMEvKaKq zISh#NFoSSa=_0|Z5h;&QZRn)Z_b=5Z#acfvOw6byz~hjA>eW5?Mmpg>A5rR za^)*`&dZt{wI3eti|q&B6#n>~>XUPL5$ApepPGXAK0`tdDmwJz{azhDcxDjBh$<-u zQ#*B{4%-2LvarPa{?pb2SgG>S#p+bOGDchGh{qv@OZa9po-Wmt;lO+_W^S(NfyFfX zxTAMA^D0(N^I^Aa8UpEgnYd8Hd!v(S8L`_vEiF!Fn&MvE9A!JNNzX-ZtM;~;qcq;q zF4qh=gk_t$>14|)ovyLe9SHoLM%7|-h%zF!IU4bQtgvlUX71Q+4pOTFFG{O@?qxzqcN{g;jaKIp_Val zM=2+UMla9|SgNz)wt%)z@SBQSf#@n%MuBn4Dc?<7pUhR-iW`_beH2?@;9zPhaHZ{I zCed4e*@VIdrMXY;%b})&{a(0D>8P;>JCr?Bbd;EonY%Zp=FxaA2kwK8#i*t4qL0j2 zDZLJDMPu%e#Uh6~{DN3#>O*fRM7d5i#G*v;k0V@7B1Mm~n1i3KAw+#(P5O=q8?IwF ziDYdZlN%aQ_7nZFC|7A=zB{MiTDKj9>1L+vta!JN66L?E+?W90C7$^(@fR zZ3Otf*fq}rjgF(>?GAoC69Mf!O5)@0nFxH`Il*PSLyz8~Ob&11y)io*n(4KNo?AVrB zcUNZ8&o^&>_}AXM&5zT+y-E?v7JOtM%LXpNa1VBNCG!l_inAiym-m^q?ZK-3swrX2 zt=PKI$Yllh>s`@hRg1)-pK@%o`Zl9~UOs($owdgUG`V{iXcHEj61EJQfvyqKFa-gV zw-4{q{9ojqlK+0r%5L>C+m*#$;$pV7Z3a65+aC1(PCkhKjyOPvDGcaCRy69-sAmN^ zk}$2gz2*8}2lwGd*7>)U&h5>y+OE%Ts(sOz1sVi&c+~)w5)MnrrtC6vap10h2KA4& z-Fp0)%Y6>8NGSSH&i;%kuCITjUfV;d*Tqge;w7iIuJ|CIe4TA;c}ry}Ue&vUd`XmF zQ_7VLWNmg=?AKfQBpkTqdQ*l0Aw}c`E!s>LFm#3e zL>JI4NXW@kT~lwvfHb8S7!qZFI*|)*TYfCJqPwrdST-3WhZ!c$z^6BtcDNtc0=HrC zQQ}b@rGR0f5RCGpZgRi0SXNJW#j)*d{WSF4?yE4gOyJG&_0Kp^UbYq)n|RoCBd|(CF>kWmf>PkoeaJ~ShmKLpn|Vj!W8GJhl_}qCxnIMbmliG1ee=uDKY#b;eP&F8B(fLSA}WwduOOA#)IorsNu1fl9+JBUrK&0R zbti5?04+*uYBC_Qd%)LAHfCyBL&D`T^rzc{_&89{bixgiIr3T`VCxU$R(@LlGyh&i zgDKbfTWA`~TOe!7VhwNB*BVOJpRl4Thh|WrSdq3jez*#~i=HrlawJh~e!$j~tWJu| z+REHR*~oMRo}K(0WW^d&b+v*VO2L88)v;;H{pvyJ?W_bmn0VN}+5hi<|4)~_dzXPt zW^H6I<4_h2yvH|X1|Kdbjdj(6j+t0h&FWZn;zstfKRBvirrG2U-51zr(CIUsJ~Qc^ zoVGXOQ0%Lf=j;~vf#xA!jP8h4?pAgttjs+$UH9_pZBfbgPc4fGE@u80 z^ESa`P;0&xK);Zkd|9AmrgmAeDXKlVcyN+$$_;b}!8Po)k1!vl9(}}i&}LPfkSaO^ zI`SQZt(VGLepZyOpI$h)E;kJc zGzZ2o=DhOu{Wt%Cspad9+JUfD8$PeMAZBW+#j%TE;+`d(wl&DVI$`8jT=s`nce|qb zz{8qaklr6OH*P>Z^?*CvSMnx9JI)%w10!titNpsZZ{dG&TODNer+T7e4!(2>`P;s& zDvQOhDy`{%)MnrR@b-F&2DQ0PQLe>pDZ9pw5)rpBd9PM?rpOv!a4+DiV2ud7DIs#3 zLo7%#&vOo)Qhr3L?jeL`(l|y~Q5@o}M-qYj6=oFhzM z$g{Z`2+Orm;+T`b9>3@Z0|tQO;Q++Llew};7#glF%~iR>CrAgl41O#pd3YJ=d^K5-OB1lv#p zA}OtZC`C)9$aRX`x=w6j2|KnFxG{Eg38B#Wxzi9rK@asWDLLL!PytJ{;-SYQ`3#YL_xzBOG_ zbmtmX4BQ9TxFUksmBiZ5X=9CoJy0~@c*0N-l9s7$xy)5+^XL7!I7rfz6&r{(Ih<>5# zA4rC~@9msreM@S+$dDoZYmT%xvcvM@cDSB`}XhQJUSA3L3Vt3H~!Vxz_lmZ z5FP7-7yWb74Q=wsRI!uik2v)RFY^n37`7_LYZP!G;!Iysh<}m~91m>=(wK_D++E0r zKFF=X+or^}ai^($3+<{Vt9HevY^{I%;8izl_a^-t{Xe$8dv}u_*vKa)w~Zm=bYS&k zwLgA3`>9yT=h(m;UTiL#m-gjTR)_rMrPrBn-GeSkELEXi=7+7j89xTIkB{>SUA+~b}?`R0A)BBZy$sn=eN zm$Xe->3wjMJ{`0V^Mh>ddmr?F&@`7w->NgC&c~OgHHzLCoNwDxI70>7KFE*Pj!9U; zcnJ5dTx?}OKorOB?)NZDo6JW?Cdiea?>_0N4>+uw>UYh}BTIrI~O2+=o9HR&u*So8_K6d+c#_O*HU_D!aK zyHJ0xVmDo;@Zx~=U*sQ$=YJIC1_jT%>LbdsuO`D1V<@MI4_hV9RpQ+E|2I6Fsk3I* z-_I_E7LU(}h@XKYO#B#sScQG};3@lm$G@^^@WENAaL}hIVZ;iW;7%=xF{<0fM*|~7 z+0<4+;zyVew5RR-(3d~HyZ*|_6SftsvF)b@S3}LRvuScaJ=j&CQmQf7?9bW@3?fb2 zoIY|@_aKHtXtBHKGz#*R$wv{l+^l<$jKl*P$U{C)T zY`F8<13L8gi#&R7kNnlgp>DfPQ%0IvCY2A{dU>mU1TzlemN;U>DEWP2A9U{x%Xs$A z9J`Qjid7gCr8IqiNMQZ0mTmDrWBAW+Rxo#@`i8Pu`{lbofAi+6EXRg|3%p!cEvsLJ zDZMPFnXs%h#NBnoq>lwM?#21g&tOdUW4V?z2lShVsw^o?MNadC(= z6I9|H5`)~gkIN`oc8$jw9Lt*mqwH{2^_G75Mz)>5iW3umvR*jAEZ}o6%)vR#7G~G- zM|S>o_7hCk!4hhSKE%3nir_}(>3HLurhH1O3dT%qBI()e>+El8ybnKk|NGzn(~kN~ zdY>5|sD88zsoWV0G@ZC<#fUE$4@3UvY;HXS2zLq6k zHA!H48OptX^pcrG!vVHPM(%d83i2`Z(L0i!f1UkB>0g)7eERE8-@Jjz`KIi+O!4dR zd{mWtb%Vdo-ZUjL&D5UROofT7<%bm}m$a))MXc1l_LiTKO|^f{jDUO0$nt9#^t*>; z2x?!)y8LL13#LopTpznYho)X1b?6M0^=ISUL**I@ z>AxP|^gdmt1j$dyi?3(%^jSe9%*oZ)s>?%jP_(d$n45#9#Pyxe##{SGwimM4K-XQL zTh;3lW~cTW{)hIND|1qZ%PRaDkD!3SFlkZk)eOvps7JAALadi6@^R`tUH3oz9fgVB z#TXKQL@hj6VB0Tw*%lAb0yb;0zI^|qvo8-?f%5FZAv@rJ&TY~Fi9S&VBj`WaqaXEb zr=G|3u$in-aPhEf6x+xDVyS zp{N?Q&{H;}?@XP$u6HoATkh9~x^mA+Q; zN{y5EO2|tsyHIN`k2lACcN}^)Q&l)Phimik{18y@_CTQ>FgkM^IjEQ^Djdwilz1M7 z9H3LCzHD$hgVQ5ws`KBe)p=ZMZX*{pH5$;VrF}g$H7=l*GI*)srj~+Az;gKoEL|j3 zav~c`m|o+x%w8MP?V8%P2nGN4%NRO;+Z1{c3{Yr(3|CVy4Ag3iZ}d?Fq=PjI^7glU z7;B<|>~RwgD zOQ}3nfp#e@yfl}bNLKnNhGG-;3#`Hp7i^D<-sRbXNtZ6r-xYh=Z?K}W97wl0Sv%HW z&-&{TO3c@LVnAR+uWoPGuNz>&^s5IHh+f_SO<34fX_pIxd^k32vkVBVKH~>E9$-&f z$-99jF##~AoCxIW6!Sm+ki|cLW9pm=Wmj+O%>xgD7$~6k{fQ9Ho$QO?O4yz3v~QVM zLnDo6PG)%WMcRtj7;t2*Y~QC0mHMfZ6L3d>Y$VxiTAhFfW*o%&1_ro_n+hhL&(w%I z3>|J5Z(7J!e+1O*n?A=NFgQxX9~>$8U+5_YItcb)7|1pay0_C!B74k#0Ch-Su#NO9 zWP553v%Yb@ZseDrgETeK+dlPC6lkzS(9Aj|pt|f81&|sVx`m0L;P;w1zN@>z{11-~ zu`)YAJ2P_=LzMR`1qzaz`o8Ka+3Wee@YeXmuK$?;*8kLROYh|W8Q$b5ygY{;Wz|k; zQb>$UH5GkUH+-5H0j-vQmVj%7+Ys)ZHfLlSHj3H3$Y(djetlo9yF0b*hVyd894JPA zHs>>Qo;Qo^Qs_07`UYl)wqnC-CdZSz}bf*umWH?Qk zadj$hAHz3)bRR>Mg3^dulQmdO3IoFt1$nmIuVs}fzb9YAUbqAr{WN5~Xq^B^gkichRP ziBB7#5ysxsHLS(?*c{lvy@_tO<8k));}+H!>JguJB@PF_ZEBp^|MKtea6&)e_8<4^ z>$Xq*ERGEotAD%x=iyT@9r>-)V9U?cm# z!{}HEDsaZ^aaL4_Jy);kF zi#GG~)Cr;-CL(Gq1Ks!4M-h;A%lB{O)8GGi9rlWU^E@4IX2@woW`?@Jw2%#DRBU8N zZ3819dOKMR)o-ciEzD`N6D^D%5;aXS)Z=U;WvvLf-l4}Fd-d6AXnp@e5Tl***5|_%*83KflD0?A9D~P9kKX)zEt?FI=_YHS#J ztn7w=8m#d(OEG(W3ye3qgp}{B{3OfNBghzgQ*&x>r;#CtjQLug7hSblc~PQ-?3-U@ zI8G@*K`&<5ceH0X#hu>Ae2*KDPiHl7Lq?yg+Ld!$ruFCToj?jlXN`}O`JZ$R+SPUz=hXZJg zjZRGAqb8>PYtCkP)(bcml8b3v#9h_mw)Tm?Q3B=d@9JdsS9MZ<`?|j? z+`%p!mxmJlMyfz26V4ZJv>Au@>V1KU^Zr_{98TUy_iY_vx^L_ka2kYP3!@?IxYZL= z9kOX%uIlw+1Za3G@UzpI4c=;2;6}HsxilCHW{4uyK>PTD-)qu2%g% z(|Zm3Brp2i2SV+Jhlbo(Om!%KM{TS6-DYR36rtW(u1@F58Cf&CE4u>c6b3$nc>Q)5 zx2?w37xR(hpY-{mQ%M9K$3z$T@Q|AeW{u4HC@X5EkUUz7>DnbXT6p=zpXky{sQHWa zuG-6+p%)?&Q@ldo*YP#87mYel;ks^%RY_gR_HI|*!kpIdi|I+C$LIQgGuco5rW9(k zt$g0J+%2pl+@YLBCEK|=urhUV+lt$5vGK7omvuNF&}vo!pc$Ti;a<8sZ20-oOcwZN zZy&llc?<6wh;!Avh%UP2EwBwgWlv}tcM9FR>rcE0FN}>Wf~D5@W;!>4c@vEn!1rsj zeLHM7N@J$>Js)>sJ6ueEmV(=hFPOV|If$+ez`nh`%$>SH*udsq-@e_H4Y)z<*v8%9 zcHD`#ep|N>5C7`P9{4%%(G<70FrFgcyU|dZKb74N0Vgwkl6V#P&Iwx|daLNZF8!iE zo8#HMw`NqcV?+UUYWq0cT=~d@FuLRRSR~^n45nTc)X;wvI*K$p9F?~(-CC!?GebzWsWX$?J`IL_K!V9p1}_z&lc{9s$dJttAK~QaZiWs z{eG!NQyB_Tgy|bL}h$JXqj#I;3?I0mE^NYJXcl8V?68 z&{G(4I!2NXhCT)`lWx1q`=sMeK?WY89eS}EN3wBr=?CtAW%@`DUtpvr)oCYPeHX%@ zvrl0#ij#7YD;J@W05Kg2aG_1d0w|=sn)g0fg9MW(vo6$eXP#nGdv_n7LWp}%ds)|; z$71Ey(XaXkMT}KXbXEEkNgE?bCmrO{L2e!V zd-;%=19Yz|*YZ=bJB)*)F#&X~YFvD$|L^YWue0lKuZ;?8Tf#)*NGcA`nRz6<{uW|8 zXjUq~t_mXCs<|Ci+KAAD?-qwd9=_0&iT~XA&xwD3ts(iQPX=<Je!Pgr5%GKS1s#dS6H#82+0?r_ujja1RC2v5hi-l#ZfK~!fgdW1 zI1ST(r_%g(tYjZa{hmx3c#_blfy$;I(VChJBYKf|U41mNFebx+W0d!6TdHoGdY292 zm9bg&LBmd=IEr2t!JCw%`r}6`qPo|{<$?WWfxBRPrua2wcaHXRACq^$-FK}u`j`TYXOmvWA z`6hmt0&y)B`2V}Fv##p4<^T7>9X2uqKmtpe_>s<-kRcy^(8ys_NLR$ipdS+vauE8_ z71lB6#e{tv#CHtEYz%pu#CBu_Z#*FuV~_+hlp`}tV`|jJbZX-|j}eX08)PwrK8(bF zm=J9-558=Ii=!9_ju?a(ue_pB5(A+RLq5k55?wJ6!%+UDXdpjU zOumRyPY^#(a6V_;y%0hlm~R+>FBnrV^6Amnsbx7xFg_J?D=tH;HnB!Mg&!F@b4yLt5w+n|MvKd0fd)B@Rk|M*_P9 zF&r^qIPvKGAzOlI!@w@C*e#A|8zGOKT_+Qc&=L%~5&;-RXNg2#@q`PBVVH;_@%So^ zkRT!FCSpi9Hi#$SN60aa2{MWL;SJ^y^f-w4L8E$1QdaxLTrk_1lf>G+ytDOvo84eF z6KZ#o2qGQ`5Zpm<{TRoWW;SWut>(-zQ8j&J}_ed zPMCNYe(!?-CCa^tZJ~|y@SFxYBB+8If$WM^-F`|0(MCfMm{O1*pkJ>4a|Cg(OF|lk zv6_JBf~7%lD8(O+%RRoNp~%JQh!gcJ`k~B#e=@f^@vo*>LxY@Cyg(R#&oo#Qs1pw& zXpUtZkAPH*IdHvVzn}i=j6iwmIA)?wBBK0EDBx)aIPabhM*$t!+Kn9^%<${30xRz7 zk>c-^1(BkLW|`;*b^@fh1KSR|?qT%A0dQvy%>QP!T>BYvVgBziCoBStN5fUgR7XQr z0iFaYWh~##y6p?ix_x$k=57CEBwBx?^M30li|h8Ya@~%&+7(zlvX3moCs97J47Xwr zQ7xaJC&Iv=0GrdPB)#?=Dr)NLv(`omGN+J34i36@*XU!uLFnY~u!E*=ZmwJ_q^9^V zb0+#3wg8LK9Gim%nXMa7zzr~syvL*SZ5>&45cS=~@%HGaSGp&E+fyIIqn}Y6%@Pjq z2D5|#xWPQ31I_wzO(8lFv2`JzNc&XKxpio_(yI;SR&)@0-zHIr+0GMD_9Ry5Om@$+ z-LX3sY6##Jv*>#WEMj^;+GKUxBZv`~e^)mbb_T#73f;~DjQy&`F`Su!F`|%l zTLllP*gg(CCyu`9e-6~L-3(-9@>pzi0e?IY{=vxvkkMe}V1UvMi0f+WwtN!c+&D7? zG#`E1L&V#EI84A-KHh9WajS2f!|Zcgp^27W{FzrPB=K+GLXQQKhI+F3C|JPWK z$moGh^xCyDQXA@to1r4gNOmC_2Yo2KK#@iZ#d^PgK5>SpSnmEOsuu&Vjtccu@41WV zz4|oN8C^~fkq7CutiSf6JIZDsEWysZ)d9P`{o<{WOiwI~tjyVa<1Kst<}GezQHub2 zl;ip-QU3NVMftce6A=18EA}wDp~a!fKQ46nIGZG%o>9mj7iLOA|4005S&jY-VPiLu z@9KSj6=O3_JUKDuV)u|+@9)e`cXI}~;F>kng@%5IoyK4lwNLBT#y$dQaF$zVdG8Dt z!r2Q%|5$peZXZNjZT1-PECf3V@eIp)Xpo67l()S1rcPX&<1fE@;gTG)c%zF>`LQqb z$=rA_a%B4W#l*?%+#h2IRp(}Sc@~xQ7|0SEhnq7S5 zf|%i-2!Kjtxz7BKqFhZf!xb#P{8<(;q`G(&qWVhxjUnh{J>6F!Z!g*Ydd_%*zd1D3 zHakbcuib=IW}|mkC+i8%Lf+zpZ9+>J@AAJLw-4ED#vuHXO?gHjX7ZjKH?rHD%@!Pg zkU1A*PC(|_m%psK8Vbu*2xPAQ#vpXE{?^Y!-r~33CbX2n$WmU+7@XsG-zFp|Gx8d7 z?#xT}y_pjfdHcUDSGrz##erFHVHN~tk$w3SZiK{wG=CL_SaKnj1Y!v|4Je2n$vzHI_>*#F9WP zIi=JXo^uh-3Bq}DDUH8xHX*^eNXNP4;#?A(OH$3U?%EX><%*zO^>4`x+cEQhnHy!P z{>CtLvi{9phP=h!Y@5(>UfOm{@U9rc85dzj5N4)ncg2C2b0Ovgg7sD#!vz;%K@fOv zwXtMB*(HHkraXyrF2XrMNO-G_`2`o}g5X?m-!@}>$wj#&D3_*O>37Y8ajyQx5OlKs zT{{nXi@$3&p=Er|d)JKNjEgXTBM3A86E@~^F3z0b%+qleT$}~LS#V2|F}mIP%v zDoL|22D>bs6O40c+nDa658*=njrrHf`m%Zv@)j?vHlgKvK|5K&TGiOjxF9nELi6B^P5!FqR3WC|*u&LV|E^2OZ{r%Mi$=`Wu7L z$@-#t8S)k{sy3med`UYe;TvO&XIzvSL6K$k`|_qyeV}s=%$y4|CopqfDKe%DF2;gj zEI6ge7%sU8OM<6_JR#w-nE&c&D$ zjCmTyf{U>r7_0(h43}Jgge5^pEI{#gs!d2hF6b+bgZlHi8|Jn88w1hF`f~a@bwBXD2;CQBIpadi2*iwe^NsC-3$h>}i`1JRFGw~afw65nU1vuy&bb)p1j7tW zz78PZSYYghR*7u!nfo0@%Z^EAXyUNB8ePLC(CpGp^OgD=^RAPB^;BPlyuD=m>-oN- zP0&zPW^HF&kQo7)O=intFmo=W80wf3l7GTi?JjaOHOGwhUZ*_ zbAn*rrx_~~7hH%70&$T9amj_aBoLRnqMpw}5Hs~R2B4Gm@7pZoE&jgQgqHAZ(2kxn zIKO1Go)MH8`%N%^)^je*oWR&-(EO5vvEX7X2*!f*CK$sd7hy>d%)2&Qav;vR5a$Hq z+!WJu4#Wi);(|b2&~E-!2u6Liq7Pgr>)(cX$Xonvun8^c`J_J(W*mPPY(j!D;}jrk z$j-)$=3{0vqRP-Bg2K#M;<$XmP`v^F)`G@z#)X&>2-4kLut3-u znbDlgUg_$8gLCT{^J2Nwyo7pQ?J!NhAnKfK{Ak*Fd-`Fyy*~HcqhUB(f(}h*WNK!6^C1&JOo-T69 zB}~g@E~Q*2m$V)=m&X>l-zSAJgj}Lrv+yV~MK0ex&-45JcRuHQKCk!t^FFWhK7YK= z>!4@6EpCo#!FqEN|5*SxRXvhWE@@yqS~8Jzn>;*w=L|Wc-s%lKFF=nc{&t;wE?9Dk z3cZcTXosZJ^crS7T%?n@e_9mjLX&oAm20MT!_L7 zk|i!A`>$|axuCX*{r@~r^`{#LKG1Ws(B0jOvQ-buzHDwv>JzkDq5tpA1xnX3I8^%$ z)nwRkVRD3cPO8)n{wCR!-8WJU%kq?t>g`&M&|qMPdkqC zI&b@B%sWbtQ!epc@8*y{PLnH+%*BSXq&|W8#`6e)rd+odoVN@*_^wqiUswRk|%kh)jvRX zQ2YRv*qjLXPSprdqBVk{foOp1O0*8huAc0kC#KtzWxtntmCQEL5y?m4ZcE}X>JjOb zOkfvRrbZHQE_>wpR!C03UMmXb86O0*3jmI5I6ho>H>ua)esEkcLB zu#Lq?ps~GPfWVGa9-LrsKPFV68E1p&hZD^2$AlwH8Z93W!3j>nHrk|d26_|1c-_Kj zkeDDXs~!W{aUoMMfxb&M<*dWyH(rN9eLZA%o(SFG1v?g#AcyS@1qAX^N8yAk_hV`x zWNrG?J}UDj+awd%t7`Mm3@#5tehCHqz?tWp!M$L}6?4EkRcAzr<|qdJsULN>Lv|TX zxTQifx;W3y&t861R8<{ko?U}L3U#9&+^1a!gs+QrjwKlKlj)QcCK zal57PZ;XlTa=;=MKWGL&{@;Ju^sd7R6ZgUO5P(i~ry|udlESvl1P-X$KnZXJ{NFjv zDTFE0un_2~G9avF0E!@}M}x!AfCf)Z8bo;w0g~^Gs5Ti4*S!EHz}KaaOV}vmv%O#h z0Uiy;NdbCltdyvh(QI23Z~$y$On~V**@58p+oO)Oz}+L6pz`XRX}fEaEMMkHU8x2sfAT48ovtIJ;YT z>nrM1K7;Lt0*-;*bj{%fGRP(UsJfZ6Ob8>F!#N=}k*7wX6+sv;I3kS@fSaF}BdE{G z{TXSOtxgt4i>hAAHere6JGp2e@rQ{-CQFpbs+q!4`Am>qoRy~b#R}Uv4Yo3^Oyd8t zkrQapc@h}skGtFE;EuQ$86&p8YXys^o5RhK$W>evZtiRagwe<0xPtsp;!}sH%t{J7 zDib)N>clgL%cGFm$FxBrpS0}h4(hw_ z?%oI^H+oC7K^R}9LGnelLS9g@FNPB_nmX9AOMu|FnH0pupF%G>mlqwV%1Qb5y0}Gh zsgJm_9+lb3&YuI$P>x?XVgYa1i{vWQRA^fo6G6z11#cJuHlAoWtI!&);ekxu zX%Pn~^b*H_s?KBE`Tz&eP6-z9-EzozT$IsVd_ROiv1;YGfOJvTHFEesUt&HNz)#U2 ztI+&+LsxNtFka?s1c4hHa~ILvFp)S0VccxxG)PX6R=jS3?3m(HFhgILYG1JG=~@eT zx7@!RnNA#mFg#j0xnh2E;i3ERfj5ZNiGU@lPKXNa$T@g0^ZYc&ArYO$Fr+dE*%Ut? z;27HNtp&U-3$tV!@HVD6qd~9WDyT2teUc|qZP0k^0e{fblr4G!EyjG3Pb==Ck&p*L zN8z!zn+p-T87@hDfvYJ^GzKNcm>*vbbR*~tNK^M)Rvg1P{D@JRP!4NLhfkOUv;mpE zZn4sdd_&^i5Q9^nk_x+(!}QWA!m;!JjA1g`6Zj5xLL^riF-DOn-B*J#bjF>`+oT)h zMJD_@HqFgG)^d_#rB%J?e_}d;{>!CDcQL;|i0T+WWUT+Rrcg~iI%G?0<=m$YhDGORJD;eyvxFlp*kKm7j*bukC z;<}&)MB6jzbD@`y%tkuEa=B;M-?fbtefsQ}ZE(e%q7Dg2Gn@Y;thlTu{`RkvRa_Hn zkxCTT+De_{T}NaW7+SGcCB6*Ho-W;|JS=sAD3Vnk=s!*ihN6 z!HPvSO2h-{y1y!2M`)E=wfgBY6}fPGRKP6#(Yiv9&rryAW;P|QxHRH6UPG|Lab#2OU)=0zZvpD-&MdqbZ6yp9~f1o?*a+=;x@x-*$0 zfzKuPUvAzGybG@ge^^}S?-WZn<0Sueo<9+{k^MO^v@^-}L&sH_fLkL;c~n>-fd-R<5Y`{r1PZ zQZzK)KYFUO+{D^NEcP9bcHfY>0P^@dkaVkRB_i6nVl4RFfm2HZFxx7pqrgiZr-G(z z54V**XtC+9ZI)MX?bWxdiPN#z8$pYp*ZeH{tM#w21V_Fo*UY zjFcaJR0_8ucL395(zTfKC-CZ2^3)xvxRvW-iW6lm@7!*VGe1R3{5aR}u|spd&PFVr z=G<}$`Mdj-P$-jmtMT`8v2}JOel@5;tLQ{B;9T)Bm&^wx0{!;Fdr(G8m38E4*t^nM z{CSGxuFyCsw>44HZjPgv!jm&EO(H|MovGU$W_u4f>QX*BRXNn~BHt4~*KaA&hT31c ztn^yi#|U8O+5Uq6YHtwX2ipQ?{$KMw12p_uk~u7S3#j zzX1m0z6DC`u)JC;j+fT5zg_EWw3i4`$!AcA5swB3=UZRLDJ_p0SW*jTgY6A1XR=&g z^z`wwkM2aSm2alXmf<~$nj|r=YCsSC`(;gEJB$RDb#W7F(z&pe*946jY#9k{aK#RdiEOI_47- zPHU1sTTfQ-S-WwlV+(s%>+`+%xUpq+Uf@P*=ruJ?7kF;ApFV5eM@_KqpY*UdK zcfEetdSQ>5?Io>Da1Jv23tOMnKZ)=f4(n>Ua&k4~hIY~XHNV@IB@5?^K>@nqJKOJ` qRZVX{mIViWKl*#0zW>JoV>k2L`~M6-pj*AylyWM1yD6il2k<{%P!b^k diff --git a/docs/versions.yaml b/docs/versions.yaml index 43dfa10ac3cb..580e355d1ab6 100644 --- a/docs/versions.yaml +++ b/docs/versions.yaml @@ -22,4 +22,4 @@ "1.26": 1.26.7 "1.27": 1.27.3 "1.28": 1.28.1 -"1.29": 1.29.1 +"1.29": 1.29.2 From 79c64c5b4e0b2321d515690c7d7a956d190a8aa4 Mon Sep 17 00:00:00 2001 From: "Adi (Suissa) Peleg" Date: Wed, 20 Mar 2024 11:07:15 -0400 Subject: [PATCH 104/124] tests: refactor SSL TAP tests (#32992) Moved SSL TAP based tests to be under the transport_socket/tap extension directory (old TODO). --------- Signed-off-by: Adi Suissa-Peleg --- test/common/tls/integration/BUILD | 22 +- .../tls/integration/ssl_integration_test.cc | 553 +++--------------- .../integration/ssl_integration_test_base.cc | 66 +++ ...ion_test.h => ssl_integration_test_base.h} | 7 - test/extensions/transport_sockets/tap/BUILD | 21 + .../tap/ssl_tap_integration_test.cc | 329 +++++++++++ 6 files changed, 516 insertions(+), 482 deletions(-) create mode 100644 test/common/tls/integration/ssl_integration_test_base.cc rename test/common/tls/integration/{ssl_integration_test.h => ssl_integration_test_base.h} (84%) create mode 100644 test/extensions/transport_sockets/tap/ssl_tap_integration_test.cc diff --git a/test/common/tls/integration/BUILD b/test/common/tls/integration/BUILD index a8dc565d34cb..9ecd12407c87 100644 --- a/test/common/tls/integration/BUILD +++ b/test/common/tls/integration/BUILD @@ -3,7 +3,6 @@ load( "envoy_cc_test", "envoy_cc_test_library", "envoy_package", - "envoy_select_admin_functionality", ) licenses(["notice"]) # Apache 2 @@ -12,7 +11,16 @@ envoy_package() envoy_cc_test_library( name = "ssl_integration_test_lib", - hdrs = ["ssl_integration_test.h"], + srcs = [ + "ssl_integration_test_base.cc", + ], + hdrs = ["ssl_integration_test_base.h"], + deps = [ + "//test/common/config:dummy_config_proto_cc_proto", + "//test/integration:http_integration_lib", + "//test/mocks/secret:secret_mocks", + "//test/test_common:utility_lib", + ], ) envoy_cc_test( @@ -37,7 +45,6 @@ envoy_cc_test( "//source/extensions/transport_sockets/tls:config", "//test/common/config:dummy_config_proto_cc_proto", "//test/common/tls/cert_validator:timed_cert_validator", - "//test/integration:http_integration_lib", "//test/integration/filters:stream_info_to_headers_filter_lib", "//test/mocks/secret:secret_mocks", "//test/test_common:test_runtime_lib", @@ -45,14 +52,7 @@ envoy_cc_test( "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", - "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", - ] + envoy_select_admin_functionality([ - "//source/extensions/transport_sockets/tap:config", - "//test/extensions/common/tap:common", - "@envoy_api//envoy/config/tap/v3:pkg_cc_proto", - "@envoy_api//envoy/data/tap/v3:pkg_cc_proto", - "@envoy_api//envoy/extensions/transport_sockets/tap/v3:pkg_cc_proto", - ]), + ], ) envoy_cc_test_library( diff --git a/test/common/tls/integration/ssl_integration_test.cc b/test/common/tls/integration/ssl_integration_test.cc index ffae471e9e90..27de146cd5fe 100644 --- a/test/common/tls/integration/ssl_integration_test.cc +++ b/test/common/tls/integration/ssl_integration_test.cc @@ -1,5 +1,3 @@ -#include "ssl_integration_test.h" - #include #include @@ -19,6 +17,7 @@ #include "test/common/config/dummy_config.pb.h" #include "test/common/tls/cert_validator/timed_cert_validator.h" +#include "test/common/tls/integration/ssl_integration_test_base.h" #include "test/integration/autonomous_upstream.h" #include "test/integration/integration.h" #include "test/integration/ssl_utility.h" @@ -33,14 +32,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#ifdef ENVOY_ADMIN_FUNCTIONALITY -#include "envoy/config/tap/v3/common.pb.h" -#include "envoy/data/tap/v3/wrapper.pb.h" -#include "envoy/extensions/transport_sockets/tap/v3/tap.pb.h" -#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" -#include "test/extensions/common/tap/common.h" -#endif - using testing::StartsWith; namespace Envoy { @@ -49,155 +40,11 @@ using Extensions::TransportSockets::Tls::ContextImplPeer; namespace Ssl { -void SslIntegrationTestBase::initialize() { - config_helper_.addSslConfig(ConfigHelper::ServerSslOptions() - .setRsaCert(server_rsa_cert_) - .setRsaCertOcspStaple(server_rsa_cert_ocsp_staple_) - .setEcdsaCert(server_ecdsa_cert_) - .setEcdsaCertOcspStaple(server_ecdsa_cert_ocsp_staple_) - .setOcspStapleRequired(ocsp_staple_required_) - .setTlsV13(server_tlsv1_3_) - .setCurves(server_curves_) - .setExpectClientEcdsaCert(client_ecdsa_cert_) - .setTlsKeyLogFilter(keylog_local_, keylog_remote_, - keylog_local_negative_, - keylog_remote_negative_, keylog_path_, - keylog_multiple_ips_, version_)); - - HttpIntegrationTest::initialize(); - - context_manager_ = std::make_unique( - server_factory_context_); - - registerTestServerPorts({"http"}); -} - -void SslIntegrationTestBase::TearDown() { - HttpIntegrationTest::cleanupUpstreamAndDownstream(); - codec_client_.reset(); - context_manager_.reset(); -} - -Network::ClientConnectionPtr -SslIntegrationTestBase::makeSslClientConnection(const ClientSslTransportOptions& options) { - Network::Address::InstanceConstSharedPtr address = getSslAddress(version_, lookupPort("http")); - if (debug_with_s_client_) { - const std::string s_client_cmd = TestEnvironment::substitute( - "openssl s_client -connect " + address->asString() + - " -showcerts -debug -msg -CAfile " - "{{ test_rundir }}/test/config/integration/certs/cacert.pem " - "-servername lyft.com -cert " - "{{ test_rundir }}/test/config/integration/certs/clientcert.pem " - "-key " - "{{ test_rundir }}/test/config/integration/certs/clientkey.pem ", - version_); - ENVOY_LOG_MISC(debug, "Executing {}", s_client_cmd); - RELEASE_ASSERT(::system(s_client_cmd.c_str()) == 0, ""); - } - auto client_transport_socket_factory_ptr = - createClientSslTransportSocketFactory(options, *context_manager_, *api_); - return dispatcher_->createClientConnection( - address, Network::Address::InstanceConstSharedPtr(), - client_transport_socket_factory_ptr->createTransportSocket({}, nullptr), nullptr, nullptr); -} - -void SslIntegrationTestBase::checkStats() { - const uint32_t expected_handshakes = debug_with_s_client_ ? 2 : 1; - Stats::CounterSharedPtr counter = test_server_->counter(listenerStatPrefix("ssl.handshake")); - EXPECT_EQ(expected_handshakes, counter->value()); - counter->reset(); -} - -class SslKeyLogTest : public SslIntegrationTest { +class SslIntegrationTest : public testing::TestWithParam, + public SslIntegrationTestBase { public: - void setLogPath() { - keylog_path_ = TestEnvironment::temporaryPath(TestUtility::uniqueFilename()); - } - void setLocalFilter() { - keylog_local_ = true; - keylog_remote_ = false; - keylog_local_negative_ = false; - keylog_remote_negative_ = false; - } - void setRemoteFilter() { - keylog_remote_ = true; - keylog_local_ = false; - keylog_local_negative_ = false; - keylog_remote_negative_ = false; - } - void setBothLocalAndRemoteFilter() { - keylog_local_ = true; - keylog_remote_ = true; - keylog_local_negative_ = false; - keylog_remote_negative_ = false; - } - void setNeitherLocalNorRemoteFilter() { - keylog_remote_ = false; - keylog_local_ = false; - keylog_local_negative_ = false; - keylog_remote_negative_ = false; - } - void setNegative() { - keylog_local_ = true; - keylog_remote_ = true; - keylog_local_negative_ = true; - keylog_remote_negative_ = true; - } - void setLocalNegative() { - keylog_local_ = true; - keylog_remote_ = true; - keylog_local_negative_ = false; - keylog_remote_negative_ = true; - } - void setRemoteNegative() { - keylog_local_ = true; - keylog_remote_ = true; - keylog_local_negative_ = true; - keylog_remote_negative_ = false; - } - void setMultipleIps() { - keylog_local_ = true; - keylog_remote_ = true; - keylog_local_negative_ = false; - keylog_remote_negative_ = false; - keylog_multiple_ips_ = true; - } - void logCheck() { - EXPECT_TRUE(api_->fileSystem().fileExists(keylog_path_)); - std::string log = waitForAccessLog(keylog_path_); - if (server_tlsv1_3_) { - /** The key log for TLS1.3 is as follows: - * CLIENT_HANDSHAKE_TRAFFIC_SECRET - c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 - b335f2ce9079d824a7d2f5ef9af6572d43942d6803bac1ae9de1e840c15c993ae4efdf4ac087877031d1936d5bb858e3 - SERVER_HANDSHAKE_TRAFFIC_SECRET - c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 - f498c03446c936d8a17f31669dd54cee2d9bc8d5b7e1a658f677b5cd6e0965111c2331fcc337c01895ec9a0ed12be34a - CLIENT_TRAFFIC_SECRET_0 c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 - 0bbbb2056f3d35a3b610c5cc8ae0b9b63a120912ff25054ee52b853fefc59e12e9fdfebc409347c737394457bfd36bde - SERVER_TRAFFIC_SECRET_0 c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 - bd3e1757174d82c308515a0c02b981084edda53e546df551ddcf78043bff831c07ff93c7ab3e8ef9e2206c8319c25331 - EXPORTER_SECRET c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 - 6bd19fbdd12e6710159bcb406fd42a580c41236e2d53072dba3064f9b3ff214662081f023e9b22325e31fee5bb11b172 - */ - EXPECT_THAT(log, testing::HasSubstr("CLIENT_TRAFFIC_SECRET")); - EXPECT_THAT(log, testing::HasSubstr("SERVER_TRAFFIC_SECRET")); - EXPECT_THAT(log, testing::HasSubstr("CLIENT_HANDSHAKE_TRAFFIC_SECRET")); - EXPECT_THAT(log, testing::HasSubstr("SERVER_HANDSHAKE_TRAFFIC_SECRET")); - EXPECT_THAT(log, testing::HasSubstr("EXPORTER_SECRET")); - } else { - /** The key log for TLS1.1/1.2 is as follows: - * CLIENT_RANDOM 5a479a50fe3e85295840b84e298aeb184cecc34ced22d963e16b01dc48c9530f - d6840f8100e4ceeb282946cdd72fe403b8d0724ee816ab2d0824b6d6b5033d333ec4b2e77f515226f5d829e137855ef1 - */ - EXPECT_THAT(log, testing::HasSubstr("CLIENT_RANDOM")); - } - } - void negativeCheck() { - EXPECT_TRUE(api_->fileSystem().fileExists(keylog_path_)); - auto size = api_->fileSystem().fileSize(keylog_path_); - EXPECT_EQ(size, 0); - } + SslIntegrationTest() : SslIntegrationTestBase(GetParam()) {} + void TearDown() override { SslIntegrationTestBase::TearDown(); }; }; INSTANTIATE_TEST_SUITE_P(IpVersions, SslIntegrationTest, @@ -1096,320 +943,98 @@ TEST_P(SslCertficateIntegrationTest, BothEcdsaAndRsaWithOcspResponseStaplingRequ checkStats(); } -#ifdef ENVOY_ADMIN_FUNCTIONALITY -// TODO(zuercher): write an additional OCSP integration test that validates behavior with an -// expired OCSP response. (Requires OCSP client-side support in upstream TLS.) - -// TODO(mattklein123): Move this into a dedicated integration test for the tap transport socket as -// well as add more tests. -class SslTapIntegrationTest : public SslIntegrationTest { +class SslKeyLogTest : public SslIntegrationTest { public: - void initialize() override { - // TODO(mattklein123): Merge/use the code in ConfigHelper::setTapTransportSocket(). - config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - // The test supports tapping either the downstream or upstream connection, but not both. - if (upstream_tap_) { - setupUpstreamTap(bootstrap); - } else { - setupDownstreamTap(bootstrap); - } - }); - SslIntegrationTest::initialize(); - // This confuses our socket counting. - debug_with_s_client_ = false; + void setLogPath() { + keylog_path_ = TestEnvironment::temporaryPath(TestUtility::uniqueFilename()); } - - void setupUpstreamTap(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.tap"); - envoy::config::core::v3::TransportSocket raw_transport_socket; - raw_transport_socket.set_name("envoy.transport_sockets.raw_buffer"); - envoy::extensions::transport_sockets::tap::v3::Tap tap_config = - createTapConfig(raw_transport_socket); - tap_config.mutable_transport_socket()->MergeFrom(raw_transport_socket); - transport_socket->mutable_typed_config()->PackFrom(tap_config); + void setLocalFilter() { + keylog_local_ = true; + keylog_remote_ = false; + keylog_local_negative_ = false; + keylog_remote_negative_ = false; } - - void setupDownstreamTap(envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - auto* filter_chain = - bootstrap.mutable_static_resources()->mutable_listeners(0)->mutable_filter_chains(0); - // Configure inner SSL transport socket based on existing config. - envoy::config::core::v3::TransportSocket ssl_transport_socket; - auto* transport_socket = filter_chain->mutable_transport_socket(); - ssl_transport_socket.Swap(transport_socket); - // Configure outer tap transport socket. - transport_socket->set_name("envoy.transport_sockets.tap"); - envoy::extensions::transport_sockets::tap::v3::Tap tap_config = - createTapConfig(ssl_transport_socket); - tap_config.mutable_transport_socket()->MergeFrom(ssl_transport_socket); - transport_socket->mutable_typed_config()->PackFrom(tap_config); + void setRemoteFilter() { + keylog_remote_ = true; + keylog_local_ = false; + keylog_local_negative_ = false; + keylog_remote_negative_ = false; } - - envoy::extensions::transport_sockets::tap::v3::Tap - createTapConfig(const envoy::config::core::v3::TransportSocket& inner_transport) { - envoy::extensions::transport_sockets::tap::v3::Tap tap_config; - tap_config.mutable_common_config()->mutable_static_config()->mutable_match()->set_any_match( - true); - auto* output_config = - tap_config.mutable_common_config()->mutable_static_config()->mutable_output_config(); - if (max_rx_bytes_.has_value()) { - output_config->mutable_max_buffered_rx_bytes()->set_value(max_rx_bytes_.value()); - } - if (max_tx_bytes_.has_value()) { - output_config->mutable_max_buffered_tx_bytes()->set_value(max_tx_bytes_.value()); + void setBothLocalAndRemoteFilter() { + keylog_local_ = true; + keylog_remote_ = true; + keylog_local_negative_ = false; + keylog_remote_negative_ = false; + } + void setNeitherLocalNorRemoteFilter() { + keylog_remote_ = false; + keylog_local_ = false; + keylog_local_negative_ = false; + keylog_remote_negative_ = false; + } + void setNegative() { + keylog_local_ = true; + keylog_remote_ = true; + keylog_local_negative_ = true; + keylog_remote_negative_ = true; + } + void setLocalNegative() { + keylog_local_ = true; + keylog_remote_ = true; + keylog_local_negative_ = false; + keylog_remote_negative_ = true; + } + void setRemoteNegative() { + keylog_local_ = true; + keylog_remote_ = true; + keylog_local_negative_ = true; + keylog_remote_negative_ = false; + } + void setMultipleIps() { + keylog_local_ = true; + keylog_remote_ = true; + keylog_local_negative_ = false; + keylog_remote_negative_ = false; + keylog_multiple_ips_ = true; + } + void logCheck() { + EXPECT_TRUE(api_->fileSystem().fileExists(keylog_path_)); + std::string log = waitForAccessLog(keylog_path_); + if (server_tlsv1_3_) { + /** The key log for TLS1.3 is as follows: + * CLIENT_HANDSHAKE_TRAFFIC_SECRET + c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 + b335f2ce9079d824a7d2f5ef9af6572d43942d6803bac1ae9de1e840c15c993ae4efdf4ac087877031d1936d5bb858e3 + SERVER_HANDSHAKE_TRAFFIC_SECRET + c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 + f498c03446c936d8a17f31669dd54cee2d9bc8d5b7e1a658f677b5cd6e0965111c2331fcc337c01895ec9a0ed12be34a + CLIENT_TRAFFIC_SECRET_0 c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 + 0bbbb2056f3d35a3b610c5cc8ae0b9b63a120912ff25054ee52b853fefc59e12e9fdfebc409347c737394457bfd36bde + SERVER_TRAFFIC_SECRET_0 c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 + bd3e1757174d82c308515a0c02b981084edda53e546df551ddcf78043bff831c07ff93c7ab3e8ef9e2206c8319c25331 + EXPORTER_SECRET c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 + 6bd19fbdd12e6710159bcb406fd42a580c41236e2d53072dba3064f9b3ff214662081f023e9b22325e31fee5bb11b172 + */ + EXPECT_THAT(log, testing::HasSubstr("CLIENT_TRAFFIC_SECRET")); + EXPECT_THAT(log, testing::HasSubstr("SERVER_TRAFFIC_SECRET")); + EXPECT_THAT(log, testing::HasSubstr("CLIENT_HANDSHAKE_TRAFFIC_SECRET")); + EXPECT_THAT(log, testing::HasSubstr("SERVER_HANDSHAKE_TRAFFIC_SECRET")); + EXPECT_THAT(log, testing::HasSubstr("EXPORTER_SECRET")); + } else { + /** The key log for TLS1.1/1.2 is as follows: + * CLIENT_RANDOM 5a479a50fe3e85295840b84e298aeb184cecc34ced22d963e16b01dc48c9530f + d6840f8100e4ceeb282946cdd72fe403b8d0724ee816ab2d0824b6d6b5033d333ec4b2e77f515226f5d829e137855ef1 + */ + EXPECT_THAT(log, testing::HasSubstr("CLIENT_RANDOM")); } - output_config->set_streaming(streaming_tap_); - - auto* output_sink = output_config->mutable_sinks()->Add(); - output_sink->set_format(format_); - output_sink->mutable_file_per_tap()->set_path_prefix(path_prefix_); - tap_config.mutable_transport_socket()->MergeFrom(inner_transport); - return tap_config; } - - std::string path_prefix_ = TestEnvironment::temporaryPath("ssl_trace"); - envoy::config::tap::v3::OutputSink::Format format_{ - envoy::config::tap::v3::OutputSink::PROTO_BINARY}; - absl::optional max_rx_bytes_; - absl::optional max_tx_bytes_; - bool upstream_tap_{}; - bool streaming_tap_{}; + void negativeCheck() { + EXPECT_TRUE(api_->fileSystem().fileExists(keylog_path_)); + auto size = api_->fileSystem().fileSize(keylog_path_); + EXPECT_EQ(size, 0); + } }; -INSTANTIATE_TEST_SUITE_P(IpVersions, SslTapIntegrationTest, - testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), - TestUtility::ipTestParamsToString); - -// Validate two back-to-back requests with binary proto output. -TEST_P(SslTapIntegrationTest, TwoRequestsWithBinaryProto) { - initialize(); - ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection({}); - }; - - // First request (ID will be +1 since the client will also bump). - const uint64_t first_id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; - codec_client_ = makeHttpConnection(creator()); - Http::TestRequestHeaderMapImpl post_request_headers{ - {":method", "POST"}, {":path", "/test/long/url"}, - {":scheme", "http"}, {":authority", "sni.lyft.com"}, - {"x-lyft-user-id", "123"}, {"x-forwarded-for", "10.0.0.1"}}; - auto response = - sendRequestAndWaitForResponse(post_request_headers, 128, default_response_headers_, 256); - EXPECT_TRUE(upstream_request_->complete()); - EXPECT_EQ(128, upstream_request_->bodyLength()); - ASSERT_TRUE(response->complete()); - EXPECT_EQ("200", response->headers().getStatusValue()); - EXPECT_EQ(256, response->body().size()); - checkStats(); - envoy::config::core::v3::Address expected_local_address; - Network::Utility::addressToProtobufAddress( - *codec_client_->connection()->connectionInfoProvider().remoteAddress(), - expected_local_address); - envoy::config::core::v3::Address expected_remote_address; - Network::Utility::addressToProtobufAddress( - *codec_client_->connection()->connectionInfoProvider().localAddress(), - expected_remote_address); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); - envoy::data::tap::v3::TraceWrapper trace; - TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, first_id), trace, *api_); - // Validate general expected properties in the trace. - EXPECT_EQ(first_id, trace.socket_buffered_trace().trace_id()); - EXPECT_THAT(expected_local_address, - ProtoEq(trace.socket_buffered_trace().connection().local_address())); - EXPECT_THAT(expected_remote_address, - ProtoEq(trace.socket_buffered_trace().connection().remote_address())); - ASSERT_GE(trace.socket_buffered_trace().events().size(), 2); - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), - "POST /test/long/url HTTP/1.1")); - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), - "HTTP/1.1 200 OK")); - EXPECT_FALSE(trace.socket_buffered_trace().read_truncated()); - EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); - - // Verify a second request hits a different file. - const uint64_t second_id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; - codec_client_ = makeHttpConnection(creator()); - Http::TestRequestHeaderMapImpl get_request_headers{ - {":method", "GET"}, {":path", "/test/long/url"}, - {":scheme", "http"}, {":authority", "sni.lyft.com"}, - {"x-lyft-user-id", "123"}, {"x-forwarded-for", "10.0.0.1"}}; - response = - sendRequestAndWaitForResponse(get_request_headers, 128, default_response_headers_, 256); - EXPECT_TRUE(upstream_request_->complete()); - EXPECT_EQ(128, upstream_request_->bodyLength()); - ASSERT_TRUE(response->complete()); - EXPECT_EQ("200", response->headers().getStatusValue()); - EXPECT_EQ(256, response->body().size()); - checkStats(); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 2); - TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, second_id), trace, *api_); - // Validate second connection ID. - EXPECT_EQ(second_id, trace.socket_buffered_trace().trace_id()); - ASSERT_GE(trace.socket_buffered_trace().events().size(), 2); - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), - "GET /test/long/url HTTP/1.1")); - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), - "HTTP/1.1 200 OK")); - EXPECT_FALSE(trace.socket_buffered_trace().read_truncated()); - EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); -} - -// Verify that truncation works correctly across multiple transport socket frames. -TEST_P(SslTapIntegrationTest, TruncationWithMultipleDataFrames) { - max_rx_bytes_ = 4; - max_tx_bytes_ = 5; - - initialize(); - ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection({}); - }; - - const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; - codec_client_ = makeHttpConnection(creator()); - const Http::TestRequestHeaderMapImpl request_headers{{":method", "GET"}, - {":path", "/test/long/url"}, - {":scheme", "http"}, - {":authority", "sni.lyft.com"}}; - auto result = codec_client_->startRequest(request_headers); - auto response = std::move(result.second); - Buffer::OwnedImpl data1("one"); - result.first.encodeData(data1, false); - Buffer::OwnedImpl data2("two"); - result.first.encodeData(data2, true); - waitForNextUpstreamRequest(); - const Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; - upstream_request_->encodeHeaders(response_headers, false); - Buffer::OwnedImpl data3("three"); - upstream_request_->encodeData(data3, false); - response->waitForBodyData(5); - Buffer::OwnedImpl data4("four"); - upstream_request_->encodeData(data4, true); - ASSERT_TRUE(response->waitForEndStream()); - - checkStats(); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); - - envoy::data::tap::v3::TraceWrapper trace; - TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, id), trace, *api_); - - ASSERT_EQ(trace.socket_buffered_trace().events().size(), 2); - EXPECT_TRUE(trace.socket_buffered_trace().events(0).read().data().truncated()); - EXPECT_TRUE(trace.socket_buffered_trace().events(1).write().data().truncated()); - EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); - EXPECT_TRUE(trace.socket_buffered_trace().write_truncated()); -} - -// Validate a single request with text proto output. -TEST_P(SslTapIntegrationTest, RequestWithTextProto) { - format_ = envoy::config::tap::v3::OutputSink::PROTO_TEXT; - ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection({}); - }; - - // Disable for this test because it uses connection IDs, which disrupts the accounting below - // leading to the wrong path for the `pb_text` being used. - skip_tag_extraction_rule_check_ = true; - - const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; - testRouterRequestAndResponseWithBody(1024, 512, false, false, &creator); - checkStats(); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); - envoy::data::tap::v3::TraceWrapper trace; - TestUtility::loadFromFile(fmt::format("{}_{}.pb_text", path_prefix_, id), trace, *api_); - // Test some obvious properties. - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), - "GET /test/long/url HTTP/1.1")); - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), - "HTTP/1.1 200 OK")); - EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); - EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); -} - -// Validate a single request with JSON (body as string) output. This test uses an upstream tap. -TEST_P(SslTapIntegrationTest, RequestWithJsonBodyAsStringUpstreamTap) { - upstream_tap_ = true; - max_rx_bytes_ = 5; - max_tx_bytes_ = 4; - - format_ = envoy::config::tap::v3::OutputSink::JSON_BODY_AS_STRING; - ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection({}); - }; - - // Disable for this test because it uses connection IDs, which disrupts the accounting below - // leading to the wrong path for the `pb_text` being used. - skip_tag_extraction_rule_check_ = true; - - const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 2; - testRouterRequestAndResponseWithBody(512, 1024, false, false, &creator); - checkStats(); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); - test_server_.reset(); - - // This must be done after server shutdown so that connection pool connections are closed and - // the tap written. - envoy::data::tap::v3::TraceWrapper trace; - TestUtility::loadFromFile(fmt::format("{}_{}.json", path_prefix_, id), trace, *api_); - - // Test some obvious properties. - EXPECT_EQ(trace.socket_buffered_trace().events(0).write().data().as_string(), "GET "); - EXPECT_EQ(trace.socket_buffered_trace().events(1).read().data().as_string(), "HTTP/"); - EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); - EXPECT_TRUE(trace.socket_buffered_trace().write_truncated()); -} - -// Validate a single request with length delimited binary proto output. This test uses an upstream -// tap. -TEST_P(SslTapIntegrationTest, RequestWithStreamingUpstreamTap) { - upstream_tap_ = true; - streaming_tap_ = true; - max_rx_bytes_ = 5; - max_tx_bytes_ = 4; - - format_ = envoy::config::tap::v3::OutputSink::PROTO_BINARY_LENGTH_DELIMITED; - ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection({}); - }; - - // Disable for this test because it uses connection IDs, which disrupts the accounting below - // leading to the wrong path for the `pb_text` being used. - skip_tag_extraction_rule_check_ = true; - - const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 2; - testRouterRequestAndResponseWithBody(512, 1024, false, false, &creator); - checkStats(); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); - test_server_.reset(); - - // This must be done after server shutdown so that connection pool connections are closed and - // the tap written. - std::vector traces = - Extensions::Common::Tap::readTracesFromFile( - fmt::format("{}_{}.pb_length_delimited", path_prefix_, id)); - ASSERT_GE(traces.size(), 4); - - // The initial connection message has no local address, but has a remote address (not connected - // yet). - EXPECT_TRUE(traces[0].socket_streamed_trace_segment().has_connection()); - EXPECT_FALSE(traces[0].socket_streamed_trace_segment().connection().has_local_address()); - EXPECT_TRUE(traces[0].socket_streamed_trace_segment().connection().has_remote_address()); - - // Verify truncated request/response data. - EXPECT_EQ(traces[1].socket_streamed_trace_segment().event().write().data().as_bytes(), "GET "); - EXPECT_TRUE(traces[1].socket_streamed_trace_segment().event().write().data().truncated()); - EXPECT_EQ(traces[2].socket_streamed_trace_segment().event().read().data().as_bytes(), "HTTP/"); - EXPECT_TRUE(traces[2].socket_streamed_trace_segment().event().read().data().truncated()); -} -#endif - INSTANTIATE_TEST_SUITE_P(IpVersions, SslKeyLogTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), TestUtility::ipTestParamsToString); diff --git a/test/common/tls/integration/ssl_integration_test_base.cc b/test/common/tls/integration/ssl_integration_test_base.cc new file mode 100644 index 000000000000..2a89ffee0972 --- /dev/null +++ b/test/common/tls/integration/ssl_integration_test_base.cc @@ -0,0 +1,66 @@ +#include "ssl_integration_test_base.h" + +namespace Envoy { +namespace Ssl { + +void SslIntegrationTestBase::initialize() { + config_helper_.addSslConfig(ConfigHelper::ServerSslOptions() + .setRsaCert(server_rsa_cert_) + .setRsaCertOcspStaple(server_rsa_cert_ocsp_staple_) + .setEcdsaCert(server_ecdsa_cert_) + .setEcdsaCertOcspStaple(server_ecdsa_cert_ocsp_staple_) + .setOcspStapleRequired(ocsp_staple_required_) + .setTlsV13(server_tlsv1_3_) + .setCurves(server_curves_) + .setExpectClientEcdsaCert(client_ecdsa_cert_) + .setTlsKeyLogFilter(keylog_local_, keylog_remote_, + keylog_local_negative_, + keylog_remote_negative_, keylog_path_, + keylog_multiple_ips_, version_)); + + HttpIntegrationTest::initialize(); + + context_manager_ = std::make_unique( + server_factory_context_); + + registerTestServerPorts({"http"}); +} + +void SslIntegrationTestBase::TearDown() { + HttpIntegrationTest::cleanupUpstreamAndDownstream(); + codec_client_.reset(); + context_manager_.reset(); +} + +Network::ClientConnectionPtr +SslIntegrationTestBase::makeSslClientConnection(const ClientSslTransportOptions& options) { + Network::Address::InstanceConstSharedPtr address = getSslAddress(version_, lookupPort("http")); + if (debug_with_s_client_) { + const std::string s_client_cmd = TestEnvironment::substitute( + "openssl s_client -connect " + address->asString() + + " -showcerts -debug -msg -CAfile " + "{{ test_rundir }}/test/config/integration/certs/cacert.pem " + "-servername lyft.com -cert " + "{{ test_rundir }}/test/config/integration/certs/clientcert.pem " + "-key " + "{{ test_rundir }}/test/config/integration/certs/clientkey.pem ", + version_); + ENVOY_LOG_MISC(debug, "Executing {}", s_client_cmd); + RELEASE_ASSERT(::system(s_client_cmd.c_str()) == 0, ""); + } + auto client_transport_socket_factory_ptr = + createClientSslTransportSocketFactory(options, *context_manager_, *api_); + return dispatcher_->createClientConnection( + address, Network::Address::InstanceConstSharedPtr(), + client_transport_socket_factory_ptr->createTransportSocket({}, nullptr), nullptr, nullptr); +} + +void SslIntegrationTestBase::checkStats() { + const uint32_t expected_handshakes = debug_with_s_client_ ? 2 : 1; + Stats::CounterSharedPtr counter = test_server_->counter(listenerStatPrefix("ssl.handshake")); + EXPECT_EQ(expected_handshakes, counter->value()); + counter->reset(); +} + +} // namespace Ssl +} // namespace Envoy diff --git a/test/common/tls/integration/ssl_integration_test.h b/test/common/tls/integration/ssl_integration_test_base.h similarity index 84% rename from test/common/tls/integration/ssl_integration_test.h rename to test/common/tls/integration/ssl_integration_test_base.h index b2bd345a4288..63366d2d1ff9 100644 --- a/test/common/tls/integration/ssl_integration_test.h +++ b/test/common/tls/integration/ssl_integration_test_base.h @@ -49,12 +49,5 @@ class SslIntegrationTestBase : public HttpIntegrationTest { std::unique_ptr context_manager_; }; -class SslIntegrationTest : public testing::TestWithParam, - public SslIntegrationTestBase { -public: - SslIntegrationTest() : SslIntegrationTestBase(GetParam()) {} - void TearDown() override { SslIntegrationTestBase::TearDown(); }; -}; - } // namespace Ssl } // namespace Envoy diff --git a/test/extensions/transport_sockets/tap/BUILD b/test/extensions/transport_sockets/tap/BUILD index ca752227472b..6071e12726cd 100644 --- a/test/extensions/transport_sockets/tap/BUILD +++ b/test/extensions/transport_sockets/tap/BUILD @@ -1,6 +1,7 @@ load( "//bazel:envoy_build_system.bzl", "envoy_package", + "envoy_select_admin_functionality", ) load( "//test/extensions:extensions_build_system.bzl", @@ -22,3 +23,23 @@ envoy_extension_cc_test( "//test/test_common:simulated_time_system_lib", ], ) + +envoy_extension_cc_test( + name = "ssl_tap_integration_test", + srcs = envoy_select_admin_functionality(["ssl_tap_integration_test.cc"]), + data = [ + "//test/config/integration/certs", + ], + extension_names = ["envoy.transport_sockets.tap"], + deps = [ + "//source/common/network:connection_lib", + "//source/extensions/transport_sockets/tap:config", + "//source/extensions/transport_sockets/tls:config", + "//test/common/tls/integration:ssl_integration_test_lib", + "//test/extensions/common/tap:common", + "@envoy_api//envoy/config/tap/v3:pkg_cc_proto", + "@envoy_api//envoy/data/tap/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/transport_sockets/tap/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/transport_sockets/tap/ssl_tap_integration_test.cc b/test/extensions/transport_sockets/tap/ssl_tap_integration_test.cc new file mode 100644 index 000000000000..3eee173d9a04 --- /dev/null +++ b/test/extensions/transport_sockets/tap/ssl_tap_integration_test.cc @@ -0,0 +1,329 @@ +#include "envoy/config/tap/v3/common.pb.h" +#include "envoy/data/tap/v3/wrapper.pb.h" +#include "envoy/extensions/transport_sockets/tap/v3/tap.pb.h" +#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" + +#include "source/common/network/connection_impl.h" + +#include "test/common/tls/integration/ssl_integration_test_base.h" +#include "test/extensions/common/tap/common.h" + +namespace Envoy { +namespace Ssl { + +// TODO(zuercher): write an additional OCSP integration test that validates behavior with an +// expired OCSP response. (Requires OCSP client-side support in upstream TLS.) +class SslTapIntegrationTest : public testing::TestWithParam, + public SslIntegrationTestBase { +public: + SslTapIntegrationTest() : SslIntegrationTestBase(GetParam()) {} + + void TearDown() override { SslIntegrationTestBase::TearDown(); }; + + void initialize() override { + // TODO(mattklein123): Merge/use the code in ConfigHelper::setTapTransportSocket(). + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + // The test supports tapping either the downstream or upstream connection, but not both. + if (upstream_tap_) { + setupUpstreamTap(bootstrap); + } else { + setupDownstreamTap(bootstrap); + } + }); + SslIntegrationTestBase::initialize(); + // This confuses our socket counting. + debug_with_s_client_ = false; + } + + void setupUpstreamTap(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.tap"); + envoy::config::core::v3::TransportSocket raw_transport_socket; + raw_transport_socket.set_name("envoy.transport_sockets.raw_buffer"); + envoy::extensions::transport_sockets::tap::v3::Tap tap_config = + createTapConfig(raw_transport_socket); + tap_config.mutable_transport_socket()->MergeFrom(raw_transport_socket); + transport_socket->mutable_typed_config()->PackFrom(tap_config); + } + + void setupDownstreamTap(envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto* filter_chain = + bootstrap.mutable_static_resources()->mutable_listeners(0)->mutable_filter_chains(0); + // Configure inner SSL transport socket based on existing config. + envoy::config::core::v3::TransportSocket ssl_transport_socket; + auto* transport_socket = filter_chain->mutable_transport_socket(); + ssl_transport_socket.Swap(transport_socket); + // Configure outer tap transport socket. + transport_socket->set_name("envoy.transport_sockets.tap"); + envoy::extensions::transport_sockets::tap::v3::Tap tap_config = + createTapConfig(ssl_transport_socket); + tap_config.mutable_transport_socket()->MergeFrom(ssl_transport_socket); + transport_socket->mutable_typed_config()->PackFrom(tap_config); + } + + envoy::extensions::transport_sockets::tap::v3::Tap + createTapConfig(const envoy::config::core::v3::TransportSocket& inner_transport) { + envoy::extensions::transport_sockets::tap::v3::Tap tap_config; + tap_config.mutable_common_config()->mutable_static_config()->mutable_match()->set_any_match( + true); + auto* output_config = + tap_config.mutable_common_config()->mutable_static_config()->mutable_output_config(); + if (max_rx_bytes_.has_value()) { + output_config->mutable_max_buffered_rx_bytes()->set_value(max_rx_bytes_.value()); + } + if (max_tx_bytes_.has_value()) { + output_config->mutable_max_buffered_tx_bytes()->set_value(max_tx_bytes_.value()); + } + output_config->set_streaming(streaming_tap_); + + auto* output_sink = output_config->mutable_sinks()->Add(); + output_sink->set_format(format_); + output_sink->mutable_file_per_tap()->set_path_prefix(path_prefix_); + tap_config.mutable_transport_socket()->MergeFrom(inner_transport); + return tap_config; + } + + std::string path_prefix_ = TestEnvironment::temporaryPath("ssl_trace"); + envoy::config::tap::v3::OutputSink::Format format_{ + envoy::config::tap::v3::OutputSink::PROTO_BINARY}; + absl::optional max_rx_bytes_; + absl::optional max_tx_bytes_; + bool upstream_tap_{}; + bool streaming_tap_{}; +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, SslTapIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +// Validate two back-to-back requests with binary proto output. +TEST_P(SslTapIntegrationTest, TwoRequestsWithBinaryProto) { + initialize(); + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + + // First request (ID will be +1 since the client will also bump). + const uint64_t first_id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; + codec_client_ = makeHttpConnection(creator()); + Http::TestRequestHeaderMapImpl post_request_headers{ + {":method", "POST"}, {":path", "/test/long/url"}, + {":scheme", "http"}, {":authority", "sni.lyft.com"}, + {"x-lyft-user-id", "123"}, {"x-forwarded-for", "10.0.0.1"}}; + auto response = + sendRequestAndWaitForResponse(post_request_headers, 128, default_response_headers_, 256); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(128, upstream_request_->bodyLength()); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); + EXPECT_EQ(256, response->body().size()); + checkStats(); + envoy::config::core::v3::Address expected_local_address; + Network::Utility::addressToProtobufAddress( + *codec_client_->connection()->connectionInfoProvider().remoteAddress(), + expected_local_address); + envoy::config::core::v3::Address expected_remote_address; + Network::Utility::addressToProtobufAddress( + *codec_client_->connection()->connectionInfoProvider().localAddress(), + expected_remote_address); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); + envoy::data::tap::v3::TraceWrapper trace; + TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, first_id), trace, *api_); + // Validate general expected properties in the trace. + EXPECT_EQ(first_id, trace.socket_buffered_trace().trace_id()); + EXPECT_THAT(expected_local_address, + ProtoEq(trace.socket_buffered_trace().connection().local_address())); + EXPECT_THAT(expected_remote_address, + ProtoEq(trace.socket_buffered_trace().connection().remote_address())); + ASSERT_GE(trace.socket_buffered_trace().events().size(), 2); + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), + "POST /test/long/url HTTP/1.1")); + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), + "HTTP/1.1 200 OK")); + EXPECT_FALSE(trace.socket_buffered_trace().read_truncated()); + EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); + + // Verify a second request hits a different file. + const uint64_t second_id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; + codec_client_ = makeHttpConnection(creator()); + Http::TestRequestHeaderMapImpl get_request_headers{ + {":method", "GET"}, {":path", "/test/long/url"}, + {":scheme", "http"}, {":authority", "sni.lyft.com"}, + {"x-lyft-user-id", "123"}, {"x-forwarded-for", "10.0.0.1"}}; + response = + sendRequestAndWaitForResponse(get_request_headers, 128, default_response_headers_, 256); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(128, upstream_request_->bodyLength()); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); + EXPECT_EQ(256, response->body().size()); + checkStats(); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 2); + TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, second_id), trace, *api_); + // Validate second connection ID. + EXPECT_EQ(second_id, trace.socket_buffered_trace().trace_id()); + ASSERT_GE(trace.socket_buffered_trace().events().size(), 2); + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), + "GET /test/long/url HTTP/1.1")); + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), + "HTTP/1.1 200 OK")); + EXPECT_FALSE(trace.socket_buffered_trace().read_truncated()); + EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); +} + +// Verify that truncation works correctly across multiple transport socket frames. +TEST_P(SslTapIntegrationTest, TruncationWithMultipleDataFrames) { + max_rx_bytes_ = 4; + max_tx_bytes_ = 5; + + initialize(); + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + + const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; + codec_client_ = makeHttpConnection(creator()); + const Http::TestRequestHeaderMapImpl request_headers{{":method", "GET"}, + {":path", "/test/long/url"}, + {":scheme", "http"}, + {":authority", "sni.lyft.com"}}; + auto result = codec_client_->startRequest(request_headers); + auto response = std::move(result.second); + Buffer::OwnedImpl data1("one"); + result.first.encodeData(data1, false); + Buffer::OwnedImpl data2("two"); + result.first.encodeData(data2, true); + waitForNextUpstreamRequest(); + const Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; + upstream_request_->encodeHeaders(response_headers, false); + Buffer::OwnedImpl data3("three"); + upstream_request_->encodeData(data3, false); + response->waitForBodyData(5); + Buffer::OwnedImpl data4("four"); + upstream_request_->encodeData(data4, true); + ASSERT_TRUE(response->waitForEndStream()); + + checkStats(); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); + + envoy::data::tap::v3::TraceWrapper trace; + TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, id), trace, *api_); + + ASSERT_EQ(trace.socket_buffered_trace().events().size(), 2); + EXPECT_TRUE(trace.socket_buffered_trace().events(0).read().data().truncated()); + EXPECT_TRUE(trace.socket_buffered_trace().events(1).write().data().truncated()); + EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); + EXPECT_TRUE(trace.socket_buffered_trace().write_truncated()); +} + +// Validate a single request with text proto output. +TEST_P(SslTapIntegrationTest, RequestWithTextProto) { + format_ = envoy::config::tap::v3::OutputSink::PROTO_TEXT; + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + + // Disable for this test because it uses connection IDs, which disrupts the accounting below + // leading to the wrong path for the `pb_text` being used. + skip_tag_extraction_rule_check_ = true; + + const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; + testRouterRequestAndResponseWithBody(1024, 512, false, false, &creator); + checkStats(); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); + envoy::data::tap::v3::TraceWrapper trace; + TestUtility::loadFromFile(fmt::format("{}_{}.pb_text", path_prefix_, id), trace, *api_); + // Test some obvious properties. + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), + "GET /test/long/url HTTP/1.1")); + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), + "HTTP/1.1 200 OK")); + EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); + EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); +} + +// Validate a single request with JSON (body as string) output. This test uses an upstream tap. +TEST_P(SslTapIntegrationTest, RequestWithJsonBodyAsStringUpstreamTap) { + upstream_tap_ = true; + max_rx_bytes_ = 5; + max_tx_bytes_ = 4; + + format_ = envoy::config::tap::v3::OutputSink::JSON_BODY_AS_STRING; + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + + // Disable for this test because it uses connection IDs, which disrupts the accounting below + // leading to the wrong path for the `pb_text` being used. + skip_tag_extraction_rule_check_ = true; + + const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 2; + testRouterRequestAndResponseWithBody(512, 1024, false, false, &creator); + checkStats(); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); + test_server_.reset(); + + // This must be done after server shutdown so that connection pool connections are closed and + // the tap written. + envoy::data::tap::v3::TraceWrapper trace; + TestUtility::loadFromFile(fmt::format("{}_{}.json", path_prefix_, id), trace, *api_); + + // Test some obvious properties. + EXPECT_EQ(trace.socket_buffered_trace().events(0).write().data().as_string(), "GET "); + EXPECT_EQ(trace.socket_buffered_trace().events(1).read().data().as_string(), "HTTP/"); + EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); + EXPECT_TRUE(trace.socket_buffered_trace().write_truncated()); +} + +// Validate a single request with length delimited binary proto output. This test uses an upstream +// tap. +TEST_P(SslTapIntegrationTest, RequestWithStreamingUpstreamTap) { + upstream_tap_ = true; + streaming_tap_ = true; + max_rx_bytes_ = 5; + max_tx_bytes_ = 4; + + format_ = envoy::config::tap::v3::OutputSink::PROTO_BINARY_LENGTH_DELIMITED; + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + + // Disable for this test because it uses connection IDs, which disrupts the accounting below + // leading to the wrong path for the `pb_text` being used. + skip_tag_extraction_rule_check_ = true; + + const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 2; + testRouterRequestAndResponseWithBody(512, 1024, false, false, &creator); + checkStats(); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); + test_server_.reset(); + + // This must be done after server shutdown so that connection pool connections are closed and + // the tap written. + std::vector traces = + Extensions::Common::Tap::readTracesFromFile( + fmt::format("{}_{}.pb_length_delimited", path_prefix_, id)); + ASSERT_GE(traces.size(), 4); + + // The initial connection message has no local address, but has a remote address (not connected + // yet). + EXPECT_TRUE(traces[0].socket_streamed_trace_segment().has_connection()); + EXPECT_FALSE(traces[0].socket_streamed_trace_segment().connection().has_local_address()); + EXPECT_TRUE(traces[0].socket_streamed_trace_segment().connection().has_remote_address()); + + // Verify truncated request/response data. + EXPECT_EQ(traces[1].socket_streamed_trace_segment().event().write().data().as_bytes(), "GET "); + EXPECT_TRUE(traces[1].socket_streamed_trace_segment().event().write().data().truncated()); + EXPECT_EQ(traces[2].socket_streamed_trace_segment().event().read().data().as_bytes(), "HTTP/"); + EXPECT_TRUE(traces[2].socket_streamed_trace_segment().event().read().data().truncated()); +} + +} // namespace Ssl +} // namespace Envoy From 1cfe7c92b26706dd9ac7ff3603d2c447588308de Mon Sep 17 00:00:00 2001 From: yanjunxiang-google <78807980+yanjunxiang-google@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:19:49 -0400 Subject: [PATCH 105/124] Fixing fuzzer test crash in route_fuzz_test.cc. (#32987) Signed-off-by: Yanjun Xiang --- ...erfuzz-testcase-minimized-route_fuzz_test-6160522024124416 | 1 + test/common/router/route_fuzz_test.cc | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 test/common/router/route_corpus/clusterfuzz-testcase-minimized-route_fuzz_test-6160522024124416 diff --git a/test/common/router/route_corpus/clusterfuzz-testcase-minimized-route_fuzz_test-6160522024124416 b/test/common/router/route_corpus/clusterfuzz-testcase-minimized-route_fuzz_test-6160522024124416 new file mode 100644 index 000000000000..4ebb1b44c22e --- /dev/null +++ b/test/common/router/route_corpus/clusterfuzz-testcase-minimized-route_fuzz_test-6160522024124416 @@ -0,0 +1 @@ +config { e: " " virtual_hosts { name: " " domains: " " matcher { matcher_list { matchers { predicate { single_predicate { input { name: " " typed_config { type_url: " /envoy.type.matcher.v3.HttpRequestHeaderMatchInput" e: " " } } value_match { custom { name: " " typed_config { l: " " e: " " } } } } } on_match { action { name: " " typed_config { type_url: "/envoy.config.route.v3.Route" value: "\n\002b\000\032 " } } } } } on_no_match { action { name: " " typed_config { type_url: "/envoy.config.route.v3.Route" value: "\n\002b\000\032 " } } } } } } e: 4 \ No newline at end of file diff --git a/test/common/router/route_fuzz_test.cc b/test/common/router/route_fuzz_test.cc index 93d545f9b58e..184795a864d6 100644 --- a/test/common/router/route_fuzz_test.cc +++ b/test/common/router/route_fuzz_test.cc @@ -140,6 +140,10 @@ DEFINE_PROTO_FUZZER(const test::common::router::RouteTestCase& input) { static NiceMock stream_info; static NiceMock factory_context; static ScopedInjectableLoader engine(std::make_unique()); + static ScopedInjectableLoader tls_inject( + std::make_unique()); + static ScopedInjectableLoader api_inject(std::make_unique()); + try { if (!validateConfig(input)) { return; From 74d958479ce88c12b2fc74c498367566291b6bd9 Mon Sep 17 00:00:00 2001 From: yanjunxiang-google <78807980+yanjunxiang-google@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:29:38 -0400 Subject: [PATCH 106/124] Adding an integration test to reproduce a race issue with logical host. (#32967) Adding an integration test to reproduce: #32850 This test is able to reproduce the race issue when run with below command: bazel test test/extensions/clusters/common:logical_host_integration_test --config=clang-tsan --cache_test_results=no --runs_per_test=100 --config=remote This test is used by all the approaches mentioned under #32850 to verify the fix, including: #32830. This PR is to separate the test from protocol_integration_test.cc. And just to have a simple integration test using logical DNS cluster. Signed-off-by: Yanjun Xiang --- test/extensions/clusters/common/BUILD | 14 +++ .../common/logical_host_integration_test.cc | 105 ++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 test/extensions/clusters/common/logical_host_integration_test.cc diff --git a/test/extensions/clusters/common/BUILD b/test/extensions/clusters/common/BUILD index 0b6e67ac8126..f3f49cbaba0b 100644 --- a/test/extensions/clusters/common/BUILD +++ b/test/extensions/clusters/common/BUILD @@ -16,3 +16,17 @@ envoy_cc_test( "//test/mocks/upstream:host_mocks", ], ) + +envoy_cc_test( + name = "logical_host_integration_test", + srcs = ["logical_host_integration_test.cc"], + deps = [ + "//source/common/config:utility_lib", + "//source/extensions/clusters/common:logical_host_lib", + "//source/extensions/clusters/logical_dns:logical_dns_cluster_lib", + "//test/integration:http_integration_lib", + "//test/mocks/network:network_mocks", + "//test/test_common:registry_lib", + "//test/test_common:threadsafe_singleton_injector_lib", + ], +) diff --git a/test/extensions/clusters/common/logical_host_integration_test.cc b/test/extensions/clusters/common/logical_host_integration_test.cc new file mode 100644 index 000000000000..bf46d6225c3b --- /dev/null +++ b/test/extensions/clusters/common/logical_host_integration_test.cc @@ -0,0 +1,105 @@ +#include "source/common/config/utility.h" + +#include "test/integration/http_integration.h" +#include "test/mocks/network/mocks.h" +#include "test/test_common/registry.h" +#include "test/test_common/threadsafe_singleton_injector.h" + +namespace Envoy { +namespace Extensions { +namespace Clusters { + +// Logical Host integration test. +class LogicalHostIntegrationTest : public testing::TestWithParam, + public HttpIntegrationTest { +public: + LogicalHostIntegrationTest() + : HttpIntegrationTest(Http::CodecType::HTTP1, GetParam()), + registered_dns_factory_(dns_resolver_factory_) {} + + void createUpstreams() override { HttpIntegrationTest::createUpstreams(); } + + NiceMock dns_resolver_factory_; + Registry::InjectFactory registered_dns_factory_; + uint32_t address_ = 0; + std::string first_address_string_; +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, LogicalHostIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +// Reproduces a race from https://github.com/envoyproxy/envoy/issues/32850. +// The test is by mocking the DNS resolver to return multiple different +// addresses, also config dns_refresh_rate to be extremely fast. +TEST_P(LogicalHostIntegrationTest, DISABLED_LogicalDNSRaceCrashTest) { + // first_address_string_ is used to make connections. It needs + // to match with the IpVersion of the test. + if (version_ == Network::Address::IpVersion::v4) { + first_address_string_ = "127.0.0.1"; + } else { + first_address_string_ = "::1"; + } + + auto dns_resolver = std::make_shared(); + EXPECT_CALL(dns_resolver_factory_, createDnsResolver(_, _, _)) + .WillRepeatedly(testing::Return(dns_resolver)); + EXPECT_CALL(*dns_resolver, resolve(_, _, _)) + .WillRepeatedly( + Invoke([&](const std::string&, Network::DnsLookupFamily, + Network::DnsResolver::ResolveCb dns_callback) -> Network::ActiveDnsQuery* { + // Keep changing the returned addresses to force address update. + dns_callback(Network::DnsResolver::ResolutionStatus::Success, + TestUtility::makeDnsResponse({ + // The only significant address is the first one; the other ones are + // just used to populate a list + // whose maintenance is race-prone. + first_address_string_, + absl::StrCat("127.0.0.", address_), + absl::StrCat("127.0.0.", address_ + 1), + absl::StrCat("127.0.0.", address_ + 2), + absl::StrCat("127.0.0.", address_ + 3), + absl::StrCat("127.0.0.", address_ + 4), + absl::StrCat("127.0.0.", address_ + 5), + absl::StrCat("127.0.0.", address_ + 6), + absl::StrCat("127.0.0.", address_ + 7), + absl::StrCat("127.0.0.", address_ + 8), + absl::StrCat("127.0.0.", address_ + 9), + "::2", + "::3", + "::4", + "::5", + "::6", + "::7", + "::8", + "::9", + })); + address_ = (address_ + 1) % 128; + return nullptr; + })); + config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + RELEASE_ASSERT(bootstrap.mutable_static_resources()->clusters_size() == 1, ""); + auto& cluster = *bootstrap.mutable_static_resources()->mutable_clusters(0); + cluster.set_type(envoy::config::cluster::v3::Cluster::LOGICAL_DNS); + cluster.set_dns_lookup_family(envoy::config::cluster::v3::Cluster::ALL); + // Make the refresh rate fast to hit the R/W race. + cluster.mutable_dns_refresh_rate()->set_nanos(1000001); + }); + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + auto* route = hcm.mutable_route_config()->mutable_virtual_hosts(0)->mutable_routes(0); + route->mutable_route()->mutable_auto_host_rewrite()->set_value(true); + }); + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = + sendRequestAndWaitForResponse(default_request_headers_, 0, default_response_headers_, 0); + + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); +} + +} // namespace Clusters +} // namespace Extensions +} // namespace Envoy From 21832d158c1447a3bb0af5c1ea58f63cfa56dc16 Mon Sep 17 00:00:00 2001 From: Alexcei Date: Wed, 20 Mar 2024 21:04:42 +0300 Subject: [PATCH 107/124] Oauth2 lifetime of refresh token (#32278) Signed-off-by: Alexcei Co-authored-by: Kateryna Nezdolii --- .../filters/http/oauth2/v3/oauth.proto | 9 +- bazel/repository_locations.bzl | 2 +- changelogs/current.yaml | 7 + source/extensions/filters/http/oauth2/BUILD | 3 + .../extensions/filters/http/oauth2/filter.cc | 49 ++- .../extensions/filters/http/oauth2/filter.h | 7 + .../filters/http/oauth2/filter_test.cc | 307 +++++++++++++++++- 7 files changed, 373 insertions(+), 11 deletions(-) diff --git a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto index 48524521cf84..aa5f70b2897a 100644 --- a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto +++ b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto @@ -74,7 +74,7 @@ message OAuth2Credentials { // OAuth config // -// [#next-free-field: 15] +// [#next-free-field: 16] message OAuth2Config { enum AuthType { // The ``client_id`` and ``client_secret`` will be sent in the URL encoded request body. @@ -142,6 +142,13 @@ message OAuth2Config { // Automatic access token refresh will be performed for these requests, if enabled. // This behavior can be useful for AJAX requests. repeated config.route.v3.HeaderMatcher deny_redirect_matcher = 14; + + // The default lifetime in seconds of the refresh token, if the exp (expiration time) claim is omitted in the refresh token or the refresh token is not JWT. + // + // If this value is not set, it will default to ``604800s``. In this case, the cookie with the refresh token will be expired + // in a week. + // This setting is only considered if ``use_refresh_token`` is set to true, otherwise the authorization server expiration or ``defaul_expires_in`` is used. + google.protobuf.Duration default_refresh_token_expires_in = 15; } // Filter config. diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 76571cb1f65f..0a0272e51a7b 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -837,7 +837,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( strip_prefix = "jwt_verify_lib-{version}", urls = ["https://github.com/google/jwt_verify_lib/archive/{version}.tar.gz"], use_category = ["dataplane_ext"], - extensions = ["envoy.filters.http.jwt_authn", "envoy.filters.http.gcp_authn"], + extensions = ["envoy.filters.http.jwt_authn", "envoy.filters.http.gcp_authn", "envoy.filters.http.oauth2"], release_date = "2023-05-17", cpe = "N/A", license = "Apache-2.0", diff --git a/changelogs/current.yaml b/changelogs/current.yaml index dc0280318732..c36030eaa1c2 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -184,6 +184,13 @@ bug_fixes: change: | When performing a token refresh and forwarding tokens upstream, replace existing token cookies rather than appending as another Cookie header. +- area: oauth + change: | + The refresh and access tokens are not expired simultaneously so the access token can be updated using + the refresh token. The expiration time of the refresh token is taken from the exp claim of jwt by default. + If the claim is ommited in the jwt then :ref:`default_refresh_token_expires_in + ` + specifies the lifetime of the refresh token. The default value is ``604800`` seconds (a week). removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` diff --git a/source/extensions/filters/http/oauth2/BUILD b/source/extensions/filters/http/oauth2/BUILD index a236dfa7583f..e33ddce15638 100644 --- a/source/extensions/filters/http/oauth2/BUILD +++ b/source/extensions/filters/http/oauth2/BUILD @@ -44,6 +44,9 @@ envoy_cc_library( name = "oauth_lib", srcs = ["filter.cc"], hdrs = ["filter.h"], + external_deps = [ + "jwt_verify_lib", + ], deps = [ ":oauth_client", "//envoy/server:filter_config_interface", diff --git a/source/extensions/filters/http/oauth2/filter.cc b/source/extensions/filters/http/oauth2/filter.cc index 5bd8aba7030a..cda4885331f7 100644 --- a/source/extensions/filters/http/oauth2/filter.cc +++ b/source/extensions/filters/http/oauth2/filter.cc @@ -24,6 +24,10 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" +#include "jwt_verify_lib/jwt.h" +#include "jwt_verify_lib/status.h" + +using namespace std::chrono_literals; namespace Envoy { namespace Extensions { @@ -197,7 +201,9 @@ FilterConfig::FilterConfig( cookie_names_(proto_config.credentials().cookie_names()), auth_type_(getAuthType(proto_config.auth_type())), use_refresh_token_(proto_config.use_refresh_token().value()), - default_expires_in_(PROTOBUF_GET_SECONDS_OR_DEFAULT(proto_config, default_expires_in, 0)) { + default_expires_in_(PROTOBUF_GET_SECONDS_OR_DEFAULT(proto_config, default_expires_in, 0)), + default_refresh_token_expires_in_( + PROTOBUF_GET_SECONDS_OR_DEFAULT(proto_config, default_refresh_token_expires_in, 604800)) { if (!cluster_manager.clusters().hasCluster(oauth_token_endpoint_.cluster())) { throw EnvoyException(fmt::format("OAuth2 filter: unknown cluster '{}' in config. Please " "specify which cluster to direct OAuth requests to.", @@ -233,9 +239,7 @@ void OAuth2CookieValidator::setParams(const Http::RequestHeaderMap& headers, secret_.assign(secret.begin(), secret.end()); } -bool OAuth2CookieValidator::canUpdateTokenByRefreshToken() const { - return (!token_.empty() && !refresh_token_.empty()); -} +bool OAuth2CookieValidator::canUpdateTokenByRefreshToken() const { return !refresh_token_.empty(); } bool OAuth2CookieValidator::hmacIsValid() const { return ( @@ -362,6 +366,9 @@ Http::FilterHeadersStatus OAuth2Filter::decodeHeaders(Http::RequestHeaderMap& he // Check if we can update the access token via a refresh token. if (config_->useRefreshToken() && validator_->canUpdateTokenByRefreshToken()) { + + ENVOY_LOG(debug, "Trying to update the access token using the refresh token"); + // try to update access token by refresh token oauth_client_->asyncRefreshAccessToken(validator_->refreshToken(), config_->clientId(), config_->clientSecret(), config_->authType()); @@ -533,6 +540,7 @@ void OAuth2Filter::updateTokens(const std::string& access_token, const std::stri id_token_ = id_token; refresh_token_ = refresh_token; expires_in_ = std::to_string(expires_in.count()); + expires_refresh_token_in_ = getExpiresTimeForRefreshToken(refresh_token, expires_in); const auto new_epoch = time_source_.systemTime() + expires_in; new_expires_ = std::to_string( @@ -552,6 +560,34 @@ std::string OAuth2Filter::getEncodedToken() const { return encoded_token; } +std::string +OAuth2Filter::getExpiresTimeForRefreshToken(const std::string& refresh_token, + const std::chrono::seconds& expires_in) const { + if (config_->useRefreshToken()) { + ::google::jwt_verify::Jwt jwt; + if (jwt.parseFromString(refresh_token) == ::google::jwt_verify::Status::Ok && jwt.exp_ != 0) { + const std::chrono::seconds expirationFromJwt = std::chrono::seconds{jwt.exp_}; + const std::chrono::seconds now = + std::chrono::time_point_cast(time_source_.systemTime()) + .time_since_epoch(); + + if (now < expirationFromJwt) { + const auto expiration_epoch = expirationFromJwt - now; + return std::to_string(expiration_epoch.count()); + } else { + ENVOY_LOG(debug, "An expiration time in the refresh token is less of the current time"); + return "0"; + } + } + ENVOY_LOG(debug, "The refresh token is not JWT or exp claim is ommited. The lifetime of the " + "refresh token is taken from filter configuration"); + const std::chrono::seconds default_refresh_token_expires_in = + config_->defaultRefreshTokenExpiresIn(); + return std::to_string(default_refresh_token_expires_in.count()); + } + return std::to_string(expires_in.count()); +} + void OAuth2Filter::onGetAccessTokenSuccess(const std::string& access_code, const std::string& id_token, const std::string& refresh_token, @@ -669,9 +705,12 @@ void OAuth2Filter::addResponseCookies(Http::ResponseHeaderMap& headers, } if (!refresh_token_.empty()) { + const std::string refresh_token_cookie_tail_http_only = + fmt::format(CookieTailHttpOnlyFormatString, expires_refresh_token_in_); + headers.addReferenceKey(Http::Headers::get().SetCookie, absl::StrCat(cookie_names.refresh_token_, "=", refresh_token_, - cookie_attribute_httponly)); + refresh_token_cookie_tail_http_only)); } } } diff --git a/source/extensions/filters/http/oauth2/filter.h b/source/extensions/filters/http/oauth2/filter.h index f593f9b9b4aa..ca5153b9d144 100644 --- a/source/extensions/filters/http/oauth2/filter.h +++ b/source/extensions/filters/http/oauth2/filter.h @@ -143,6 +143,9 @@ class FilterConfig { const AuthType& authType() const { return auth_type_; } bool useRefreshToken() const { return use_refresh_token_; } std::chrono::seconds defaultExpiresIn() const { return default_expires_in_; } + std::chrono::seconds defaultRefreshTokenExpiresIn() const { + return default_refresh_token_expires_in_; + } private: static FilterStats generateStats(const std::string& prefix, Stats::Scope& scope); @@ -167,6 +170,7 @@ class FilterConfig { const AuthType auth_type_; const bool use_refresh_token_{}; const std::chrono::seconds default_expires_in_; + const std::chrono::seconds default_refresh_token_expires_in_; }; using FilterConfigSharedPtr = std::shared_ptr; @@ -265,6 +269,7 @@ class OAuth2Filter : public Http::PassThroughFilter, std::string id_token_; std::string refresh_token_; std::string expires_in_; + std::string expires_refresh_token_in_; std::string new_expires_; absl::string_view host_; std::string state_; @@ -284,6 +289,8 @@ class OAuth2Filter : public Http::PassThroughFilter, Http::FilterHeadersStatus signOutUser(const Http::RequestHeaderMap& headers); std::string getEncodedToken() const; + std::string getExpiresTimeForRefreshToken(const std::string& refresh_token, + const std::chrono::seconds& expires_in) const; void addResponseCookies(Http::ResponseHeaderMap& headers, const std::string& encoded_token) const; const std::string& bearerPrefix() const; }; diff --git a/test/extensions/filters/http/oauth2/filter_test.cc b/test/extensions/filters/http/oauth2/filter_test.cc index bf0822bdbcf9..66cf1d976c13 100644 --- a/test/extensions/filters/http/oauth2/filter_test.cc +++ b/test/extensions/filters/http/oauth2/filter_test.cc @@ -111,7 +111,8 @@ class OAuth2Test : public testing::TestWithParam { getConfig(bool forward_bearer_token = true, bool use_refresh_token = false, ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType auth_type = ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: - OAuth2Config_AuthType_URL_ENCODED_BODY) { + OAuth2Config_AuthType_URL_ENCODED_BODY, + int default_refresh_token_expires_in = 0) { envoy::extensions::filters::http::oauth2::v3::OAuth2Config p; auto* endpoint = p.mutable_token_endpoint(); endpoint->set_cluster("auth.example.com"); @@ -125,6 +126,12 @@ class OAuth2Test : public testing::TestWithParam { auto* useRefreshToken = p.mutable_use_refresh_token(); useRefreshToken->set_value(use_refresh_token); + + if (default_refresh_token_expires_in != 0) { + auto* refresh_token_expires_in = p.mutable_default_refresh_token_expires_in(); + refresh_token_expires_in->set_seconds(default_refresh_token_expires_in); + } + p.set_auth_type(auth_type); p.add_auth_scopes("user"); p.add_auth_scopes("openid"); @@ -1751,6 +1758,298 @@ TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokens) { std::chrono::seconds(600)); } +TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokensUseRefreshToken) { + init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */)); + TestScopedRuntime scoped_runtime; + if (GetParam() == 1) { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "false"}, + }); + oauthHMAC = + "ZTEzMmIyYzRmNTdmMTdiY2IyYmViZDE3ODA5ZDliOTE2MTRlNzNjYjc4MjBlMTVlOWY1OTM2ZjViZjM4MzAwNA==;"; + } else { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + oauthHMAC = "4TKyxPV/F7yyvr0XgJ2bkWFOc8t4IOFen1k29b84MAQ=;"; + } + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(1000))); + + // host_ must be set, which is guaranteed (ASAN). + Http::TestRequestHeaderMapImpl request_headers{ + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Path.get(), "/_signout"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + }; + filter_->decodeHeaders(request_headers, false); + + // Expected response after the callback is complete. + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=" + oauthHMAC + "path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=1600;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=access_code;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=some-id-token;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=some-refresh-token;path=/;Max-Age=604800;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), ""}, + }; + + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); + + filter_->onGetAccessTokenSuccess("access_code", "some-id-token", "some-refresh-token", + std::chrono::seconds(600)); +} + +TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokensUseRefreshTokenAndDefaultRefreshTokenExpiresIn) { + + init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */, + ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: + OAuth2Config_AuthType_URL_ENCODED_BODY /* encoded_body_type */, + 1200 /* default_refresh_token_expires_in */)); + TestScopedRuntime scoped_runtime; + if (GetParam() == 1) { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "false"}, + }); + oauthHMAC = + "ZTEzMmIyYzRmNTdmMTdiY2IyYmViZDE3ODA5ZDliOTE2MTRlNzNjYjc4MjBlMTVlOWY1OTM2ZjViZjM4MzAwNA==;"; + } else { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + oauthHMAC = "4TKyxPV/F7yyvr0XgJ2bkWFOc8t4IOFen1k29b84MAQ=;"; + } + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(1000))); + + // host_ must be set, which is guaranteed (ASAN). + Http::TestRequestHeaderMapImpl request_headers{ + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Path.get(), "/_signout"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + }; + filter_->decodeHeaders(request_headers, false); + + // Expected response after the callback is complete. + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=" + oauthHMAC + "path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=1600;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=access_code;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=some-id-token;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=some-refresh-token;path=/;Max-Age=1200;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), ""}, + }; + + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); + + filter_->onGetAccessTokenSuccess("access_code", "some-id-token", "some-refresh-token", + std::chrono::seconds(600)); +} + +/** + * Scenario: The Oauth filter saves cookies with tokens after successful receipt of the tokens. + * + * Expected behavior: The lifetime of the refresh token cookie is taken from the exp claim of the + * refresh token. + */ + +TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokensUseRefreshTokenAndRefreshTokenExpiresInFromJwt) { + + init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */, + ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: + OAuth2Config_AuthType_URL_ENCODED_BODY /* encoded_body_type */, + 1200 /* default_refresh_token_expires_in */)); + TestScopedRuntime scoped_runtime; + if (GetParam() == 1) { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "false"}, + }); + oauthHMAC = + "MGE2YWQyNjU0YjBmMTA1ZDQzZTE0ODA0OWVlY2Y2Nzc2YjNjZWZjNjI3MDI4M2E5YzUwMGFkMTNkMmM5ZjNkMw==;"; + } else { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + oauthHMAC = "CmrSZUsPEF1D4UgEnuz2d2s878YnAoOpxQCtE9LJ89M=;"; + } + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(1000))); + + // host_ must be set, which is guaranteed (ASAN). + Http::TestRequestHeaderMapImpl request_headers{ + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Path.get(), "/_signout"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + }; + filter_->decodeHeaders(request_headers, false); + + const std::string refreshToken = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJ1bmlxdWVfbmFtZSI6ImFsZXhjZWk4OCIsInN1YiI6ImFsZXhjZWk4OCIsImp0aSI6IjQ5ZTFjMzc1IiwiYXVkIjoi" + "dGVzdCIsIm5iZiI6MTcwNzQxNDYzNSwiZXhwIjoyNTU0NDE2MDAwLCJpYXQiOjE3MDc0MTQ2MzYsImlzcyI6ImRvdG5l" + "dC11c2VyLWp3dHMifQ.LaGOw6x0-m7r-WzxgCIdPnAfp0O1hy6mW4klq9Vs2XM"; + + // Expected response after the callback is complete. + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=" + oauthHMAC + "path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=1600;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=access_code;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=some-id-token;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=" + refreshToken + ";path=/;Max-Age=2554415000;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), ""}, + }; + + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); + + filter_->onGetAccessTokenSuccess("access_code", "some-id-token", refreshToken, + std::chrono::seconds(600)); +} + +/** + * Scenario: The Oauth filter doesn't save cookie with refresh token because the token is expired. + * + * Expected behavior: The age of the cookie with refresh token is equal to zero. + */ + +TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokensUseRefreshTokenAndExpiredRefreshToken) { + + init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */, + ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: + OAuth2Config_AuthType_URL_ENCODED_BODY /* encoded_body_type */, + 1200 /* default_refresh_token_expires_in */)); + TestScopedRuntime scoped_runtime; + if (GetParam() == 1) { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "false"}, + }); + oauthHMAC = + "ZWY3NDZlMDcwNTM3MmIxZmZiNDRmZTBkZDcyY2JlZjEwOWUxMDExOGMwZDc5NDBlYTMxNzRhMGZiY2U0ZDY5Mg==;"; + } else { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + oauthHMAC = "73RuBwU3Kx/7RP4N1yy+8QnhARjA15QOoxdKD7zk1pI=;"; + } + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(2554515000))); + + // host_ must be set, which is guaranteed (ASAN). + Http::TestRequestHeaderMapImpl request_headers{ + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Path.get(), "/_signout"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + }; + filter_->decodeHeaders(request_headers, false); + + const std::string refreshToken = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJ1bmlxdWVfbmFtZSI6ImFsZXhjZWk4OCIsInN1YiI6ImFsZXhjZWk4OCIsImp0aSI6IjQ5ZTFjMzc1IiwiYXVkIjoi" + "dGVzdCIsIm5iZiI6MTcwNzQxNDYzNSwiZXhwIjoyNTU0NDE2MDAwLCJpYXQiOjE3MDc0MTQ2MzYsImlzcyI6ImRvdG5l" + "dC11c2VyLWp3dHMifQ.LaGOw6x0-m7r-WzxgCIdPnAfp0O1hy6mW4klq9Vs2XM"; + + // Expected response after the callback is complete. + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=" + oauthHMAC + "path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=2554515600;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=access_code;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=some-id-token;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=" + refreshToken + ";path=/;Max-Age=0;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), ""}, + }; + + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); + + filter_->onGetAccessTokenSuccess("access_code", "some-id-token", refreshToken, + std::chrono::seconds(600)); +} + +/** + * Scenario: The Oauth filter receipts the refresh token without exp claim. + * + * Expected behavior: The age of the cookie with refresh token is equal to default value. + */ + +TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokensUseRefreshTokenAndNoExpClaimInRefreshToken) { + + init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */, + ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: + OAuth2Config_AuthType_URL_ENCODED_BODY /* encoded_body_type */, + 1200 /* default_refresh_token_expires_in */)); + TestScopedRuntime scoped_runtime; + if (GetParam() == 1) { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "false"}, + }); + oauthHMAC = + "N2FlNDRlNzQwZjgyNmI4YTdmZjQ5YTBjOWQ3ZTc0N2UyYTg3YTJiOTg4NThmZmQyZjg1YjFlZmIwMGZlNTdjMg==;"; + } else { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + oauthHMAC = "euROdA+Ca4p/9JoMnX50fiqHormIWP/S+Fse+wD+V8I=;"; + } + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(1000))); + + // host_ must be set, which is guaranteed (ASAN). + Http::TestRequestHeaderMapImpl request_headers{ + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Path.get(), "/_signout"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + }; + filter_->decodeHeaders(request_headers, false); + + const std::string refreshToken = + "eyJhbGciOiJIUzI1NiJ9." + "eyJSb2xlIjoiQWRtaW4iLCJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZSIsImlhdCI6MTcwODA2" + "NDcyOH0.92H-X2Oa4ECNmFLZBWBHP0BJyEHDprLkEIc2JBJYwkI"; + + // Expected response after the callback is complete. + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=" + oauthHMAC + "path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=1600;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=access_code;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=some-id-token;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=" + refreshToken + ";path=/;Max-Age=1200;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), ""}, + }; + + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); + + filter_->onGetAccessTokenSuccess("access_code", "some-id-token", refreshToken, + std::chrono::seconds(600)); +} + INSTANTIATE_TEST_SUITE_P(EndcodingParams, OAuth2Test, testing::Values(1, 0)); TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokens_oauth_use_standard_max_age_value) { @@ -1843,7 +2142,7 @@ TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokens_oauth_make_token_cookie_http {Http::Headers::get().SetCookie.get(), "BearerToken=access_code;path=/;Max-Age=600;secure"}, {Http::Headers::get().SetCookie.get(), "IdToken=some-id-token;path=/;Max-Age=600;secure"}, {Http::Headers::get().SetCookie.get(), - "RefreshToken=some-refresh-token;path=/;Max-Age=600;secure"}, + "RefreshToken=some-refresh-token;path=/;Max-Age=600;secure;HttpOnly"}, {Http::Headers::get().Location.get(), ""}, }; @@ -2266,7 +2565,7 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessToken) { "BearerToken=accessToken;path=/;Max-Age=10;secure;HttpOnly"}, {Http::Headers::get().SetCookie.get(), "IdToken=idToken;path=/;Max-Age=10;secure;HttpOnly"}, {Http::Headers::get().SetCookie.get(), - "RefreshToken=refreshToken;path=/;Max-Age=10;secure;HttpOnly"}, + "RefreshToken=refreshToken;path=/;Max-Age=604800;secure;HttpOnly"}, }; EXPECT_THAT(response_headers, HeaderMapEqualRef(&expected_response_headers)); @@ -2335,7 +2634,7 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessTokenWithBasicAuth) { "BearerToken=accessToken;path=/;Max-Age=10;secure;HttpOnly"}, {Http::Headers::get().SetCookie.get(), "IdToken=idToken;path=/;Max-Age=10;secure;HttpOnly"}, {Http::Headers::get().SetCookie.get(), - "RefreshToken=refreshToken;path=/;Max-Age=10;secure;HttpOnly"}, + "RefreshToken=refreshToken;path=/;Max-Age=604800;secure;HttpOnly"}, }; EXPECT_THAT(response_headers, HeaderMapEqualRef(&expected_response_headers)); From 02f391bcfbe9697c6f284e9f8ea06b848cb4a042 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 20 Mar 2024 16:25:12 -0400 Subject: [PATCH 108/124] mobile: triggering mobile ci on mobile .bazelrc changes (#33011) Signed-off-by: Alyssa Wilk --- .github/config.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/config.yml b/.github/config.yml index b8d00e19072d..18e83d891fee 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -172,6 +172,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-android-all: @@ -181,6 +182,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py push: never @@ -191,6 +193,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-asan: @@ -200,6 +203,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-cc: @@ -209,6 +213,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-compile-time-cc: @@ -221,6 +226,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-coverage: @@ -230,12 +236,14 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-core: paths: - "**/*" - "*" + - mobile/.bazelrc mobile-format: paths: - .bazelrc @@ -243,6 +251,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-ios: @@ -252,6 +261,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-ios-all: @@ -261,6 +271,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py push: never @@ -271,6 +282,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-perf: @@ -280,6 +292,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-release-validation: @@ -289,6 +302,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-tsan: @@ -298,6 +312,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py precheck-deps: From 412a936100888530cfa1746621a933e78edc78af Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 20 Mar 2024 17:17:52 -0400 Subject: [PATCH 109/124] tls: cleaning up unused noboring code (#32912) #32748 was an attempt to move no-boring code to contrib. Per investigation at that time, the noboring build was already broken, so cleaning up that code entirely. If needed, #32748 can be reinstated instead. This also cleans up some unneeded DNS code. --------- Signed-off-by: Alyssa Wilk --- envoy/ssl/context_manager.h | 11 ---- source/extensions/all_extensions.bzl | 1 + source/extensions/extensions_build_config.bzl | 1 + .../transport_sockets/tls/config.cc | 9 --- .../extensions/transport_sockets/tls/config.h | 8 --- source/server/BUILD | 12 +--- source/server/config_validation/BUILD | 13 +--- source/server/config_validation/dispatcher.h | 1 - source/server/config_validation/dns.cc | 13 ---- source/server/config_validation/dns.h | 23 ------- source/server/config_validation/server.cc | 6 +- source/server/config_validation/server.h | 1 - source/server/server.cc | 5 +- source/server/ssl_context_manager.cc | 65 ------------------- source/server/ssl_context_manager.h | 14 ---- test/integration/BUILD | 1 + test/server/BUILD | 13 ---- test/server/config_validation/BUILD | 2 - test/server/ssl_context_manager_test.cc | 40 ------------ 19 files changed, 12 insertions(+), 227 deletions(-) delete mode 100644 source/server/config_validation/dns.cc delete mode 100644 source/server/config_validation/dns.h delete mode 100644 source/server/ssl_context_manager.cc delete mode 100644 source/server/ssl_context_manager.h delete mode 100644 test/server/ssl_context_manager_test.cc diff --git a/envoy/ssl/context_manager.h b/envoy/ssl/context_manager.h index 1cd6f4054472..3c0b243a47c6 100644 --- a/envoy/ssl/context_manager.h +++ b/envoy/ssl/context_manager.h @@ -77,16 +77,5 @@ class ContextManager { using ContextManagerPtr = std::unique_ptr; -class ContextManagerFactory : public Config::UntypedFactory { -public: - ~ContextManagerFactory() override = default; - virtual ContextManagerPtr - createContextManager(Server::Configuration::CommonFactoryContext& factory_context) PURE; - - // There could be only one factory thus the name is static. - std::string name() const override { return "ssl_context_manager"; } - std::string category() const override { return "envoy.ssl_context_manager"; } -}; - } // namespace Ssl } // namespace Envoy diff --git a/source/extensions/all_extensions.bzl b/source/extensions/all_extensions.bzl index 8cbdeb844d81..f239a04b9537 100644 --- a/source/extensions/all_extensions.bzl +++ b/source/extensions/all_extensions.bzl @@ -36,6 +36,7 @@ _core_extensions = [ "envoy.network.dns_resolver.cares", "envoy.network.dns_resolver.apple", "envoy.load_balancing_policies.round_robin", + "envoy.transport_sockets.tls", ] # Return all core extensions to be compiled into Envoy. diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 1425006ffaf4..66191fd96be8 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -297,6 +297,7 @@ EXTENSIONS = { "envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config", "envoy.transport_sockets.starttls": "//source/extensions/transport_sockets/starttls:config", "envoy.transport_sockets.tcp_stats": "//source/extensions/transport_sockets/tcp_stats:config", + "envoy.transport_sockets.tls": "//source/extensions/transport_sockets/tls:config", "envoy.transport_sockets.internal_upstream": "//source/extensions/transport_sockets/internal_upstream:config", # diff --git a/source/extensions/transport_sockets/tls/config.cc b/source/extensions/transport_sockets/tls/config.cc index 1d652c0d1545..d89789c6e2ad 100644 --- a/source/extensions/transport_sockets/tls/config.cc +++ b/source/extensions/transport_sockets/tls/config.cc @@ -51,15 +51,6 @@ ProtobufTypes::MessagePtr DownstreamSslSocketFactory::createEmptyConfigProto() { LEGACY_REGISTER_FACTORY(DownstreamSslSocketFactory, Server::Configuration::DownstreamTransportSocketConfigFactory, "tls"); -Ssl::ContextManagerPtr SslContextManagerFactory::createContextManager( - Server::Configuration::CommonFactoryContext& factory_context) { - return std::make_unique(factory_context); -} - -static Envoy::Registry::RegisterInternalFactory - ssl_manager_registered; - } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/source/extensions/transport_sockets/tls/config.h b/source/extensions/transport_sockets/tls/config.h index d55bbb67e73b..3e39adc38bfb 100644 --- a/source/extensions/transport_sockets/tls/config.h +++ b/source/extensions/transport_sockets/tls/config.h @@ -42,14 +42,6 @@ class DownstreamSslSocketFactory DECLARE_FACTORY(DownstreamSslSocketFactory); -class SslContextManagerFactory : public Ssl::ContextManagerFactory { -public: - Ssl::ContextManagerPtr - createContextManager(Server::Configuration::CommonFactoryContext& factory_context) override; -}; - -DECLARE_FACTORY(SslContextManagerFactory); - } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/source/server/BUILD b/source/server/BUILD index bca0cd626d7e..6fa2605b1efd 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -418,7 +418,6 @@ envoy_cc_library( ":listener_hooks_lib", ":listener_manager_factory_lib", ":regex_engine_lib", - ":ssl_context_manager_lib", ":utils_lib", ":worker_lib", "//envoy/event:dispatcher_interface", @@ -463,6 +462,7 @@ envoy_cc_library( "//source/common/signal:fatal_error_handler_lib", "//source/common/singleton:manager_impl_lib", "//source/common/stats:thread_local_store_lib", + "//source/common/tls:context_lib", "//source/common/upstream:cluster_manager_lib", "//source/common/upstream:health_discovery_service_lib", "//source/common/version:version_lib", @@ -484,16 +484,6 @@ envoy_cc_library( ], ) -envoy_cc_library( - name = "ssl_context_manager_lib", - srcs = ["ssl_context_manager.cc"], - hdrs = ["ssl_context_manager.h"], - deps = [ - "//envoy/registry", - "//envoy/ssl:context_manager_interface", - ], -) - envoy_cc_library( name = "listener_hooks_lib", hdrs = ["listener_hooks.h"], diff --git a/source/server/config_validation/BUILD b/source/server/config_validation/BUILD index eba39b9bc6d2..f917244c0268 100644 --- a/source/server/config_validation/BUILD +++ b/source/server/config_validation/BUILD @@ -50,7 +50,6 @@ envoy_cc_library( hdrs = [ "connection.h", "dispatcher.h", - "dns.h", ], deps = [ "//envoy/event:dispatcher_interface", @@ -60,16 +59,6 @@ envoy_cc_library( ], ) -envoy_cc_library( - name = "dns_lib", - srcs = ["dns.cc"], - hdrs = ["dns.h"], - deps = [ - "//envoy/event:dispatcher_interface", - "//envoy/network:dns_interface", - ], -) - envoy_cc_library( name = "server_lib", srcs = ["server.cc"], @@ -79,7 +68,6 @@ envoy_cc_library( ":admin_lib", ":api_lib", ":cluster_manager_lib", - ":dns_lib", "//envoy/server:drain_manager_interface", "//envoy/server:instance_interface", "//envoy/ssl:context_manager_interface", @@ -100,6 +88,7 @@ envoy_cc_library( "//source/common/stats:stats_lib", "//source/common/thread_local:thread_local_lib", "//source/common/version:version_lib", + "//source/extensions/transport_sockets/tls:config", "//source/server:configuration_lib", "//source/server:hot_restart_nop_lib", "//source/server:overload_manager_lib", diff --git a/source/server/config_validation/dispatcher.h b/source/server/config_validation/dispatcher.h index 33d3c39ed200..bd2cd87f38d2 100644 --- a/source/server/config_validation/dispatcher.h +++ b/source/server/config_validation/dispatcher.h @@ -3,7 +3,6 @@ #include "envoy/event/dispatcher.h" #include "source/common/event/dispatcher_impl.h" -#include "source/server/config_validation/dns.h" namespace Envoy { namespace Event { diff --git a/source/server/config_validation/dns.cc b/source/server/config_validation/dns.cc deleted file mode 100644 index c590bf181133..000000000000 --- a/source/server/config_validation/dns.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include "source/server/config_validation/dns.h" - -namespace Envoy { -namespace Network { - -ActiveDnsQuery* ValidationDnsResolver::resolve(const std::string&, DnsLookupFamily, - ResolveCb callback) { - callback(DnsResolver::ResolutionStatus::Success, {}); - return nullptr; -} - -} // namespace Network -} // namespace Envoy diff --git a/source/server/config_validation/dns.h b/source/server/config_validation/dns.h deleted file mode 100644 index 3777256579b0..000000000000 --- a/source/server/config_validation/dns.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "envoy/event/dispatcher.h" -#include "envoy/network/dns.h" - -namespace Envoy { -namespace Network { - -/** - * DnsResolver to be used in config validation runs. Every DNS query immediately fails to resolve, - * since we never need DNS information to validate a config. (If a config contains an unresolvable - * name, it still passes validation -- for example, we might be running validation in a test - * environment, while the name resolves fine in prod.) - */ -class ValidationDnsResolver : public DnsResolver { -public: - // Network::DnsResolver - ActiveDnsQuery* resolve(const std::string& dns_name, DnsLookupFamily dns_lookup_family, - ResolveCb callback) override; -}; - -} // namespace Network -} // namespace Envoy diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index a2d915b5e66d..3ad1568e78fe 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -13,12 +13,12 @@ #include "source/common/local_info/local_info_impl.h" #include "source/common/protobuf/utility.h" #include "source/common/singleton/manager_impl.h" +#include "source/common/tls/context_manager_impl.h" #include "source/common/version/version.h" #include "source/server/admin/admin_factory_context.h" #include "source/server/listener_manager_factory.h" #include "source/server/overload_manager_impl.h" #include "source/server/regex_engine.h" -#include "source/server/ssl_context_manager.h" #include "source/server/utils.h" namespace Envoy { @@ -132,7 +132,9 @@ void ValidationInstance::initialize(const Options& options, "Component factory should not return nullptr from createDrainManager()"); secret_manager_ = std::make_unique(admin()->getConfigTracker()); - ssl_context_manager_ = createContextManager("ssl_context_manager", server_contexts_); + ssl_context_manager_ = + std::make_unique(server_contexts_); + cluster_manager_factory_ = std::make_unique( server_contexts_, stats(), threadLocal(), http_context_, [this]() -> Network::DnsResolverSharedPtr { return this->dnsResolver(); }, diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 9f1bb41c031d..777b5a1ff6d2 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -27,7 +27,6 @@ #include "source/server/config_validation/admin.h" #include "source/server/config_validation/api.h" #include "source/server/config_validation/cluster_manager.h" -#include "source/server/config_validation/dns.h" #include "source/server/hot_restart_nop_impl.h" #include "source/server/server.h" diff --git a/source/server/server.cc b/source/server/server.cc index fc3a50beeed8..50ab8ab9dde6 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -50,13 +50,13 @@ #include "source/common/stats/stats_matcher_impl.h" #include "source/common/stats/thread_local_store.h" #include "source/common/stats/timespan_impl.h" +#include "source/common/tls/context_manager_impl.h" #include "source/common/upstream/cluster_manager_impl.h" #include "source/common/version/version.h" #include "source/server/configuration_impl.h" #include "source/server/listener_hooks.h" #include "source/server/listener_manager_factory.h" #include "source/server/regex_engine.h" -#include "source/server/ssl_context_manager.h" #include "source/server/utils.h" namespace Envoy { @@ -749,7 +749,8 @@ absl::Status InstanceBase::initializeOrThrow(Network::Address::InstanceConstShar } // Once we have runtime we can initialize the SSL context manager. - ssl_context_manager_ = createContextManager("ssl_context_manager", server_contexts_); + ssl_context_manager_ = + std::make_unique(server_contexts_); cluster_manager_factory_ = std::make_unique( serverFactoryContext(), stats_store_, thread_local_, http_context_, diff --git a/source/server/ssl_context_manager.cc b/source/server/ssl_context_manager.cc deleted file mode 100644 index fadc32283163..000000000000 --- a/source/server/ssl_context_manager.cc +++ /dev/null @@ -1,65 +0,0 @@ -#include "source/server/ssl_context_manager.h" - -#include - -#include "envoy/common/exception.h" -#include "envoy/registry/registry.h" - -namespace Envoy { -namespace Server { - -/** - * A stub that provides a SSL context manager capable of reporting on - * certificates' data in case there's no TLS implementation built - * into Envoy. - */ -class SslContextManagerNoTlsStub final : public Envoy::Ssl::ContextManager { - Ssl::ClientContextSharedPtr - createSslClientContext(Stats::Scope& /* scope */, - const Envoy::Ssl::ClientContextConfig& /* config */) override { - throwException(); - } - - Ssl::ServerContextSharedPtr createSslServerContext( - Stats::Scope& /* scope */, const Envoy::Ssl::ServerContextConfig& /* config */, - const std::vector& /* server_names */, Ssl::ContextAdditionalInitFunc) override { - throwException(); - } - - absl::optional daysUntilFirstCertExpires() const override { - return absl::make_optional(std::numeric_limits::max()); - } - absl::optional secondsUntilFirstOcspResponseExpires() const override { - return absl::nullopt; - } - - void iterateContexts(std::function /* callback */) override{}; - - Ssl::PrivateKeyMethodManager& privateKeyMethodManager() override { throwException(); } - - void removeContext(const Envoy::Ssl::ContextSharedPtr& old_context) override { - if (old_context) { - throwEnvoyExceptionOrPanic("SSL is not supported in this configuration"); - } - } - -private: - [[noreturn]] void throwException() { - throwEnvoyExceptionOrPanic("SSL is not supported in this configuration"); - } -}; - -Ssl::ContextManagerPtr -createContextManager(const std::string& factory_name, - Server::Configuration::CommonFactoryContext& factory_context) { - Ssl::ContextManagerFactory* factory = - Registry::FactoryRegistry::getFactory(factory_name); - if (factory != nullptr) { - return factory->createContextManager(factory_context); - } - - return std::make_unique(); -} - -} // namespace Server -} // namespace Envoy diff --git a/source/server/ssl_context_manager.h b/source/server/ssl_context_manager.h deleted file mode 100644 index c296955703fe..000000000000 --- a/source/server/ssl_context_manager.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "envoy/common/time.h" -#include "envoy/ssl/context_manager.h" - -namespace Envoy { -namespace Server { - -Ssl::ContextManagerPtr -createContextManager(const std::string& factory_name, - Server::Configuration::CommonFactoryContext& factory_context); - -} // namespace Server -} // namespace Envoy diff --git a/test/integration/BUILD b/test/integration/BUILD index 06afa7651b01..9db08519afbb 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -373,6 +373,7 @@ envoy_cc_test_binary( "//source/exe:process_wide_lib", "//source/exe:stripped_main_base_lib", "//source/extensions/listener_managers/validation_listener_manager:validation_listener_manager_lib", + "//source/extensions/transport_sockets/tls:config", ], ) diff --git a/test/server/BUILD b/test/server/BUILD index 2938f1dd3429..eb3641017ec0 100644 --- a/test/server/BUILD +++ b/test/server/BUILD @@ -363,19 +363,6 @@ envoy_cc_test( ], ) -envoy_cc_test( - name = "ssl_context_manager_test", - srcs = ["ssl_context_manager_test.cc"], - deps = [ - "//source/server:ssl_context_manager_lib", - "//test/mocks/server:server_factory_context_mocks", - "//test/mocks/ssl:ssl_mocks", - "//test/mocks/stats:stats_mocks", - "//test/test_common:simulated_time_system_lib", - "//test/test_common:utility_lib", - ], -) - envoy_cc_test_library( name = "utility_lib", hdrs = ["utility.h"], diff --git a/test/server/config_validation/BUILD b/test/server/config_validation/BUILD index ed55171c3d66..7d03991b9d65 100644 --- a/test/server/config_validation/BUILD +++ b/test/server/config_validation/BUILD @@ -17,7 +17,6 @@ envoy_cc_test( "//source/common/stats:stats_lib", "//source/common/tls:context_lib", "//source/server/config_validation:cluster_manager_lib", - "//source/server/config_validation:dns_lib", "//test/mocks/access_log:access_log_mocks", "//test/mocks/event:event_mocks", "//test/mocks/http:http_mocks", @@ -78,7 +77,6 @@ envoy_cc_test( "//source/common/event:libevent_lib", "//source/common/stats:isolated_store_lib", "//source/server/config_validation:api_lib", - "//source/server/config_validation:dns_lib", "//test/test_common:environment_lib", "//test/test_common:network_utility_lib", "//test/test_common:test_time_lib", diff --git a/test/server/ssl_context_manager_test.cc b/test/server/ssl_context_manager_test.cc deleted file mode 100644 index 4a651e61dc31..000000000000 --- a/test/server/ssl_context_manager_test.cc +++ /dev/null @@ -1,40 +0,0 @@ -#include - -#include "source/server/ssl_context_manager.h" - -#include "test/mocks/server/server_factory_context.h" -#include "test/mocks/ssl/mocks.h" -#include "test/mocks/stats/mocks.h" -#include "test/test_common/utility.h" - -#include "gtest/gtest.h" - -namespace Envoy { -namespace Server { -namespace { - -TEST(SslContextManager, createStub) { - Stats::MockStore store; - Stats::Scope& scope(*store.rootScope()); - Ssl::MockClientContextConfig client_config; - Ssl::MockServerContextConfig server_config; - std::vector server_names; - NiceMock server_factory_context; - - Ssl::ContextManagerPtr manager = - createContextManager("fake_factory_name", server_factory_context); - - // Check we've created a stub, not real manager. - EXPECT_EQ(manager->daysUntilFirstCertExpires().value(), std::numeric_limits::max()); - EXPECT_EQ(manager->secondsUntilFirstOcspResponseExpires(), absl::nullopt); - EXPECT_THROW_WITH_MESSAGE(manager->createSslClientContext(scope, client_config), EnvoyException, - "SSL is not supported in this configuration"); - EXPECT_THROW_WITH_MESSAGE( - manager->createSslServerContext(scope, server_config, server_names, nullptr), EnvoyException, - "SSL is not supported in this configuration"); - EXPECT_NO_THROW(manager->iterateContexts([](const Envoy::Ssl::Context&) -> void {})); -} - -} // namespace -} // namespace Server -} // namespace Envoy From cb62b70de463f2de598069bd3362395d1c195ded Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Wed, 20 Mar 2024 18:12:23 -0400 Subject: [PATCH 110/124] mobile: Disable the Swift and Kotlin proxying tests (#33015) They are causing issues on CI. See https://github.com/envoyproxy/envoy/issues/33014. Signed-off-by: Ali Beyad --- ...foIntentPerformHTTPSRequestUsingAsyncProxyTest.kt | 2 ++ ...oxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt | 2 ++ .../proxying/HTTPRequestUsingProxyTest.swift | 12 ++++++++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt index c2b1815800d9..2af1a663feda 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt @@ -16,6 +16,7 @@ import io.envoyproxy.envoymobile.engine.testing.TestJni import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors import java.util.concurrent.TimeUnit +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito @@ -39,6 +40,7 @@ class ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest { JniLibrary.load() } + @Ignore("https://github.com/envoyproxy/envoy/issues/33014") @Test fun `performs an HTTPs request through a proxy using async DNS resolution`() { TestJni.startHttpsProxyTestServer() diff --git a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt index 14632160bc31..5521050f4037 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt @@ -16,6 +16,7 @@ import io.envoyproxy.envoymobile.engine.testing.TestJni import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors import java.util.concurrent.TimeUnit +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito @@ -39,6 +40,7 @@ class ProxyInfoIntentPerformHTTPSRequestUsingProxyTest { JniLibrary.load() } + @Ignore("https://github.com/envoyproxy/envoy/issues/33014") @Test fun `performs an HTTPs request through a proxy`() { TestJni.startHttpsProxyTestServer() diff --git a/mobile/test/swift/integration/proxying/HTTPRequestUsingProxyTest.swift b/mobile/test/swift/integration/proxying/HTTPRequestUsingProxyTest.swift index 424159b9e6e4..50d7705d3e01 100644 --- a/mobile/test/swift/integration/proxying/HTTPRequestUsingProxyTest.swift +++ b/mobile/test/swift/integration/proxying/HTTPRequestUsingProxyTest.swift @@ -12,7 +12,8 @@ final class HTTPRequestUsingProxyTest: XCTestCase { register_test_extensions() } - func testHTTPRequestUsingProxy() throws { + // https://github.com/envoyproxy/envoy/issues/33014 + func skipped_testHTTPRequestUsingProxy() throws { EnvoyTestServer.startHttpProxyServer() let port = EnvoyTestServer.getEnvoyPort() @@ -65,7 +66,8 @@ final class HTTPRequestUsingProxyTest: XCTestCase { EnvoyTestServer.shutdownTestServer() } - func testHTTPSRequestUsingProxy() throws { + // https://github.com/envoyproxy/envoy/issues/33014 + func skipped_testHTTPSRequestUsingProxy() throws { EnvoyTestServer.startHttpsProxyServer() let port = EnvoyTestServer.getEnvoyPort() @@ -120,7 +122,8 @@ final class HTTPRequestUsingProxyTest: XCTestCase { EnvoyTestServer.shutdownTestServer() } - func testHTTPSRequestUsingPacFileUrlResolver() throws { + // https://github.com/envoyproxy/envoy/issues/33014 + func skipped_testHTTPSRequestUsingPacFileUrlResolver() throws { EnvoyTestServer.startHttpsProxyServer() let port = EnvoyTestServer.getEnvoyPort() @@ -175,7 +178,8 @@ final class HTTPRequestUsingProxyTest: XCTestCase { EnvoyTestServer.shutdownTestServer() } - func testHTTPRequestUsingProxyCancelStream() throws { + // https://github.com/envoyproxy/envoy/issues/33014 + func skipped_testHTTPRequestUsingProxyCancelStream() throws { EnvoyTestServer.startHttpProxyServer() let port = EnvoyTestServer.getEnvoyPort() From d7e8768900199e6ea6b07d00f53fcb01838524cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 22:25:01 +0000 Subject: [PATCH 111/124] build(deps): bump actions/download-artifact from 3.0.2 to 4.1.4 (#32680) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3.0.2 to 4.1.4. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/9bc31d5ccc31df68ecc42ccf4149144866c47d8a...c850b930e6ba138125429b7e5c93fc707a7f8427) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/mobile-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mobile-release.yml b/.github/workflows/mobile-release.yml index c0a0e3981b97..007ac2b6b819 100644 --- a/.github/workflows/mobile-release.yml +++ b/.github/workflows/mobile-release.yml @@ -100,7 +100,7 @@ jobs: fetch-depth: 0 - name: Add safe directory run: git config --global --add safe.directory /__w/envoy/envoy - - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 with: name: ${{ matrix.output }}_android_aar_sources path: . From dbbe285538d2710b620f0086184377b6e4d9d8c5 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Wed, 20 Mar 2024 18:26:20 -0400 Subject: [PATCH 112/124] network: Refactor Network::Utility functions (#32858) 1. Moves functions belonging in the anonymous namespace together. 2. Moves different readFromSocket modes (GRO, mmsg) to separate functions to improve readability. Signed-off-by: Ali Beyad --- source/common/network/utility.cc | 202 ++++++++++++++++++------------- 1 file changed, 116 insertions(+), 86 deletions(-) diff --git a/source/common/network/utility.cc b/source/common/network/utility.cc index eeacd295e028..bec004ed38de 100644 --- a/source/common/network/utility.cc +++ b/source/common/network/utility.cc @@ -33,6 +33,7 @@ namespace Envoy { namespace Network { +namespace { Address::InstanceConstSharedPtr instanceOrNull(StatusOr address) { if (address.ok()) { @@ -41,6 +42,8 @@ Address::InstanceConstSharedPtr instanceOrNull(StatusOr(); - IoHandle::RecvMsgOutput output(1, packets_dropped); +Api::IoCallUint64Result readFromSocketRecvGro(IoHandle& handle, + const Address::Instance& local_address, + UdpPacketProcessor& udp_packet_processor, + MonotonicTime receive_time, + uint32_t* packets_dropped) { + ASSERT(Api::OsSysCallsSingleton::get().supportsUdpGro(), + "cannot use GRO when the platform doesn't support it."); + Buffer::InstancePtr buffer = std::make_unique(); + IoHandle::RecvMsgOutput output(1, packets_dropped); - // TODO(yugant): Avoid allocating 24k for each read by getting memory from UdpPacketProcessor - const uint64_t max_rx_datagram_size_with_gro = - NUM_DATAGRAMS_PER_RECEIVE * udp_packet_processor.maxDatagramSize(); - ENVOY_LOG_MISC(trace, "starting gro recvmsg with max={}", max_rx_datagram_size_with_gro); + // TODO(yugant): Avoid allocating 24k for each read by getting memory from UdpPacketProcessor + const uint64_t max_rx_datagram_size_with_gro = + NUM_DATAGRAMS_PER_RECEIVE * udp_packet_processor.maxDatagramSize(); + ENVOY_LOG_MISC(trace, "starting gro recvmsg with max={}", max_rx_datagram_size_with_gro); - Api::IoCallUint64Result result = - receiveMessage(max_rx_datagram_size_with_gro, buffer, output, handle, local_address); + Api::IoCallUint64Result result = + receiveMessage(max_rx_datagram_size_with_gro, buffer, output, handle, local_address); - if (!result.ok() || output.msg_[0].truncated_and_dropped_) { - return result; - } + if (!result.ok() || output.msg_[0].truncated_and_dropped_) { + return result; + } - const uint64_t gso_size = output.msg_[0].gso_size_; - ENVOY_LOG_MISC(trace, "gro recvmsg bytes {} with gso_size as {}", result.return_value_, - gso_size); + const uint64_t gso_size = output.msg_[0].gso_size_; + ENVOY_LOG_MISC(trace, "gro recvmsg bytes {} with gso_size as {}", result.return_value_, gso_size); - // Skip gso segmentation and proceed as a single payload. - if (gso_size == 0u) { - passPayloadToProcessor( - result.return_value_, std::move(buffer), std::move(output.msg_[0].peer_address_), - std::move(output.msg_[0].local_address_), udp_packet_processor, receive_time); - return result; - } + // Skip gso segmentation and proceed as a single payload. + if (gso_size == 0u) { + passPayloadToProcessor( + result.return_value_, std::move(buffer), std::move(output.msg_[0].peer_address_), + std::move(output.msg_[0].local_address_), udp_packet_processor, receive_time); + return result; + } - // Segment the buffer read by the recvmsg syscall into gso_sized sub buffers. - // TODO(mattklein123): The following code should be optimized to avoid buffer copies, either by - // switching to slices or by using a CoW buffer type. - while (buffer->length() > 0) { - const uint64_t bytes_to_copy = std::min(buffer->length(), gso_size); - Buffer::InstancePtr sub_buffer = std::make_unique(); - sub_buffer->move(*buffer, bytes_to_copy); - passPayloadToProcessor(bytes_to_copy, std::move(sub_buffer), output.msg_[0].peer_address_, - output.msg_[0].local_address_, udp_packet_processor, receive_time); - } + // Segment the buffer read by the recvmsg syscall into gso_sized sub buffers. + // TODO(mattklein123): The following code should be optimized to avoid buffer copies, either by + // switching to slices or by using a CoW buffer type. + while (buffer->length() > 0) { + const uint64_t bytes_to_copy = std::min(buffer->length(), gso_size); + Buffer::InstancePtr sub_buffer = std::make_unique(); + sub_buffer->move(*buffer, bytes_to_copy); + passPayloadToProcessor(bytes_to_copy, std::move(sub_buffer), output.msg_[0].peer_address_, + output.msg_[0].local_address_, udp_packet_processor, receive_time); + } + return result; +} + +Api::IoCallUint64Result readFromSocketRecvMmsg(IoHandle& handle, + const Address::Instance& local_address, + UdpPacketProcessor& udp_packet_processor, + MonotonicTime receive_time, + uint32_t* packets_dropped) { + ASSERT(Api::OsSysCallsSingleton::get().supportsMmsg(), + "cannot use recvmmsg when the platform doesn't support it."); + const auto max_rx_datagram_size = udp_packet_processor.maxDatagramSize(); + + // Buffer::ReservationSingleSlice is always passed by value, and can only be constructed + // by Buffer::Instance::reserve(), so this is needed to keep a fixed array + // in which all elements are legally constructed. + struct BufferAndReservation { + BufferAndReservation(uint64_t max_rx_datagram_size) + : buffer_(std::make_unique()), + reservation_(buffer_->reserveSingleSlice(max_rx_datagram_size, true)) {} + + Buffer::InstancePtr buffer_; + Buffer::ReservationSingleSlice reservation_; + }; + constexpr uint32_t num_slices_per_packet = 1u; + absl::InlinedVector buffers; + RawSliceArrays slices(NUM_DATAGRAMS_PER_RECEIVE, + absl::FixedArray(num_slices_per_packet)); + for (uint32_t i = 0; i < NUM_DATAGRAMS_PER_RECEIVE; i++) { + buffers.push_back(max_rx_datagram_size); + slices[i][0] = buffers[i].reservation_.slice(); + } + + IoHandle::RecvMsgOutput output(NUM_DATAGRAMS_PER_RECEIVE, packets_dropped); + ENVOY_LOG_MISC(trace, "starting recvmmsg with packets={} max={}", NUM_DATAGRAMS_PER_RECEIVE, + max_rx_datagram_size); + Api::IoCallUint64Result result = handle.recvmmsg(slices, local_address.ip()->port(), output); + if (!result.ok()) { return result; } - if (recv_msg_method == UdpRecvMsgMethod::RecvMmsg) { - ASSERT(Api::OsSysCallsSingleton::get().supportsMmsg(), - "cannot use recvmmsg when the platform doesn't support it."); - const auto max_rx_datagram_size = udp_packet_processor.maxDatagramSize(); - - // Buffer::ReservationSingleSlice is always passed by value, and can only be constructed - // by Buffer::Instance::reserve(), so this is needed to keep a fixed array - // in which all elements are legally constructed. - struct BufferAndReservation { - BufferAndReservation(uint64_t max_rx_datagram_size) - : buffer_(std::make_unique()), - reservation_(buffer_->reserveSingleSlice(max_rx_datagram_size, true)) {} - - Buffer::InstancePtr buffer_; - Buffer::ReservationSingleSlice reservation_; - }; - constexpr uint32_t num_slices_per_packet = 1u; - absl::InlinedVector buffers; - RawSliceArrays slices(NUM_DATAGRAMS_PER_RECEIVE, - absl::FixedArray(num_slices_per_packet)); - for (uint32_t i = 0; i < NUM_DATAGRAMS_PER_RECEIVE; i++) { - buffers.push_back(max_rx_datagram_size); - slices[i][0] = buffers[i].reservation_.slice(); - } - - IoHandle::RecvMsgOutput output(NUM_DATAGRAMS_PER_RECEIVE, packets_dropped); - ENVOY_LOG_MISC(trace, "starting recvmmsg with packets={} max={}", NUM_DATAGRAMS_PER_RECEIVE, - max_rx_datagram_size); - Api::IoCallUint64Result result = handle.recvmmsg(slices, local_address.ip()->port(), output); - if (!result.ok()) { - return result; + uint64_t packets_read = result.return_value_; + ENVOY_LOG_MISC(trace, "recvmmsg read {} packets", packets_read); + for (uint64_t i = 0; i < packets_read; ++i) { + if (output.msg_[i].truncated_and_dropped_) { + continue; } - uint64_t packets_read = result.return_value_; - ENVOY_LOG_MISC(trace, "recvmmsg read {} packets", packets_read); - for (uint64_t i = 0; i < packets_read; ++i) { - if (output.msg_[i].truncated_and_dropped_) { - continue; - } - - Buffer::RawSlice* slice = slices[i].data(); - const uint64_t msg_len = output.msg_[i].msg_len_; - ASSERT(msg_len <= slice->len_); - ENVOY_LOG_MISC(debug, "Receive a packet with {} bytes from {}", msg_len, - output.msg_[i].peer_address_->asString()); + Buffer::RawSlice* slice = slices[i].data(); + const uint64_t msg_len = output.msg_[i].msg_len_; + ASSERT(msg_len <= slice->len_); + ENVOY_LOG_MISC(debug, "Receive a packet with {} bytes from {}", msg_len, + output.msg_[i].peer_address_->asString()); - buffers[i].reservation_.commit(std::min(max_rx_datagram_size, msg_len)); + buffers[i].reservation_.commit(std::min(max_rx_datagram_size, msg_len)); - passPayloadToProcessor(msg_len, std::move(buffers[i].buffer_), output.msg_[i].peer_address_, - output.msg_[i].local_address_, udp_packet_processor, receive_time); - } - return result; + passPayloadToProcessor(msg_len, std::move(buffers[i].buffer_), output.msg_[i].peer_address_, + output.msg_[i].local_address_, udp_packet_processor, receive_time); } + return result; +} +Api::IoCallUint64Result readFromSocketRecvMsg(IoHandle& handle, + const Address::Instance& local_address, + UdpPacketProcessor& udp_packet_processor, + MonotonicTime receive_time, + uint32_t* packets_dropped) { Buffer::InstancePtr buffer = std::make_unique(); IoHandle::RecvMsgOutput output(1, packets_dropped); @@ -719,6 +732,23 @@ Utility::readFromSocket(IoHandle& handle, const Address::Instance& local_address return result; } +} // namespace + +Api::IoCallUint64Result +Utility::readFromSocket(IoHandle& handle, const Address::Instance& local_address, + UdpPacketProcessor& udp_packet_processor, MonotonicTime receive_time, + UdpRecvMsgMethod recv_msg_method, uint32_t* packets_dropped) { + if (recv_msg_method == UdpRecvMsgMethod::RecvMsgWithGro) { + return readFromSocketRecvGro(handle, local_address, udp_packet_processor, receive_time, + packets_dropped); + } else if (recv_msg_method == UdpRecvMsgMethod::RecvMmsg) { + return readFromSocketRecvMmsg(handle, local_address, udp_packet_processor, receive_time, + packets_dropped); + } + return readFromSocketRecvMsg(handle, local_address, udp_packet_processor, receive_time, + packets_dropped); +} + Api::IoErrorPtr Utility::readPacketsFromSocket(IoHandle& handle, const Address::Instance& local_address, UdpPacketProcessor& udp_packet_processor, From 24b3d3588d2d7be39366dbbaf2387aa56edc3585 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 20 Mar 2024 22:30:53 -0400 Subject: [PATCH 113/124] HCM: removing most exceptions (#32985) Signed-off-by: Alyssa Wilk --- envoy/common/exception.h | 6 + .../network/http_connection_manager/config.cc | 125 ++++--- .../network/http_connection_manager/config.h | 9 +- .../config_filter_chain_test.cc | 59 ++-- .../config_filter_dependencies_test.cc | 81 +++-- .../http_connection_manager/config_test.cc | 334 ++++++++++-------- .../config_test_base.h | 5 +- 7 files changed, 354 insertions(+), 265 deletions(-) diff --git a/envoy/common/exception.h b/envoy/common/exception.h index 946e92788739..745936ee26bc 100644 --- a/envoy/common/exception.h +++ b/envoy/common/exception.h @@ -29,6 +29,12 @@ class EnvoyException : public std::runtime_error { EnvoyException(const std::string& message) : std::runtime_error(message) {} }; +#define SET_AND_RETURN_IF_NOT_OK(check_status, set_status) \ + if (!check_status.ok()) { \ + set_status = check_status; \ + return; \ + } + #define THROW_IF_NOT_OK_REF(status) \ do { \ if (!(status).ok()) { \ diff --git a/source/extensions/filters/network/http_connection_manager/config.cc b/source/extensions/filters/network/http_connection_manager/config.cc index a016f5058240..458c3eeb3e92 100644 --- a/source/extensions/filters/network/http_connection_manager/config.cc +++ b/source/extensions/filters/network/http_connection_manager/config.cc @@ -72,9 +72,11 @@ FilterFactoryMap::const_iterator findUpgradeCaseInsensitive(const FilterFactoryM std::unique_ptr createInternalAddressConfig( const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - config) { + config, + absl::Status& creation_status) { if (config.has_internal_address_config()) { - return std::make_unique(config.internal_address_config()); + return std::make_unique(config.internal_address_config(), + creation_status); } return std::make_unique(); @@ -128,7 +130,8 @@ envoy::extensions::filters::network::http_connection_manager::v3::HttpConnection Http::HeaderValidatorFactoryPtr createHeaderValidatorFactory([[maybe_unused]] const envoy::extensions::filters::network:: http_connection_manager::v3::HttpConnectionManager& config, - [[maybe_unused]] Server::Configuration::FactoryContext& context) { + [[maybe_unused]] Server::Configuration::FactoryContext& context, + absl::Status& creation_status) { Http::HeaderValidatorFactoryPtr header_validator_factory; #ifdef ENVOY_ENABLE_UHV @@ -172,27 +175,31 @@ createHeaderValidatorFactory([[maybe_unused]] const envoy::extensions::filters:: auto* factory = Envoy::Config::Utility::getFactory( header_validator_config); if (!factory) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( fmt::format("Header validator extension not found: '{}'", header_validator_config.name())); + return nullptr; } header_validator_factory = factory->createFromProto(header_validator_config.typed_config(), context.serverFactoryContext()); if (!header_validator_factory) { - throwEnvoyExceptionOrPanic(fmt::format("Header validator extension could not be created: '{}'", - header_validator_config.name())); + creation_status = absl::InvalidArgumentError(fmt::format( + "Header validator extension could not be created: '{}'", header_validator_config.name())); + return nullptr; } #else if (config.has_typed_header_validation_config()) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( fmt::format("This Envoy binary does not support header validator extensions.: '{}'", config.typed_header_validation_config().name())); + return nullptr; } if (Runtime::runtimeFeatureEnabled( "envoy.reloadable_features.enable_universal_header_validator")) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Header validator can not be enabled since this Envoy binary does not support it."); + return nullptr; } #endif return header_validator_factory; @@ -239,7 +246,7 @@ Utility::Singletons Utility::createSingletons(Server::Configuration::FactoryCont tracer_manager, filter_config_provider_manager}; } -std::shared_ptr Utility::createConfig( +absl::StatusOr> Utility::createConfig( const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& proto_config, Server::Configuration::FactoryContext& context, Http::DateProvider& date_provider, @@ -247,9 +254,13 @@ std::shared_ptr Utility::createConfig( Config::ConfigProviderManager& scoped_routes_config_provider_manager, Tracing::TracerManager& tracer_manager, FilterConfigProviderManager& filter_config_provider_manager) { - return std::make_shared( + absl::Status creation_status = absl::OkStatus(); + auto config = std::make_shared( proto_config, context, date_provider, route_config_provider_manager, - scoped_routes_config_provider_manager, tracer_manager, filter_config_provider_manager); + scoped_routes_config_provider_manager, tracer_manager, filter_config_provider_manager, + creation_status); + RETURN_IF_NOT_OK(creation_status); + return config; } Network::FilterFactoryCb @@ -267,10 +278,13 @@ HttpConnectionManagerFilterConfigFactory::createFilterFactoryFromProtoAndHopByHo Server::Configuration::FactoryContext& context, bool clear_hop_by_hop_headers) { Utility::Singletons singletons = Utility::createSingletons(context); - auto filter_config = Utility::createConfig( - proto_config, context, *singletons.date_provider_, *singletons.route_config_provider_manager_, - *singletons.scoped_routes_config_provider_manager_, *singletons.tracer_manager_, - *singletons.filter_config_provider_manager_); + auto filter_config = THROW_OR_RETURN_VALUE( + Utility::createConfig(proto_config, context, *singletons.date_provider_, + *singletons.route_config_provider_manager_, + *singletons.scoped_routes_config_provider_manager_, + *singletons.tracer_manager_, + *singletons.filter_config_provider_manager_), + std::shared_ptr); // This lambda captures the shared_ptrs created above, thus preserving the // reference count. @@ -310,10 +324,11 @@ LEGACY_REGISTER_FACTORY(HttpConnectionManagerFilterConfigFactory, InternalAddressConfig::InternalAddressConfig( const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager:: - InternalAddressConfig& config) + InternalAddressConfig& config, + absl::Status& creation_status) : unix_sockets_(config.unix_sockets()) { auto list_or_error = Network::Address::IpList::create(config.cidr_ranges()); - THROW_IF_STATUS_NOT_OK(list_or_error, throw); + SET_AND_RETURN_IF_NOT_OK(list_or_error.status(), creation_status); cidr_ranges_ = std::move(list_or_error.value()); } @@ -324,13 +339,13 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( Router::RouteConfigProviderManager& route_config_provider_manager, Config::ConfigProviderManager& scoped_routes_config_provider_manager, Tracing::TracerManager& tracer_manager, - FilterConfigProviderManager& filter_config_provider_manager) + FilterConfigProviderManager& filter_config_provider_manager, absl::Status& creation_status) : context_(context), stats_prefix_(fmt::format("http.{}.", config.stat_prefix())), stats_(Http::ConnectionManagerImpl::generateStats(stats_prefix_, context_.scope())), tracing_stats_( Http::ConnectionManagerImpl::generateTracingStats(stats_prefix_, context_.scope())), use_remote_address_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, use_remote_address, false)), - internal_address_config_(createInternalAddressConfig(config)), + internal_address_config_(createInternalAddressConfig(config, creation_status)), xff_num_trusted_hops_(config.xff_num_trusted_hops()), skip_xff_append_(config.skip_xff_append()), via_(config.via()), route_config_provider_manager_(route_config_provider_manager), @@ -397,16 +412,19 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( ? std::make_unique( config.proxy_status_config()) : nullptr), - header_validator_factory_(createHeaderValidatorFactory(config, context)), + header_validator_factory_(createHeaderValidatorFactory(config, context, creation_status)), append_local_overload_(config.append_local_overload()), append_x_forwarded_port_(config.append_x_forwarded_port()), add_proxy_protocol_connection_state_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, add_proxy_protocol_connection_state, true)) { + if (!creation_status.ok()) { + return; + } auto options_or_error = Http2::Utility::initializeAndValidateOptions( config.http2_protocol_options(), config.has_stream_error_on_invalid_http_message(), config.stream_error_on_invalid_http_message()); - THROW_IF_STATUS_NOT_OK(options_or_error, throw); + SET_AND_RETURN_IF_NOT_OK(options_or_error.status(), creation_status); http2_options_ = options_or_error.value(); if (!idle_timeout_) { idle_timeout_ = std::chrono::hours(1); @@ -415,8 +433,9 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( } if (config.strip_any_host_port() && config.strip_matching_host_port()) { - throwEnvoyExceptionOrPanic(fmt::format( + creation_status = absl::InvalidArgumentError(fmt::format( "Error: Only one of `strip_matching_host_port` or `strip_any_host_port` can be set.")); + return; } if (config.strip_any_host_port()) { @@ -438,7 +457,7 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( envoy::extensions::request_id::uuid::v3::UuidRequestIdConfig()); } auto extension_or_error = Http::RequestIDExtensionFactory::fromProto(final_rid_config, context_); - THROW_IF_STATUS_NOT_OK(extension_or_error, throw); + SET_AND_RETURN_IF_NOT_OK(extension_or_error.status(), creation_status); request_id_extension_ = extension_or_error.value(); // Check if IP detection extensions were configured, otherwise fall back to XFF. @@ -452,13 +471,15 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( extension->mutable_typed_config()->PackFrom(xff_config); } else { if (use_remote_address_) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Original IP detection extensions and use_remote_address may not be mixed"); + return; } if (xff_num_trusted_hops_ > 0) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Original IP detection extensions and xff_num_trusted_hops may not be mixed"); + return; } } @@ -467,14 +488,16 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( auto* factory = Envoy::Config::Utility::getFactory(extension_config); if (!factory) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( fmt::format("Original IP detection extension not found: '{}'", extension_config.name())); + return; } auto extension = factory->createExtension(extension_config.typed_config(), context_); if (!extension) { - throwEnvoyExceptionOrPanic(fmt::format( + creation_status = absl::InvalidArgumentError(fmt::format( "Original IP detection extension could not be created: '{}'", extension_config.name())); + return; } original_ip_detection_extensions_.push_back(extension); } @@ -485,14 +508,16 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( auto* factory = Envoy::Config::Utility::getFactory(extension_config); if (!factory) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( fmt::format("Early header mutation extension not found: '{}'", extension_config.name())); + return; } auto extension = factory->createExtension(extension_config.typed_config(), context_); if (!extension) { - throwEnvoyExceptionOrPanic(fmt::format( + creation_status = absl::InvalidArgumentError(fmt::format( "Early header mutation extension could not be created: '{}'", extension_config.name())); + return; } early_header_mutation_extensions_.push_back(std::move(extension)); } @@ -579,13 +604,15 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( if (config.has_access_log_options()) { if (config.flush_access_log_on_new_request() /* deprecated */) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Only one of flush_access_log_on_new_request or access_log_options can be specified."); + return; } if (config.has_access_log_flush_interval()) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Only one of access_log_flush_interval or access_log_options can be specified."); + return; } flush_access_log_on_new_request_ = @@ -637,41 +664,46 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( #ifdef ENVOY_ENABLE_QUIC codec_type_ = CodecType::HTTP3; if (!context_.listenerInfo().isQuic()) { - throwEnvoyExceptionOrPanic("HTTP/3 codec configured on non-QUIC listener."); + creation_status = absl::InvalidArgumentError("HTTP/3 codec configured on non-QUIC listener."); + return; } #else - throwEnvoyExceptionOrPanic("HTTP3 configured but not enabled in the build."); + creation_status = absl::InvalidArgumentError("HTTP3 configured but not enabled in the build."); + return; #endif break; } if (codec_type_ != CodecType::HTTP3 && context_.listenerInfo().isQuic()) { - throwEnvoyExceptionOrPanic("Non-HTTP/3 codec configured on QUIC listener."); + creation_status = absl::InvalidArgumentError("Non-HTTP/3 codec configured on QUIC listener."); + return; } Http::FilterChainHelper helper(filter_config_provider_manager_, context_.serverFactoryContext(), context_.serverFactoryContext().clusterManager(), context_, stats_prefix_); - THROW_IF_NOT_OK(helper.processFilters(config.http_filters(), "http", "http", filter_factories_)); + SET_AND_RETURN_IF_NOT_OK( + helper.processFilters(config.http_filters(), "http", "http", filter_factories_), + creation_status); for (const auto& upgrade_config : config.upgrade_configs()) { const std::string& name = upgrade_config.upgrade_type(); const bool enabled = upgrade_config.has_enabled() ? upgrade_config.enabled().value() : true; if (findUpgradeCaseInsensitive(upgrade_filter_factories_, name) != upgrade_filter_factories_.end()) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( fmt::format("Error: multiple upgrade configs with the same name: '{}'", name)); + return; } if (!upgrade_config.filters().empty()) { std::unique_ptr factories = std::make_unique(); Http::DependencyManager upgrade_dependency_manager; - THROW_IF_NOT_OK( - helper.processFilters(upgrade_config.filters(), name, "http upgrade", *factories)); + SET_AND_RETURN_IF_NOT_OK( + helper.processFilters(upgrade_config.filters(), name, "http upgrade", *factories), + creation_status); // TODO(auni53): Validate encode dependencies too. auto status = upgrade_dependency_manager.validDecodeDependencies(); - if (!status.ok()) { - throwEnvoyExceptionOrPanic(std::string(status.message())); - } + SET_AND_RETURN_IF_NOT_OK(status, creation_status); upgrade_filter_factories_.emplace( std::make_pair(name, FilterConfig{std::move(factories), enabled})); @@ -803,10 +835,13 @@ HttpConnectionManagerFactory::createHttpConnectionManagerFactoryFromProto( Server::Configuration::FactoryContext& context, bool clear_hop_by_hop_headers) { Utility::Singletons singletons = Utility::createSingletons(context); - auto filter_config = Utility::createConfig( - proto_config, context, *singletons.date_provider_, *singletons.route_config_provider_manager_, - *singletons.scoped_routes_config_provider_manager_, *singletons.tracer_manager_, - *singletons.filter_config_provider_manager_); + auto filter_config = THROW_OR_RETURN_VALUE( + Utility::createConfig(proto_config, context, *singletons.date_provider_, + *singletons.route_config_provider_manager_, + *singletons.scoped_routes_config_provider_manager_, + *singletons.tracer_manager_, + *singletons.filter_config_provider_manager_), + std::shared_ptr); // This lambda captures the shared_ptrs created above, thus preserving the // reference count. diff --git a/source/extensions/filters/network/http_connection_manager/config.h b/source/extensions/filters/network/http_connection_manager/config.h index b03d65e22c1c..b18e396a8367 100644 --- a/source/extensions/filters/network/http_connection_manager/config.h +++ b/source/extensions/filters/network/http_connection_manager/config.h @@ -100,7 +100,8 @@ DECLARE_FACTORY(MobileHttpConnectionManagerFilterConfigFactory); class InternalAddressConfig : public Http::InternalAddressConfig { public: InternalAddressConfig(const envoy::extensions::filters::network::http_connection_manager::v3:: - HttpConnectionManager::InternalAddressConfig& config); + HttpConnectionManager::InternalAddressConfig& config, + absl::Status& creation_status); bool isInternalAddress(const Network::Address::Instance& address) const override { if (address.type() == Network::Address::Type::Pipe) { @@ -134,7 +135,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, Router::RouteConfigProviderManager& route_config_provider_manager, Config::ConfigProviderManager& scoped_routes_config_provider_manager, Tracing::TracerManager& tracer_manager, - FilterConfigProviderManager& filter_config_provider_manager); + FilterConfigProviderManager& filter_config_provider_manager, absl::Status& creation_status); // Http::FilterChainFactory bool createFilterChain( @@ -406,9 +407,9 @@ class Utility { * @param date_provider the singleton used in config creation. * @param route_config_provider_manager the singleton used in config creation. * @param scoped_routes_config_provider_manager the singleton used in config creation. - * @return a shared_ptr to the created config object. + * @return a shared_ptr to the created config object or a creation error */ - static std::shared_ptr createConfig( + static absl::StatusOr> createConfig( const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& proto_config, Server::Configuration::FactoryContext& context, Http::DateProvider& date_provider, diff --git a/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc b/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc index 372a971e0a18..1dac87eb74d5 100644 --- a/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc @@ -47,7 +47,8 @@ TEST_F(FilterChainTest, CreateFilterChain) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(basic_config_), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); NiceMock manager; EXPECT_CALL(manager.callbacks_, addStreamFilter(_)); // Buffer @@ -81,7 +82,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(basic_config_), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); NiceMock manager; EXPECT_CALL(manager.callbacks_, addStreamDecoderFilter(_)); // Router @@ -89,7 +91,7 @@ stat_prefix: router } TEST_F(FilterChainTest, CreateFilterChainWithDisabledTerminalFilter) { - const std::string config_yaml = R"EOF( + const std::string yaml_string = R"EOF( codec_type: http1 server_name: foo stat_prefix: router @@ -112,11 +114,7 @@ stat_prefix: router )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig(parseHttpConnectionManagerFromYaml(config_yaml), context_, - date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Error: the last (terminal) filter (envoy.filters.http.router) in the chain cannot be " "disabled by default."); } @@ -155,7 +153,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); NiceMock manager; Http::StreamDecoderFilterSharedPtr missing_config_filter; @@ -180,9 +179,11 @@ TEST_F(FilterChainTest, CreateUpgradeFilterChain) { auto hcm_config = parseHttpConnectionManagerFromYaml(basic_config_); hcm_config.add_upgrade_configs()->set_upgrade_type("websocket"); - HttpConnectionManagerConfig config( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); NiceMock manager; ; @@ -228,9 +229,11 @@ TEST_F(FilterChainTest, CreateUpgradeFilterChainHCMDisabled) { hcm_config.add_upgrade_configs()->set_upgrade_type("websocket"); hcm_config.mutable_upgrade_configs(0)->mutable_enabled()->set_value(false); - HttpConnectionManagerConfig config( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); NiceMock manager; ; @@ -283,9 +286,11 @@ TEST_F(FilterChainTest, CreateCustomUpgradeFilterChain) { "\x19" "envoy.filters.http.router"); - HttpConnectionManagerConfig config( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); { NiceMock manager; @@ -327,9 +332,10 @@ TEST_F(FilterChainTest, CreateCustomUpgradeFilterChainWithRouterNotLast) { "encoder-decoder-buffer-filter"); EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), + HttpConnectionManagerConfig(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_), EnvoyException, "Error: terminal filter named envoy.filters.http.router of type envoy.filters.http.router " "must be the last filter in a http upgrade filter chain."); @@ -340,11 +346,12 @@ TEST_F(FilterChainTest, InvalidConfig) { hcm_config.add_upgrade_configs()->set_upgrade_type("WEBSOCKET"); hcm_config.add_upgrade_configs()->set_upgrade_type("websocket"); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, "Error: multiple upgrade configs with the same name: 'websocket'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Error: multiple upgrade configs with the same name: 'websocket'"); } } // namespace diff --git a/test/extensions/filters/network/http_connection_manager/config_filter_dependencies_test.cc b/test/extensions/filters/network/http_connection_manager/config_filter_dependencies_test.cc index 7d03a24e9fe4..75154ec224a4 100644 --- a/test/extensions/filters/network/http_connection_manager/config_filter_dependencies_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_filter_dependencies_test.cc @@ -82,9 +82,10 @@ TEST_F(HttpConnectionManagerConfigTest, UnregisteredFilterException) { hcm_config.add_http_filters()->set_name("envoy.filters.http.router"); EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), + HttpConnectionManagerConfig(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_), EnvoyException, "Didn't find a registered implementation for name: 'test.pantry'"); } @@ -102,7 +103,8 @@ TEST_F(HttpConnectionManagerConfigTest, AllDependenciesSatisfiedOk) { HttpConnectionManagerConfig(hcm_config, context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); } // PantryFilter provides a potato, which is not required by any other filter. @@ -116,7 +118,8 @@ TEST_F(HttpConnectionManagerConfigTest, UnusedProvidencyOk) { HttpConnectionManagerConfig(hcm_config, context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); } // ChefFilter requires a potato, but no filter provides it. @@ -128,12 +131,12 @@ TEST_F(HttpConnectionManagerConfigTest, UnmetDependencyError) { ChefFilterFactory cf; Registry::InjectFactory rc(cf); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, - "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); } // ChefFilter requires a potato, but no preceding filter provides it. @@ -149,12 +152,12 @@ TEST_F(HttpConnectionManagerConfigTest, MisorderedDependenciesError) { ChefFilterFactory cf; Registry::InjectFactory rc(cf); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, - "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); } TEST_F(HttpConnectionManagerConfigTest, UpgradeUnmetDependencyError) { @@ -168,12 +171,12 @@ TEST_F(HttpConnectionManagerConfigTest, UpgradeUnmetDependencyError) { ChefFilterFactory cf; Registry::InjectFactory rc(cf); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, - "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); } TEST_F(HttpConnectionManagerConfigTest, UpgradeDependencyOK) { @@ -189,9 +192,11 @@ TEST_F(HttpConnectionManagerConfigTest, UpgradeDependencyOK) { ChefFilterFactory cf; Registry::InjectFactory rc(cf); - HttpConnectionManagerConfig(hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); } // Dependencies provided in the HCM config filter chain do not satisfy @@ -211,12 +216,12 @@ TEST_F(HttpConnectionManagerConfigTest, UpgradeFilterChainDependenciesIsolatedFr ChefFilterFactory cf; Registry::InjectFactory rc(cf); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, - "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); } // Dependencies provided in one upgrade filter chain do not satisfy @@ -238,12 +243,12 @@ TEST_F(HttpConnectionManagerConfigTest, UpgradeFilterChainDependenciesIsolatedFr ChefFilterFactory cf; Registry::InjectFactory rc(cf); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, - "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); } } // namespace diff --git a/test/extensions/filters/network/http_connection_manager/config_test.cc b/test/extensions/filters/network/http_connection_manager/config_test.cc index f47c2f1a1525..d1a2a7a738f0 100644 --- a/test/extensions/filters/network/http_connection_manager/config_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_test.cc @@ -178,7 +178,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(128, config.tracingConfig()->max_path_tag_length_); EXPECT_EQ(*context_.server_factory_context_.local_info_.address_, config.localAddress()); @@ -214,27 +215,21 @@ stat_prefix: router { EXPECT_CALL(listener_info_, isQuic()).WillOnce(Return(false)); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, "HTTP/3 codec configured on non-QUIC listener."); + EXPECT_THROW_WITH_MESSAGE(createHttpConnectionManagerConfig(yaml_string), EnvoyException, + "HTTP/3 codec configured on non-QUIC listener."); } { + creation_status_ = absl::OkStatus(); EXPECT_CALL(listener_info_, isQuic()).WillOnce(Return(true)); HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); } #else - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig(parseHttpConnectionManagerFromYaml(yaml_string), context_, - date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, "HTTP3 configured but not enabled in the build."); + EXPECT_THROW_WITH_MESSAGE(createHttpConnectionManagerConfig(yaml_string), EnvoyException, + "HTTP3 configured but not enabled in the build."); #endif } @@ -261,12 +256,8 @@ stat_prefix: router EXPECT_CALL(listener_info_, isQuic()).WillOnce(Return(true)); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, - date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, "Non-HTTP/3 codec configured on QUIC listener."); + EXPECT_THROW_WITH_MESSAGE(createHttpConnectionManagerConfig(yaml_string), EnvoyException, + "Non-HTTP/3 codec configured on QUIC listener."); } TEST_F(HttpConnectionManagerConfigTest, TracingNotEnabledAndNoTracingConfigInBootstrap) { @@ -297,7 +288,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // By default, tracer must be a null object (Tracing::NullTracer) rather than nullptr. EXPECT_THAT(config.tracer().get(), WhenDynamicCastTo(NotNull())); @@ -338,7 +330,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Even though tracer provider is configured in the bootstrap config, a given filter instance // should not have a tracer associated with it. @@ -376,7 +369,8 @@ tracing: {} # notice that tracing is enabled HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Actual Tracer must be obtained from the HttpTracerManager. EXPECT_THAT(config.tracer(), Eq(tracer_)); @@ -419,7 +413,8 @@ tracing: {} # notice that tracing is enabled HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Actual Tracer must be obtained from the HttpTracerManager. EXPECT_THAT(config.tracer(), Eq(tracer_)); @@ -480,7 +475,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Actual Tracer must be obtained from the HttpTracerManager. EXPECT_THAT(config.tracer(), Eq(tracer_)); @@ -512,7 +508,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); std::vector custom_tags{"ltag", "etag", "rtag", "mtag"}; const Tracing::CustomTagMap& custom_tag_map = config.tracingConfig()->custom_tags_; @@ -538,7 +535,8 @@ TEST_F(HttpConnectionManagerConfigTest, SamplingDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(100, config.tracingConfig()->client_sampling_.numerator()); EXPECT_EQ(Tracing::DefaultMaxPathTagLength, config.tracingConfig()->max_path_tag_length_); @@ -575,7 +573,8 @@ TEST_F(HttpConnectionManagerConfigTest, SamplingConfigured) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(1, config.tracingConfig()->client_sampling_.numerator()); EXPECT_EQ(envoy::type::v3::FractionalPercent::HUNDRED, @@ -611,7 +610,8 @@ TEST_F(HttpConnectionManagerConfigTest, FractionalSamplingConfigured) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(0, config.tracingConfig()->client_sampling_.numerator()); EXPECT_EQ(envoy::type::v3::FractionalPercent::HUNDRED, @@ -647,7 +647,8 @@ TEST_F(HttpConnectionManagerConfigTest, OverallSampling) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); Stats::TestUtil::TestStore store; Api::ApiPtr api = Api::createApiForTest(store); @@ -696,7 +697,8 @@ TEST_F(HttpConnectionManagerConfigTest, UnixSocketInternalAddress) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); Network::Address::PipeInstance unixAddress{"/foo"}; Network::Address::Ipv4Instance internalIpAddress{"127.0.0.1", 0, nullptr}; Network::Address::Ipv4Instance externalIpAddress{"12.0.0.1", 0, nullptr}; @@ -724,7 +726,8 @@ TEST_F(HttpConnectionManagerConfigTest, CidrRangeBasedInternalAddress) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); Network::Address::Ipv4Instance first_internal_ip_address{"100.64.0.10", 0, nullptr}; Network::Address::Ipv4Instance second_internal_ip_address{"50.20.0.5", 0, nullptr}; // This address is in the list of acceptable addresses (based on RFC1918) when the new config is @@ -756,7 +759,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(60, config.maxRequestHeadersKb()); } @@ -775,7 +779,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbConfigured) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(16, config.maxRequestHeadersKb()); } @@ -794,7 +799,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbMaxConfigurable) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(8192, config.maxRequestHeadersKb()); } @@ -816,7 +822,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbMaxConfiguredViaRunti HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(9000, config.maxRequestHeadersKb()); } @@ -836,7 +843,8 @@ TEST_F(HttpConnectionManagerConfigTest, DisabledStreamIdleTimeout) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(0, config.streamIdleTimeout().count()); } @@ -857,7 +865,8 @@ TEST_F(HttpConnectionManagerConfigTest, CommonHttpProtocolIdleTimeout) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(1000, config.idleTimeout().value().count()); } @@ -876,7 +885,8 @@ TEST_F(HttpConnectionManagerConfigTest, CommonHttpProtocolIdleTimeoutDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(std::chrono::hours(1), config.idleTimeout().value()); } @@ -897,7 +907,8 @@ TEST_F(HttpConnectionManagerConfigTest, CommonHttpProtocolIdleTimeoutOff) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.idleTimeout().has_value()); } @@ -916,7 +927,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultMaxRequestHeaderCount) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(100, config.maxRequestHeadersCount()); } @@ -937,7 +949,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeaderCountConfigurable) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(200, config.maxRequestHeadersCount()); } @@ -956,7 +969,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultMaxRequestPerConnection) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(0, config.maxRequestsPerConnection()); } @@ -977,7 +991,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestPerConnectionConfigurable) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(5, config.maxRequestsPerConnection()); } @@ -1000,7 +1015,8 @@ TEST_F(HttpConnectionManagerConfigTest, ServerOverwrite) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(HttpConnectionManagerConfig::HttpConnectionManagerProto::OVERWRITE, config.serverHeaderTransformation()); } @@ -1024,7 +1040,8 @@ TEST_F(HttpConnectionManagerConfigTest, ServerAppendIfAbsent) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(HttpConnectionManagerConfig::HttpConnectionManagerProto::APPEND_IF_ABSENT, config.serverHeaderTransformation()); } @@ -1048,7 +1065,8 @@ TEST_F(HttpConnectionManagerConfigTest, ServerPassThrough) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(HttpConnectionManagerConfig::HttpConnectionManagerProto::PASS_THROUGH, config.serverHeaderTransformation()); } @@ -1073,7 +1091,8 @@ TEST_F(HttpConnectionManagerConfigTest, SchemeOverwrite) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(config.schemeToSet(), "http"); } @@ -1097,7 +1116,8 @@ TEST_F(HttpConnectionManagerConfigTest, NormalizePathDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); #ifdef ENVOY_NORMALIZE_PATH_BY_DEFAULT EXPECT_TRUE(config.shouldNormalizePath()); #else @@ -1127,7 +1147,8 @@ TEST_F(HttpConnectionManagerConfigTest, NormalizePathRuntime) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.shouldNormalizePath()); } @@ -1154,7 +1175,8 @@ TEST_F(HttpConnectionManagerConfigTest, NormalizePathTrue) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.shouldNormalizePath()); } @@ -1181,7 +1203,8 @@ TEST_F(HttpConnectionManagerConfigTest, NormalizePathFalse) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.shouldNormalizePath()); } @@ -1200,7 +1223,8 @@ TEST_F(HttpConnectionManagerConfigTest, MergeSlashesDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.shouldMergeSlashes()); } @@ -1220,7 +1244,8 @@ TEST_F(HttpConnectionManagerConfigTest, MergeSlashesTrue) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.shouldMergeSlashes()); } @@ -1240,7 +1265,8 @@ TEST_F(HttpConnectionManagerConfigTest, MergeSlashesFalse) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.shouldMergeSlashes()); } @@ -1259,7 +1285,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemovePortDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::StripPortType::None, config.stripPortType()); } @@ -1279,7 +1306,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemovePortTrue) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::StripPortType::MatchingHost, config.stripPortType()); } @@ -1298,11 +1326,7 @@ TEST_F(HttpConnectionManagerConfigTest, BothStripOptionsAreSet) { )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig(parseHttpConnectionManagerFromYaml(yaml_string), context_, - date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Error: Only one of `strip_matching_host_port` or `strip_any_host_port` can be set."); } @@ -1322,7 +1346,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemovePortFalse) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::StripPortType::None, config.stripPortType()); } @@ -1342,7 +1367,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemoveAnyPortTrue) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::StripPortType::Any, config.stripPortType()); } @@ -1362,7 +1388,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemoveAnyPortFalse) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::StripPortType::None, config.stripPortType()); } @@ -1381,7 +1408,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemoveTrailingDotDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(false, config.shouldStripTrailingHostDot()); } @@ -1401,7 +1429,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemoveTrailingDotTrue) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(true, config.shouldStripTrailingHostDot()); } @@ -1421,7 +1450,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemoveTrailingDotFalse) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(false, config.shouldStripTrailingHostDot()); } @@ -1440,7 +1470,8 @@ TEST_F(HttpConnectionManagerConfigTest, HeadersWithUnderscoresAllowedByDefault) HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::config::core::v3::HttpProtocolOptions::ALLOW, config.headersWithUnderscoresAction()); } @@ -1462,7 +1493,8 @@ TEST_F(HttpConnectionManagerConfigTest, HeadersWithUnderscoresDroppedByConfig) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER, config.headersWithUnderscoresAction()); } @@ -1484,7 +1516,8 @@ TEST_F(HttpConnectionManagerConfigTest, HeadersWithUnderscoresRequestRejectedByC HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::config::core::v3::HttpProtocolOptions::REJECT_REQUEST, config.headersWithUnderscoresAction()); } @@ -1504,7 +1537,8 @@ TEST_F(HttpConnectionManagerConfigTest, ConfiguredRequestTimeout) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(53 * 1000, config.requestTimeout().count()); } @@ -1523,7 +1557,8 @@ TEST_F(HttpConnectionManagerConfigTest, DisabledRequestTimeout) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(0, config.requestTimeout().count()); } @@ -1541,7 +1576,8 @@ TEST_F(HttpConnectionManagerConfigTest, UnconfiguredRequestTimeout) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(0, config.requestTimeout().count()); } @@ -1624,7 +1660,8 @@ TEST_F(HttpConnectionManagerConfigTest, FlushAccessLogOnNewRequest) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(base_yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.flushAccessLogOnNewRequest()); } @@ -1638,7 +1675,8 @@ TEST_F(HttpConnectionManagerConfigTest, FlushAccessLogOnNewRequest) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.flushAccessLogOnNewRequest()); } @@ -1652,7 +1690,8 @@ TEST_F(HttpConnectionManagerConfigTest, FlushAccessLogOnNewRequest) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.flushAccessLogOnNewRequest()); } @@ -1687,7 +1726,8 @@ TEST_F(HttpConnectionManagerConfigTest, HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.flushAccessLogOnNewRequest()); } @@ -1700,11 +1740,7 @@ TEST_F(HttpConnectionManagerConfigTest, )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Only one of flush_access_log_on_new_request or access_log_options can be specified."); } @@ -1716,11 +1752,7 @@ TEST_F(HttpConnectionManagerConfigTest, )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Only one of access_log_flush_interval or access_log_options can be specified."); } } @@ -1749,7 +1781,8 @@ TEST_F(HttpConnectionManagerConfigTest, AccessLogFlushInterval) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(base_yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.accessLogFlushInterval().has_value()); } @@ -1763,7 +1796,8 @@ TEST_F(HttpConnectionManagerConfigTest, AccessLogFlushInterval) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.accessLogFlushInterval().has_value()); EXPECT_EQ(std::chrono::seconds(1), config.accessLogFlushInterval().value()); @@ -1798,7 +1832,8 @@ TEST_F(HttpConnectionManagerConfigTest, DEPRECATED_FEATURE_TEST(DeprecatedAccess HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.accessLogFlushInterval().has_value()); EXPECT_EQ(std::chrono::seconds(1), config.accessLogFlushInterval().value()); @@ -1812,11 +1847,7 @@ TEST_F(HttpConnectionManagerConfigTest, DEPRECATED_FEATURE_TEST(DeprecatedAccess )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Only one of access_log_flush_interval or access_log_options can be specified."); } @@ -1828,11 +1859,7 @@ TEST_F(HttpConnectionManagerConfigTest, DEPRECATED_FEATURE_TEST(DeprecatedAccess )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Only one of flush_access_log_on_new_request or access_log_options can be specified."); } } @@ -2145,7 +2172,8 @@ TEST_F(HttpConnectionManagerConfigTest, AlwaysSetRequestIdInResponseDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.alwaysSetRequestIdInResponse()); } @@ -2164,7 +2192,8 @@ TEST_F(HttpConnectionManagerConfigTest, AlwaysSetRequestIdInResponseConfigured) HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.alwaysSetRequestIdInResponse()); } @@ -2237,7 +2266,8 @@ TEST_F(HttpConnectionManagerConfigTest, CustomRequestIDExtension) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); auto request_id_extension = dynamic_cast(config.requestIDExtension().get()); ASSERT_NE(nullptr, request_id_extension); @@ -2303,7 +2333,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultRequestIDExtension) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); auto request_id_extension = dynamic_cast( config.requestIDExtension().get()); ASSERT_NE(nullptr, request_id_extension); @@ -2330,7 +2361,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultRequestIDExtensionWithParams) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); auto request_id_extension = dynamic_cast( config.requestIDExtension().get()); ASSERT_NE(nullptr, request_id_extension); @@ -2481,7 +2513,8 @@ TEST_F(HttpConnectionManagerConfigTest, OriginalIPDetectionExtension) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); const auto& original_ip_detection_extensions = config.originalIpDetectionExtensions(); EXPECT_EQ(1, original_ip_detection_extensions.size()); @@ -2507,7 +2540,8 @@ TEST_F(HttpConnectionManagerConfigTest, EarlyHeaderMutationExtension) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); const auto& early_header_mutation_extensions = config.earlyHeaderMutationExtensions(); EXPECT_EQ(1, early_header_mutation_extensions.size()); @@ -2827,7 +2861,8 @@ TEST_F(HttpConnectionManagerConfigTest, PathWithEscapedSlashesActionDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::KEEP_UNCHANGED, config.pathWithEscapedSlashesAction()); @@ -2856,7 +2891,8 @@ TEST_F(HttpConnectionManagerConfigTest, PathWithEscapedSlashesActionDefaultOverr HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::UNESCAPE_AND_REDIRECT, config.pathWithEscapedSlashesAction()); @@ -2868,7 +2904,8 @@ TEST_F(HttpConnectionManagerConfigTest, PathWithEscapedSlashesActionDefaultOverr HttpConnectionManagerConfig config1(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::UNESCAPE_AND_FORWARD, config1.pathWithEscapedSlashesAction()); @@ -2901,7 +2938,8 @@ TEST_F(HttpConnectionManagerConfigTest, HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::REJECT_REQUEST, config.pathWithEscapedSlashesAction()); @@ -2936,7 +2974,8 @@ TEST_F(HttpConnectionManagerConfigTest, PathWithEscapedSlashesActionDefaultOverr HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::KEEP_UNCHANGED, config.pathWithEscapedSlashesAction()); @@ -2960,7 +2999,8 @@ TEST_F(HttpConnectionManagerConfigTest, SetCurrentClientCertDetailsCertAndChain) HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::ForwardClientCertType::AppendForward, config.forwardClientCert()); EXPECT_EQ(2, config.setCurrentClientCertDetails().size()); EXPECT_EQ(Http::ClientCertDetailsType::Cert, config.setCurrentClientCertDetails()[0]); @@ -3060,18 +3100,15 @@ TEST_F(HttpConnectionManagerConfigTest, HeaderValidatorConfig) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_NE(nullptr, config.makeHeaderValidator(Http::Protocol::Http2)); #else // If UHV is disabled, providing config should result in rejection - EXPECT_THROW( - { - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); - }, - EnvoyException); + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); #endif } @@ -3102,20 +3139,18 @@ TEST_F(HttpConnectionManagerConfigTest, HeaderValidatorConfigWithRuntimeDisabled HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Without envoy.reloadable_features.enable_universal_header_validator runtime set, UHV is always // disabled EXPECT_EQ(nullptr, config.makeHeaderValidator(Http::Protocol::Http2)); #else // If UHV is disabled, providing config should result in rejection - EXPECT_THROW( - { - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); - }, - EnvoyException); + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_FALSE(creation_status_.ok()); #endif } @@ -3148,7 +3183,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultHeaderValidatorConfigWithRuntimeE HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_NE(nullptr, config.makeHeaderValidator(Http::Protocol::Http2)); EXPECT_FALSE(proto_config.restrict_http_methods()); EXPECT_FALSE(proto_config.strip_fragment_from_path()); @@ -3161,14 +3197,11 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultHeaderValidatorConfigWithRuntimeE #else // If UHV is disabled, enabling envoy.reloadable_features.enable_universal_header_validator should // result in rejection - EXPECT_THROW( - { - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); - }, - EnvoyException); + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_FALSE(creation_status_.ok()); #endif } @@ -3196,7 +3229,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultHeaderValidatorConfig) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Without envoy.reloadable_features.enable_universal_header_validator runtime set, UHV is always // disabled @@ -3239,7 +3273,8 @@ TEST_F(HttpConnectionManagerConfigTest, TranslateLegacyConfigToDefaultHeaderVali HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_NE(nullptr, config.makeHeaderValidator(Http::Protocol::Http2)); EXPECT_TRUE(proto_config.strip_fragment_from_path()); EXPECT_FALSE(proto_config.restrict_http_methods()); @@ -3252,14 +3287,11 @@ TEST_F(HttpConnectionManagerConfigTest, TranslateLegacyConfigToDefaultHeaderVali #else // If UHV is disabled, enabling envoy.reloadable_features.enable_universal_header_validator should // result in rejection - EXPECT_THROW( - { - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); - }, - EnvoyException); + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_FALSE(creation_status_.ok()); #endif } diff --git a/test/extensions/filters/network/http_connection_manager/config_test_base.h b/test/extensions/filters/network/http_connection_manager/config_test_base.h index 0af8d4902e29..e7bb332ef745 100644 --- a/test/extensions/filters/network/http_connection_manager/config_test_base.h +++ b/test/extensions/filters/network/http_connection_manager/config_test_base.h @@ -51,11 +51,14 @@ class HttpConnectionManagerConfigTest : public testing::Test { std::make_shared>()}; TestScopedRuntime scoped_runtime_; void createHttpConnectionManagerConfig(const std::string& yaml) { + creation_status_ = absl::OkStatus(); HttpConnectionManagerConfig(parseHttpConnectionManagerFromYaml(yaml), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + THROW_IF_NOT_OK(creation_status_); } + absl::Status creation_status_{absl::OkStatus()}; }; class PassThroughFilterFactory : public Extensions::HttpFilters::Common::FactoryBase< From 14ccf40ff27864c11d259277bf58a91f4255c679 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 21 Mar 2024 00:09:16 -0400 Subject: [PATCH 114/124] server: removing a few more exceptions (#32981) Risk Level: low Testing: updated tests Docs Changes: n/a Release Notes: n/a envoyproxy/envoy-mobile#176 Signed-off-by: Alyssa Wilk --- source/server/config_validation/server.cc | 4 ++-- source/server/server.cc | 18 +++++++++--------- source/server/server.h | 8 ++++---- test/config_test/config_test.cc | 16 ++++++++++------ 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index 3ad1568e78fe..79065df65da0 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -88,8 +88,8 @@ void ValidationInstance::initialize(const Options& options, // If we get all the way through that stripped-down initialization flow, to the point where we'd // be ready to serve, then the config has passed validation. // Handle configuration that needs to take place prior to the main configuration load. - InstanceUtil::loadBootstrapConfig(bootstrap_, options, - messageValidationContext().staticValidationVisitor(), *api_); + THROW_IF_NOT_OK(InstanceUtil::loadBootstrapConfig( + bootstrap_, options, messageValidationContext().staticValidationVisitor(), *api_)); if (bootstrap_.has_application_log_config()) { THROW_IF_NOT_OK( diff --git a/source/server/server.cc b/source/server/server.cc index 50ab8ab9dde6..4df8def7c43e 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -364,17 +364,16 @@ registerCustomInlineHeadersFromBootstrap(const envoy::config::bootstrap::v3::Boo } // namespace -void InstanceUtil::loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& bootstrap, - const Options& options, - ProtobufMessage::ValidationVisitor& validation_visitor, - Api::Api& api) { +absl::Status InstanceUtil::loadBootstrapConfig( + envoy::config::bootstrap::v3::Bootstrap& bootstrap, const Options& options, + ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api) { const std::string& config_path = options.configPath(); const std::string& config_yaml = options.configYaml(); const envoy::config::bootstrap::v3::Bootstrap& config_proto = options.configProto(); // One of config_path and config_yaml or bootstrap should be specified. if (config_path.empty() && config_yaml.empty() && config_proto.ByteSizeLong() == 0) { - throwEnvoyExceptionOrPanic( + return absl::InvalidArgumentError( "At least one of --config-path or --config-yaml or Options::configProto() " "should be non-empty"); } @@ -384,7 +383,7 @@ void InstanceUtil::loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& MessageUtil::loadFromFile(config_path, bootstrap, validation_visitor, api); #else if (!config_path.empty()) { - throwEnvoyExceptionOrPanic("Cannot load from file with YAML disabled\n"); + return absl::InvalidArgumentError("Cannot load from file with YAML disabled\n"); } UNREFERENCED_PARAMETER(api); #endif @@ -396,13 +395,14 @@ void InstanceUtil::loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& // TODO(snowp): The fact that we do a merge here doesn't seem to be covered under test. bootstrap.MergeFrom(bootstrap_override); #else - throwEnvoyExceptionOrPanic("Cannot load from YAML with YAML disabled\n"); + return absl::InvalidArgumentError("Cannot load from YAML with YAML disabled\n"); #endif } if (config_proto.ByteSizeLong() != 0) { bootstrap.MergeFrom(config_proto); } MessageUtil::validate(bootstrap, validation_visitor); + return absl::OkStatus(); } void InstanceBase::initialize(Network::Address::InstanceConstSharedPtr local_address, @@ -455,8 +455,8 @@ absl::Status InstanceBase::initializeOrThrow(Network::Address::InstanceConstShar } // Handle configuration that needs to take place prior to the main configuration load. - InstanceUtil::loadBootstrapConfig(bootstrap_, options_, - messageValidationContext().staticValidationVisitor(), *api_); + RETURN_IF_NOT_OK(InstanceUtil::loadBootstrapConfig( + bootstrap_, options_, messageValidationContext().staticValidationVisitor(), *api_)); bootstrap_config_update_time_ = time_source_.systemTime(); if (bootstrap_.has_application_log_config()) { diff --git a/source/server/server.h b/source/server/server.h index 0d4d76bc97f1..ae9b1ea95bc0 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -140,10 +140,10 @@ class InstanceUtil : Logger::Loggable { * @param validation_visitor message validation visitor instance. * @param api reference to the Api object */ - static void loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& bootstrap, - const Options& options, - ProtobufMessage::ValidationVisitor& validation_visitor, - Api::Api& api); + static absl::Status loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& bootstrap, + const Options& options, + ProtobufMessage::ValidationVisitor& validation_visitor, + Api::Api& api); }; /** diff --git a/test/config_test/config_test.cc b/test/config_test/config_test.cc index c1e484675815..78b7969328fc 100644 --- a/test/config_test/config_test.cc +++ b/test/config_test/config_test.cc @@ -97,8 +97,10 @@ class ConfigTest { .Times(AtLeast(0)); envoy::config::bootstrap::v3::Bootstrap bootstrap; - Server::InstanceUtil::loadBootstrapConfig( - bootstrap, options_, server_.messageValidationContext().staticValidationVisitor(), *api_); + EXPECT_TRUE(Server::InstanceUtil::loadBootstrapConfig( + bootstrap, options_, + server_.messageValidationContext().staticValidationVisitor(), *api_) + .ok()); absl::Status creation_status; Server::Configuration::InitialImpl initial_config(bootstrap, creation_status); THROW_IF_NOT_OK_REF(creation_status); @@ -213,8 +215,9 @@ void testMerge() { OptionsImpl options(Server::createTestOptionsImpl("envoyproxy_io_proxy.yaml", overlay, Network::Address::IpVersion::v6)); envoy::config::bootstrap::v3::Bootstrap bootstrap; - Server::InstanceUtil::loadBootstrapConfig(bootstrap, options, - ProtobufMessage::getStrictValidationVisitor(), *api); + ASSERT_TRUE(Server::InstanceUtil::loadBootstrapConfig( + bootstrap, options, ProtobufMessage::getStrictValidationVisitor(), *api) + .ok()); EXPECT_EQ(2, bootstrap.static_resources().clusters_size()); } @@ -243,8 +246,9 @@ uint32_t run(const std::string& directory) { Envoy::Server::createTestOptionsImpl(filename, "", Network::Address::IpVersion::v6)); ConfigTest test1(options); envoy::config::bootstrap::v3::Bootstrap bootstrap; - Server::InstanceUtil::loadBootstrapConfig( - bootstrap, options, ProtobufMessage::getStrictValidationVisitor(), *api); + EXPECT_TRUE(Server::InstanceUtil::loadBootstrapConfig( + bootstrap, options, ProtobufMessage::getStrictValidationVisitor(), *api) + .ok()); ENVOY_LOG_MISC(info, "testing {} as yaml.", filename); OptionsImpl config = asConfigYaml(options, *api); ConfigTest test2(config); From 667e96312130ac2bcbb7c1c598f4d63746d6f0c4 Mon Sep 17 00:00:00 2001 From: cai <142059836+cqi1217@users.noreply.github.com> Date: Wed, 20 Mar 2024 21:23:25 -0700 Subject: [PATCH 115/124] json to metadata content type regex match (#32774) There is a case when somebody is uploading a file with "content type: multipart/form-data; boundary=------------------------75b5d728d1539bb5"; since the header value will change every time, we can not write a config to allow it in previous proto. Then we need a regex match to allow it. Risk Level: low Testing: unit test Signed-off-by: Cai Qi --- .../filters/http/json_to_metadata/v3/BUILD | 5 +- .../v3/json_to_metadata.proto | 6 + .../filters/http/json_to_metadata/filter.cc | 55 ++++-- .../filters/http/json_to_metadata/filter.h | 5 +- .../http/json_to_metadata/filter_test.cc | 165 ++++++++++++++++++ 5 files changed, 217 insertions(+), 19 deletions(-) diff --git a/api/envoy/extensions/filters/http/json_to_metadata/v3/BUILD b/api/envoy/extensions/filters/http/json_to_metadata/v3/BUILD index 29ebf0741406..bfc486330911 100644 --- a/api/envoy/extensions/filters/http/json_to_metadata/v3/BUILD +++ b/api/envoy/extensions/filters/http/json_to_metadata/v3/BUILD @@ -5,5 +5,8 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = ["@com_github_cncf_xds//udpa/annotations:pkg"], + deps = [ + "//envoy/type/matcher/v3:pkg", + "@com_github_cncf_xds//udpa/annotations:pkg", + ], ) diff --git a/api/envoy/extensions/filters/http/json_to_metadata/v3/json_to_metadata.proto b/api/envoy/extensions/filters/http/json_to_metadata/v3/json_to_metadata.proto index 8d7b53d1c841..97f59330161d 100644 --- a/api/envoy/extensions/filters/http/json_to_metadata/v3/json_to_metadata.proto +++ b/api/envoy/extensions/filters/http/json_to_metadata/v3/json_to_metadata.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.extensions.filters.http.json_to_metadata.v3; +import "envoy/type/matcher/v3/regex.proto"; + import "google/protobuf/struct.proto"; import "udpa/annotations/status.proto"; @@ -108,6 +110,10 @@ message JsonToMetadata { // Allowed empty content-type for json to metadata transformation. // Default to false. bool allow_empty_content_type = 3; + + // Allowed content-type by regex match for json to metadata transformation. + // This can be used in parallel with ``allow_content_types``. + type.matcher.v3.RegexMatcher allow_content_types_regex = 4; } // At least one of request_rules and response_rules must be provided. diff --git a/source/extensions/filters/http/json_to_metadata/filter.cc b/source/extensions/filters/http/json_to_metadata/filter.cc index 18c31b1e5736..fe24b2a622d1 100644 --- a/source/extensions/filters/http/json_to_metadata/filter.cc +++ b/source/extensions/filters/http/json_to_metadata/filter.cc @@ -60,6 +60,28 @@ struct JsonValueToProtobufValueConverter { } }; +absl::flat_hash_set generateAllowContentTypes( + const Protobuf::RepeatedPtrField& proto_allow_content_types) { + if (proto_allow_content_types.empty()) { + return {Http::Headers::get().ContentTypeValues.Json}; + } + + absl::flat_hash_set allow_content_types; + for (const auto& request_allowed_content_type : proto_allow_content_types) { + allow_content_types.insert(request_allowed_content_type); + } + return allow_content_types; +} + +Regex::CompiledMatcherPtr generateAllowContentTypeRegexs( + const envoy::type::matcher::v3::RegexMatcher& proto_allow_content_types_regex) { + + Regex::CompiledMatcherPtr allow_content_types_regex; + allow_content_types_regex = Regex::Utility::parseRegex(proto_allow_content_types_regex); + + return allow_content_types_regex; +} + } // anonymous namespace Rule::Rule(const ProtoRule& rule) : rule_(rule) { @@ -96,7 +118,17 @@ FilterConfig::FilterConfig( response_allow_content_types_( generateAllowContentTypes(proto_config.response_rules().allow_content_types())), request_allow_empty_content_type_(proto_config.request_rules().allow_empty_content_type()), - response_allow_empty_content_type_(proto_config.response_rules().allow_empty_content_type()) { + response_allow_empty_content_type_(proto_config.response_rules().allow_empty_content_type()), + request_allow_content_types_regex_( + proto_config.request_rules().has_allow_content_types_regex() + ? generateAllowContentTypeRegexs( + proto_config.request_rules().allow_content_types_regex()) + : nullptr), + response_allow_content_types_regex_( + proto_config.response_rules().has_allow_content_types_regex() + ? generateAllowContentTypeRegexs( + proto_config.response_rules().allow_content_types_regex()) + : nullptr) { if (request_rules_.empty() && response_rules_.empty()) { throw EnvoyException("json_to_metadata_filter: Per filter configs must at least specify " "either request or response rules"); @@ -111,25 +143,14 @@ Rules FilterConfig::generateRules(const ProtobufRepeatedRule& proto_rules) const return rules; } -absl::flat_hash_set FilterConfig::generateAllowContentTypes( - const Protobuf::RepeatedPtrField& proto_allow_content_types) const { - if (proto_allow_content_types.empty()) { - return {Http::Headers::get().ContentTypeValues.Json}; - } - - absl::flat_hash_set allow_content_types; - for (const auto& request_allowed_content_type : proto_allow_content_types) { - allow_content_types.insert(request_allowed_content_type); - } - return allow_content_types; -} - bool FilterConfig::requestContentTypeAllowed(absl::string_view content_type) const { if (content_type.empty()) { return request_allow_empty_content_type_; } - return request_allow_content_types_.contains(content_type); + return request_allow_content_types_.contains(content_type) || + (request_allow_content_types_regex_ && + request_allow_content_types_regex_->match(content_type)); } bool FilterConfig::responseContentTypeAllowed(absl::string_view content_type) const { @@ -137,7 +158,9 @@ bool FilterConfig::responseContentTypeAllowed(absl::string_view content_type) co return response_allow_empty_content_type_; } - return response_allow_content_types_.contains(content_type); + return response_allow_content_types_.contains(content_type) || + (response_allow_content_types_regex_ && + response_allow_content_types_regex_->match(content_type)); } void Filter::applyKeyValue(const std::string& value, const KeyValuePair& keyval, diff --git a/source/extensions/filters/http/json_to_metadata/filter.h b/source/extensions/filters/http/json_to_metadata/filter.h index 99f4feea7047..b7a50e5356e6 100644 --- a/source/extensions/filters/http/json_to_metadata/filter.h +++ b/source/extensions/filters/http/json_to_metadata/filter.h @@ -11,6 +11,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/buffer/buffer_impl.h" +#include "source/common/common/matchers.h" #include "source/extensions/filters/http/common/pass_through_filter.h" #include "absl/strings/string_view.h" @@ -74,8 +75,6 @@ class FilterConfig { private: using ProtobufRepeatedRule = Protobuf::RepeatedPtrField; Rules generateRules(const ProtobufRepeatedRule& proto_rule) const; - absl::flat_hash_set generateAllowContentTypes( - const Protobuf::RepeatedPtrField& proto_allow_content_types) const; JsonToMetadataStats rqstats_; JsonToMetadataStats respstats_; const Rules request_rules_; @@ -84,6 +83,8 @@ class FilterConfig { const absl::flat_hash_set response_allow_content_types_; const bool request_allow_empty_content_type_; const bool response_allow_empty_content_type_; + const Regex::CompiledMatcherPtr request_allow_content_types_regex_; + const Regex::CompiledMatcherPtr response_allow_content_types_regex_; }; const uint32_t MAX_PAYLOAD_VALUE_LEN = 8 * 1024; diff --git a/test/extensions/filters/http/json_to_metadata/filter_test.cc b/test/extensions/filters/http/json_to_metadata/filter_test.cc index e2bd9365cfe7..87a90f95c24a 100644 --- a/test/extensions/filters/http/json_to_metadata/filter_test.cc +++ b/test/extensions/filters/http/json_to_metadata/filter_test.cc @@ -1592,6 +1592,171 @@ TEST_F(FilterTest, ResponseBodyWithRequestRule) { EXPECT_EQ(getCounterValue("json_to_metadata.resp.invalid_json_body"), 0); } +TEST_F(FilterTest, RequestAllowContentTypeRegex) { + initializeFilter(R"EOF( +request_rules: + rules: + - selectors: + - key: version + on_present: + metadata_namespace: envoy.lb + key: version + allow_content_types_regex: + google_re2: {} + regex: "application/.*" +)EOF"); + const std::string request_body = R"delimiter({"version":"good version"})delimiter"; + const std::map expected = {{"version", "good version"}}; + + Http::TestRequestHeaderMapImpl matched_incoming_headers{ + {":path", "/ping"}, {":method", "GET"}, {"Content-Type", "application/better-json"}}; + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(matched_incoming_headers, false)); + + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + testRequestWithBody(request_body); + + EXPECT_EQ(getCounterValue("json_to_metadata.rq.success"), 1); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.mismatched_content_type"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.invalid_json_body"), 0); +} + +TEST_F(FilterTest, RequestAllowContentTypemultipleRegex) { + initializeFilter(R"EOF( +request_rules: + rules: + - selectors: + - key: version + on_present: + metadata_namespace: envoy.lb + key: version + allow_content_types_regex: + google_re2: {} + regex: "(?:text|application)/.*" +)EOF"); + const std::string request_body = R"delimiter({"version":"good version"})delimiter"; + const std::map expected = {{"version", "good version"}}; + + Http::TestRequestHeaderMapImpl matched_incoming_headers{ + {":path", "/ping"}, {":method", "GET"}, {"Content-Type", "application/better-json"}}; + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(matched_incoming_headers, false)); + + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + testRequestWithBody(request_body); + + EXPECT_EQ(getCounterValue("json_to_metadata.rq.success"), 1); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.mismatched_content_type"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.invalid_json_body"), 0); +} + +TEST_F(FilterTest, RequestAllowContentTypeandRegex) { + initializeFilter(R"EOF( +request_rules: + rules: + - selectors: + - key: version + on_present: + metadata_namespace: envoy.lb + key: version + on_missing: + metadata_namespace: envoy.lb + key: version + value: 'unknown' + allow_content_types: + - application/better-json + allow_content_types_regex: + google_re2: {} + regex: "application/.*" +)EOF"); + const std::string request_body = R"delimiter({"version":"good version"})delimiter"; + const std::map expected = {{"version", "good version"}}; + + Http::TestRequestHeaderMapImpl matched_incoming_headers{ + {":path", "/ping"}, {":method", "GET"}, {"Content-Type", "application/better-json"}}; + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(matched_incoming_headers, false)); + + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + testRequestWithBody(request_body); + + EXPECT_EQ(getCounterValue("json_to_metadata.rq.success"), 1); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.mismatched_content_type"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.invalid_json_body"), 0); +} + +TEST_F(FilterTest, RequestAllowContentTypeRegexMatch) { + initializeFilter(R"EOF( +request_rules: + rules: + - selectors: + - key: version + on_present: + metadata_namespace: envoy.lb + key: version + on_missing: + metadata_namespace: envoy.lb + key: version + value: 'unknown' + allow_content_types: + - application/better-json + allow_content_types_regex: + google_re2: {} + regex: "application/.*" +)EOF"); + const std::string request_body_json = R"delimiter({"version":"good version"})delimiter"; + const std::map expected_json = {{"version", "good version"}}; + + Http::TestRequestHeaderMapImpl matched_incoming_headers_json{ + {":path", "/ping"}, {":method", "GET"}, {"Content-Type", "application/json"}}; + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(matched_incoming_headers_json, false)); + + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected_json))); + testRequestWithBody(request_body_json); + + EXPECT_EQ(getCounterValue("json_to_metadata.rq.success"), 1); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.mismatched_content_type"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.invalid_json_body"), 0); +} + +TEST_F(FilterTest, CustomRequestAllowContentTypeNoMatch) { + initializeFilter(R"EOF( +request_rules: + rules: + - selectors: + - key: version + on_present: + metadata_namespace: envoy.lb + key: version + allow_content_types: + - "text/plain" + allow_content_types_regex: + google_re2: {} + regex: "application/.*" +)EOF"); + + Http::TestRequestHeaderMapImpl nomatch_incoming_headers{ + {":path", "/ping"}, {":method", "POST"}, {"Content-Type", "image/png"}}; + EXPECT_EQ(Http::FilterHeadersStatus::Continue, + filter_->decodeHeaders(nomatch_incoming_headers, false)); + + testRequestWithBody("{}"); + + EXPECT_EQ(getCounterValue("json_to_metadata.rq.success"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.mismatched_content_type"), 1); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.invalid_json_body"), 0); +} + } // namespace JsonToMetadata } // namespace HttpFilters } // namespace Extensions From a0cb8714ca8cc33c61d1a7622385493cf3cc8126 Mon Sep 17 00:00:00 2001 From: "Antonio V. Leonti" <53806445+antoniovleonti@users.noreply.github.com> Date: Thu, 21 Mar 2024 00:32:01 -0400 Subject: [PATCH 116/124] fix result field in ext_authz fuzzing corpus. (#32986) The result field was being set incorrectly, so all fuzzer test cases were just using the OK result regardless of what appeared to be set in the corpus. Risk Level: none Testing: will strictly improve our fuzzing coverage for ext_authz Signed-off-by: Antonio Leonti --- .../filters/http/ext_authz/ext_authz_corpus/bad_config | 3 ++- .../filters/http/ext_authz/ext_authz_corpus/error_fail_close | 4 +--- .../filters/http/ext_authz/ext_authz_corpus/example | 4 +--- .../filters/http/ext_authz/ext_authz_corpus/metadata_context | 4 +--- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/test/extensions/filters/http/ext_authz/ext_authz_corpus/bad_config b/test/extensions/filters/http/ext_authz/ext_authz_corpus/bad_config index 0467323519e3..c4cf684b3521 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_corpus/bad_config +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/bad_config @@ -30,4 +30,5 @@ filter_metadata { value { } } -} \ No newline at end of file +} + diff --git a/test/extensions/filters/http/ext_authz/ext_authz_corpus/error_fail_close b/test/extensions/filters/http/ext_authz/ext_authz_corpus/error_fail_close index 90eb4a71ef41..82d08abfa9cb 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_corpus/error_fail_close +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/error_fail_close @@ -26,6 +26,4 @@ request_data { } } } -result { - error {} -} +result: ERROR diff --git a/test/extensions/filters/http/ext_authz/ext_authz_corpus/example b/test/extensions/filters/http/ext_authz/ext_authz_corpus/example index f51532b69fc4..48b7bf01c193 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_corpus/example +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/example @@ -4,6 +4,4 @@ request_data { } -result { - error {} -} +result: ERROR diff --git a/test/extensions/filters/http/ext_authz/ext_authz_corpus/metadata_context b/test/extensions/filters/http/ext_authz/ext_authz_corpus/metadata_context index e8ee26efe390..403ad1070239 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_corpus/metadata_context +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/metadata_context @@ -61,9 +61,7 @@ filter_metadata { } } } -result { - ok {} -} +result: OK request_data { headers { headers { From e49abad125c262cbcdb9065f38f1338c6d23af31 Mon Sep 17 00:00:00 2001 From: phlax Date: Thu, 21 Mar 2024 04:32:37 +0000 Subject: [PATCH 117/124] ci/macos: Increase runner timeout (#32996) Signed-off-by: Ryan Northey --- .github/workflows/envoy-macos.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/envoy-macos.yml b/.github/workflows/envoy-macos.yml index 087aa8a3433d..f3d41dad8268 100644 --- a/.github/workflows/envoy-macos.yml +++ b/.github/workflows/envoy-macos.yml @@ -51,6 +51,7 @@ jobs: steps-post: steps-pre: ${{ matrix.steps-pre }} target: ${{ matrix.target }} + timeout-minutes: 90 trusted: ${{ fromJSON(needs.load.outputs.trusted) }} strategy: fail-fast: false From 05424b2fb2e529ed7d342b258cefb3e6d071743a Mon Sep 17 00:00:00 2001 From: phlax Date: Thu, 21 Mar 2024 04:33:17 +0000 Subject: [PATCH 118/124] ci/ios: Increase action timeouts (#33008) Signed-off-by: Ryan Northey --- .github/workflows/mobile-ios_build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mobile-ios_build.yml b/.github/workflows/mobile-ios_build.yml index f64d6a42f5d3..c31edc2ef7e8 100644 --- a/.github/workflows/mobile-ios_build.yml +++ b/.github/workflows/mobile-ios_build.yml @@ -106,7 +106,7 @@ jobs: app: //examples/swift/hello_world:app expected-status: 200 target: swift-hello-world - timeout-minutes: 50 + timeout-minutes: 90 apps: permissions: @@ -141,7 +141,7 @@ jobs: ANDROID_NDK_HOME: ANDROID_HOME: target: ${{ matrix.target }} - timeout-minutes: 50 + timeout-minutes: 90 trusted: ${{ fromJSON(needs.load.outputs.trusted) }} working-directory: mobile strategy: From c4fe01c44cb75bceb0342a79e20375674557563c Mon Sep 17 00:00:00 2001 From: Thomas Ebner <96168670+samohte@users.noreply.github.com> Date: Thu, 21 Mar 2024 06:48:32 +0100 Subject: [PATCH 119/124] opentelemetrytracer: Dynatrace sampler: Enable adaptative sampling (#32848) * opentelemetrytracer: Dynatrace sampler to fetch configuration from an API (#21) * Dynatrace sampler to fetch configuration from an API Signed-off-by: Thomas Ebner <96168670+samohte@users.noreply.github.com> Co-authored-by: Joao Grassi <5938087+joaopgrassi@users.noreply.github.com> * review feedback: add log msg, remove timeout adjustment Signed-off-by: thomas.ebner --------- Signed-off-by: Thomas Ebner <96168670+samohte@users.noreply.github.com> Signed-off-by: thomas.ebner Co-authored-by: Joao Grassi <5938087+joaopgrassi@users.noreply.github.com> --- .../samplers/v3/dynatrace_sampler.proto | 2 +- changelogs/current.yaml | 3 + .../samplers/dynatrace/sampler_config.cc | 5 +- .../samplers/dynatrace/sampler_config.h | 8 +- .../dynatrace/sampler_config_provider.cc | 88 ++++++++- .../dynatrace/sampler_config_provider.h | 19 +- .../dynatrace/sampler_config_provider_test.cc | 181 +++++++++++++++++- .../samplers/dynatrace/sampler_config_test.cc | 20 +- 8 files changed, 304 insertions(+), 22 deletions(-) diff --git a/api/envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.proto b/api/envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.proto index b74e96a6a416..e4d64e24eb07 100644 --- a/api/envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.proto +++ b/api/envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.proto @@ -33,7 +33,7 @@ message DynatraceSamplerConfig { // .. code-block:: yaml // // http_uri: - // uri: .dev.dynatracelabs.com/api/v2/otlp/v1/traces + // uri: .dev.dynatracelabs.com/api/v2/samplingConfiguration // cluster: dynatrace // timeout: 10s // diff --git a/changelogs/current.yaml b/changelogs/current.yaml index c36030eaa1c2..a19ed1acdae7 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -365,6 +365,9 @@ new_features: change: | Added :ref:`rules_stat_prefix ` to allow adding custom prefix to the stats emitted by rules. +- area: tracing + change: | + Dynatrace sampler fetches configuration from Dynatrace API. deprecated: - area: listener diff --git a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.cc b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.cc index b3a6f2b91c45..eee907c002f4 100644 --- a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.cc +++ b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.cc @@ -8,18 +8,19 @@ namespace Extensions { namespace Tracers { namespace OpenTelemetry { -void SamplerConfig::parse(const std::string& json) { +bool SamplerConfig::parse(const std::string& json) { const auto result = Envoy::Json::Factory::loadFromStringNoThrow(json); if (result.ok()) { const auto& obj = result.value(); if (obj->hasObject("rootSpansPerMinute")) { const auto value = obj->getInteger("rootSpansPerMinute", default_root_spans_per_minute_); root_spans_per_minute_.store(value); - return; + return true; } } // Didn't get a value, reset to default root_spans_per_minute_.store(default_root_spans_per_minute_); + return false; } } // namespace OpenTelemetry diff --git a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.h b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.h index 6f576b2d5afb..3763de3f7a25 100644 --- a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.h +++ b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.h @@ -22,12 +22,18 @@ class SamplerConfig { ? default_root_spans_per_minute : ROOT_SPANS_PER_MINUTE_DEFAULT), root_spans_per_minute_(default_root_spans_per_minute_) {} + + SamplerConfig(const SamplerConfig&) = delete; + SamplerConfig& operator=(const SamplerConfig&) = delete; + /** * @brief Parses a json string containing the expected root spans per minute. * * @param json A string containing the configuration. + * + * @return true if parsing was successful, false otherwise */ - void parse(const std::string& json); + bool parse(const std::string& json); /** * @brief Returns wanted root spans per minute diff --git a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.cc b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.cc index 465cb9e1bd5f..badc4458c131 100644 --- a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.cc +++ b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.cc @@ -1,5 +1,7 @@ #include "source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h" +#include + #include "source/common/common/enum_to_int.h" #include "source/common/http/utility.h" @@ -8,10 +10,92 @@ namespace Extensions { namespace Tracers { namespace OpenTelemetry { +static constexpr std::chrono::seconds INITIAL_TIMER_DURATION{10}; +static constexpr std::chrono::minutes TIMER_INTERVAL{5}; + +namespace { + +bool reEnableTimer(Http::Code response_code) { + switch (response_code) { + case Http::Code::OK: + case Http::Code::TooManyRequests: + case Http::Code::InternalServerError: + case Http::Code::BadGateway: + case Http::Code::ServiceUnavailable: + case Http::Code::GatewayTimeout: + return true; + default: + return false; + } +} + +} // namespace + SamplerConfigProviderImpl::SamplerConfigProviderImpl( - Server::Configuration::TracerFactoryContext& /*context*/, + Server::Configuration::TracerFactoryContext& context, const envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig& config) - : sampler_config_(config.root_spans_per_minute()) {} + : cluster_manager_(context.serverFactoryContext().clusterManager()), + http_uri_(config.http_uri()), + authorization_header_value_(absl::StrCat("Api-Token ", config.token())), + sampler_config_(config.root_spans_per_minute()) { + + timer_ = context.serverFactoryContext().mainThreadDispatcher().createTimer([this]() -> void { + const auto thread_local_cluster = cluster_manager_.getThreadLocalCluster(http_uri_.cluster()); + if (thread_local_cluster == nullptr) { + ENVOY_LOG(error, "SamplerConfigProviderImpl failed: [cluster = {}] is not configured", + http_uri_.cluster()); + } else { + Http::RequestMessagePtr message = Http::Utility::prepareHeaders(http_uri_); + message->headers().setReferenceMethod(Http::Headers::get().MethodValues.Get); + message->headers().setReference(Http::CustomHeaders::get().Authorization, + authorization_header_value_); + active_request_ = thread_local_cluster->httpAsyncClient().send( + std::move(message), *this, + Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds( + DurationUtil::durationToMilliseconds(http_uri_.timeout())))); + } + }); + + timer_->enableTimer(std::chrono::seconds(INITIAL_TIMER_DURATION)); +} + +SamplerConfigProviderImpl::~SamplerConfigProviderImpl() { + if (active_request_) { + active_request_->cancel(); + } +} + +void SamplerConfigProviderImpl::onSuccess(const Http::AsyncClient::Request& /*request*/, + Http::ResponseMessagePtr&& http_response) { + active_request_ = nullptr; + const auto response_code = Http::Utility::getResponseStatus(http_response->headers()); + bool json_valid = false; + if (response_code == enumToInt(Http::Code::OK)) { + ENVOY_LOG(debug, "Received sampling configuration from Dynatrace: {}", + http_response->bodyAsString()); + json_valid = sampler_config_.parse(http_response->bodyAsString()); + if (!json_valid) { + ENVOY_LOG(warn, "Failed to parse sampling configuration received from Dynatrace: {}", + http_response->bodyAsString()); + } + } else { + ENVOY_LOG(warn, "Failed to get sampling configuration from Dynatrace: {}", response_code); + } + + if (json_valid || reEnableTimer(static_cast(response_code))) { + timer_->enableTimer(std::chrono::seconds(TIMER_INTERVAL)); + } else { + ENVOY_LOG(error, "Stopped to query sampling configuration from Dynatrace."); + } +} + +void SamplerConfigProviderImpl::onFailure(const Http::AsyncClient::Request& /*request*/, + Http::AsyncClient::FailureReason reason) { + active_request_ = nullptr; + timer_->enableTimer(std::chrono::seconds(TIMER_INTERVAL)); + ENVOY_LOG(warn, "Failed to get sampling configuration from Dynatrace. Reason {}", + enumToInt(reason)); +} const SamplerConfig& SamplerConfigProviderImpl::getSamplerConfig() const { return sampler_config_; } diff --git a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h index 6dd464d4f6c7..ddb42ac5fb6a 100644 --- a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h +++ b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h @@ -40,18 +40,33 @@ class SamplerConfigProvider { }; class SamplerConfigProviderImpl : public SamplerConfigProvider, - public Logger::Loggable { + public Logger::Loggable, + public Http::AsyncClient::Callbacks { public: SamplerConfigProviderImpl( Server::Configuration::TracerFactoryContext& context, const envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig& config); + void onSuccess(const Http::AsyncClient::Request& request, + Http::ResponseMessagePtr&& response) override; + + void onFailure(const Http::AsyncClient::Request& request, + Http::AsyncClient::FailureReason reason) override; + + void onBeforeFinalizeUpstreamSpan(Envoy::Tracing::Span& /*span*/, + const Http::ResponseHeaderMap* /*response_headers*/) override{}; + const SamplerConfig& getSamplerConfig() const override; - ~SamplerConfigProviderImpl() override = default; + ~SamplerConfigProviderImpl() override; private: + Event::TimerPtr timer_; + Upstream::ClusterManager& cluster_manager_; + envoy::config::core::v3::HttpUri http_uri_; + const std::string authorization_header_value_; + Http::AsyncClient::Request* active_request_{}; SamplerConfig sampler_config_; }; diff --git a/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider_test.cc b/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider_test.cc index d5a8c68d71b8..bdc0add55955 100644 --- a/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider_test.cc +++ b/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider_test.cc @@ -18,17 +18,190 @@ namespace Envoy { namespace Extensions { namespace Tracers { namespace OpenTelemetry { - using testing::NiceMock; using testing::Return; using testing::ReturnRef; class SamplerConfigProviderTest : public testing::Test { public: + SamplerConfigProviderTest() + : request_(&tracer_factory_context_.server_factory_context_.cluster_manager_ + .thread_local_cluster_.async_client_) { + + const std::string yaml_string = R"EOF( + tenant: "abc12345" + cluster_id: -1743916452 + token: "tokenval" + http_uri: + cluster: "cluster_name" + uri: "https://testhost.com/api/v2/samplingConfiguration" + timeout: 0.250s + root_spans_per_minute: 1000 + )EOF"; + TestUtility::loadFromYaml(yaml_string, proto_config_); + + ON_CALL(tracer_factory_context_.server_factory_context_.cluster_manager_, + getThreadLocalCluster(_)) + .WillByDefault(Return(&tracer_factory_context_.server_factory_context_.cluster_manager_ + .thread_local_cluster_)); + timer_ = new NiceMock( + &tracer_factory_context_.server_factory_context_.dispatcher_); + ON_CALL(tracer_factory_context_.server_factory_context_.dispatcher_, createTimer_(_)) + .WillByDefault(Invoke([this](Event::TimerCb) { return timer_; })); + } + protected: NiceMock tracer_factory_context_; + envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig proto_config_; + NiceMock* timer_; + Http::MockAsyncClientRequest request_; }; +MATCHER_P(MessageMatcher, unusedArg, "") { + // prefix 'Api-Token' should be added to 'tokenval' set via SamplerConfigProvider constructor + return (arg->headers() + .get(Http::CustomHeaders::get().Authorization)[0] + ->value() + .getStringView() == "Api-Token tokenval") && + (arg->headers().get(Http::Headers::get().Path)[0]->value().getStringView() == + "/api/v2/samplingConfiguration") && + (arg->headers().get(Http::Headers::get().Host)[0]->value().getStringView() == + "testhost.com") && + (arg->headers().get(Http::Headers::get().Method)[0]->value().getStringView() == "GET"); +} + +// Test that a request is sent if timer fires +TEST_F(SamplerConfigProviderTest, TestRequestIsSent) { + EXPECT_CALL(tracer_factory_context_.server_factory_context_.cluster_manager_.thread_local_cluster_ + .async_client_, + send_(MessageMatcher("unused-arg"), _, _)); + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); +} + +// Test that a pending request is canceled +TEST_F(SamplerConfigProviderTest, TestPendingRequestIsCanceled) { + class TestRequest : public Http::AsyncClient::Request { + public: + MOCK_METHOD(void, cancel, ()); + }; + + NiceMock test_request; + EXPECT_CALL(test_request, cancel()); + ON_CALL(tracer_factory_context_.server_factory_context_.cluster_manager_.thread_local_cluster_ + .async_client_, + send_(_, _, _)) + .WillByDefault(Return(&test_request)); + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); +} + +// Test receiving http response code 200 and valid json +TEST_F(SamplerConfigProviderTest, TestResponseOkValidJson) { + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + + Http::ResponseMessagePtr message(new Http::ResponseMessageImpl( + Http::ResponseHeaderMapPtr{new Http::TestResponseHeaderMapImpl{{":status", "200"}}})); + message->body().add("{\n \"rootSpansPerMinute\" : 4356 \n }"); + config_provider.onSuccess(request_, std::move(message)); + EXPECT_EQ(config_provider.getSamplerConfig().getRootSpansPerMinute(), 4356); + EXPECT_TRUE(timer_->enabled()); +} + +// Test receiving http response code 200 and invalid json +TEST_F(SamplerConfigProviderTest, TestResponseOkInvalidJson) { + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + + Http::ResponseMessagePtr message(new Http::ResponseMessageImpl( + Http::ResponseHeaderMapPtr{new Http::TestResponseHeaderMapImpl{{":status", "200"}}})); + message->body().add("{\n "); + config_provider.onSuccess(request_, std::move(message)); + EXPECT_EQ(config_provider.getSamplerConfig().getRootSpansPerMinute(), + SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); + EXPECT_TRUE(timer_->enabled()); +} + +void doTestResponseCode(Http::Code response_code, bool timer_enabled, + SamplerConfigProviderImpl& config_provider, + Http::MockAsyncClientRequest& request, NiceMock* timer, + int line_num) { + SCOPED_TRACE(absl::StrCat(__FUNCTION__, " called from line ", line_num)); + Http::ResponseMessagePtr message(new Http::ResponseMessageImpl(Http::ResponseHeaderMapPtr{ + new Http::TestResponseHeaderMapImpl{{":status", std::to_string(enumToInt(response_code))}}})); + message->body().add("{\n \"rootSpansPerMinute\" : 1000 \n }"); + config_provider.onSuccess(request, std::move(message)); + EXPECT_EQ(timer->enabled(), timer_enabled); +} + +// Test that timer is re-enabled depending on the response code +TEST_F(SamplerConfigProviderTest, TestReenableTimer) { + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::Forbidden, false, config_provider, request_, timer_, __LINE__); + doTestResponseCode(Http::Code::NotFound, false, config_provider, request_, timer_, __LINE__); + doTestResponseCode(Http::Code::OK, true, config_provider, request_, timer_, __LINE__); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::TooManyRequests, true, config_provider, request_, timer_, + __LINE__); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::InternalServerError, true, config_provider, request_, timer_, + __LINE__); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::BadGateway, true, config_provider, request_, timer_, __LINE__); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::ServiceUnavailable, true, config_provider, request_, timer_, + __LINE__); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::GatewayTimeout, true, config_provider, request_, timer_, __LINE__); + timer_->invokeCallback(); +} + +// Test receiving http response code != 200 +TEST_F(SamplerConfigProviderTest, TestResponseErrorCode) { + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + + Http::ResponseMessagePtr message(new Http::ResponseMessageImpl( + Http::ResponseHeaderMapPtr{new Http::TestResponseHeaderMapImpl{{":status", "401"}}})); + message->body().add("{\n \"rootSpansPerMinute\" : 4356 \n }"); + config_provider.onSuccess(request_, std::move(message)); + EXPECT_EQ(config_provider.getSamplerConfig().getRootSpansPerMinute(), + SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); + EXPECT_FALSE(timer_->enabled()); +} + +// Test sending failed +TEST_F(SamplerConfigProviderTest, TestOnFailure) { + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + config_provider.onFailure(request_, Http::AsyncClient::FailureReason::Reset); + EXPECT_EQ(config_provider.getSamplerConfig().getRootSpansPerMinute(), + SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); + EXPECT_TRUE(timer_->enabled()); +} + +// Test calling onBeforeFinalizeUpstreamSpan +TEST_F(SamplerConfigProviderTest, TestOnBeforeFinalizeUpstreamSpan) { + Tracing::MockSpan child_span_; + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + // onBeforeFinalizeUpstreamSpan() is an empty method, nothing to ASSERT, nothing should happen + config_provider.onBeforeFinalizeUpstreamSpan(child_span_, nullptr); +} + +// Test invoking the timer if no cluster can be found +TEST_F(SamplerConfigProviderTest, TestNoCluster) { + // simulate no configured cluster, return nullptr. + ON_CALL(tracer_factory_context_.server_factory_context_.cluster_manager_, + getThreadLocalCluster(_)) + .WillByDefault(Return(nullptr)); + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + // nothing to assert, should not crash or throw. +} + +// Test that configured value is used TEST_F(SamplerConfigProviderTest, TestValueConfigured) { const std::string yaml_string = R"EOF( tenant: "abc12345" @@ -39,7 +212,6 @@ TEST_F(SamplerConfigProviderTest, TestValueConfigured) { uri: "https://testhost.com/otlp/v1/traces" timeout: 0.250s root_spans_per_minute: 3456 - )EOF"; envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig proto_config; @@ -49,6 +221,7 @@ TEST_F(SamplerConfigProviderTest, TestValueConfigured) { EXPECT_EQ(config_provider.getSamplerConfig().getRootSpansPerMinute(), 3456); } +// Test using a config without a setting for configured root spans TEST_F(SamplerConfigProviderTest, TestNoValueConfigured) { const std::string yaml_string = R"EOF( tenant: "abc12345" @@ -57,8 +230,7 @@ TEST_F(SamplerConfigProviderTest, TestNoValueConfigured) { http_uri: cluster: "cluster_name" uri: "https://testhost.com/otlp/v1/traces" - timeout: 0.250s - + timeout: 500s )EOF"; envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig proto_config; @@ -69,6 +241,7 @@ TEST_F(SamplerConfigProviderTest, TestNoValueConfigured) { SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); } +// Test using a config with 0 configured root spans TEST_F(SamplerConfigProviderTest, TestValueZeroConfigured) { const std::string yaml_string = R"EOF( tenant: "abc12345" diff --git a/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_test.cc b/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_test.cc index 8cb45a8ca3e6..f849b582c5f1 100644 --- a/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_test.cc +++ b/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_test.cc @@ -14,25 +14,25 @@ namespace OpenTelemetry { TEST(SamplerConfigTest, TestParsing) { // default_root_spans_per_minute not set, ROOT_SPANS_PER_MINUTE_DEFAULT should be used SamplerConfig config(0); - config.parse("{\n \"rootSpansPerMinute\" : 2000 \n }"); + EXPECT_TRUE(config.parse("{\n \"rootSpansPerMinute\" : 2000 \n }")); EXPECT_EQ(config.getRootSpansPerMinute(), 2000u); - config.parse("{\n \"rootSpansPerMinute\" : 10000 \n }"); + EXPECT_TRUE(config.parse("{\n \"rootSpansPerMinute\" : 10000 \n }")); EXPECT_EQ(config.getRootSpansPerMinute(), 10000u); // unexpected json, default value should be used - config.parse("{}"); + EXPECT_FALSE(config.parse("{}")); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse(""); + EXPECT_FALSE(config.parse("")); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse("\\"); + EXPECT_FALSE(config.parse("\\")); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse(" { "); + EXPECT_FALSE(config.parse(" { ")); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse("{\n \"rootSpansPerMinute\" : 10000 "); // closing } is missing + EXPECT_FALSE(config.parse("{\n \"rootSpansPerMinute\" : 10000 ")); // closing } is missing EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); } @@ -41,19 +41,19 @@ TEST(SamplerConfigTest, TestDefaultConfig) { { SamplerConfig config(0); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse(" { "); // parse invalid json, default value should still be used + EXPECT_FALSE(config.parse(" { ")); // parse invalid json, default value should still be used EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); } { SamplerConfig config(900); EXPECT_EQ(config.getRootSpansPerMinute(), 900); - config.parse(" { "); + EXPECT_FALSE(config.parse(" { ")); EXPECT_EQ(config.getRootSpansPerMinute(), 900); } { SamplerConfig config(SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse(" { "); + EXPECT_FALSE(config.parse(" { ")); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); } } From 92bc812541f649386bb45e035a2ce8bb1ee6505f Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 21 Mar 2024 03:02:07 -0400 Subject: [PATCH 120/124] dispatcher: cleaning up constructor (#32975) Signed-off-by: Alyssa Wilk --- source/common/event/dispatcher_impl.cc | 8 ++++---- source/common/event/dispatcher_impl.h | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/source/common/event/dispatcher_impl.cc b/source/common/event/dispatcher_impl.cc index 778c8c779287..d89072dc00f7 100644 --- a/source/common/event/dispatcher_impl.cc +++ b/source/common/event/dispatcher_impl.cc @@ -55,16 +55,16 @@ DispatcherImpl::DispatcherImpl(const std::string& name, Api::Api& api, Event::TimeSystem& time_system, const ScaledRangeTimerManagerFactory& scaled_timer_factory, const Buffer::WatermarkFactorySharedPtr& watermark_factory) - : DispatcherImpl(name, api.threadFactory(), api.timeSource(), api.randomGenerator(), - api.fileSystem(), time_system, scaled_timer_factory, + : DispatcherImpl(name, api.threadFactory(), api.timeSource(), api.fileSystem(), time_system, + scaled_timer_factory, watermark_factory != nullptr ? watermark_factory : std::make_shared( api.bootstrap().overload_manager().buffer_factory_config())) {} DispatcherImpl::DispatcherImpl(const std::string& name, Thread::ThreadFactory& thread_factory, - TimeSource& time_source, Random::RandomGenerator&, - Filesystem::Instance& file_system, Event::TimeSystem& time_system, + TimeSource& time_source, Filesystem::Instance& file_system, + Event::TimeSystem& time_system, const ScaledRangeTimerManagerFactory& scaled_timer_factory, const Buffer::WatermarkFactorySharedPtr& watermark_factory) : name_(name), thread_factory_(thread_factory), time_source_(time_source), diff --git a/source/common/event/dispatcher_impl.h b/source/common/event/dispatcher_impl.h index 6b133fe10984..5e48b2a83829 100644 --- a/source/common/event/dispatcher_impl.h +++ b/source/common/event/dispatcher_impl.h @@ -43,10 +43,9 @@ class DispatcherImpl : Logger::Loggable, DispatcherImpl(const std::string& name, Api::Api& api, Event::TimeSystem& time_system, const ScaledRangeTimerManagerFactory& scaled_timer_factory, const Buffer::WatermarkFactorySharedPtr& watermark_factory); - // TODO(alyssawilk) remove random_generator. DispatcherImpl(const std::string& name, Thread::ThreadFactory& thread_factory, - TimeSource& time_source, Random::RandomGenerator& random_generator, - Filesystem::Instance& file_system, Event::TimeSystem& time_system, + TimeSource& time_source, Filesystem::Instance& file_system, + Event::TimeSystem& time_system, const ScaledRangeTimerManagerFactory& scaled_timer_factory, const Buffer::WatermarkFactorySharedPtr& watermark_factory); ~DispatcherImpl() override; From dcd18ffca5d0cf06730449ea7c538de15ce3253f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 08:16:37 +0000 Subject: [PATCH 121/124] build(deps): bump golang from 1.22.0-bookworm to 1.22.1-bookworm in /examples/shared/golang (#32725) build(deps): bump golang in /examples/shared/golang Bumps golang from 1.22.0-bookworm to 1.22.1-bookworm. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/shared/golang/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shared/golang/Dockerfile b/examples/shared/golang/Dockerfile index 3d06d98e72c2..5a7a92edfe95 100644 --- a/examples/shared/golang/Dockerfile +++ b/examples/shared/golang/Dockerfile @@ -3,7 +3,7 @@ RUN rm -f /etc/apt/apt.conf.d/docker-clean \ && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | tee /etc/apt/apt.conf.d/keep-cache -FROM golang:1.22.0-bookworm@sha256:925fe3fa28ba428cf67a7947ae838f8a1523117b40e3e6b5106c378e3f97fa29 as golang-base +FROM golang:1.22.1-bookworm@sha256:d996c645c9934e770e64f05fc2bc103755197b43fd999b3aa5419142e1ee6d78 as golang-base FROM golang-base as golang-control-plane-builder From 1a45c29aec7b17310cb97870315c7f3217355958 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 08:24:26 +0000 Subject: [PATCH 122/124] build(deps): bump actions/dependency-review-action from 4.1.3 to 4.2.3 (#33022) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.1.3 to 4.2.3. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/9129d7d40b8c12c1ed0f60400d00c92d437adcce...0fa40c3c10055986a88de3baa0d6ec17c5a894b3) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/_precheck_deps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_precheck_deps.yml b/.github/workflows/_precheck_deps.yml index 241fdce64090..f1ac65e0a3e1 100644 --- a/.github/workflows/_precheck_deps.yml +++ b/.github/workflows/_precheck_deps.yml @@ -55,4 +55,4 @@ jobs: ref: ${{ fromJSON(inputs.request).request.sha }} persist-credentials: false - name: Dependency Review - uses: actions/dependency-review-action@9129d7d40b8c12c1ed0f60400d00c92d437adcce # v4.1.3 + uses: actions/dependency-review-action@0fa40c3c10055986a88de3baa0d6ec17c5a894b3 # v4.2.3 From a89fcf0df316d48c9eaf7b80b56ef731928815a7 Mon Sep 17 00:00:00 2001 From: "dependency-envoy[bot]" <148525496+dependency-envoy[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 09:50:18 +0000 Subject: [PATCH 123/124] deps: Bump `com_github_google_flatbuffers` -> 24.3.7 (#33025) * deps: Bump `com_github_google_flatbuffers` -> 24.3.7 +add patch to remove `@npm` requirement Signed-off-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> Signed-off-by: Ryan Northey --- bazel/flatbuffers.patch | 25 +++++++++++++++++++++++++ bazel/repositories.bzl | 6 +++++- bazel/repository_locations.bzl | 6 +++--- 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 bazel/flatbuffers.patch diff --git a/bazel/flatbuffers.patch b/bazel/flatbuffers.patch new file mode 100644 index 000000000000..67a7d5f5d2b8 --- /dev/null +++ b/bazel/flatbuffers.patch @@ -0,0 +1,25 @@ +# Pending resolution of https://github.com/google/flatbuffers/issues/7988 +diff --git a/BUILD.bazel b/BUILD.bazel +index b4f015a0..8cf7a55e 100644 +--- a/BUILD.bazel ++++ b/BUILD.bazel +@@ -1,5 +1,3 @@ +-load("@aspect_rules_js//npm:defs.bzl", "npm_link_package") +-load("@npm//:defs.bzl", "npm_link_all_packages") + load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") + + licenses(["notice"]) +@@ -8,13 +6,6 @@ package( + default_visibility = ["//visibility:public"], + ) + +-npm_link_all_packages(name = "node_modules") +- +-npm_link_package( +- name = "node_modules/flatbuffers", +- src = "//ts:flatbuffers", +-) +- + exports_files([ + "LICENSE", + "tsconfig.json", diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 4756615a3ab8..d91aa712bdee 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -358,7 +358,11 @@ def envoy_dependencies(skip_targets = []): _com_github_google_perfetto() _utf8_range() _rules_ruby() - external_http_archive("com_github_google_flatbuffers") + external_http_archive( + "com_github_google_flatbuffers", + patch_args = ["-p1"], + patches = ["@envoy//bazel:flatbuffers.patch"], + ) external_http_archive("bazel_toolchains") external_http_archive("bazel_compdb") external_http_archive("envoy_build_tools") diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 0a0272e51a7b..d1aecef16519 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -1250,8 +1250,8 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "FlatBuffers", project_desc = "FlatBuffers is a cross platform serialization library architected for maximum memory efficiency", project_url = "https://github.com/google/flatbuffers", - version = "23.3.3", - sha256 = "8aff985da30aaab37edf8e5b02fda33ed4cbdd962699a8e2af98fdef306f4e4d", + version = "24.3.7", + sha256 = "bfff9d2150fcff88f844e8c608b02b2a0e94c92aea39b04c0624783464304784", strip_prefix = "flatbuffers-{version}", urls = ["https://github.com/google/flatbuffers/archive/v{version}.tar.gz"], use_category = ["dataplane_ext"], @@ -1272,7 +1272,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.matching.inputs.cel_data_input", "envoy.matching.matchers.cel_matcher", ], - release_date = "2023-03-03", + release_date = "2024-03-07", cpe = "cpe:2.3:a:google:flatbuffers:*", license = "Apache-2.0", license_url = "https://github.com/google/flatbuffers/blob/v{version}/LICENSE", From a74ebbe94f4f512f7dc9d59bfc12df3eb04a5819 Mon Sep 17 00:00:00 2001 From: "dependency-envoy[bot]" <148525496+dependency-envoy[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 10:05:25 +0000 Subject: [PATCH 124/124] deps/api: Bump `envoy_toolshed` -> 0.1.2 (#33027) Signed-off-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> Co-authored-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> --- api/bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/bazel/repository_locations.bzl b/api/bazel/repository_locations.bzl index d7853884cac6..b6212e61e8ac 100644 --- a/api/bazel/repository_locations.bzl +++ b/api/bazel/repository_locations.bzl @@ -166,12 +166,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "envoy_toolshed", project_desc = "Tooling, libraries, runners and checkers for Envoy proxy's CI", project_url = "https://github.com/envoyproxy/toolshed", - version = "0.1.1", - sha256 = "ee759b57270a2747f3f2a3d6ecaad63b834dd9887505a9f1c919d72429dbeffd", + version = "0.1.2", + sha256 = "d3139c43a0097d694e86cd61690b596528ad874ef7a11e42bed932a96bc44618", strip_prefix = "toolshed-bazel-v{version}/bazel", urls = ["https://github.com/envoyproxy/toolshed/archive/bazel-v{version}.tar.gz"], use_category = ["build"], - release_date = "2023-10-21", + release_date = "2024-03-21", cpe = "N/A", license = "Apache-2.0", license_url = "https://github.com/envoyproxy/envoy/blob/bazel-v{version}/LICENSE",