diff --git a/api/API_VERSIONING.md b/api/API_VERSIONING.md index 3f5d41e710a9c..f1bf6a8912e08 100644 --- a/api/API_VERSIONING.md +++ b/api/API_VERSIONING.md @@ -72,6 +72,7 @@ An exception to the above policy exists for: * API versions tagged `vNalpha`. Within an alpha major version, arbitrary breaking changes are allowed. * Any field, message or enum with a `[#not-implemented-hide:..` comment. * Any proto with a `(udpa.annotations.file_status).work_in_progress` option annotation. +* Any proto marked as [#alpha:]. Note that changes to default values for wrapped types, e.g. `google.protobuf.UInt32Value` are not governed by the above policy. Any management server requiring stability across Envoy API or diff --git a/api/bazel/repository_locations.bzl b/api/bazel/repository_locations.bzl index 968c6a9ffa286..bb35ff7754b82 100644 --- a/api/bazel/repository_locations.bzl +++ b/api/bazel/repository_locations.bzl @@ -44,9 +44,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 = "b88cc788a63e5b38ee334a2e702c67901355ae2c", - sha256 = "3220df8564f217665b6e17776569c5f748178c2b9cbf83bb55a13ddc0a3738f0", - release_date = "2021-03-23", + version = "dd25fe81a44506ab21ea666fb70b3b1c4bb183ee", + sha256 = "9184235cd31272679e4c7f9232c341d4ea75351ded74d3fbba28b05c290bfa71", + release_date = "2021-07-22", strip_prefix = "xds-{version}", urls = ["https://github.com/cncf/xds/archive/{version}.tar.gz"], use_category = ["api"], diff --git a/api/envoy/extensions/common/matching/v3/BUILD b/api/envoy/extensions/common/matching/v3/BUILD index 5fa93360e6558..1afd4545d9608 100644 --- a/api/envoy/extensions/common/matching/v3/BUILD +++ b/api/envoy/extensions/common/matching/v3/BUILD @@ -6,8 +6,10 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ + "//envoy/annotations:pkg", "//envoy/config/common/matcher/v3:pkg", "//envoy/config/core/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", + "@com_github_cncf_udpa//xds/type/matcher/v3:pkg", ], ) diff --git a/api/envoy/extensions/common/matching/v3/extension_matcher.proto b/api/envoy/extensions/common/matching/v3/extension_matcher.proto index e317d885af393..eee82a381633b 100644 --- a/api/envoy/extensions/common/matching/v3/extension_matcher.proto +++ b/api/envoy/extensions/common/matching/v3/extension_matcher.proto @@ -5,6 +5,9 @@ package envoy.extensions.common.matching.v3; import "envoy/config/common/matcher/v3/matcher.proto"; import "envoy/config/core/v3/extension.proto"; +import "xds/type/matcher/v3/matcher.proto"; + +import "envoy/annotations/deprecation.proto"; import "udpa/annotations/status.proto"; import "validate/validate.proto"; @@ -21,8 +24,12 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // // [#alpha:] message ExtensionWithMatcher { + // The associated matcher. This is deprecated in favor of xds_matcher. + config.common.matcher.v3.Matcher matcher = 1 + [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"]; + // The associated matcher. - config.common.matcher.v3.Matcher matcher = 1 [(validate.rules).message = {required: true}]; + xds.type.matcher.v3.Matcher xds_matcher = 3; // The underlying extension config. config.core.v3.TypedExtensionConfig extension_config = 2 diff --git a/api/envoy/extensions/common/matching/v4alpha/BUILD b/api/envoy/extensions/common/matching/v4alpha/BUILD index 95ccc22a554af..8082008a9d98f 100644 --- a/api/envoy/extensions/common/matching/v4alpha/BUILD +++ b/api/envoy/extensions/common/matching/v4alpha/BUILD @@ -6,9 +6,9 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/config/common/matcher/v4alpha:pkg", "//envoy/config/core/v4alpha:pkg", "//envoy/extensions/common/matching/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", + "@com_github_cncf_udpa//xds/type/matcher/v3:pkg", ], ) diff --git a/api/envoy/extensions/common/matching/v4alpha/extension_matcher.proto b/api/envoy/extensions/common/matching/v4alpha/extension_matcher.proto index 88ac7c7570f8d..9077facc29a41 100644 --- a/api/envoy/extensions/common/matching/v4alpha/extension_matcher.proto +++ b/api/envoy/extensions/common/matching/v4alpha/extension_matcher.proto @@ -2,9 +2,10 @@ syntax = "proto3"; package envoy.extensions.common.matching.v4alpha; -import "envoy/config/common/matcher/v4alpha/matcher.proto"; import "envoy/config/core/v4alpha/extension.proto"; +import "xds/type/matcher/v3/matcher.proto"; + import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -25,8 +26,12 @@ message ExtensionWithMatcher { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.common.matching.v3.ExtensionWithMatcher"; + reserved 1; + + reserved "matcher"; + // The associated matcher. - config.common.matcher.v4alpha.Matcher matcher = 1 [(validate.rules).message = {required: true}]; + xds.type.matcher.v3.Matcher xds_matcher = 3; // The underlying extension config. config.core.v4alpha.TypedExtensionConfig extension_config = 2 diff --git a/docs/root/configuration/http/http_filters/_include/composite.yaml b/docs/root/configuration/http/http_filters/_include/composite.yaml index d45969ba43615..256647da314d0 100644 --- a/docs/root/configuration/http/http_filters/_include/composite.yaml +++ b/docs/root/configuration/http/http_filters/_include/composite.yaml @@ -33,7 +33,7 @@ static_resources: name: composite typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.composite.v3.Composite - matcher: + xds_matcher: matcher_tree: input: name: request-headers diff --git a/docs/root/intro/arch_overview/advanced/matching/_include/complicated.yaml b/docs/root/intro/arch_overview/advanced/matching/_include/complicated.yaml index 9efc4cbf217e8..6111adfd23eea 100644 --- a/docs/root/intro/arch_overview/advanced/matching/_include/complicated.yaml +++ b/docs/root/intro/arch_overview/advanced/matching/_include/complicated.yaml @@ -29,7 +29,7 @@ static_resources: percentage: numerator: 0 denominator: HUNDRED - matcher: + xds_matcher: # The top level matcher is a matcher tree which conceptually selects one of several subtrees. matcher_tree: input: diff --git a/docs/root/intro/arch_overview/advanced/matching/_include/request_response.yaml b/docs/root/intro/arch_overview/advanced/matching/_include/request_response.yaml index bf4721e48e24f..5fc3a5c3e8ecb 100644 --- a/docs/root/intro/arch_overview/advanced/matching/_include/request_response.yaml +++ b/docs/root/intro/arch_overview/advanced/matching/_include/request_response.yaml @@ -29,7 +29,7 @@ static_resources: percentage: numerator: 0 denominator: HUNDRED - matcher: + xds_matcher: matcher_list: matchers: - predicate: diff --git a/docs/root/intro/arch_overview/advanced/matching/_include/simple.yaml b/docs/root/intro/arch_overview/advanced/matching/_include/simple.yaml index 836deb8191825..1433fa75d1089 100644 --- a/docs/root/intro/arch_overview/advanced/matching/_include/simple.yaml +++ b/docs/root/intro/arch_overview/advanced/matching/_include/simple.yaml @@ -29,7 +29,7 @@ static_resources: percentage: numerator: 0 denominator: HUNDRED - matcher: + xds_matcher: matcher_tree: input: name: request-headers diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 0a4f5b399f64f..cb50b34621ed8 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -60,6 +60,9 @@ New Features Deprecated ---------- + +* api: the :ref:`matcher ` field has been deprecated in favor of + :ref:`matcher ` in order to break a build dependency. * cluster: :ref:`max_requests_per_connection ` is deprecated in favor of :ref:`max_requests_per_connection `. * http: the HeaderMatcher fields :ref:`exact_match `, :ref:`safe_regex_match `, :ref:`prefix_match `, :ref:`suffix_match ` and diff --git a/generated_api_shadow/bazel/repository_locations.bzl b/generated_api_shadow/bazel/repository_locations.bzl index 968c6a9ffa286..bb35ff7754b82 100644 --- a/generated_api_shadow/bazel/repository_locations.bzl +++ b/generated_api_shadow/bazel/repository_locations.bzl @@ -44,9 +44,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 = "b88cc788a63e5b38ee334a2e702c67901355ae2c", - sha256 = "3220df8564f217665b6e17776569c5f748178c2b9cbf83bb55a13ddc0a3738f0", - release_date = "2021-03-23", + version = "dd25fe81a44506ab21ea666fb70b3b1c4bb183ee", + sha256 = "9184235cd31272679e4c7f9232c341d4ea75351ded74d3fbba28b05c290bfa71", + release_date = "2021-07-22", strip_prefix = "xds-{version}", urls = ["https://github.com/cncf/xds/archive/{version}.tar.gz"], use_category = ["api"], diff --git a/generated_api_shadow/envoy/extensions/common/matching/v3/BUILD b/generated_api_shadow/envoy/extensions/common/matching/v3/BUILD index 5fa93360e6558..1afd4545d9608 100644 --- a/generated_api_shadow/envoy/extensions/common/matching/v3/BUILD +++ b/generated_api_shadow/envoy/extensions/common/matching/v3/BUILD @@ -6,8 +6,10 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ + "//envoy/annotations:pkg", "//envoy/config/common/matcher/v3:pkg", "//envoy/config/core/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", + "@com_github_cncf_udpa//xds/type/matcher/v3:pkg", ], ) diff --git a/generated_api_shadow/envoy/extensions/common/matching/v3/extension_matcher.proto b/generated_api_shadow/envoy/extensions/common/matching/v3/extension_matcher.proto index e317d885af393..eee82a381633b 100644 --- a/generated_api_shadow/envoy/extensions/common/matching/v3/extension_matcher.proto +++ b/generated_api_shadow/envoy/extensions/common/matching/v3/extension_matcher.proto @@ -5,6 +5,9 @@ package envoy.extensions.common.matching.v3; import "envoy/config/common/matcher/v3/matcher.proto"; import "envoy/config/core/v3/extension.proto"; +import "xds/type/matcher/v3/matcher.proto"; + +import "envoy/annotations/deprecation.proto"; import "udpa/annotations/status.proto"; import "validate/validate.proto"; @@ -21,8 +24,12 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // // [#alpha:] message ExtensionWithMatcher { + // The associated matcher. This is deprecated in favor of xds_matcher. + config.common.matcher.v3.Matcher matcher = 1 + [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"]; + // The associated matcher. - config.common.matcher.v3.Matcher matcher = 1 [(validate.rules).message = {required: true}]; + xds.type.matcher.v3.Matcher xds_matcher = 3; // The underlying extension config. config.core.v3.TypedExtensionConfig extension_config = 2 diff --git a/generated_api_shadow/envoy/extensions/common/matching/v4alpha/BUILD b/generated_api_shadow/envoy/extensions/common/matching/v4alpha/BUILD index 95ccc22a554af..5337b3622aa76 100644 --- a/generated_api_shadow/envoy/extensions/common/matching/v4alpha/BUILD +++ b/generated_api_shadow/envoy/extensions/common/matching/v4alpha/BUILD @@ -6,9 +6,11 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ + "//envoy/annotations:pkg", "//envoy/config/common/matcher/v4alpha:pkg", "//envoy/config/core/v4alpha:pkg", "//envoy/extensions/common/matching/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", + "@com_github_cncf_udpa//xds/type/matcher/v3:pkg", ], ) diff --git a/generated_api_shadow/envoy/extensions/common/matching/v4alpha/extension_matcher.proto b/generated_api_shadow/envoy/extensions/common/matching/v4alpha/extension_matcher.proto index 88ac7c7570f8d..2fdfab9317750 100644 --- a/generated_api_shadow/envoy/extensions/common/matching/v4alpha/extension_matcher.proto +++ b/generated_api_shadow/envoy/extensions/common/matching/v4alpha/extension_matcher.proto @@ -5,6 +5,9 @@ package envoy.extensions.common.matching.v4alpha; import "envoy/config/common/matcher/v4alpha/matcher.proto"; import "envoy/config/core/v4alpha/extension.proto"; +import "xds/type/matcher/v3/matcher.proto"; + +import "envoy/annotations/deprecation.proto"; import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -25,8 +28,12 @@ message ExtensionWithMatcher { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.common.matching.v3.ExtensionWithMatcher"; + // The associated matcher. This is deprecated in favor of xds_matcher. + config.common.matcher.v4alpha.Matcher hidden_envoy_deprecated_matcher = 1 + [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"]; + // The associated matcher. - config.common.matcher.v4alpha.Matcher matcher = 1 [(validate.rules).message = {required: true}]; + xds.type.matcher.v3.Matcher xds_matcher = 3; // The underlying extension config. config.core.v4alpha.TypedExtensionConfig extension_config = 2 diff --git a/source/common/common/BUILD b/source/common/common/BUILD index 5614a214ba5a8..ae549efefb824 100644 --- a/source/common/common/BUILD +++ b/source/common/common/BUILD @@ -301,6 +301,7 @@ envoy_cc_library( "//envoy/common:regex_interface", "//source/common/protobuf:utility_lib", "//source/common/stats:symbol_table_lib", + "@com_github_cncf_udpa//xds/type/matcher/v3:pkg_cc_proto", "@com_googlesource_code_re2//:re2", "@envoy_api//envoy/type/matcher/v3:pkg_cc_proto", ], diff --git a/source/common/common/matchers.cc b/source/common/common/matchers.cc index ec93e4bbf11c1..cb57af2869f3f 100644 --- a/source/common/common/matchers.cc +++ b/source/common/common/matchers.cc @@ -23,7 +23,8 @@ ValueMatcherConstSharedPtr ValueMatcher::create(const envoy::type::matcher::v3:: case envoy::type::matcher::v3::ValueMatcher::MatchPatternCase::kDoubleMatch: return std::make_shared(v.double_match()); case envoy::type::matcher::v3::ValueMatcher::MatchPatternCase::kStringMatch: - return std::make_shared(v.string_match()); + return std::make_shared>>( + v.string_match()); case envoy::type::matcher::v3::ValueMatcher::MatchPatternCase::kBoolMatch: return std::make_shared(v.bool_match()); case envoy::type::matcher::v3::ValueMatcher::MatchPatternCase::kPresentMatch: @@ -63,65 +64,6 @@ bool DoubleMatcher::match(const ProtobufWkt::Value& value) const { }; } -StringMatcherImpl::StringMatcherImpl(const envoy::type::matcher::v3::StringMatcher& matcher) - : matcher_(matcher) { - if (matcher.match_pattern_case() == - envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kSafeRegex) { - if (matcher.ignore_case()) { - throw EnvoyException("ignore_case has no effect for safe_regex."); - } - regex_ = Regex::Utility::parseRegex(matcher_.safe_regex()); - } else if (matcher.match_pattern_case() == - envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kContains) { - if (matcher_.ignore_case()) { - // Cache the lowercase conversion of the Contains matcher for future use - lowercase_contains_match_ = absl::AsciiStrToLower(matcher_.contains()); - } - } -} - -bool StringMatcherImpl::match(const ProtobufWkt::Value& value) const { - if (value.kind_case() != ProtobufWkt::Value::kStringValue) { - return false; - } - - return match(value.string_value()); -} - -bool StringMatcherImpl::match(const absl::string_view value) const { - switch (matcher_.match_pattern_case()) { - case envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kExact: - return matcher_.ignore_case() ? absl::EqualsIgnoreCase(value, matcher_.exact()) - : value == matcher_.exact(); - case envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kPrefix: - return matcher_.ignore_case() ? absl::StartsWithIgnoreCase(value, matcher_.prefix()) - : absl::StartsWith(value, matcher_.prefix()); - case envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kSuffix: - return matcher_.ignore_case() ? absl::EndsWithIgnoreCase(value, matcher_.suffix()) - : absl::EndsWith(value, matcher_.suffix()); - case envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kContains: - return matcher_.ignore_case() - ? absl::StrContains(absl::AsciiStrToLower(value), lowercase_contains_match_) - : absl::StrContains(value, matcher_.contains()); - case envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kHiddenEnvoyDeprecatedRegex: - FALLTHRU; - case envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kSafeRegex: - return regex_->match(value); - default: - NOT_REACHED_GCOVR_EXCL_LINE; - } -} - -bool StringMatcherImpl::getCaseSensitivePrefixMatch(std::string& prefix) const { - if (matcher_.match_pattern_case() == - envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kPrefix && - !matcher_.ignore_case()) { - prefix = matcher_.prefix(); - return true; - } - return false; -} - ListMatcher::ListMatcher(const envoy::type::matcher::v3::ListMatcher& matcher) : matcher_(matcher) { ASSERT(matcher_.match_pattern_case() == envoy::type::matcher::v3::ListMatcher::MatchPatternCase::kOneOf); diff --git a/source/common/common/matchers.h b/source/common/common/matchers.h index 3bcf1a88bf12b..486d8dbb875dc 100644 --- a/source/common/common/matchers.h +++ b/source/common/common/matchers.h @@ -2,6 +2,7 @@ #include +#include "envoy/common/exception.h" #include "envoy/common/matchers.h" #include "envoy/common/regex.h" #include "envoy/config/core/v3/base.pb.h" @@ -11,9 +12,12 @@ #include "envoy/type/matcher/v3/string.pb.h" #include "envoy/type/matcher/v3/value.pb.h" +#include "source/common/common/regex.h" #include "source/common/common/utility.h" #include "source/common/protobuf/protobuf.h" +#include "absl/strings/match.h" + namespace Envoy { namespace Matchers { @@ -81,15 +85,55 @@ class UniversalStringMatcher : public StringMatcher { bool match(absl::string_view) const override { return true; } }; +template class StringMatcherImpl : public ValueMatcher, public StringMatcher { public: - explicit StringMatcherImpl(const envoy::type::matcher::v3::StringMatcher& 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."); + } + 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()); + } + } + } // StringMatcher - bool match(const absl::string_view value) const override; - bool match(const ProtobufWkt::Value& value) const override; - - const envoy::type::matcher::v3::StringMatcher& matcher() const { return 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); + default: + NOT_REACHED_GCOVR_EXCL_LINE; + } + } + bool match(const ProtobufWkt::Value& value) const override { + + if (value.kind_case() != ProtobufWkt::Value::kStringValue) { + return false; + } + + return match(value.string_value()); + } + + const StringMatcherType& matcher() const { return matcher_; } /** * Helps applications optimize the case where a matcher is a case-sensitive @@ -98,10 +142,18 @@ class StringMatcherImpl : public ValueMatcher, public StringMatcher { * @param prefix the returned prefix string * @return true if the matcher is a case-sensitive prefix-match. */ - bool getCaseSensitivePrefixMatch(std::string& prefix) const; + bool getCaseSensitivePrefixMatch(std::string& prefix) const { + if (matcher_.match_pattern_case() == + envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kPrefix && + !matcher_.ignore_case()) { + prefix = matcher_.prefix(); + return true; + } + return false; + } private: - const envoy::type::matcher::v3::StringMatcher matcher_; + const StringMatcherType matcher_; Regex::CompiledMatcherPtr regex_; std::string lowercase_contains_match_; }; @@ -149,7 +201,7 @@ class PathMatcher : public StringMatcher { bool match(const absl::string_view path) const override; private: - const StringMatcherImpl matcher_; + const StringMatcherImpl matcher_; }; } // namespace Matchers diff --git a/source/common/common/regex.cc b/source/common/common/regex.cc index 6b2e0050b487b..0cc34cf7e5e95 100644 --- a/source/common/common/regex.cc +++ b/source/common/common/regex.cc @@ -1,48 +1,29 @@ #include "source/common/common/regex.h" #include "envoy/common/exception.h" -#include "envoy/runtime/runtime.h" #include "envoy/type/matcher/v3/regex.pb.h" #include "source/common/common/assert.h" #include "source/common/common/fmt.h" -#include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table_impl.h" - -#include "re2/re2.h" namespace Envoy { namespace Regex { -namespace { - -class CompiledGoogleReMatcher : public CompiledMatcher { -public: - CompiledGoogleReMatcher(const envoy::type::matcher::v3::RegexMatcher& config) - : regex_(config.regex(), re2::RE2::Quiet) { - if (!regex_.ok()) { - throw EnvoyException(regex_.error()); - } - - const uint32_t regex_program_size = static_cast(regex_.ProgramSize()); - // Check if the deprecated field max_program_size is set first, and follow the old logic if so. - if (config.google_re2().has_max_program_size()) { - const uint32_t max_program_size = - PROTOBUF_GET_WRAPPED_OR_DEFAULT(config.google_re2(), max_program_size, 100); - if (regex_program_size > max_program_size) { - throw EnvoyException(fmt::format("regex '{}' RE2 program size of {} > max program size of " - "{}. Increase configured max program size if necessary.", - config.regex(), regex_program_size, max_program_size)); - } - return; - } +CompiledGoogleReMatcher::CompiledGoogleReMatcher(const std::string& regex, + bool do_program_size_check) + : regex_(regex, re2::RE2::Quiet) { + if (!regex_.ok()) { + throw EnvoyException(regex_.error()); + } + if (do_program_size_check) { Runtime::Loader* runtime = Runtime::LoaderSingleton::getExisting(); if (runtime) { Stats::Scope& root_scope = runtime->getRootScope(); - // TODO(perf): It would be more efficient to create the stats (program size histogram, warning - // counter) on startup and not with each regex match. + const uint32_t regex_program_size = static_cast(regex_.ProgramSize()); + // TODO(perf): It would be more efficient to create the stats (program size histogram, + // warning counter) on startup and not with each regex match. Stats::StatNameManagedStorage program_size_stat_name("re2.program_size", root_scope.symbolTable()); Stats::Histogram& program_size_stat = root_scope.histogramFromStatName( @@ -59,8 +40,7 @@ class CompiledGoogleReMatcher : public CompiledMatcher { throw EnvoyException(fmt::format("regex '{}' RE2 program size of {} > max program size of " "{} set for the error level threshold. Increase " "configured max program size if necessary.", - config.regex(), regex_program_size, - max_program_size_error_level)); + regex, regex_program_size, max_program_size_error_level)); } const uint32_t max_program_size_warn_level = @@ -71,34 +51,27 @@ class CompiledGoogleReMatcher : public CompiledMatcher { warn, "regex '{}' RE2 program size of {} > max program size of {} set for the warn " "level threshold. Increase configured max program size if necessary.", - config.regex(), regex_program_size, max_program_size_warn_level); + regex, regex_program_size, max_program_size_warn_level); } } } +} - // CompiledMatcher - bool match(absl::string_view value) const override { - return re2::RE2::FullMatch(re2::StringPiece(value.data(), value.size()), regex_); - } - - // CompiledMatcher - std::string replaceAll(absl::string_view value, absl::string_view substitution) const override { - std::string result = std::string(value); - re2::RE2::GlobalReplace(&result, regex_, - re2::StringPiece(substitution.data(), substitution.size())); - return result; +CompiledGoogleReMatcher::CompiledGoogleReMatcher( + const envoy::type::matcher::v3::RegexMatcher& config) + : CompiledGoogleReMatcher(config.regex(), !config.google_re2().has_max_program_size()) { + const uint32_t regex_program_size = static_cast(regex_.ProgramSize()); + + // Check if the deprecated field max_program_size is set first, and follow the old logic if so. + if (config.google_re2().has_max_program_size()) { + const uint32_t max_program_size = + PROTOBUF_GET_WRAPPED_OR_DEFAULT(config.google_re2(), max_program_size, 100); + if (regex_program_size > max_program_size) { + throw EnvoyException(fmt::format("regex '{}' RE2 program size of {} > max program size of " + "{}. Increase configured max program size if necessary.", + config.regex(), regex_program_size, max_program_size)); + } } - -private: - const re2::RE2 regex_; -}; - -} // namespace - -CompiledMatcherPtr Utility::parseRegex(const envoy::type::matcher::v3::RegexMatcher& matcher) { - // Google Re is the only currently supported engine. - ASSERT(matcher.has_google_re2()); - return std::make_unique(matcher); } std::regex Utility::parseStdRegex(const std::string& regex, std::regex::flag_type flags) { diff --git a/source/common/common/regex.h b/source/common/common/regex.h index e360cab01b96b..e2aee56170abe 100644 --- a/source/common/common/regex.h +++ b/source/common/common/regex.h @@ -4,11 +4,44 @@ #include #include "envoy/common/regex.h" +#include "envoy/runtime/runtime.h" #include "envoy/type/matcher/v3/regex.pb.h" +#include "source/common/common/assert.h" +#include "source/common/protobuf/utility.h" +#include "source/common/stats/symbol_table_impl.h" + +#include "re2/re2.h" +#include "xds/type/matcher/v3/regex.pb.h" + namespace Envoy { namespace Regex { +class CompiledGoogleReMatcher : public CompiledMatcher { +public: + explicit CompiledGoogleReMatcher(const std::string& regex, bool do_program_size_check); + + explicit CompiledGoogleReMatcher(const xds::type::matcher::v3::RegexMatcher& config) + : CompiledGoogleReMatcher(config.regex(), false) {} + + explicit CompiledGoogleReMatcher(const envoy::type::matcher::v3::RegexMatcher& config); + + // CompiledMatcher + bool match(absl::string_view value) const override { + return re2::RE2::FullMatch(re2::StringPiece(value.data(), value.size()), regex_); + } + + // CompiledMatcher + std::string replaceAll(absl::string_view value, absl::string_view substitution) const override { + std::string result = std::string(value); + re2::RE2::GlobalReplace(&result, regex_, + re2::StringPiece(substitution.data(), substitution.size())); + return result; + } + +private: + const re2::RE2 regex_; +}; enum class Type { Re2, StdRegex }; /** @@ -29,7 +62,12 @@ class Utility { /** * Construct a compiled regex matcher from a match config. */ - static CompiledMatcherPtr parseRegex(const envoy::type::matcher::v3::RegexMatcher& matcher); + template + static CompiledMatcherPtr parseRegex(const RegexMatcherType& matcher) { + // Google Re is the only currently supported engine. + ASSERT(matcher.has_google_re2()); + return std::make_unique(matcher); + } }; } // namespace Regex diff --git a/source/common/http/header_utility.cc b/source/common/http/header_utility.cc index b0fc8d02fe5e3..b8fb6d43617a6 100644 --- a/source/common/http/header_utility.cc +++ b/source/common/http/header_utility.cc @@ -69,7 +69,9 @@ HeaderUtility::HeaderData::HeaderData(const envoy::config::route::v3::HeaderMatc break; case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase::kStringMatch: header_match_type_ = HeaderMatchType::StringMatch; - string_match_ = std::make_unique(config.string_match()); + string_match_ = + std::make_unique>( + config.string_match()); break; case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase:: HEADER_MATCH_SPECIFIER_NOT_SET: diff --git a/source/common/http/match_wrapper/config.cc b/source/common/http/match_wrapper/config.cc index 3ed2eece72a21..30c70adb2fac5 100644 --- a/source/common/http/match_wrapper/config.cc +++ b/source/common/http/match_wrapper/config.cc @@ -105,10 +105,17 @@ Envoy::Http::FilterFactoryCb MatchWrapperConfig::createFilterFactoryFromProtoTyp MatchTreeValidationVisitor validation_visitor(*factory.matchingRequirements()); Envoy::Http::Matching::HttpFilterActionContext action_context{prefix, context}; - auto match_tree = Matcher::MatchTreeFactory( - action_context, context.getServerFactoryContext(), validation_visitor) - .create(proto_config.matcher()); + Matcher::MatchTreeFactory + matcher_factory(action_context, context.getServerFactoryContext(), validation_visitor); + Matcher::MatchTreeFactoryCb factory_cb; + if (proto_config.has_xds_matcher()) { + factory_cb = matcher_factory.create(proto_config.xds_matcher()); + } else if (proto_config.has_matcher()) { + factory_cb = matcher_factory.create(proto_config.matcher()); + } else { + throw EnvoyException("one of `matcher` and `matcher_tree` must be set."); + } if (!validation_visitor.errors().empty()) { // TODO(snowp): Output all violations. @@ -116,8 +123,8 @@ Envoy::Http::FilterFactoryCb MatchWrapperConfig::createFilterFactoryFromProtoTyp validation_visitor.errors()[0])); } - return [filter_factory, match_tree](Envoy::Http::FilterChainFactoryCallbacks& callbacks) -> void { - DelegatingFactoryCallbacks delegated_callbacks(callbacks, match_tree()); + return [filter_factory, factory_cb](Envoy::Http::FilterChainFactoryCallbacks& callbacks) -> void { + DelegatingFactoryCallbacks delegated_callbacks(callbacks, factory_cb()); return filter_factory(delegated_callbacks); }; diff --git a/source/common/matcher/matcher.h b/source/common/matcher/matcher.h index 40f52b3ae699c..8afa2a0b96394 100644 --- a/source/common/matcher/matcher.h +++ b/source/common/matcher/matcher.h @@ -77,11 +77,12 @@ template class MatchTreeFactory { : action_factory_context_(context), server_factory_context_(server_factory_context), validation_visitor_(validation_visitor) {} - MatchTreeFactoryCb create(const envoy::config::common::matcher::v3::Matcher& config) { + // TODO(snowp): Remove this type parameter once we only have one Matcher proto. + template MatchTreeFactoryCb create(const MatcherType& config) { switch (config.matcher_type_case()) { - case envoy::config::common::matcher::v3::Matcher::kMatcherTree: + case MatcherType::kMatcherTree: return createTreeMatcher(config); - case envoy::config::common::matcher::v3::Matcher::kMatcherList: + case MatcherType::kMatcherList: return createListMatcher(config); default: NOT_REACHED_GCOVR_EXCL_LINE; @@ -90,14 +91,15 @@ template class MatchTreeFactory { } private: - MatchTreeFactoryCb - createListMatcher(const envoy::config::common::matcher::v3::Matcher& config) { + template + MatchTreeFactoryCb createListMatcher(const MatcherType& config) { std::vector, OnMatchFactoryCb>> matcher_factories; matcher_factories.reserve(config.matcher_list().matchers().size()); for (const auto& matcher : config.matcher_list().matchers()) { - matcher_factories.push_back(std::make_pair(createFieldMatcher(matcher.predicate()), - *createOnMatch(matcher.on_match()))); + matcher_factories.push_back(std::make_pair( + createFieldMatcher(matcher.predicate()), + *createOnMatch(matcher.on_match()))); } auto on_no_match = createOnMatch(config.on_no_match()); @@ -114,13 +116,12 @@ template class MatchTreeFactory { }; } - template + template FieldMatcherFactoryCb createAggregateFieldMatcherFactoryCb( - const Protobuf::RepeatedPtrField< - envoy::config::common::matcher::v3::Matcher::MatcherList::Predicate>& predicates) { + const Protobuf::RepeatedPtrField& predicates) { std::vector> sub_matchers; for (const auto& predicate : predicates) { - sub_matchers.emplace_back(createFieldMatcher(predicate)); + sub_matchers.emplace_back(createFieldMatcher(predicate)); } return [sub_matchers]() { @@ -134,10 +135,10 @@ template class MatchTreeFactory { }; } - FieldMatcherFactoryCb createFieldMatcher( - const envoy::config::common::matcher::v3::Matcher::MatcherList::Predicate& field_predicate) { + template + FieldMatcherFactoryCb createFieldMatcher(const FieldMatcherType& field_predicate) { switch (field_predicate.match_type_case()) { - case (envoy::config::common::matcher::v3::Matcher::MatcherList::Predicate::kSinglePredicate): { + case (PredicateType::kSinglePredicate): { auto data_input = createDataInput(field_predicate.single_predicate().input()); auto input_matcher = createInputMatcher(field_predicate.single_predicate()); @@ -145,14 +146,14 @@ template class MatchTreeFactory { return std::make_unique>(data_input(), input_matcher()); }; } - case (envoy::config::common::matcher::v3::Matcher::MatcherList::Predicate::kOrMatcher): - return createAggregateFieldMatcherFactoryCb>( + case (PredicateType::kOrMatcher): + return createAggregateFieldMatcherFactoryCb, PredicateType>( field_predicate.or_matcher().predicate()); - case (envoy::config::common::matcher::v3::Matcher::MatcherList::Predicate::kAndMatcher): - return createAggregateFieldMatcherFactoryCb>( + case (PredicateType::kAndMatcher): + return createAggregateFieldMatcherFactoryCb, PredicateType>( field_predicate.and_matcher().predicate()); - case (envoy::config::common::matcher::v3::Matcher::MatcherList::Predicate::kNotMatcher): { - auto matcher_factory = createFieldMatcher(field_predicate.not_matcher()); + case (PredicateType::kNotMatcher): { + auto matcher_factory = createFieldMatcher(field_predicate.not_matcher()); return [matcher_factory]() { return std::make_unique>(matcher_factory()); @@ -163,10 +164,10 @@ template class MatchTreeFactory { } } - MatchTreeFactoryCb - createTreeMatcher(const envoy::config::common::matcher::v3::Matcher& matcher) { + template + MatchTreeFactoryCb createTreeMatcher(const MatcherType& matcher) { switch (matcher.matcher_tree().tree_type_case()) { - case envoy::config::common::matcher::v3::Matcher_MatcherTree::kExactMatchMap: { + case MatcherType::MatcherTree::kExactMatchMap: { std::vector>> match_children; match_children.reserve(matcher.matcher_tree().exact_match_map().map().size()); @@ -187,16 +188,17 @@ template class MatchTreeFactory { return multimap_matcher; }; } - case envoy::config::common::matcher::v3::Matcher_MatcherTree::kPrefixMatchMap: + case MatcherType::MatcherTree::kPrefixMatchMap: NOT_IMPLEMENTED_GCOVR_EXCL_LINE; - case envoy::config::common::matcher::v3::Matcher_MatcherTree::kCustomMatch: + case MatcherType::MatcherTree::kCustomMatch: NOT_IMPLEMENTED_GCOVR_EXCL_LINE; default: NOT_REACHED_GCOVR_EXCL_LINE; } } - absl::optional> - createOnMatch(const envoy::config::common::matcher::v3::Matcher::OnMatch& on_match) { + + template + absl::optional> createOnMatch(const OnMatchType& on_match) { if (on_match.has_matcher()) { return [matcher_factory = create(on_match.matcher())]() { return OnMatch{{}, matcher_factory()}; @@ -231,8 +233,8 @@ template class MatchTreeFactory { const CommonProtocolInputPtr common_protocol_input_; }; - DataInputFactoryCb - createDataInput(const envoy::config::core::v3::TypedExtensionConfig& config) { + template + DataInputFactoryCb createDataInput(const TypedExtensionConfigType& config) { auto* factory = Config::Utility::getFactory>(config); if (factory != nullptr) { validation_visitor_.validateDataInput(*factory, config.typed_config().type_url()); @@ -257,17 +259,15 @@ template class MatchTreeFactory { [common_input]() { return std::make_unique(common_input()); }; } - InputMatcherFactoryCb createInputMatcher( - const envoy::config::common::matcher::v3::Matcher::MatcherList::Predicate::SinglePredicate& - predicate) { + template + InputMatcherFactoryCb createInputMatcher(const SinglePredicateType& predicate) { switch (predicate.matcher_case()) { - case envoy::config::common::matcher::v3::Matcher::MatcherList::Predicate::SinglePredicate:: - kValueMatch: + case SinglePredicateType::kValueMatch: return [value_match = predicate.value_match()]() { - return std::make_unique(value_match); + return std::make_unique>>( + value_match); }; - case envoy::config::common::matcher::v3::Matcher::MatcherList::Predicate::SinglePredicate:: - kCustomMatch: { + case SinglePredicateType::kCustomMatch: { auto& factory = Config::Utility::getAndCheckFactory(predicate.custom_match()); ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( diff --git a/source/common/matcher/value_input_matcher.h b/source/common/matcher/value_input_matcher.h index e381f393550c2..0cfdb3a39ba78 100644 --- a/source/common/matcher/value_input_matcher.h +++ b/source/common/matcher/value_input_matcher.h @@ -7,10 +7,9 @@ namespace Envoy { namespace Matcher { -class StringInputMatcher : public InputMatcher { +template class StringInputMatcher : public InputMatcher { public: - explicit StringInputMatcher(const envoy::type::matcher::v3::StringMatcher& matcher) - : matcher_(matcher) {} + explicit StringInputMatcher(const StringMatcherType& matcher) : matcher_(matcher) {} bool match(absl::optional input) override { if (!input) { @@ -21,7 +20,7 @@ class StringInputMatcher : public InputMatcher { } private: - const Matchers::StringMatcherImpl matcher_; + const Matchers::StringMatcherImpl matcher_; }; } // namespace Matcher diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 79ffc9aa09bb6..8f1ac60964efe 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -223,7 +223,9 @@ CorsPolicyImpl::CorsPolicyImpl(const envoy::config::route::v3::CorsPolicy& confi ? config.hidden_envoy_deprecated_enabled().value() : true) { for (const auto& string_match : config.allow_origin_string_match()) { - allow_origins_.push_back(std::make_unique(string_match)); + allow_origins_.push_back( + std::make_unique>( + string_match)); } if (config.has_allow_credentials()) { allow_credentials_ = PROTOBUF_GET_WRAPPED_REQUIRED(config, allow_credentials); diff --git a/source/common/router/config_utility.cc b/source/common/router/config_utility.cc index f94d6b68a19ef..0c618ffd70cf3 100644 --- a/source/common/router/config_utility.cc +++ b/source/common/router/config_utility.cc @@ -14,7 +14,7 @@ namespace Envoy { namespace Router { namespace { -absl::optional +absl::optional> maybeCreateStringMatcher(const envoy::config::route::v3::QueryParameterMatcher& config) { switch (config.query_parameter_match_specifier_case()) { case envoy::config::route::v3::QueryParameterMatcher::QueryParameterMatchSpecifierCase:: diff --git a/source/common/router/config_utility.h b/source/common/router/config_utility.h index 3c8167c2faa38..4895f103de3a7 100644 --- a/source/common/router/config_utility.h +++ b/source/common/router/config_utility.h @@ -43,7 +43,8 @@ class ConfigUtility { private: const std::string name_; - const absl::optional matcher_; + const absl::optional> + matcher_; }; using QueryParameterMatcherPtr = std::unique_ptr; diff --git a/source/common/stats/histogram_impl.h b/source/common/stats/histogram_impl.h index 4090434f46edd..03992a06cd7ba 100644 --- a/source/common/stats/histogram_impl.h +++ b/source/common/stats/histogram_impl.h @@ -28,7 +28,8 @@ class HistogramSettingsImpl : public HistogramSettings { static ConstSupportedBuckets& defaultBuckets(); private: - using Config = std::pair; + using Config = std::pair, + ConstSupportedBuckets>; const std::vector configs_{}; }; diff --git a/source/common/stats/stats_matcher_impl.h b/source/common/stats/stats_matcher_impl.h index b9cad0fa35da2..a0d3a085cc3fb 100644 --- a/source/common/stats/stats_matcher_impl.h +++ b/source/common/stats/stats_matcher_impl.h @@ -52,7 +52,7 @@ class StatsMatcherImpl : public StatsMatcher { OptRef symbol_table_; std::unique_ptr stat_name_pool_; - std::vector matchers_; + std::vector> matchers_; std::vector prefixes_; }; diff --git a/source/common/upstream/health_checker_impl.h b/source/common/upstream/health_checker_impl.h index ec3b615ea5abe..a4000fc2ecc59 100644 --- a/source/common/upstream/health_checker_impl.h +++ b/source/common/upstream/health_checker_impl.h @@ -163,7 +163,8 @@ class HttpHealthCheckerImpl : public HealthCheckerImplBase { const std::string path_; const std::string host_value_; - absl::optional service_name_matcher_; + absl::optional> + service_name_matcher_; Router::HeaderParserPtr request_headers_parser_; const HttpStatusChecker http_status_checker_; 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 f80b3022080a0..4dba952fede7d 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 @@ -86,7 +86,9 @@ std::vector createStringMatchers(const envoy::type::matcher::v3::ListStringMatcher& list) { std::vector matchers; for (const auto& matcher : list.patterns()) { - matchers.push_back(std::make_unique(matcher)); + matchers.push_back( + std::make_unique>( + matcher)); } return matchers; } @@ -136,7 +138,9 @@ ClientConfig::toRequestMatchers(const envoy::type::matcher::v3::ListStringMatche for (const auto& key : keys) { envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact(key.get()); - matchers.push_back(std::make_unique(matcher)); + matchers.push_back( + std::make_unique>( + matcher)); } return std::make_shared(std::move(matchers)); @@ -157,7 +161,9 @@ ClientConfig::toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher if (matchers.empty()) { envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact(Http::Headers::get().Host.get()); - matchers.push_back(std::make_unique(matcher)); + matchers.push_back( + std::make_unique>( + matcher)); return std::make_shared(std::move(matchers)); } @@ -171,7 +177,9 @@ ClientConfig::toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher for (const auto& key : keys) { envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact(key.get()); - matchers.push_back(std::make_unique(matcher)); + matchers.push_back( + std::make_unique>( + matcher)); } return std::make_shared(std::move(matchers)); diff --git a/source/extensions/filters/common/rbac/matchers.h b/source/extensions/filters/common/rbac/matchers.h index 472b4a2c9c17e..8b8c4d87bc15e 100644 --- a/source/extensions/filters/common/rbac/matchers.h +++ b/source/extensions/filters/common/rbac/matchers.h @@ -171,14 +171,17 @@ class AuthenticatedMatcher : public Matcher { public: AuthenticatedMatcher(const envoy::config::rbac::v3::Principal::Authenticated& auth) : matcher_(auth.has_principal_name() - ? absl::make_optional(auth.principal_name()) + ? absl::make_optional< + Matchers::StringMatcherImpl>( + auth.principal_name()) : absl::nullopt) {} bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; private: - const absl::optional matcher_; + const absl::optional> + matcher_; }; /** @@ -222,10 +225,13 @@ class MetadataMatcher : public Matcher { * Perform a match against the request server from the client's connection * request. This is typically TLS SNI. */ -class RequestedServerNameMatcher : public Matcher, Envoy::Matchers::StringMatcherImpl { +class RequestedServerNameMatcher + : public Matcher, + Envoy::Matchers::StringMatcherImpl { public: RequestedServerNameMatcher(const envoy::type::matcher::v3::StringMatcher& requested_server_name) - : Envoy::Matchers::StringMatcherImpl(requested_server_name) {} + : Envoy::Matchers::StringMatcherImpl( + requested_server_name) {} bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; diff --git a/source/extensions/filters/http/cache/cache_headers_utils.cc b/source/extensions/filters/http/cache/cache_headers_utils.cc index 73a9c5b5625a8..c020828f6a638 100644 --- a/source/extensions/filters/http/cache/cache_headers_utils.cc +++ b/source/extensions/filters/http/cache/cache_headers_utils.cc @@ -234,7 +234,9 @@ VaryHeader::VaryHeader( const Protobuf::RepeatedPtrField& allow_list) { for (const auto& rule : allow_list) { - allow_list_.emplace_back(std::make_unique(rule)); + allow_list_.emplace_back( + std::make_unique>( + rule)); } } diff --git a/source/extensions/filters/http/csrf/csrf_filter.h b/source/extensions/filters/http/csrf/csrf_filter.h index 5697cf99f59e4..b52b9473a884f 100644 --- a/source/extensions/filters/http/csrf/csrf_filter.h +++ b/source/extensions/filters/http/csrf/csrf_filter.h @@ -39,7 +39,8 @@ class CsrfPolicy : public Router::RouteSpecificFilterConfig { : policy_(policy), runtime_(runtime) { for (const auto& additional_origin : policy.additional_origins()) { additional_origins_.emplace_back( - std::make_unique(additional_origin)); + std::make_unique>( + additional_origin)); } } 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 1a36230b8c5b3..f19f7b9d13eb6 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h @@ -123,7 +123,7 @@ class MethodRouteEntryImpl : public RouteEntryImplBase { uint64_t random_value) const override; private: - const Matchers::StringMatcherImpl method_name_; + const Matchers::StringMatcherImpl method_name_; std::shared_ptr parameter_route_; }; diff --git a/source/extensions/filters/network/rocketmq_proxy/router/route_matcher.h b/source/extensions/filters/network/rocketmq_proxy/router/route_matcher.h index 06aec7221d250..bb2d40d9e59f8 100644 --- a/source/extensions/filters/network/rocketmq_proxy/router/route_matcher.h +++ b/source/extensions/filters/network/rocketmq_proxy/router/route_matcher.h @@ -42,7 +42,7 @@ class RouteEntryImpl : public RouteEntry, private: bool headersMatch(const Http::HeaderMap& headers) const; - const Matchers::StringMatcherImpl topic_name_; + const Matchers::StringMatcherImpl topic_name_; const std::string cluster_name_; const std::vector config_headers_; Envoy::Router::MetadataMatchCriteriaConstPtr metadata_match_criteria_; diff --git a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc index 0585939c4173b..6cd624f2e38fa 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc @@ -228,7 +228,8 @@ int DefaultCertValidator::doVerifyCertChain( Envoy::Ssl::ClientValidationStatus DefaultCertValidator::verifyCertificate( X509* cert, const std::vector& verify_san_list, - const std::vector& subject_alt_name_matchers) { + const std::vector>& + subject_alt_name_matchers) { Envoy::Ssl::ClientValidationStatus validated = Envoy::Ssl::ClientValidationStatus::NotValidated; if (!verify_san_list.empty()) { @@ -307,7 +308,9 @@ bool DefaultCertValidator::dnsNameMatch(const absl::string_view dns_name, } bool DefaultCertValidator::matchSubjectAltName( - X509* cert, const std::vector& subject_alt_name_matchers) { + X509* cert, + const std::vector>& + subject_alt_name_matchers) { bssl::UniquePtr san_names( static_cast(X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr))); if (san_names == nullptr) { diff --git a/source/extensions/transport_sockets/tls/cert_validator/default_validator.h b/source/extensions/transport_sockets/tls/cert_validator/default_validator.h index fb7e7acab1f4e..4d5daaf0205e0 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/default_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/default_validator.h @@ -52,9 +52,10 @@ class DefaultCertValidator : public CertValidator { Envoy::Ssl::CertificateDetailsPtr getCaCertInformation() const override; // Utility functions. - Envoy::Ssl::ClientValidationStatus - verifyCertificate(X509* cert, const std::vector& verify_san_list, - const std::vector& subject_alt_name_matchers); + Envoy::Ssl::ClientValidationStatus verifyCertificate( + X509* cert, const std::vector& verify_san_list, + const std::vector>& + subject_alt_name_matchers); /** * Verifies certificate hash for pinning. The hash is a hex-encoded SHA-256 of the DER-encoded @@ -101,9 +102,10 @@ class DefaultCertValidator : public CertValidator { * @param subject_alt_name_matchers the configured matchers to match * @return true if the verification succeeds */ - static bool - matchSubjectAltName(X509* cert, - const std::vector& subject_alt_name_matchers); + static bool matchSubjectAltName( + X509* cert, + const std::vector>& + subject_alt_name_matchers); private: const Envoy::Ssl::CertificateValidationContextConfig* config_; @@ -113,7 +115,8 @@ class DefaultCertValidator : public CertValidator { bool allow_untrusted_certificate_{false}; bssl::UniquePtr ca_cert_; std::string ca_file_path_; - std::vector subject_alt_name_matchers_; + std::vector> + subject_alt_name_matchers_; std::vector> verify_certificate_hash_list_; std::vector> verify_certificate_spki_list_; bool verify_trusted_ca_{false}; 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 fc3a045d02558..b4cc068e908e5 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 @@ -67,7 +67,8 @@ class SPIFFEValidator : public CertValidator { bool allow_expired_certificate_{false}; std::vector> ca_certs_; std::string ca_file_name_; - std::vector subject_alt_name_matchers_{}; + std::vector> + subject_alt_name_matchers_{}; absl::flat_hash_map trust_bundle_stores_; SslStats& stats_; diff --git a/test/common/http/match_wrapper/config_test.cc b/test/common/http/match_wrapper/config_test.cc index f89f1a1f458a6..8287d9e5fd2d0 100644 --- a/test/common/http/match_wrapper/config_test.cc +++ b/test/common/http/match_wrapper/config_test.cc @@ -57,7 +57,7 @@ TEST(MatchWrapper, DisabledByDefault) { name: test typed_config: "@type": type.googleapis.com/google.protobuf.StringValue -matcher: +xds_matcher: matcher_tree: input: name: request-headers @@ -90,6 +90,57 @@ TEST(MatchWrapper, WithMatcher) { NiceMock factory_context; + const auto config = + TestUtility::parseYaml(R"EOF( +extension_config: + name: test + typed_config: + "@type": type.googleapis.com/google.protobuf.StringValue +xds_matcher: + matcher_tree: + input: + name: request-headers + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + header_name: default-matcher-header + exact_match_map: + map: + match: + action: + name: skip + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.common.matcher.action.v3.SkipFilter +)EOF"); + + MatchWrapperConfig match_wrapper_config; + auto cb = match_wrapper_config.createFilterFactoryFromProto(config, "", factory_context); + + Envoy::Http::MockFilterChainFactoryCallbacks factory_callbacks; + testing::InSequence s; + + // This matches the sequence of calls in the filter factory above: the ones that call the overload + // without a match tree has a match tree added, the other one does not. + EXPECT_CALL(factory_callbacks, addStreamDecoderFilter(_, testing::NotNull())); + EXPECT_CALL(factory_callbacks, addStreamEncoderFilter(_, testing::NotNull())); + EXPECT_CALL(factory_callbacks, addStreamFilter(_, testing::NotNull())); + EXPECT_CALL(factory_callbacks, addStreamDecoderFilter(_, testing::IsNull())); + EXPECT_CALL(factory_callbacks, addStreamEncoderFilter(_, testing::IsNull())); + EXPECT_CALL(factory_callbacks, addStreamFilter(_, testing::IsNull())); + EXPECT_CALL(factory_callbacks, addAccessLogHandler(_)); + cb(factory_callbacks); +} + +TEST(MatchWrapper, DEPRECATED_FEATURE_TEST(WithDeprecatedMatcher)) { + TestScopedRuntime scoped_runtime; + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.experimental_matching_api", "true"}}); + + TestFactory test_factory; + Envoy::Registry::InjectFactory + inject_factory(test_factory); + + NiceMock factory_context; + const auto config = TestUtility::parseYaml(R"EOF( extension_config: @@ -130,6 +181,31 @@ TEST(MatchWrapper, WithMatcher) { cb(factory_callbacks); } +TEST(MatchWrapper, WithNoMatcher) { + TestScopedRuntime scoped_runtime; + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.experimental_matching_api", "true"}}); + + TestFactory test_factory; + Envoy::Registry::InjectFactory + inject_factory(test_factory); + + NiceMock factory_context; + + const auto config = + TestUtility::parseYaml(R"EOF( +extension_config: + name: test + typed_config: + "@type": type.googleapis.com/google.protobuf.StringValue +)EOF"); + + MatchWrapperConfig match_wrapper_config; + EXPECT_THROW_WITH_REGEX( + match_wrapper_config.createFilterFactoryFromProto(config, "", factory_context), + EnvoyException, "one of `matcher` and `matcher_tree` must be set."); +} + TEST(MatchWrapper, WithMatcherInvalidDataInput) { TestScopedRuntime scoped_runtime; Runtime::LoaderSingleton::getExisting()->mergeValues( @@ -147,7 +223,7 @@ TEST(MatchWrapper, WithMatcherInvalidDataInput) { name: test typed_config: "@type": type.googleapis.com/google.protobuf.StringValue -matcher: +xds_matcher: matcher_tree: input: name: request-headers 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 2c0795195efee..b0c2007a04ae7 100644 --- a/test/extensions/filters/http/cache/cache_headers_utils_test.cc +++ b/test/extensions/filters/http/cache/cache_headers_utils_test.cc @@ -463,7 +463,9 @@ TEST(GetAllMatchingHeaderNames, EmptyHeaderMap) { envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact("accept"); - ruleset.emplace_back(std::make_unique(matcher)); + ruleset.emplace_back( + std::make_unique>( + matcher)); CacheHeadersUtils::getAllMatchingHeaderNames(headers, ruleset, result); @@ -477,7 +479,9 @@ TEST(GetAllMatchingHeaderNames, SingleMatchSingleValue) { envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact("accept"); - ruleset.emplace_back(std::make_unique(matcher)); + ruleset.emplace_back( + std::make_unique>( + matcher)); CacheHeadersUtils::getAllMatchingHeaderNames(headers, ruleset, result); @@ -492,7 +496,9 @@ TEST(GetAllMatchingHeaderNames, SingleMatchMultiValue) { envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact("accept"); - ruleset.emplace_back(std::make_unique(matcher)); + ruleset.emplace_back( + std::make_unique>( + matcher)); CacheHeadersUtils::getAllMatchingHeaderNames(headers, ruleset, result); @@ -507,9 +513,13 @@ TEST(GetAllMatchingHeaderNames, MultipleMatches) { envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact("accept"); - ruleset.emplace_back(std::make_unique(matcher)); + ruleset.emplace_back( + std::make_unique>( + matcher)); matcher.set_exact("accept-language"); - ruleset.emplace_back(std::make_unique(matcher)); + ruleset.emplace_back( + std::make_unique>( + matcher)); CacheHeadersUtils::getAllMatchingHeaderNames(headers, ruleset, result); diff --git a/test/extensions/filters/http/composite/composite_filter_integration_test.cc b/test/extensions/filters/http/composite/composite_filter_integration_test.cc index b639e8cf3a7b5..cd7ffec42e28d 100644 --- a/test/extensions/filters/http/composite/composite_filter_integration_test.cc +++ b/test/extensions/filters/http/composite/composite_filter_integration_test.cc @@ -24,7 +24,7 @@ class CompositeFilterIntegrationTest : public testing::TestWithParam(config); + return std::make_unique>( + config); } Matchers::StringMatcherPtr makeStdRegexStringMatcher(const std::string& regex) { envoy::type::matcher::v3::StringMatcher config; config.MergeFrom(TestUtility::createRegexMatcher(regex)); - return std::make_unique(config); + return std::make_unique>( + config); } } // namespace diff --git a/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc b/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc index fee175872bd65..5034bfa9ce2d4 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc @@ -43,7 +43,8 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameDNSMatched) { "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(".*.example.com")); - std::vector subject_alt_name_matchers; + std::vector> + subject_alt_name_matchers; subject_alt_name_matchers.push_back(Matchers::StringMatcherImpl(matcher)); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -54,7 +55,8 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameWildcardDNSMatched) { "}}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact("api.example.com"); - std::vector subject_alt_name_matchers; + std::vector> + subject_alt_name_matchers; subject_alt_name_matchers.push_back(Matchers::StringMatcherImpl(matcher)); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -66,7 +68,8 @@ TEST(DefaultCertValidatorTest, TestMultiLevelMatch) { "}}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact("foo.api.example.com"); - std::vector subject_alt_name_matchers; + std::vector> + subject_alt_name_matchers; subject_alt_name_matchers.push_back(Matchers::StringMatcherImpl(matcher)); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -93,7 +96,8 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameURIMatched) { "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_uri_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher("spiffe://lyft.com/.*-team")); - std::vector subject_alt_name_matchers; + std::vector> + subject_alt_name_matchers; subject_alt_name_matchers.push_back(Matchers::StringMatcherImpl(matcher)); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -111,7 +115,8 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameNotMatched) { "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(".*.foo.com")); - std::vector subject_alt_name_matchers; + std::vector> + subject_alt_name_matchers; subject_alt_name_matchers.push_back(Matchers::StringMatcherImpl(matcher)); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -129,7 +134,7 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.MergeFrom(TestUtility::createRegexMatcher(".*.example.com")); - std::vector san_matchers; + std::vector> san_matchers; san_matchers.push_back(Matchers::StringMatcherImpl(matcher)); // Verify the certificate with correct SAN regex matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, san_matchers), @@ -137,7 +142,8 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { EXPECT_EQ(stats.fail_verify_san_.value(), 0); matcher.MergeFrom(TestUtility::createExactMatcher("hello.example.com")); - std::vector invalid_san_matchers; + std::vector> + invalid_san_matchers; invalid_san_matchers.push_back(Matchers::StringMatcherImpl(matcher)); // Verify the certificate with incorrect SAN exact matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, diff --git a/test/integration/extension_discovery_integration_test.cc b/test/integration/extension_discovery_integration_test.cc index ded9093dccbb9..860d69df08df7 100644 --- a/test/integration/extension_discovery_integration_test.cc +++ b/test/integration/extension_discovery_integration_test.cc @@ -30,7 +30,7 @@ std::string denyPrivateConfigWithMatcher() { "@type": type.googleapis.com/test.integration.filters.SetResponseCodeFilterConfig prefix: "/private" code: 403 - matcher: + xds_matcher: matcher_tree: input: name: request-headers @@ -84,7 +84,7 @@ class ExtensionDiscoveryIntegrationTest : public Grpc::GrpcClientIntegrationPara typed_config: "@type": type.googleapis.com/test.integration.filters.SetResponseCodeFilterConfig code: 403 - matcher: + xds_matcher: matcher_tree: input: name: request-headers diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index 48f68e41e49ac..91a1e75c36f59 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -395,7 +395,74 @@ name: matcher typed_config: "@type": type.googleapis.com/test.integration.filters.SetResponseCodeFilterConfig code: 403 - matcher: + xds_matcher: + matcher_tree: + input: + name: request-headers + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + header_name: match-header + exact_match_map: + map: + match: + action: + name: skip + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.common.matcher.action.v3.SkipFilter +)EOF"); + + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + + { + auto response = codec_client_->makeRequestWithBody(default_request_headers_, 1024); + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_THAT(response->headers(), HttpStatusIs("403")); + } + + { + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers{ + {":method", "POST"}, {":path", "/test/long/url"}, {":scheme", "http"}, + {":authority", "host"}, {"match-header", "match"}, {"content-type", "application/grpc"}}; + auto response = codec_client_->makeRequestWithBody(request_headers, 1024); + waitForNextUpstreamRequest(); + upstream_request_->encodeHeaders(default_response_headers_, true); + + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_THAT(response->headers(), HttpStatusIs("200")); + } + + auto second_codec = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers{ + {":method", "POST"}, {":path", "/test/long/url"}, {":scheme", "http"}, + {":authority", "host"}, {"match-header", "not-match"}, {"content-type", "application/grpc"}}; + auto response = second_codec->makeRequestWithBody(request_headers, 1024); + + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_THAT(response->headers(), HttpStatusIs("200")); + + codec_client_->close(); + second_codec->close(); +} + +// Verifies that we can construct a match tree with a filter using the new matcher tree proto, and +// that we are able to skip filter invocation through the match tree. +TEST_P(IntegrationTest, MatchingHttpFilterConstructionNewProto) { + concurrency_ = 2; + config_helper_.addRuntimeOverride("envoy.reloadable_features.experimental_matching_api", "true"); + + config_helper_.addFilter(R"EOF( +name: matcher +typed_config: + "@type": type.googleapis.com/envoy.extensions.common.matching.v3.ExtensionWithMatcher + extension_config: + name: set-response-code + typed_config: + "@type": type.googleapis.com/test.integration.filters.SetResponseCodeFilterConfig + code: 403 + xds_matcher: matcher_tree: input: name: request-headers diff --git a/tools/proto_format/proto_sync.py b/tools/proto_format/proto_sync.py index 8a2c589772ae6..1ba5fd5ce4be6 100755 --- a/tools/proto_format/proto_sync.py +++ b/tools/proto_format/proto_sync.py @@ -273,6 +273,9 @@ def get_import_deps(proto_path): if import_path.startswith('udpa/annotations/'): imports.append('@com_github_cncf_udpa//udpa/annotations:pkg') continue + if import_path.startswith('xds/type/matcher/v3/'): + imports.append('@com_github_cncf_udpa//xds/type/matcher/v3:pkg') + continue # Special case handling for UDPA core. if import_path.startswith('xds/core/v3/'): imports.append('@com_github_cncf_udpa//xds/core/v3:pkg')