/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 |