#pragma once #include "envoy/matcher/matcher.h" namespace Envoy { namespace Matcher { /** * The result of a field match. */ struct FieldMatchResult { // Encodes whether we were able to perform the match. MatchState match_state_; // The result, if matching was completed. absl::optional result_; bool result() const { return *result_; } }; /** * Base class for matching against a single input. */ template class FieldMatcher { public: virtual ~FieldMatcher() = default; /** * Attempts to match against the provided data. * @returns absl::optional if matching was possible, the result of the match. Otherwise * absl::nullopt if the data is not available. */ virtual FieldMatchResult match(const DataType& data) PURE; }; template using FieldMatcherPtr = std::unique_ptr>; /** * A FieldMatcher that attempts to match multiple FieldMatchers, evaluating to true iff all the * FieldMatchers evaluate to true. * * If any of the underlying FieldMatchers are unable to produce a result, absl::nullopt is returned. */ template class AllFieldMatcher : public FieldMatcher { public: explicit AllFieldMatcher(std::vector>&& matchers) : matchers_(std::move(matchers)) {} FieldMatchResult match(const DataType& data) override { for (const auto& matcher : matchers_) { const auto result = matcher->match(data); // If we are unable to decide on a match at this point, propagate this up to defer // the match result until we have the requisite data. if (result.match_state_ == MatchState::UnableToMatch) { return result; } if (!result.result()) { return result; } } return {MatchState::MatchComplete, true}; } private: const std::vector> matchers_; }; /** * A FieldMatcher that attempts to match multiple FieldMatchers, evaluating to true iff any of the * FieldMatchers evaluate to true. * * If any of the underlying FieldMatchers are unable to produce a result before we see a successful * match, absl::nullopt is returned. */ template class AnyFieldMatcher : public FieldMatcher { public: explicit AnyFieldMatcher(std::vector>&& matchers) : matchers_(std::move(matchers)) {} FieldMatchResult match(const DataType& data) override { bool unable_to_match_some_matchers = false; for (const auto& matcher : matchers_) { const auto result = matcher->match(data); if (result.match_state_ == MatchState::UnableToMatch) { unable_to_match_some_matchers = true; } if (result.result()) { return {MatchState::MatchComplete, true}; } } // If we didn't find a successful match but not all matchers could be evaluated, // return UnableToMatch to defer the match result. if (unable_to_match_some_matchers) { return {MatchState::UnableToMatch, absl::nullopt}; } return {MatchState::MatchComplete, false}; } private: const std::vector> matchers_; }; /** * Implementation of a FieldMatcher that extracts an input value from the provided data and attempts * to match using an InputMatcher. absl::nullopt is returned whenever the data is not available or * if we failed to match and there is more data available. * A consequence of this is that if a match result is desired, care should be taken so that matching * is done with all the data available at some point. */ template class SingleFieldMatcher : public FieldMatcher, Logger::Loggable { public: SingleFieldMatcher(DataInputPtr&& data_input, InputMatcherPtr&& input_matcher) : data_input_(std::move(data_input)), input_matcher_(std::move(input_matcher)) {} FieldMatchResult match(const DataType& data) override { const auto input = data_input_->get(data); ENVOY_LOG(debug, "Attempting to match {}", input); if (input.data_availability_ == DataInputGetResult::DataAvailability::NotAvailable) { return {MatchState::UnableToMatch, absl::nullopt}; } const auto current_match = input_matcher_->match(input.data_); if (!current_match && input.data_availability_ == DataInputGetResult::DataAvailability::MoreDataMightBeAvailable) { ENVOY_LOG(debug, "No match yet; delaying result as more data might be available."); return {MatchState::UnableToMatch, absl::nullopt}; } ENVOY_LOG(debug, "Match result: {}", current_match); return {MatchState::MatchComplete, current_match}; } private: const DataInputPtr data_input_; const InputMatcherPtr input_matcher_; }; template using SingleFieldMatcherPtr = std::unique_ptr>; } // namespace Matcher } // namespace Envoy