Line data Source code
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 56 : 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 0 : ProtobufTypes::MessagePtr createEmptyConfigProto() override { 34 0 : return std::make_unique<envoy::extensions::filters::common::matcher::action::v3::SkipFilter>(); 35 0 : } 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 0 : requirements) { 47 0 : if (requirements.has_data_input_allow_list()) { 48 0 : data_input_allowlist_ = requirements.data_input_allow_list().type_url(); 49 0 : } 50 0 : } 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 0 : : delegated_callbacks_(delegated_callbacks), match_tree_(match_tree) {} 75 : 76 0 : Event::Dispatcher& dispatcher() override { return delegated_callbacks_.dispatcher(); } 77 0 : void addStreamDecoderFilter(Envoy::Http::StreamDecoderFilterSharedPtr filter) override { 78 0 : auto delegating_filter = 79 0 : std::make_shared<DelegatingStreamFilter>(match_tree_, std::move(filter), nullptr); 80 0 : delegated_callbacks_.addStreamDecoderFilter(std::move(delegating_filter)); 81 0 : } 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 0 : void addStreamFilter(Envoy::Http::StreamFilterSharedPtr filter) override { 88 0 : auto delegating_filter = std::make_shared<DelegatingStreamFilter>(match_tree_, filter, filter); 89 0 : delegated_callbacks_.addStreamFilter(std::move(delegating_filter)); 90 0 : } 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 0 : MatchDataUpdateFunc data_update_func) { 104 0 : if (match_tree_evaluated_) { 105 0 : return; 106 0 : } 107 : 108 : // If no match tree is set, interpret as a skip. 109 0 : if (!has_match_tree_) { 110 0 : skip_filter_ = true; 111 0 : match_tree_evaluated_ = true; 112 0 : return; 113 0 : } 114 : 115 0 : ASSERT(matching_data_ != nullptr); 116 0 : data_update_func(*matching_data_); 117 : 118 0 : const auto match_result = 119 0 : Matcher::evaluateMatch<Envoy::Http::HttpMatchingData>(*match_tree_, *matching_data_); 120 : 121 0 : match_tree_evaluated_ = match_result.match_state_ == Matcher::MatchState::MatchComplete; 122 : 123 0 : 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 0 : } 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 0 : encoder_filter_(std::move(encoder_filter)) { 140 : 141 0 : if (encoder_filter_ != nullptr) { 142 0 : base_filter_ = encoder_filter_.get(); 143 0 : } else { 144 0 : base_filter_ = decoder_filter_.get(); 145 0 : } 146 0 : match_state_.setBaseFilter(base_filter_); 147 0 : } 148 : 149 : Envoy::Http::FilterHeadersStatus 150 0 : DelegatingStreamFilter::decodeHeaders(Envoy::Http::RequestHeaderMap& headers, bool end_stream) { 151 0 : const auto* per_route_config = 152 0 : Envoy::Http::Utility::resolveMostSpecificPerFilterConfig<FilterConfigPerRoute>( 153 0 : decoder_callbacks_); 154 : 155 0 : if (per_route_config != nullptr) { 156 0 : match_state_.setMatchTree(per_route_config->matchTree()); 157 0 : } else { 158 0 : ENVOY_LOG( 159 0 : trace, 160 0 : "No per route config found, thus the matcher tree can not be built from per route config"); 161 0 : } 162 : 163 0 : match_state_.evaluateMatchTree([&headers](Envoy::Http::Matching::HttpMatchingDataImpl& data) { 164 0 : data.onRequestHeaders(headers); 165 0 : }); 166 0 : if (match_state_.skipFilter()) { 167 0 : return Envoy::Http::FilterHeadersStatus::Continue; 168 0 : } 169 0 : return decoder_filter_->decodeHeaders(headers, end_stream); 170 0 : } 171 : 172 : Envoy::Http::FilterDataStatus DelegatingStreamFilter::decodeData(Buffer::Instance& data, 173 0 : bool end_stream) { 174 0 : if (match_state_.skipFilter()) { 175 0 : return Envoy::Http::FilterDataStatus::Continue; 176 0 : } 177 0 : return decoder_filter_->decodeData(data, end_stream); 178 0 : } 179 : Envoy::Http::FilterTrailersStatus 180 0 : DelegatingStreamFilter::decodeTrailers(Envoy::Http::RequestTrailerMap& trailers) { 181 0 : match_state_.evaluateMatchTree([&trailers](Envoy::Http::Matching::HttpMatchingDataImpl& data) { 182 0 : data.onRequestTrailers(trailers); 183 0 : }); 184 0 : if (match_state_.skipFilter()) { 185 0 : return Envoy::Http::FilterTrailersStatus::Continue; 186 0 : } 187 0 : return decoder_filter_->decodeTrailers(trailers); 188 0 : } 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 0 : void DelegatingStreamFilter::decodeComplete() { 199 0 : if (match_state_.skipFilter()) { 200 0 : return; 201 0 : } 202 0 : decoder_filter_->decodeComplete(); 203 0 : } 204 : 205 : void DelegatingStreamFilter::setDecoderFilterCallbacks( 206 0 : Envoy::Http::StreamDecoderFilterCallbacks& callbacks) { 207 0 : match_state_.onStreamInfo(callbacks.streamInfo()); 208 0 : decoder_callbacks_ = &callbacks; 209 0 : decoder_filter_->setDecoderFilterCallbacks(callbacks); 210 0 : } 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 0 : DelegatingStreamFilter::encodeHeaders(Envoy::Http::ResponseHeaderMap& headers, bool end_stream) { 222 0 : match_state_.evaluateMatchTree([&headers](Envoy::Http::Matching::HttpMatchingDataImpl& data) { 223 0 : data.onResponseHeaders(headers); 224 0 : }); 225 0 : if (match_state_.skipFilter()) { 226 0 : return Envoy::Http::FilterHeadersStatus::Continue; 227 0 : } 228 0 : return encoder_filter_->encodeHeaders(headers, end_stream); 229 0 : } 230 : 231 : Envoy::Http::FilterDataStatus DelegatingStreamFilter::encodeData(Buffer::Instance& data, 232 0 : bool end_stream) { 233 0 : if (match_state_.skipFilter()) { 234 0 : return Envoy::Http::FilterDataStatus::Continue; 235 0 : } 236 0 : return encoder_filter_->encodeData(data, end_stream); 237 0 : } 238 : 239 : Envoy::Http::FilterTrailersStatus 240 0 : DelegatingStreamFilter::encodeTrailers(Envoy::Http::ResponseTrailerMap& trailers) { 241 0 : match_state_.evaluateMatchTree([&trailers](Envoy::Http::Matching::HttpMatchingDataImpl& data) { 242 0 : data.onResponseTrailers(trailers); 243 0 : }); 244 0 : if (match_state_.skipFilter()) { 245 0 : return Envoy::Http::FilterTrailersStatus::Continue; 246 0 : } 247 0 : return encoder_filter_->encodeTrailers(trailers); 248 0 : } 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 0 : void DelegatingStreamFilter::encodeComplete() { 259 0 : if (match_state_.skipFilter()) { 260 0 : return; 261 0 : } 262 0 : encoder_filter_->encodeComplete(); 263 0 : } 264 : 265 : void DelegatingStreamFilter::setEncoderFilterCallbacks( 266 0 : Envoy::Http::StreamEncoderFilterCallbacks& callbacks) { 267 0 : match_state_.onStreamInfo(callbacks.streamInfo()); 268 0 : encoder_callbacks_ = &callbacks; 269 0 : encoder_filter_->setEncoderFilterCallbacks(callbacks); 270 0 : } 271 : 272 : Envoy::Http::FilterFactoryCb MatchDelegateConfig::createFilterFactoryFromProtoTyped( 273 : const envoy::extensions::common::matching::v3::ExtensionWithMatcher& proto_config, 274 2 : const std::string& prefix, Server::Configuration::FactoryContext& context) { 275 : 276 2 : ASSERT(proto_config.has_extension_config()); 277 2 : auto& factory = 278 2 : Config::Utility::getAndCheckFactory<Server::Configuration::NamedHttpFilterConfigFactory>( 279 2 : proto_config.extension_config()); 280 : 281 2 : auto message = Config::Utility::translateAnyToFactoryConfig( 282 2 : proto_config.extension_config().typed_config(), context.messageValidationVisitor(), factory); 283 2 : auto filter_factory_or_error = factory.createFilterFactoryFromProto(*message, prefix, context); 284 2 : if (!filter_factory_or_error.ok()) { 285 0 : throwEnvoyExceptionOrPanic(std::string(filter_factory_or_error.status().message())); 286 0 : } 287 2 : auto filter_factory = filter_factory_or_error.value(); 288 : 289 2 : Factory::MatchTreeValidationVisitor validation_visitor(*factory.matchingRequirements()); 290 : 291 2 : Envoy::Http::Matching::HttpFilterActionContext action_context{prefix, context, 292 2 : context.serverFactoryContext()}; 293 : 294 2 : Matcher::MatchTreeFactory<Envoy::Http::HttpMatchingData, 295 2 : Envoy::Http::Matching::HttpFilterActionContext> 296 2 : matcher_factory(action_context, context.serverFactoryContext(), validation_visitor); 297 2 : absl::optional<Matcher::MatchTreeFactoryCb<Envoy::Http::HttpMatchingData>> factory_cb = 298 2 : std::nullopt; 299 2 : if (proto_config.has_xds_matcher()) { 300 0 : factory_cb = matcher_factory.create(proto_config.xds_matcher()); 301 2 : } else if (proto_config.has_matcher()) { 302 0 : factory_cb = matcher_factory.create(proto_config.matcher()); 303 0 : } 304 : 305 2 : if (!validation_visitor.errors().empty()) { 306 : // TODO(snowp): Output all violations. 307 0 : throwEnvoyExceptionOrPanic(fmt::format("requirement violation while creating match tree: {}", 308 0 : validation_visitor.errors()[0])); 309 0 : } 310 : 311 2 : Matcher::MatchTreeSharedPtr<Envoy::Http::HttpMatchingData> match_tree = nullptr; 312 : 313 2 : if (factory_cb.has_value()) { 314 0 : match_tree = factory_cb.value()(); 315 0 : } 316 : 317 2 : return [filter_factory, match_tree](Envoy::Http::FilterChainFactoryCallbacks& callbacks) -> void { 318 0 : Factory::DelegatingFactoryCallbacks delegating_callbacks(callbacks, match_tree); 319 0 : return filter_factory(delegating_callbacks); 320 0 : }; 321 2 : } 322 : 323 : Router::RouteSpecificFilterConfigConstSharedPtr 324 : MatchDelegateConfig::createRouteSpecificFilterConfigTyped( 325 : const envoy::extensions::common::matching::v3::ExtensionWithMatcherPerRoute& proto_config, 326 : Server::Configuration::ServerFactoryContext& server_context, 327 0 : ProtobufMessage::ValidationVisitor&) { 328 0 : return std::make_shared<FilterConfigPerRoute>(proto_config, server_context); 329 0 : } 330 : 331 : Matcher::MatchTreeSharedPtr<Envoy::Http::HttpMatchingData> 332 : FilterConfigPerRoute::createFilterMatchTree( 333 : const envoy::extensions::common::matching::v3::ExtensionWithMatcherPerRoute& proto_config, 334 0 : Server::Configuration::ServerFactoryContext& server_context) { 335 0 : auto requirements = 336 0 : std::make_unique<envoy::extensions::filters::common::dependency::v3::MatchingRequirements>(); 337 0 : requirements->mutable_data_input_allow_list()->add_type_url(TypeUtil::descriptorFullNameToTypeUrl( 338 0 : envoy::type::matcher::v3::HttpRequestHeaderMatchInput::default_instance().GetTypeName())); 339 0 : requirements->mutable_data_input_allow_list()->add_type_url(TypeUtil::descriptorFullNameToTypeUrl( 340 0 : xds::type::matcher::v3::HttpAttributesCelMatchInput::default_instance().GetTypeName())); 341 0 : Envoy::Http::Matching::HttpFilterActionContext action_context{ 342 0 : fmt::format("http.{}.", 343 0 : server_context.scope().symbolTable().toString(server_context.scope().prefix())), 344 0 : absl::nullopt, server_context}; 345 : 346 0 : Factory::MatchTreeValidationVisitor validation_visitor(*requirements); 347 0 : Matcher::MatchTreeFactory<Envoy::Http::HttpMatchingData, 348 0 : Envoy::Http::Matching::HttpFilterActionContext> 349 0 : matcher_factory(action_context, server_context, validation_visitor); 350 0 : Matcher::MatchTreeFactoryCb<Envoy::Http::HttpMatchingData> factory_cb = 351 0 : matcher_factory.create(proto_config.xds_matcher()); 352 0 : return factory_cb(); 353 0 : } 354 : 355 : /** 356 : * Static registration for the match delegate filter. @see RegisterFactory. 357 : * This match delegate filter is designed as delegate of all other HTTP filters. It will create 358 : * and associate a match tree with the underlying filter to help the underlying filter consume 359 : * match result of match tree. 360 : */ 361 : REGISTER_FACTORY(MatchDelegateConfig, Server::Configuration::NamedHttpFilterConfigFactory); 362 : 363 : } // namespace MatchDelegate 364 : } // namespace Http 365 : } // namespace Common 366 : } // namespace Envoy