Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions contrib/hyperscan/matching/input_matchers/source/matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class Matcher : public Envoy::Regex::CompiledMatcher, public Envoy::Matcher::Inp
// Envoy::Regex::CompiledMatcher
bool match(absl::string_view value) const override;
std::string replaceAll(absl::string_view value, absl::string_view substitution) const override;
const std::string& stringRepresentation() const override {
CONSTRUCT_ON_FIRST_USE(std::string, "Optimized for HyperScan");
}

// Envoy::Matcher::InputMatcher
bool match(const ::Envoy::Matcher::MatchingDataType& input) override;
Expand Down
6 changes: 6 additions & 0 deletions envoy/common/regex.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ class CompiledMatcher : public Matchers::StringMatcher {
*/
virtual std::string replaceAll(absl::string_view value,
absl::string_view substitution) const PURE;

/**
* Returns a string representation of the Regex matcher (the pattern to be
* matched).
*/
virtual const std::string& stringRepresentation() const PURE;
};

using CompiledMatcherPtr = std::unique_ptr<const CompiledMatcher>;
Expand Down
43 changes: 33 additions & 10 deletions source/common/common/matchers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -189,16 +189,6 @@ PathMatcher::createPrefix(const std::string& prefix, bool ignore_case,
return createPrefixPathMatcher(prefix, ignore_case, context);
}

PathMatcherConstSharedPtr
PathMatcher::createPattern(const std::string& pattern, bool ignore_case,
Server::Configuration::CommonFactoryContext& context) {
// TODO(silverstar194): implement pattern specific matcher
envoy::type::matcher::v3::StringMatcher matcher;
matcher.set_prefix(pattern);
matcher.set_ignore_case(ignore_case);
return std::make_shared<const PathMatcher>(matcher, context);
}

PathMatcherConstSharedPtr
PathMatcher::createSafeRegex(const envoy::type::matcher::v3::RegexMatcher& regex_matcher,
Server::Configuration::CommonFactoryContext& context) {
Expand All @@ -224,5 +214,38 @@ StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtension
return factory->createStringMatcher(*message, context);
}

template <class StringMatcherType>
std::unique_ptr<StringMatcherImplBase>
createStringMatcher(const StringMatcherType& config,
Server::Configuration::CommonFactoryContext& context) {
switch (config.match_pattern_case()) {
case StringMatcherType::MatchPatternCase::kExact:
return std::make_unique<ExactStringMatcher>(config.exact(), config.ignore_case());
case StringMatcherType::MatchPatternCase::kPrefix:
return std::make_unique<PrefixStringMatcher>(config.prefix(), config.ignore_case());
case StringMatcherType::MatchPatternCase::kSuffix:
return std::make_unique<SuffixStringMatcher>(config.suffix(), config.ignore_case());
case StringMatcherType::MatchPatternCase::kSafeRegex:
if (config.ignore_case()) {
ExceptionUtil::throwEnvoyException("ignore_case has no effect for safe_regex.");
}
return std::make_unique<RegexStringMatcher>(config.safe_regex(), context);
case StringMatcherType::MatchPatternCase::kContains:
return std::make_unique<ContainsStringMatcher>(config.contains(), config.ignore_case());
case StringMatcherType::MatchPatternCase::kCustom:
return std::make_unique<CustomStringMatcher>(config.custom(), context);
default:
PANIC("unexpected");
}
}

// Explicit instantiation of the `createStringMatcher` function to allow the
// implementation to be in the cc file instead of the header file.
template std::unique_ptr<StringMatcherImplBase>
createStringMatcher(const envoy::type::matcher::v3::StringMatcher& config,
Server::Configuration::CommonFactoryContext& context);
template std::unique_ptr<StringMatcherImplBase>
createStringMatcher(const xds::type::matcher::v3::StringMatcher& config,
Server::Configuration::CommonFactoryContext& context);
} // namespace Matchers
} // namespace Envoy
190 changes: 187 additions & 3 deletions source/common/common/matchers.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class DoubleMatcher : public ValueMatcher {
const envoy::type::matcher::v3::DoubleMatcher matcher_;
};

// A StringMatcher that matches all given strings (similar to the regex ".*").
class UniversalStringMatcher : public StringMatcher {
public:
bool match(absl::string_view) const override { return true; }
Expand All @@ -90,6 +91,11 @@ class UniversalStringMatcher : public StringMatcher {
StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config,
Server::Configuration::CommonFactoryContext& context);

// This class will be replaced by StringMatcherImplBase (see below) and will
// follow cleaner polymorphism and encapsulation principles. The class will be
// removed once all its usages are removed.
// TODO(adisuissa): remove this class once there all uses are replaced by
// StringMatcherImplBase/StringMatcherPtr.
template <class StringMatcherType = envoy::type::matcher::v3::StringMatcher>
class StringMatcherImpl : public ValueMatcher, public StringMatcher {
public:
Expand Down Expand Up @@ -187,6 +193,181 @@ class StringMatcherImpl : public ValueMatcher, public StringMatcher {
StringMatcherPtr custom_;
};

// An abstract common implementation for all types of StringMatchers.
class StringMatcherImplBase : public ValueMatcher, public StringMatcher {
public:
StringMatcherImplBase() = default;
virtual ~StringMatcherImplBase() = default;

// ValueMatcher
bool match(const ProtobufWkt::Value& value) const override {
if (value.kind_case() != ProtobufWkt::Value::kStringValue) {
return false;
}

return match(value.string_value());
}

// StringMatcher
virtual bool match(const absl::string_view value) const override PURE;

/**
* Helps applications optimize the case where a matcher is a case-sensitive
* prefix-match.
*
* @param prefix the returned prefix string
* @return true if the matcher is a case-sensitive prefix-match.
*/
virtual bool getCaseSensitivePrefixMatch(std::string& prefix) const {
UNREFERENCED_PARAMETER(prefix);
return false;
}

/**
* Returns a string representation of the matcher (the contents to be
* matched).
*/
virtual const std::string& stringRepresentation() const PURE;
};

// A matcher for the `exact` StringMatcher.
class ExactStringMatcher : public StringMatcherImplBase {
public:
ExactStringMatcher(absl::string_view exact, bool ignore_case)
: exact_(exact), ignore_case_(ignore_case) {}

// StringMatcher
bool match(const absl::string_view value) const override {
return ignore_case_ ? absl::EqualsIgnoreCase(value, exact_) : value == exact_;
}

const std::string& stringRepresentation() const override { return exact_; }

private:
const std::string exact_;
const bool ignore_case_;
};

// A matcher for the `prefix` StringMatcher.
class PrefixStringMatcher : public StringMatcherImplBase {
public:
PrefixStringMatcher(absl::string_view prefix, bool ignore_case)
: prefix_(prefix), ignore_case_(ignore_case) {}

// StringMatcher
bool match(const absl::string_view value) const override {
return ignore_case_ ? absl::StartsWithIgnoreCase(value, prefix_)
: absl::StartsWith(value, prefix_);
}

// StringMatcherImplBase
bool getCaseSensitivePrefixMatch(std::string& prefix) const override {
if (!ignore_case_) {
prefix = prefix_;
return true;
}
return false;
}

const std::string& stringRepresentation() const override { return prefix_; }

private:
const std::string prefix_;
const bool ignore_case_;
};

// A matcher for the `suffix` StringMatcher.
class SuffixStringMatcher : public StringMatcherImplBase {
public:
SuffixStringMatcher(absl::string_view suffix, bool ignore_case)
: suffix_(suffix), ignore_case_(ignore_case) {}

// StringMatcher
bool match(const absl::string_view value) const override {
return ignore_case_ ? absl::EndsWithIgnoreCase(value, suffix_) : absl::EndsWith(value, suffix_);
}

const std::string& stringRepresentation() const override { return suffix_; }

private:
const std::string suffix_;
const bool ignore_case_;
};

// A matcher for the `safe_regex` StringMatcher.
class RegexStringMatcher : public StringMatcherImplBase {
public:
// The RegexMatcher can either be from the ::envoy or ::xds type,
// and the templated c'tor handles both cases.
template <class RegexMatcherType>
RegexStringMatcher(const RegexMatcherType& safe_regex,
Server::Configuration::CommonFactoryContext& context)
: regex_(THROW_OR_RETURN_VALUE(Regex::Utility::parseRegex(safe_regex, context.regexEngine()),
Regex::CompiledMatcherPtr)) {}

// StringMatcher
bool match(const absl::string_view value) const override { return regex_->match(value); }

const std::string& stringRepresentation() const override {
return regex_->stringRepresentation();
}

private:
Regex::CompiledMatcherPtr regex_;
};

// A matcher for the `contains` StringMatcher.
class ContainsStringMatcher : public StringMatcherImplBase {
public:
ContainsStringMatcher(absl::string_view contents, bool ignore_case)
: contents_(ignore_case ? absl::AsciiStrToLower(contents) : contents),
ignore_case_(ignore_case) {}

// StringMatcher
bool match(const absl::string_view value) const override {
return ignore_case_ ? absl::StrContains(absl::AsciiStrToLower(value), contents_)
: absl::StrContains(value, contents_);
}

const std::string& stringRepresentation() const override {
// Note that in case where the matcher is configured to be case-insensitive
// this will return the lower-case configured string (as opposed to the
// other matchers). This is incompatible with the other matchers, but achieves
// the intent of this method.
return contents_;
}

private:
// If ignore_case is set the contents_ will contain lower-case letters.
const std::string contents_;
const bool ignore_case_;
};

// A matcher for the `custom` StringMatcher.
class CustomStringMatcher : public StringMatcherImplBase {
public:
CustomStringMatcher(const xds::core::v3::TypedExtensionConfig& custom,
Server::Configuration::CommonFactoryContext& context)
: custom_(getExtensionStringMatcher(custom, context)) {}

// StringMatcher
bool match(const absl::string_view value) const override { return custom_->match(value); }

const std::string& stringRepresentation() const override { PANIC("not implemented"); }

private:
const StringMatcherPtr custom_;
};

// Creates a StringMatcher given a config.
// Note that Envoy supports both matchers that are created using the
// `envoy::type::matcher::v3::StringMatcher` type and the
// `xds::type::matcher::v3::StringMatcher` type and therefore this function is templated.
template <class StringMatcherType = envoy::type::matcher::v3::StringMatcher>
std::unique_ptr<StringMatcherImplBase>
createStringMatcher(const StringMatcherType& config,
Server::Configuration::CommonFactoryContext& context);

class StringMatcherExtensionFactory : public Config::TypedFactory {
public:
virtual StringMatcherPtr
Expand Down Expand Up @@ -270,16 +451,19 @@ class PathMatcher : public StringMatcher {
createPrefix(const std::string& prefix, bool ignore_case,
Server::Configuration::CommonFactoryContext& context);
static PathMatcherConstSharedPtr
createPattern(const std::string& pattern, bool ignore_case,
Server::Configuration::CommonFactoryContext& context);
static PathMatcherConstSharedPtr
createSafeRegex(const envoy::type::matcher::v3::RegexMatcher& regex_matcher,
Server::Configuration::CommonFactoryContext& context);

bool match(const absl::string_view path) const override;
const StringMatcherImpl<envoy::type::matcher::v3::StringMatcher>& matcher() const {
return matcher_;
}
// TODO(adisuissa): This method will replace the `matcher()` call above.
const std::string& stringRepresentation() const {
// TODO(adisuissa): replace with: return matcher_->stringRepresentation();
// after the type of `matcher_` is replaced with type StringMatcherImplBase.
return EMPTY_STRING;
}

private:
const StringMatcherImpl<envoy::type::matcher::v3::StringMatcher> matcher_;
Expand Down
3 changes: 3 additions & 0 deletions source/common/common/regex.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class CompiledGoogleReMatcher : public CompiledMatcher {
return result;
}

// CompiledMatcher
const std::string& stringRepresentation() const override { return regex_.pattern(); }

protected:
explicit CompiledGoogleReMatcher(const std::string& regex) : regex_(regex, re2::RE2::Quiet) {
ENVOY_BUG(regex_.ok(), "Invalid regex");
Expand Down
7 changes: 7 additions & 0 deletions test/common/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ load(
"envoy_cc_fuzz_test",
"envoy_cc_test",
"envoy_package",
"envoy_proto_library",
"envoy_select_boringssl",
)

Expand Down Expand Up @@ -260,11 +261,17 @@ envoy_cc_fuzz_test(
deps = ["//source/common/common:minimal_logger_lib"],
)

envoy_proto_library(
name = "custom_test_string_matcher_proto",
srcs = ["custom_test_string_matcher.proto"],
)

envoy_cc_test(
name = "matchers_test",
srcs = ["matchers_test.cc"],
rbe_pool = "6gig",
deps = [
":custom_test_string_matcher_proto_cc_proto",
"//source/common/common:matchers_lib",
"//source/common/config:metadata_lib",
"//source/common/protobuf:utility_lib",
Expand Down
7 changes: 7 additions & 0 deletions test/common/common/custom_test_string_matcher.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
syntax = "proto3";

package test.string_matcher;

message CustomTestStringMatcher {
string exact = 1;
}
Loading