LCOV - code coverage report
Current view: top level - source/common/matcher - matcher.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 47 217 21.7 %
Date: 2024-01-05 06:35:25 Functions: 14 401 3.5 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <functional>
       4             : #include <memory>
       5             : #include <variant>
       6             : 
       7             : #include "envoy/config/common/matcher/v3/matcher.pb.h"
       8             : #include "envoy/config/core/v3/extension.pb.h"
       9             : #include "envoy/config/typed_config.h"
      10             : #include "envoy/matcher/matcher.h"
      11             : 
      12             : #include "source/common/common/assert.h"
      13             : #include "source/common/config/utility.h"
      14             : #include "source/common/matcher/exact_map_matcher.h"
      15             : #include "source/common/matcher/field_matcher.h"
      16             : #include "source/common/matcher/list_matcher.h"
      17             : #include "source/common/matcher/prefix_map_matcher.h"
      18             : #include "source/common/matcher/validation_visitor.h"
      19             : #include "source/common/matcher/value_input_matcher.h"
      20             : 
      21             : #include "absl/strings/string_view.h"
      22             : #include "absl/types/optional.h"
      23             : 
      24             : namespace Envoy {
      25             : namespace Matcher {
      26             : 
      27             : template <class ProtoType, class Base = Action> class ActionBase : public Base {
      28             : public:
      29           2 :   template <typename... Args> ActionBase(Args... args) : Base(args...) {}
      30             : 
      31           2 :   absl::string_view typeUrl() const override { return staticTypeUrl(); }
      32             : 
      33           4 :   static absl::string_view staticTypeUrl() {
      34           4 :     const static std::string typeUrl = ProtoType().GetTypeName();
      35             : 
      36           4 :     return typeUrl;
      37           4 :   }
      38             : };
      39             : 
      40             : struct MaybeMatchResult {
      41             :   const ActionFactoryCb result_;
      42             :   const MatchState match_state_;
      43             : };
      44             : 
      45             : // TODO(snowp): Make this a class that tracks the progress to speed up subsequent traversals.
      46             : template <class DataType>
      47             : static inline MaybeMatchResult evaluateMatch(MatchTree<DataType>& match_tree,
      48           2 :                                              const DataType& data) {
      49           2 :   const auto result = match_tree.match(data);
      50           2 :   if (result.match_state_ == MatchState::UnableToMatch) {
      51           0 :     return MaybeMatchResult{nullptr, MatchState::UnableToMatch};
      52           0 :   }
      53             : 
      54           2 :   if (!result.on_match_) {
      55           0 :     return {nullptr, MatchState::MatchComplete};
      56           0 :   }
      57             : 
      58           2 :   if (result.on_match_->matcher_) {
      59           0 :     return evaluateMatch(*result.on_match_->matcher_, data);
      60           0 :   }
      61             : 
      62           2 :   return MaybeMatchResult{result.on_match_->action_cb_, MatchState::MatchComplete};
      63           2 : }
      64             : 
      65             : template <class DataType> using FieldMatcherFactoryCb = std::function<FieldMatcherPtr<DataType>()>;
      66             : 
      67             : /**
      68             :  * A matcher that will always resolve to associated on_no_match. This is used when
      69             :  * the matcher is configured without a matcher, allowing for a tree that always resolves
      70             :  * to a specific OnMatch.
      71             :  */
      72             : template <class DataType> class AnyMatcher : public MatchTree<DataType> {
      73             : public:
      74             :   explicit AnyMatcher(absl::optional<OnMatch<DataType>> on_no_match)
      75           2 :       : on_no_match_(std::move(on_no_match)) {}
      76             : 
      77           2 :   typename MatchTree<DataType>::MatchResult match(const DataType&) override {
      78           2 :     return {MatchState::MatchComplete, on_no_match_};
      79           2 :   }
      80             :   const absl::optional<OnMatch<DataType>> on_no_match_;
      81             : };
      82             : 
      83             : /**
      84             :  * Constructs a data input function for a data type.
      85             :  **/
      86             : template <class DataType> class MatchInputFactory {
      87             : public:
      88             :   MatchInputFactory(ProtobufMessage::ValidationVisitor& validator,
      89             :                     MatchTreeValidationVisitor<DataType>& validation_visitor)
      90           3 :       : validator_(validator), validation_visitor_(validation_visitor) {}
      91             : 
      92           0 :   DataInputFactoryCb<DataType> createDataInput(const xds::core::v3::TypedExtensionConfig& config) {
      93           0 :     return createDataInputBase(config);
      94           0 :   }
      95             : 
      96             :   DataInputFactoryCb<DataType>
      97           0 :   createDataInput(const envoy::config::core::v3::TypedExtensionConfig& config) {
      98           0 :     return createDataInputBase(config);
      99           0 :   }
     100             : 
     101             : private:
     102             :   // Wrapper around a CommonProtocolInput that allows it to be used as a DataInput<DataType>.
     103             :   class CommonProtocolInputWrapper : public DataInput<DataType> {
     104             :   public:
     105             :     explicit CommonProtocolInputWrapper(CommonProtocolInputPtr&& common_protocol_input)
     106           0 :         : common_protocol_input_(std::move(common_protocol_input)) {}
     107             : 
     108           0 :     DataInputGetResult get(const DataType&) const override {
     109           0 :       return DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable,
     110           0 :                                 common_protocol_input_->get()};
     111           0 :     }
     112             : 
     113             :   private:
     114             :     const CommonProtocolInputPtr common_protocol_input_;
     115             :   };
     116             : 
     117             :   template <class TypedExtensionConfigType>
     118           0 :   DataInputFactoryCb<DataType> createDataInputBase(const TypedExtensionConfigType& config) {
     119           0 :     auto* factory = Config::Utility::getFactory<DataInputFactory<DataType>>(config);
     120           0 :     if (factory != nullptr) {
     121           0 :       validation_visitor_.validateDataInput(*factory, config.typed_config().type_url());
     122             : 
     123           0 :       ProtobufTypes::MessagePtr message =
     124           0 :           Config::Utility::translateAnyToFactoryConfig(config.typed_config(), validator_, *factory);
     125           0 :       auto data_input = factory->createDataInputFactoryCb(*message, validator_);
     126           0 :       return data_input;
     127           0 :     }
     128             : 
     129             :     // If the provided config doesn't match a typed input, assume that this is one of the common
     130             :     // inputs.
     131           0 :     auto& common_input_factory =
     132           0 :         Config::Utility::getAndCheckFactory<CommonProtocolInputFactory>(config);
     133           0 :     ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
     134           0 :         config.typed_config(), validator_, common_input_factory);
     135           0 :     auto common_input =
     136           0 :         common_input_factory.createCommonProtocolInputFactoryCb(*message, validator_);
     137           0 :     return
     138           0 :         [common_input]() { return std::make_unique<CommonProtocolInputWrapper>(common_input()); };
     139           0 :   }
     140             : 
     141             :   ProtobufMessage::ValidationVisitor& validator_;
     142             :   MatchTreeValidationVisitor<DataType>& validation_visitor_;
     143             : };
     144             : 
     145             : /**
     146             :  * Recursively constructs a MatchTree from a protobuf configuration.
     147             :  * @param DataType the type used as a source for DataInputs
     148             :  * @param ActionFactoryContext the context provided to Action factories
     149             :  */
     150             : template <class DataType, class ActionFactoryContext>
     151             : class MatchTreeFactory : public OnMatchFactory<DataType> {
     152             : public:
     153             :   MatchTreeFactory(ActionFactoryContext& context,
     154             :                    Server::Configuration::ServerFactoryContext& factory_context,
     155             :                    MatchTreeValidationVisitor<DataType>& validation_visitor)
     156             :       : action_factory_context_(context), server_factory_context_(factory_context),
     157           3 :         match_input_factory_(factory_context.messageValidationVisitor(), validation_visitor) {}
     158             : 
     159             :   // TODO(snowp): Remove this type parameter once we only have one Matcher proto.
     160           3 :   template <class MatcherType> MatchTreeFactoryCb<DataType> create(const MatcherType& config) {
     161           3 :     switch (config.matcher_type_case()) {
     162           0 :     case MatcherType::kMatcherTree:
     163           0 :       return createTreeMatcher(config);
     164           0 :     case MatcherType::kMatcherList:
     165           0 :       return createListMatcher(config);
     166           3 :     case MatcherType::MATCHER_TYPE_NOT_SET:
     167           3 :       return createAnyMatcher(config);
     168           3 :     }
     169           0 :     PANIC_DUE_TO_CORRUPT_ENUM;
     170           0 :   }
     171             : 
     172             :   absl::optional<OnMatchFactoryCb<DataType>>
     173           3 :   createOnMatch(const xds::type::matcher::v3::Matcher::OnMatch& on_match) override {
     174           3 :     return createOnMatchBase(on_match);
     175           3 :   }
     176             : 
     177             :   absl::optional<OnMatchFactoryCb<DataType>>
     178           0 :   createOnMatch(const envoy::config::common::matcher::v3::Matcher::OnMatch& on_match) override {
     179           0 :     return createOnMatchBase(on_match);
     180           0 :   }
     181             : 
     182             : private:
     183             :   template <class MatcherType>
     184           3 :   MatchTreeFactoryCb<DataType> createAnyMatcher(const MatcherType& config) {
     185           3 :     auto on_no_match = createOnMatch(config.on_no_match());
     186             : 
     187           3 :     return [on_no_match]() {
     188           2 :       return std::make_unique<AnyMatcher<DataType>>(
     189           2 :           on_no_match ? absl::make_optional((*on_no_match)()) : absl::nullopt);
     190           2 :     };
     191           3 :   }
     192             :   template <class MatcherType>
     193           0 :   MatchTreeFactoryCb<DataType> createListMatcher(const MatcherType& config) {
     194           0 :     std::vector<std::pair<FieldMatcherFactoryCb<DataType>, OnMatchFactoryCb<DataType>>>
     195           0 :         matcher_factories;
     196           0 :     matcher_factories.reserve(config.matcher_list().matchers().size());
     197           0 :     for (const auto& matcher : config.matcher_list().matchers()) {
     198           0 :       matcher_factories.push_back(std::make_pair(
     199           0 :           createFieldMatcher<typename MatcherType::MatcherList::Predicate>(matcher.predicate()),
     200           0 :           *createOnMatch(matcher.on_match())));
     201           0 :     }
     202             : 
     203           0 :     auto on_no_match = createOnMatch(config.on_no_match());
     204           0 :     return [matcher_factories, on_no_match]() {
     205           0 :       auto list_matcher = std::make_unique<ListMatcher<DataType>>(
     206           0 :           on_no_match ? absl::make_optional((*on_no_match)()) : absl::nullopt);
     207             : 
     208           0 :       for (const auto& matcher : matcher_factories) {
     209           0 :         list_matcher->addMatcher(matcher.first(), matcher.second());
     210           0 :       }
     211             : 
     212           0 :       return list_matcher;
     213           0 :     };
     214           0 :   }
     215             : 
     216             :   template <class MatcherT, class PredicateType, class FieldPredicateType>
     217             :   FieldMatcherFactoryCb<DataType> createAggregateFieldMatcherFactoryCb(
     218           0 :       const Protobuf::RepeatedPtrField<FieldPredicateType>& predicates) {
     219           0 :     std::vector<FieldMatcherFactoryCb<DataType>> sub_matchers;
     220           0 :     for (const auto& predicate : predicates) {
     221           0 :       sub_matchers.emplace_back(createFieldMatcher<PredicateType>(predicate));
     222           0 :     }
     223             : 
     224           0 :     return [sub_matchers]() {
     225           0 :       std::vector<FieldMatcherPtr<DataType>> matchers;
     226           0 :       matchers.reserve(sub_matchers.size());
     227           0 :       for (const auto& factory_cb : sub_matchers) {
     228           0 :         matchers.emplace_back(factory_cb());
     229           0 :       }
     230             : 
     231           0 :       return std::make_unique<MatcherT>(std::move(matchers));
     232           0 :     };
     233           0 :   }
     234             : 
     235             :   template <class PredicateType, class FieldMatcherType>
     236           0 :   FieldMatcherFactoryCb<DataType> createFieldMatcher(const FieldMatcherType& field_predicate) {
     237           0 :     switch (field_predicate.match_type_case()) {
     238           0 :     case (PredicateType::kSinglePredicate): {
     239           0 :       auto data_input =
     240           0 :           match_input_factory_.createDataInput(field_predicate.single_predicate().input());
     241           0 :       auto input_matcher = createInputMatcher(field_predicate.single_predicate());
     242             : 
     243           0 :       return [data_input, input_matcher]() {
     244           0 :         return std::make_unique<SingleFieldMatcher<DataType>>(data_input(), input_matcher());
     245           0 :       };
     246           0 :     }
     247           0 :     case (PredicateType::kOrMatcher):
     248           0 :       return createAggregateFieldMatcherFactoryCb<AnyFieldMatcher<DataType>, PredicateType>(
     249           0 :           field_predicate.or_matcher().predicate());
     250           0 :     case (PredicateType::kAndMatcher):
     251           0 :       return createAggregateFieldMatcherFactoryCb<AllFieldMatcher<DataType>, PredicateType>(
     252           0 :           field_predicate.and_matcher().predicate());
     253           0 :     case (PredicateType::kNotMatcher): {
     254           0 :       auto matcher_factory = createFieldMatcher<PredicateType>(field_predicate.not_matcher());
     255             : 
     256           0 :       return [matcher_factory]() {
     257           0 :         return std::make_unique<NotFieldMatcher<DataType>>(matcher_factory());
     258           0 :       };
     259           0 :     }
     260           0 :     case PredicateType::MATCH_TYPE_NOT_SET:
     261           0 :       PANIC_DUE_TO_PROTO_UNSET;
     262           0 :     }
     263           0 :     PANIC_DUE_TO_CORRUPT_ENUM;
     264           0 :   }
     265             : 
     266             :   template <class MatcherType>
     267           0 :   MatchTreeFactoryCb<DataType> createTreeMatcher(const MatcherType& matcher) {
     268           0 :     auto data_input = match_input_factory_.createDataInput(matcher.matcher_tree().input());
     269           0 :     auto on_no_match = createOnMatch(matcher.on_no_match());
     270             : 
     271           0 :     switch (matcher.matcher_tree().tree_type_case()) {
     272           0 :     case MatcherType::MatcherTree::kExactMatchMap: {
     273           0 :       return createMapMatcher<ExactMapMatcher>(matcher.matcher_tree().exact_match_map(), data_input,
     274           0 :                                                on_no_match);
     275           0 :     }
     276           0 :     case MatcherType::MatcherTree::kPrefixMatchMap: {
     277           0 :       return createMapMatcher<PrefixMapMatcher>(matcher.matcher_tree().prefix_match_map(),
     278           0 :                                                 data_input, on_no_match);
     279           0 :     }
     280           0 :     case MatcherType::MatcherTree::TREE_TYPE_NOT_SET:
     281           0 :       PANIC("unexpected matcher type");
     282           0 :     case MatcherType::MatcherTree::kCustomMatch: {
     283           0 :       auto& factory = Config::Utility::getAndCheckFactory<CustomMatcherFactory<DataType>>(
     284           0 :           matcher.matcher_tree().custom_match());
     285           0 :       ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
     286           0 :           matcher.matcher_tree().custom_match().typed_config(),
     287           0 :           server_factory_context_.messageValidationVisitor(), factory);
     288           0 :       return factory.createCustomMatcherFactoryCb(*message, server_factory_context_, data_input,
     289           0 :                                                   on_no_match, *this);
     290           0 :     }
     291           0 :     }
     292           0 :     PANIC_DUE_TO_CORRUPT_ENUM;
     293           0 :   }
     294             : 
     295             :   template <template <class> class MapMatcherType, class MapType>
     296             :   MatchTreeFactoryCb<DataType>
     297             :   createMapMatcher(const MapType& map, DataInputFactoryCb<DataType> data_input,
     298           0 :                    absl::optional<OnMatchFactoryCb<DataType>>& on_no_match) {
     299           0 :     std::vector<std::pair<std::string, OnMatchFactoryCb<DataType>>> match_children;
     300           0 :     match_children.reserve(map.map().size());
     301             : 
     302           0 :     for (const auto& children : map.map()) {
     303           0 :       match_children.push_back(
     304           0 :           std::make_pair(children.first, *MatchTreeFactory::createOnMatch(children.second)));
     305           0 :     }
     306             : 
     307           0 :     return [match_children, data_input, on_no_match]() {
     308           0 :       auto multimap_matcher = std::make_unique<MapMatcherType<DataType>>(
     309           0 :           data_input(), on_no_match ? absl::make_optional((*on_no_match)()) : absl::nullopt);
     310           0 :       for (const auto& children : match_children) {
     311           0 :         multimap_matcher->addChild(children.first, children.second());
     312           0 :       }
     313           0 :       return multimap_matcher;
     314           0 :     };
     315           0 :   }
     316             : 
     317             :   template <class OnMatchType>
     318           3 :   absl::optional<OnMatchFactoryCb<DataType>> createOnMatchBase(const OnMatchType& on_match) {
     319           3 :     if (on_match.has_matcher()) {
     320           0 :       return [matcher_factory = std::move(create(on_match.matcher()))]() {
     321           0 :         return OnMatch<DataType>{{}, matcher_factory()};
     322           0 :       };
     323           3 :     } else if (on_match.has_action()) {
     324           3 :       auto& factory = Config::Utility::getAndCheckFactory<ActionFactory<ActionFactoryContext>>(
     325           3 :           on_match.action());
     326           3 :       ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
     327           3 :           on_match.action().typed_config(), server_factory_context_.messageValidationVisitor(),
     328           3 :           factory);
     329             : 
     330           3 :       auto action_factory = factory.createActionFactoryCb(
     331           3 :           *message, action_factory_context_, server_factory_context_.messageValidationVisitor());
     332           3 :       return [action_factory] { return OnMatch<DataType>{action_factory, {}}; };
     333           3 :     }
     334             : 
     335           0 :     return absl::nullopt;
     336           3 :   }
     337             : 
     338             :   template <class SinglePredicateType>
     339           0 :   InputMatcherFactoryCb createInputMatcher(const SinglePredicateType& predicate) {
     340           0 :     switch (predicate.matcher_case()) {
     341           0 :     case SinglePredicateType::kValueMatch:
     342           0 :       return [value_match = predicate.value_match()]() {
     343           0 :         return std::make_unique<StringInputMatcher<std::decay_t<decltype(value_match)>>>(
     344           0 :             value_match);
     345           0 :       };
     346           0 :     case SinglePredicateType::kCustomMatch: {
     347           0 :       auto& factory =
     348           0 :           Config::Utility::getAndCheckFactory<InputMatcherFactory>(predicate.custom_match());
     349           0 :       ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
     350           0 :           predicate.custom_match().typed_config(),
     351           0 :           server_factory_context_.messageValidationVisitor(), factory);
     352           0 :       return factory.createInputMatcherFactoryCb(*message, server_factory_context_);
     353           0 :     }
     354           0 :     case SinglePredicateType::MATCHER_NOT_SET:
     355           0 :       PANIC_DUE_TO_PROTO_UNSET;
     356           0 :     }
     357           0 :     PANIC_DUE_TO_CORRUPT_ENUM;
     358           0 :   }
     359             : 
     360             :   const std::string stats_prefix_;
     361             :   ActionFactoryContext& action_factory_context_;
     362             :   Server::Configuration::ServerFactoryContext& server_factory_context_;
     363             :   MatchInputFactory<DataType> match_input_factory_;
     364             : };
     365             : } // namespace Matcher
     366             : } // namespace Envoy

Generated by: LCOV version 1.15