Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/common/http/match_delegate/config.cc
Line
Count
Source (jump to first uncovered line)
1
#include "config.h"
2
#include "source/common/http/match_delegate/config.h"
3
4
#include <memory>
5
6
#include "envoy/http/filter.h"
7
#include "envoy/registry/registry.h"
8
#include "envoy/type/matcher/v3/http_inputs.pb.h"
9
#include "envoy/type/matcher/v3/http_inputs.pb.validate.h"
10
11
#include "source/common/config/utility.h"
12
#include "source/common/http/utility.h"
13
14
#include "absl/status/status.h"
15
#include "xds/type/matcher/v3/http_inputs.pb.h"
16
17
namespace Envoy {
18
namespace Common {
19
namespace Http {
20
namespace MatchDelegate {
21
22
namespace Factory {
23
24
class SkipActionFactory
25
    : public Matcher::ActionFactory<Envoy::Http::Matching::HttpFilterActionContext> {
26
public:
27
220
  std::string name() const override { return "skip"; }
28
  Matcher::ActionFactoryCb createActionFactoryCb(const Protobuf::Message&,
29
                                                 Envoy::Http::Matching::HttpFilterActionContext&,
30
0
                                                 ProtobufMessage::ValidationVisitor&) override {
31
0
    return []() { return std::make_unique<SkipAction>(); };
32
0
  }
33
1
  ProtobufTypes::MessagePtr createEmptyConfigProto() override {
34
1
    return std::make_unique<envoy::extensions::filters::common::matcher::action::v3::SkipFilter>();
35
1
  }
36
};
37
38
REGISTER_FACTORY(SkipActionFactory,
39
                 Matcher::ActionFactory<Envoy::Http::Matching::HttpFilterActionContext>);
40
41
class MatchTreeValidationVisitor
42
    : public Matcher::MatchTreeValidationVisitor<Envoy::Http::HttpMatchingData> {
43
public:
44
  explicit MatchTreeValidationVisitor(
45
      const envoy::extensions::filters::common::dependency::v3::MatchingRequirements&
46
1.37k
          requirements) {
47
1.37k
    if (requirements.has_data_input_allow_list()) {
48
871
      data_input_allowlist_ = requirements.data_input_allow_list().type_url();
49
871
    }
50
1.37k
  }
51
  absl::Status
52
  performDataInputValidation(const Matcher::DataInputFactory<Envoy::Http::HttpMatchingData>&,
53
0
                             absl::string_view type_url) override {
54
0
    if (!data_input_allowlist_) {
55
0
      return absl::OkStatus();
56
0
    }
57
58
0
    if (std::find(data_input_allowlist_->begin(), data_input_allowlist_->end(), type_url) !=
59
0
        data_input_allowlist_->end()) {
60
0
      return absl::OkStatus();
61
0
    }
62
63
0
    return absl::InvalidArgumentError(
64
0
        fmt::format("data input typeUrl {} not permitted according to allowlist", type_url));
65
0
  }
66
67
private:
68
  absl::optional<Protobuf::RepeatedPtrField<std::string>> data_input_allowlist_;
69
};
70
71
struct DelegatingFactoryCallbacks : public Envoy::Http::FilterChainFactoryCallbacks {
72
  DelegatingFactoryCallbacks(Envoy::Http::FilterChainFactoryCallbacks& delegated_callbacks,
73
                             Matcher::MatchTreeSharedPtr<Envoy::Http::HttpMatchingData> match_tree)
74
498
      : delegated_callbacks_(delegated_callbacks), match_tree_(match_tree) {}
75
76
0
  Event::Dispatcher& dispatcher() override { return delegated_callbacks_.dispatcher(); }
77
104
  void addStreamDecoderFilter(Envoy::Http::StreamDecoderFilterSharedPtr filter) override {
78
104
    auto delegating_filter =
79
104
        std::make_shared<DelegatingStreamFilter>(match_tree_, std::move(filter), nullptr);
80
104
    delegated_callbacks_.addStreamDecoderFilter(std::move(delegating_filter));
81
104
  }
82
0
  void addStreamEncoderFilter(Envoy::Http::StreamEncoderFilterSharedPtr filter) override {
83
0
    auto delegating_filter =
84
0
        std::make_shared<DelegatingStreamFilter>(match_tree_, nullptr, std::move(filter));
85
0
    delegated_callbacks_.addStreamEncoderFilter(std::move(delegating_filter));
86
0
  }
87
394
  void addStreamFilter(Envoy::Http::StreamFilterSharedPtr filter) override {
88
394
    auto delegating_filter = std::make_shared<DelegatingStreamFilter>(match_tree_, filter, filter);
89
394
    delegated_callbacks_.addStreamFilter(std::move(delegating_filter));
90
394
  }
91
92
0
  void addAccessLogHandler(AccessLog::InstanceSharedPtr handler) override {
93
0
    delegated_callbacks_.addAccessLogHandler(std::move(handler));
94
0
  }
95
96
  Envoy::Http::FilterChainFactoryCallbacks& delegated_callbacks_;
97
  Matcher::MatchTreeSharedPtr<Envoy::Http::HttpMatchingData> match_tree_;
98
};
99
100
} // namespace Factory
101
102
void DelegatingStreamFilter::FilterMatchState::evaluateMatchTree(
103
679
    MatchDataUpdateFunc data_update_func) {
104
679
  if (match_tree_evaluated_) {
105
373
    return;
106
373
  }
107
108
  // If no match tree is set, interpret as a skip.
109
306
  if (!has_match_tree_) {
110
53
    skip_filter_ = true;
111
53
    match_tree_evaluated_ = true;
112
53
    return;
113
53
  }
114
115
253
  ASSERT(matching_data_ != nullptr);
116
253
  data_update_func(*matching_data_);
117
118
253
  const auto match_result =
119
253
      Matcher::evaluateMatch<Envoy::Http::HttpMatchingData>(*match_tree_, *matching_data_);
120
121
253
  match_tree_evaluated_ = match_result.match_state_ == Matcher::MatchState::MatchComplete;
122
123
253
  if (match_tree_evaluated_ && match_result.result_) {
124
0
    const auto result = match_result.result_();
125
0
    if (SkipAction().typeUrl() == result->typeUrl()) {
126
0
      skip_filter_ = true;
127
0
    } else {
128
0
      ASSERT(base_filter_ != nullptr);
129
0
      base_filter_->onMatchCallback(*result);
130
0
    }
131
0
  }
132
253
}
133
134
DelegatingStreamFilter::DelegatingStreamFilter(
135
    Matcher::MatchTreeSharedPtr<Envoy::Http::HttpMatchingData> match_tree,
136
    Envoy::Http::StreamDecoderFilterSharedPtr decoder_filter,
137
    Envoy::Http::StreamEncoderFilterSharedPtr encoder_filter)
138
    : match_state_(std::move(match_tree)), decoder_filter_(std::move(decoder_filter)),
139
498
      encoder_filter_(std::move(encoder_filter)) {
140
141
498
  if (encoder_filter_ != nullptr) {
142
394
    base_filter_ = encoder_filter_.get();
143
394
  } else {
144
104
    base_filter_ = decoder_filter_.get();
145
104
  }
146
498
  match_state_.setBaseFilter(base_filter_);
147
498
}
Unexecuted instantiation: Envoy::Common::Http::MatchDelegate::DelegatingStreamFilter::DelegatingStreamFilter(std::__1::shared_ptr<Envoy::Matcher::MatchTree<Envoy::Http::HttpMatchingData> >, std::__1::shared_ptr<Envoy::Http::StreamDecoderFilter>, std::__1::shared_ptr<Envoy::Http::StreamEncoderFilter>)
Envoy::Common::Http::MatchDelegate::DelegatingStreamFilter::DelegatingStreamFilter(std::__1::shared_ptr<Envoy::Matcher::MatchTree<Envoy::Http::HttpMatchingData> >, std::__1::shared_ptr<Envoy::Http::StreamDecoderFilter>, std::__1::shared_ptr<Envoy::Http::StreamEncoderFilter>)
Line
Count
Source
139
498
      encoder_filter_(std::move(encoder_filter)) {
140
141
498
  if (encoder_filter_ != nullptr) {
142
394
    base_filter_ = encoder_filter_.get();
143
394
  } else {
144
104
    base_filter_ = decoder_filter_.get();
145
104
  }
146
498
  match_state_.setBaseFilter(base_filter_);
147
498
}
148
149
Envoy::Http::FilterHeadersStatus
150
306
DelegatingStreamFilter::decodeHeaders(Envoy::Http::RequestHeaderMap& headers, bool end_stream) {
151
306
  const auto* per_route_config =
152
306
      Envoy::Http::Utility::resolveMostSpecificPerFilterConfig<FilterConfigPerRoute>(
153
306
          decoder_callbacks_);
154
155
306
  if (per_route_config != nullptr) {
156
0
    match_state_.setMatchTree(per_route_config->matchTree());
157
306
  } else {
158
306
    ENVOY_LOG(
159
306
        trace,
160
306
        "No per route config found, thus the matcher tree can not be built from per route config");
161
306
  }
162
163
306
  match_state_.evaluateMatchTree([&headers](Envoy::Http::Matching::HttpMatchingDataImpl& data) {
164
253
    data.onRequestHeaders(headers);
165
253
  });
166
306
  if (match_state_.skipFilter()) {
167
53
    return Envoy::Http::FilterHeadersStatus::Continue;
168
53
  }
169
253
  return decoder_filter_->decodeHeaders(headers, end_stream);
170
306
}
171
172
Envoy::Http::FilterDataStatus DelegatingStreamFilter::decodeData(Buffer::Instance& data,
173
464
                                                                 bool end_stream) {
174
464
  if (match_state_.skipFilter()) {
175
228
    return Envoy::Http::FilterDataStatus::Continue;
176
228
  }
177
236
  return decoder_filter_->decodeData(data, end_stream);
178
464
}
179
Envoy::Http::FilterTrailersStatus
180
81
DelegatingStreamFilter::decodeTrailers(Envoy::Http::RequestTrailerMap& trailers) {
181
81
  match_state_.evaluateMatchTree([&trailers](Envoy::Http::Matching::HttpMatchingDataImpl& data) {
182
0
    data.onRequestTrailers(trailers);
183
0
  });
184
81
  if (match_state_.skipFilter()) {
185
6
    return Envoy::Http::FilterTrailersStatus::Continue;
186
6
  }
187
75
  return decoder_filter_->decodeTrailers(trailers);
188
81
}
189
190
Envoy::Http::FilterMetadataStatus
191
0
DelegatingStreamFilter::decodeMetadata(Envoy::Http::MetadataMap& metadata_map) {
192
0
  if (match_state_.skipFilter()) {
193
0
    return Envoy::Http::FilterMetadataStatus::Continue;
194
0
  }
195
0
  return decoder_filter_->decodeMetadata(metadata_map);
196
0
}
197
198
306
void DelegatingStreamFilter::decodeComplete() {
199
306
  if (match_state_.skipFilter()) {
200
53
    return;
201
53
  }
202
253
  decoder_filter_->decodeComplete();
203
253
}
204
205
void DelegatingStreamFilter::setDecoderFilterCallbacks(
206
498
    Envoy::Http::StreamDecoderFilterCallbacks& callbacks) {
207
498
  match_state_.onStreamInfo(callbacks.streamInfo());
208
498
  decoder_callbacks_ = &callbacks;
209
498
  decoder_filter_->setDecoderFilterCallbacks(callbacks);
210
498
}
211
212
Envoy::Http::Filter1xxHeadersStatus
213
0
DelegatingStreamFilter::encode1xxHeaders(Envoy::Http::ResponseHeaderMap& headers) {
214
0
  if (match_state_.skipFilter()) {
215
0
    return Envoy::Http::Filter1xxHeadersStatus::Continue;
216
0
  }
217
0
  return encoder_filter_->encode1xxHeaders(headers);
218
0
}
219
220
Envoy::Http::FilterHeadersStatus
221
232
DelegatingStreamFilter::encodeHeaders(Envoy::Http::ResponseHeaderMap& headers, bool end_stream) {
222
232
  match_state_.evaluateMatchTree([&headers](Envoy::Http::Matching::HttpMatchingDataImpl& data) {
223
0
    data.onResponseHeaders(headers);
224
0
  });
225
232
  if (match_state_.skipFilter()) {
226
33
    return Envoy::Http::FilterHeadersStatus::Continue;
227
33
  }
228
199
  return encoder_filter_->encodeHeaders(headers, end_stream);
229
232
}
230
231
Envoy::Http::FilterDataStatus DelegatingStreamFilter::encodeData(Buffer::Instance& data,
232
391
                                                                 bool end_stream) {
233
391
  if (match_state_.skipFilter()) {
234
109
    return Envoy::Http::FilterDataStatus::Continue;
235
109
  }
236
282
  return encoder_filter_->encodeData(data, end_stream);
237
391
}
238
239
Envoy::Http::FilterTrailersStatus
240
60
DelegatingStreamFilter::encodeTrailers(Envoy::Http::ResponseTrailerMap& trailers) {
241
60
  match_state_.evaluateMatchTree([&trailers](Envoy::Http::Matching::HttpMatchingDataImpl& data) {
242
0
    data.onResponseTrailers(trailers);
243
0
  });
244
60
  if (match_state_.skipFilter()) {
245
6
    return Envoy::Http::FilterTrailersStatus::Continue;
246
6
  }
247
54
  return encoder_filter_->encodeTrailers(trailers);
248
60
}
249
250
Envoy::Http::FilterMetadataStatus
251
0
DelegatingStreamFilter::encodeMetadata(Envoy::Http::MetadataMap& metadata_map) {
252
0
  if (match_state_.skipFilter()) {
253
0
    return Envoy::Http::FilterMetadataStatus::Continue;
254
0
  }
255
0
  return decoder_filter_->decodeMetadata(metadata_map);
256
0
}
257
258
232
void DelegatingStreamFilter::encodeComplete() {
259
232
  if (match_state_.skipFilter()) {
260
33
    return;
261
33
  }
262
199
  encoder_filter_->encodeComplete();
263
199
}
264
265
void DelegatingStreamFilter::setEncoderFilterCallbacks(
266
394
    Envoy::Http::StreamEncoderFilterCallbacks& callbacks) {
267
394
  match_state_.onStreamInfo(callbacks.streamInfo());
268
394
  encoder_callbacks_ = &callbacks;
269
394
  encoder_filter_->setEncoderFilterCallbacks(callbacks);
270
394
}
271
272
Envoy::Http::FilterFactoryCb MatchDelegateConfig::createFilterFactoryFromProtoTyped(
273
    const envoy::extensions::common::matching::v3::ExtensionWithMatcher& proto_config,
274
761
    const std::string& prefix, Server::Configuration::FactoryContext& context) {
275
276
761
  ASSERT(proto_config.has_extension_config());
277
761
  auto& factory =
278
761
      Config::Utility::getAndCheckFactory<Server::Configuration::NamedHttpFilterConfigFactory>(
279
761
          proto_config.extension_config());
280
281
761
  auto message = Config::Utility::translateAnyToFactoryConfig(
282
761
      proto_config.extension_config().typed_config(), context.messageValidationVisitor(), factory);
283
761
  auto filter_factory = factory.createFilterFactoryFromProto(*message, prefix, context);
284
285
761
  Factory::MatchTreeValidationVisitor validation_visitor(*factory.matchingRequirements());
286
287
761
  Envoy::Http::Matching::HttpFilterActionContext action_context{prefix, context,
288
761
                                                                context.getServerFactoryContext()};
289
290
761
  Matcher::MatchTreeFactory<Envoy::Http::HttpMatchingData,
291
761
                            Envoy::Http::Matching::HttpFilterActionContext>
292
761
      matcher_factory(action_context, context.getServerFactoryContext(), validation_visitor);
293
761
  absl::optional<Matcher::MatchTreeFactoryCb<Envoy::Http::HttpMatchingData>> factory_cb =
294
761
      std::nullopt;
295
761
  if (proto_config.has_xds_matcher()) {
296
376
    factory_cb = matcher_factory.create(proto_config.xds_matcher());
297
385
  } else if (proto_config.has_matcher()) {
298
0
    factory_cb = matcher_factory.create(proto_config.matcher());
299
0
  }
300
301
761
  if (!validation_visitor.errors().empty()) {
302
    // TODO(snowp): Output all violations.
303
0
    throwEnvoyExceptionOrPanic(fmt::format("requirement violation while creating match tree: {}",
304
0
                                           validation_visitor.errors()[0]));
305
0
  }
306
307
761
  Matcher::MatchTreeSharedPtr<Envoy::Http::HttpMatchingData> match_tree = nullptr;
308
309
761
  if (factory_cb.has_value()) {
310
375
    match_tree = factory_cb.value()();
311
375
  }
312
313
761
  return [filter_factory, match_tree](Envoy::Http::FilterChainFactoryCallbacks& callbacks) -> void {
314
498
    Factory::DelegatingFactoryCallbacks delegating_callbacks(callbacks, match_tree);
315
498
    return filter_factory(delegating_callbacks);
316
498
  };
317
761
}
318
319
Router::RouteSpecificFilterConfigConstSharedPtr
320
MatchDelegateConfig::createRouteSpecificFilterConfigTyped(
321
    const envoy::extensions::common::matching::v3::ExtensionWithMatcherPerRoute& proto_config,
322
    Server::Configuration::ServerFactoryContext& server_context,
323
871
    ProtobufMessage::ValidationVisitor&) {
324
871
  return std::make_shared<FilterConfigPerRoute>(proto_config, server_context);
325
871
}
326
327
Matcher::MatchTreeSharedPtr<Envoy::Http::HttpMatchingData>
328
FilterConfigPerRoute::createFilterMatchTree(
329
    const envoy::extensions::common::matching::v3::ExtensionWithMatcherPerRoute& proto_config,
330
871
    Server::Configuration::ServerFactoryContext& server_context) {
331
871
  auto requirements =
332
871
      std::make_unique<envoy::extensions::filters::common::dependency::v3::MatchingRequirements>();
333
871
  requirements->mutable_data_input_allow_list()->add_type_url(TypeUtil::descriptorFullNameToTypeUrl(
334
871
      envoy::type::matcher::v3::HttpRequestHeaderMatchInput::default_instance().GetTypeName()));
335
871
  requirements->mutable_data_input_allow_list()->add_type_url(TypeUtil::descriptorFullNameToTypeUrl(
336
871
      xds::type::matcher::v3::HttpAttributesCelMatchInput::default_instance().GetTypeName()));
337
871
  Envoy::Http::Matching::HttpFilterActionContext action_context{
338
871
      fmt::format("http.{}.",
339
871
                  server_context.scope().symbolTable().toString(server_context.scope().prefix())),
340
871
      absl::nullopt, server_context};
341
342
871
  Factory::MatchTreeValidationVisitor validation_visitor(*requirements);
343
871
  Matcher::MatchTreeFactory<Envoy::Http::HttpMatchingData,
344
871
                            Envoy::Http::Matching::HttpFilterActionContext>
345
871
      matcher_factory(action_context, server_context, validation_visitor);
346
871
  Matcher::MatchTreeFactoryCb<Envoy::Http::HttpMatchingData> factory_cb =
347
871
      matcher_factory.create(proto_config.xds_matcher());
348
871
  return factory_cb();
349
871
}
350
351
/**
352
 * Static registration for the match delegate filter. @see RegisterFactory.
353
 * This match delegate filter is designed as delegate of all other HTTP filters. It will create
354
 * and associate a match tree with the underlying filter to help the underlying filter consume
355
 * match result of match tree.
356
 */
357
REGISTER_FACTORY(MatchDelegateConfig, Server::Configuration::NamedHttpFilterConfigFactory);
358
359
} // namespace MatchDelegate
360
} // namespace Http
361
} // namespace Common
362
} // namespace Envoy