LCOV - code coverage report
Current view: top level - source/extensions/common/matcher - matcher.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 0 90 0.0 %
Date: 2024-01-05 06:35:25 Functions: 0 38 0.0 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include "envoy/config/common/matcher/v3/matcher.pb.h"
       4             : 
       5             : #include "source/common/buffer/buffer_impl.h"
       6             : #include "source/common/http/header_utility.h"
       7             : 
       8             : namespace Envoy {
       9             : namespace Extensions {
      10             : namespace Common {
      11             : namespace Matcher {
      12             : 
      13             : class Matcher;
      14             : using MatcherPtr = std::unique_ptr<Matcher>;
      15             : 
      16             : /**
      17             :  * Base class for context used by individual matchers.
      18             :  * The context may be required by matchers which are called multiple times
      19             :  * and need to carry state between the calls. For example body matchers may
      20             :  * store information how any bytes of the body have been already processed
      21             :  * or what what has been already found in the body and what has yet to be found.
      22             :  */
      23             : class MatcherCtx {
      24             : public:
      25           0 :   virtual ~MatcherCtx() = default;
      26             : };
      27             : 
      28             : /**
      29             :  * Base class for all matchers.
      30             :  *
      31             :  * A high level note on the design of matching which is different from other matching in Envoy
      32             :  * due to a requirement to support streaming matching (match as new data arrives versus
      33             :  * calculating the match given all available data at once).
      34             :  * - The matching system is composed of a constant matching configuration. This is essentially
      35             :  *   a tree of matchers given logical AND, OR, NOT, etc.
      36             :  * - A per-stream/request matching status must be kept in order to compute interim match status.
      37             :  * - In order to make this computationally efficient, the matching tree is kept in a vector, with
      38             :  *   all references to other matchers implemented using an index into the vector. The vector is
      39             :  *   effectively a preorder traversal flattened N-ary tree.
      40             :  * - The previous point allows the creation of a per-stream/request vector of match statuses of
      41             :  *   the same size as the matcher vector. Then, when match status is updated given new
      42             :  *   information, the vector of match statuses can be easily updated using the same indexes as in
      43             :  *   the constant match configuration.
      44             :  * - Finally, a matches() function can be trivially implemented by looking in the status vector at
      45             :  *   the index position that the current matcher is located in.
      46             :  */
      47             : class Matcher {
      48             : public:
      49             :   struct MatchStatus {
      50           0 :     bool operator==(const MatchStatus& rhs) const {
      51           0 :       return matches_ == rhs.matches_ && might_change_status_ == rhs.might_change_status_;
      52           0 :     }
      53             : 
      54             :     bool matches_{false};            // Does the matcher currently match?
      55             :     bool might_change_status_{true}; // Is it possible for matches_ to change in subsequent updates?
      56             :     std::unique_ptr<MatcherCtx> ctx_{}; // Context used by matchers to save interim context.
      57             :   };
      58             : 
      59             :   using MatchStatusVector = std::vector<MatchStatus>;
      60             : 
      61             :   /**
      62             :    * Base class constructor for a matcher.
      63             :    * @param matchers supplies the match tree vector being built.
      64             :    */
      65             :   Matcher(const std::vector<MatcherPtr>& matchers)
      66             :       // NOTE: This code assumes that the index for the matcher being constructed has already been
      67             :       // allocated, which is why my_index_ is set to size() - 1. See buildMatcher() in
      68             :       // matcher.cc.
      69           0 :       : my_index_(matchers.size() - 1) {}
      70             : 
      71           0 :   virtual ~Matcher() = default;
      72             : 
      73             :   /**
      74             :    * @return the matcher's index in the match tree vector (see above).
      75             :    */
      76           0 :   size_t index() { return my_index_; }
      77             : 
      78             :   /**
      79             :    * Update match status when a stream is created. This might be an HTTP stream, a TCP connection,
      80             :    * etc. This allows any matchers to flip to an initial state of true if applicable.
      81             :    */
      82             :   virtual void onNewStream(MatchStatusVector& statuses) const PURE;
      83             : 
      84             :   /**
      85             :    * Update match status given HTTP request headers.
      86             :    * @param request_headers supplies the request headers.
      87             :    * @param statuses supplies the per-stream-request match status vector which must be the same
      88             :    *                 size as the match tree vector (see above).
      89             :    */
      90             :   virtual void onHttpRequestHeaders(const Http::RequestHeaderMap& request_headers,
      91             :                                     MatchStatusVector& statuses) const PURE;
      92             : 
      93             :   /**
      94             :    * Update match status given HTTP request trailers.
      95             :    * @param request_trailers supplies the request trailers.
      96             :    * @param statuses supplies the per-stream-request match status vector which must be the same
      97             :    *                 size as the match tree vector (see above).
      98             :    */
      99             :   virtual void onHttpRequestTrailers(const Http::RequestTrailerMap& request_trailers,
     100             :                                      MatchStatusVector& statuses) const PURE;
     101             : 
     102             :   /**
     103             :    * Update match status given HTTP response headers.
     104             :    * @param response_headers supplies the response headers.
     105             :    * @param statuses supplies the per-stream-request match status vector which must be the same
     106             :    *                 size as the match tree vector (see above).
     107             :    */
     108             :   virtual void onHttpResponseHeaders(const Http::ResponseHeaderMap& response_headers,
     109             :                                      MatchStatusVector& statuses) const PURE;
     110             : 
     111             :   /**
     112             :    * Update match status given HTTP response trailers.
     113             :    * @param response_headers supplies the response trailers.
     114             :    * @param statuses supplies the per-stream-request match status vector which must be the same
     115             :    *                 size as the match tree vector (see above).
     116             :    */
     117             :   virtual void onHttpResponseTrailers(const Http::ResponseTrailerMap& response_trailers,
     118             :                                       MatchStatusVector& statuses) const PURE;
     119             : 
     120             :   /**
     121             :    * Update match status given HTTP request body.
     122             :    * @param data supplies the request body.
     123             :    * @param statuses supplies the per-stream-request match status vector which must be the same
     124             :    *                 size as the match tree vector (see above).
     125             :    */
     126             :   virtual void onRequestBody(const Buffer::Instance& data, MatchStatusVector& statuses) PURE;
     127             : 
     128             :   /**
     129             :    * Update match status given HTTP response body.
     130             :    * @param data supplies the response body.
     131             :    * @param statuses supplies the per-stream-request match status vector which must be the same
     132             :    *                 size as the match tree vector (see above).
     133             :    */
     134             :   virtual void onResponseBody(const Buffer::Instance& data, MatchStatusVector& statuses) PURE;
     135             : 
     136             :   /**
     137             :    * @return whether given currently available information, the matcher matches.
     138             :    * @param statuses supplies the per-stream-request match status vector which must be the same
     139             :    *                 size as the match tree vector (see above).
     140             :    */
     141           0 :   const MatchStatus& matchStatus(const MatchStatusVector& statuses) const {
     142           0 :     return statuses[my_index_];
     143           0 :   }
     144             : 
     145             : protected:
     146             :   const size_t my_index_;
     147             : };
     148             : 
     149             : /**
     150             :  * Factory method to build a matcher given a match config. Calling this function may end
     151             :  * up recursively building many matchers, which will all be added to the passed in vector
     152             :  * of matchers. See the comments in matcher.h for the general structure of how matchers work.
     153             :  */
     154             : void buildMatcher(const envoy::config::common::matcher::v3::MatchPredicate& match_config,
     155             :                   std::vector<MatcherPtr>& matchers);
     156             : 
     157             : /**
     158             :  * Base class for logic matchers that need to forward update calls to child matchers.
     159             :  */
     160             : class LogicMatcherBase : public Matcher {
     161             : public:
     162             :   using Matcher::Matcher;
     163             : 
     164           0 :   void onNewStream(MatchStatusVector& statuses) const override {
     165           0 :     updateLocalStatus(statuses,
     166           0 :                       [](Matcher& m, MatchStatusVector& statuses) { m.onNewStream(statuses); });
     167           0 :   }
     168             :   void onHttpRequestHeaders(const Http::RequestHeaderMap& request_headers,
     169           0 :                             MatchStatusVector& statuses) const override {
     170           0 :     updateLocalStatus(statuses, [&request_headers](Matcher& m, MatchStatusVector& statuses) {
     171           0 :       m.onHttpRequestHeaders(request_headers, statuses);
     172           0 :     });
     173           0 :   }
     174             :   void onHttpRequestTrailers(const Http::RequestTrailerMap& request_trailers,
     175           0 :                              MatchStatusVector& statuses) const override {
     176           0 :     updateLocalStatus(statuses, [&request_trailers](Matcher& m, MatchStatusVector& statuses) {
     177           0 :       m.onHttpRequestTrailers(request_trailers, statuses);
     178           0 :     });
     179           0 :   }
     180             :   void onHttpResponseHeaders(const Http::ResponseHeaderMap& response_headers,
     181           0 :                              MatchStatusVector& statuses) const override {
     182           0 :     updateLocalStatus(statuses, [&response_headers](Matcher& m, MatchStatusVector& statuses) {
     183           0 :       m.onHttpResponseHeaders(response_headers, statuses);
     184           0 :     });
     185           0 :   }
     186             :   void onHttpResponseTrailers(const Http::ResponseTrailerMap& response_trailers,
     187           0 :                               MatchStatusVector& statuses) const override {
     188           0 :     updateLocalStatus(statuses, [&response_trailers](Matcher& m, MatchStatusVector& statuses) {
     189           0 :       m.onHttpResponseTrailers(response_trailers, statuses);
     190           0 :     });
     191           0 :   }
     192           0 :   void onRequestBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
     193           0 :     updateLocalStatus(statuses, [&data](Matcher& m, MatchStatusVector& statuses) {
     194           0 :       m.onRequestBody(data, statuses);
     195           0 :     });
     196           0 :   }
     197           0 :   void onResponseBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
     198           0 :     updateLocalStatus(statuses, [&data](Matcher& m, MatchStatusVector& statuses) {
     199           0 :       m.onResponseBody(data, statuses);
     200           0 :     });
     201           0 :   }
     202             : 
     203             : protected:
     204             :   using UpdateFunctor = std::function<void(Matcher&, MatchStatusVector&)>;
     205             :   virtual void updateLocalStatus(MatchStatusVector& statuses,
     206             :                                  const UpdateFunctor& functor) const PURE;
     207             : };
     208             : 
     209             : /**
     210             :  * Matcher for implementing set logic.
     211             :  */
     212             : class SetLogicMatcher : public LogicMatcherBase {
     213             : public:
     214             :   enum class Type { And, Or };
     215             : 
     216             :   SetLogicMatcher(const envoy::config::common::matcher::v3::MatchPredicate::MatchSet& configs,
     217             :                   std::vector<MatcherPtr>& matchers, Type type);
     218             : 
     219             : private:
     220             :   void updateLocalStatus(MatchStatusVector& statuses, const UpdateFunctor& functor) const override;
     221             : 
     222             :   std::vector<MatcherPtr>& matchers_;
     223             :   std::vector<size_t> indexes_;
     224             :   const Type type_;
     225             : };
     226             : 
     227             : /**
     228             :  * Not matcher.
     229             :  */
     230             : class NotMatcher : public LogicMatcherBase {
     231             : public:
     232             :   NotMatcher(const envoy::config::common::matcher::v3::MatchPredicate& config,
     233             :              std::vector<MatcherPtr>& matchers);
     234             : 
     235             : private:
     236             :   void updateLocalStatus(MatchStatusVector& statuses, const UpdateFunctor& functor) const override;
     237             : 
     238             :   std::vector<MatcherPtr>& matchers_;
     239             :   const size_t not_index_;
     240             : };
     241             : 
     242             : /**
     243             :  * A base class for a matcher that generally wants to return default values, but might override
     244             :  * a single update function.
     245             :  */
     246             : class SimpleMatcher : public Matcher {
     247             : public:
     248             :   using Matcher::Matcher;
     249             : 
     250           0 :   void onNewStream(MatchStatusVector&) const override {}
     251           0 :   void onHttpRequestHeaders(const Http::RequestHeaderMap&, MatchStatusVector&) const override {}
     252           0 :   void onHttpRequestTrailers(const Http::RequestTrailerMap&, MatchStatusVector&) const override {}
     253           0 :   void onHttpResponseHeaders(const Http::ResponseHeaderMap&, MatchStatusVector&) const override {}
     254           0 :   void onHttpResponseTrailers(const Http::ResponseTrailerMap&, MatchStatusVector&) const override {}
     255           0 :   void onRequestBody(const Buffer::Instance&, MatchStatusVector&) override {}
     256           0 :   void onResponseBody(const Buffer::Instance&, MatchStatusVector&) override {}
     257             : };
     258             : 
     259             : /**
     260             :  * Any matcher (always matches).
     261             :  */
     262             : class AnyMatcher : public SimpleMatcher {
     263             : public:
     264             :   using SimpleMatcher::SimpleMatcher;
     265             : 
     266           0 :   void onNewStream(MatchStatusVector& statuses) const override {
     267           0 :     statuses[my_index_].matches_ = true;
     268           0 :     statuses[my_index_].might_change_status_ = false;
     269           0 :   }
     270             : };
     271             : 
     272             : /**
     273             :  * Base class for the various HTTP header matchers.
     274             :  */
     275             : class HttpHeaderMatcherBase : public SimpleMatcher {
     276             : public:
     277             :   HttpHeaderMatcherBase(const envoy::config::common::matcher::v3::HttpHeadersMatch& config,
     278             :                         const std::vector<MatcherPtr>& matchers);
     279             : 
     280             : protected:
     281             :   void matchHeaders(const Http::HeaderMap& headers, MatchStatusVector& statuses) const;
     282             : 
     283             :   const std::vector<Http::HeaderUtility::HeaderDataPtr> headers_to_match_;
     284             : };
     285             : 
     286             : /**
     287             :  * HTTP request headers matcher.
     288             :  */
     289             : class HttpRequestHeadersMatcher : public HttpHeaderMatcherBase {
     290             : public:
     291             :   using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
     292             : 
     293             :   void onHttpRequestHeaders(const Http::RequestHeaderMap& request_headers,
     294           0 :                             MatchStatusVector& statuses) const override {
     295           0 :     matchHeaders(request_headers, statuses);
     296           0 :   }
     297             : };
     298             : 
     299             : /**
     300             :  * HTTP request trailers matcher.
     301             :  */
     302             : class HttpRequestTrailersMatcher : public HttpHeaderMatcherBase {
     303             : public:
     304             :   using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
     305             : 
     306             :   void onHttpRequestTrailers(const Http::RequestTrailerMap& request_trailers,
     307           0 :                              MatchStatusVector& statuses) const override {
     308           0 :     matchHeaders(request_trailers, statuses);
     309           0 :   }
     310             : };
     311             : 
     312             : /**
     313             :  * HTTP response headers matcher.
     314             :  */
     315             : class HttpResponseHeadersMatcher : public HttpHeaderMatcherBase {
     316             : public:
     317             :   using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
     318             : 
     319             :   void onHttpResponseHeaders(const Http::ResponseHeaderMap& response_headers,
     320           0 :                              MatchStatusVector& statuses) const override {
     321           0 :     matchHeaders(response_headers, statuses);
     322           0 :   }
     323             : };
     324             : 
     325             : /**
     326             :  * HTTP response trailers matcher.
     327             :  */
     328             : class HttpResponseTrailersMatcher : public HttpHeaderMatcherBase {
     329             : public:
     330             :   using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
     331             : 
     332             :   void onHttpResponseTrailers(const Http::ResponseTrailerMap& response_trailers,
     333           0 :                               MatchStatusVector& statuses) const override {
     334           0 :     matchHeaders(response_trailers, statuses);
     335           0 :   }
     336             : };
     337             : 
     338             : /**
     339             :  * Base class for body matchers.
     340             :  */
     341             : class HttpBodyMatcherBase : public SimpleMatcher {
     342             : public:
     343           0 :   HttpBodyMatcherBase(const std::vector<MatcherPtr>& matchers) : SimpleMatcher(matchers) {}
     344             : 
     345             : protected:
     346             :   // Limit search to specified number of bytes.
     347             :   // Value equal to zero means no limit.
     348             :   uint32_t limit_{};
     349             : };
     350             : 
     351             : /**
     352             :  * Context is used by HttpGenericBodyMatcher to:
     353             :  * - track how many bytes has been processed
     354             :  * - track patterns which have been found
     355             :  * - store last several seen bytes of the HTTP body (when pattern starts at the end of previous body
     356             :  *   chunk and continues at the beginning of the next body chunk)
     357             :  */
     358             : class HttpGenericBodyMatcherCtx : public MatcherCtx {
     359             : public:
     360             :   HttpGenericBodyMatcherCtx(const std::shared_ptr<std::vector<std::string>>& patterns,
     361             :                             size_t overlap_size)
     362           0 :       : patterns_(patterns) {
     363           0 :     // Initialize overlap_ buffer's capacity to fit the longest pattern - 1.
     364           0 :     // The length of the longest pattern is known and passed here as overlap_size.
     365           0 :     patterns_index_.resize(patterns_->size());
     366           0 :     std::iota(patterns_index_.begin(), patterns_index_.end(), 0);
     367           0 :     overlap_.reserve(overlap_size);
     368           0 :     capacity_ = overlap_size;
     369           0 :   }
     370           0 :   ~HttpGenericBodyMatcherCtx() override = default;
     371             : 
     372             :   // The context is initialized per each http request. The patterns_
     373             :   // shared pointer attaches to matcher's list of patterns, so patterns
     374             :   // can be referenced without copying data.
     375             :   const std::shared_ptr<const std::vector<std::string>> patterns_;
     376             :   // List stores indexes of patterns in patterns_ shared memory which
     377             :   // still need to be located in the body. When a pattern is found
     378             :   // its index is removed from the list.
     379             :   // When all patterns have been found, the list is empty.
     380             :   std::list<uint32_t> patterns_index_;
     381             :   // Buffer to store the last bytes from previous body chunk(s).
     382             :   // It will store only as many bytes as is the length of the longest
     383             :   // pattern to be found minus 1.
     384             :   // It is necessary to locate patterns which are spread across 2 or more
     385             :   // body chunks.
     386             :   std::vector<char> overlap_;
     387             :   // capacity_ tells how many bytes should be buffered. overlap_'s initial
     388             :   // capacity is set to the length of the longest pattern - 1. As patterns
     389             :   // are found, there is a possibility that not as many bytes are required to be buffered.
     390             :   // It must be tracked outside of vector, because vector::reserve does not
     391             :   // change capacity when new value is lower than current capacity.
     392             :   uint32_t capacity_{};
     393             :   // processed_bytes_ tracks how many bytes of HTTP body have been processed.
     394             :   uint32_t processed_bytes_{};
     395             : };
     396             : 
     397             : class HttpGenericBodyMatcher : public HttpBodyMatcherBase {
     398             : public:
     399             :   HttpGenericBodyMatcher(const envoy::config::common::matcher::v3::HttpGenericBodyMatch& config,
     400             :                          const std::vector<MatcherPtr>& matchers);
     401             : 
     402             : protected:
     403             :   void onBody(const Buffer::Instance&, MatchStatusVector&);
     404           0 :   void onNewStream(MatchStatusVector& statuses) const override {
     405           0 :     // Allocate a new context used for the new stream.
     406           0 :     statuses[my_index_].ctx_ =
     407           0 :         std::make_unique<HttpGenericBodyMatcherCtx>(patterns_, overlap_size_);
     408           0 :     statuses[my_index_].matches_ = false;
     409           0 :     statuses[my_index_].might_change_status_ = true;
     410           0 :   }
     411             :   bool locatePatternAcrossChunks(const std::string& pattern, const Buffer::Instance& data,
     412             :                                  const HttpGenericBodyMatcherCtx* ctx);
     413             :   void bufferLastBytes(const Buffer::Instance& data, HttpGenericBodyMatcherCtx* ctx);
     414             : 
     415             :   size_t calcLongestPatternSize(const std::list<uint32_t>& indexes) const;
     416             :   void resizeOverlapBuffer(HttpGenericBodyMatcherCtx* ctx);
     417             : 
     418             : private:
     419             :   // The following fields are initialized based on matcher config and are used
     420             :   // by all HTTP matchers.
     421             :   // List of strings which body must contain to get match.
     422             :   std::shared_ptr<std::vector<std::string>> patterns_;
     423             :   // Stores the length of the longest pattern.
     424             :   size_t overlap_size_{};
     425             : };
     426             : 
     427             : class HttpRequestGenericBodyMatcher : public HttpGenericBodyMatcher {
     428             : public:
     429             :   using HttpGenericBodyMatcher::HttpGenericBodyMatcher;
     430             : 
     431           0 :   void onRequestBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
     432           0 :     onBody(data, statuses);
     433           0 :   }
     434             : };
     435             : 
     436             : class HttpResponseGenericBodyMatcher : public HttpGenericBodyMatcher {
     437             : public:
     438             :   using HttpGenericBodyMatcher::HttpGenericBodyMatcher;
     439             : 
     440           0 :   void onResponseBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
     441           0 :     onBody(data, statuses);
     442           0 :   }
     443             : };
     444             : 
     445             : } // namespace Matcher
     446             : } // namespace Common
     447             : } // namespace Extensions
     448             : } // namespace Envoy

Generated by: LCOV version 1.15