LCOV - code coverage report
Current view: top level - source/common/http/match_delegate - config.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 26 232 11.2 %
Date: 2024-01-05 06:35:25 Functions: 2 36 5.6 %

          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

Generated by: LCOV version 1.15