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
|