Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/extensions/common/matcher/matcher.h
Line
Count
Source (jump to first uncovered line)
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
310
  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
4.54k
      : my_index_(matchers.size() - 1) {}
70
71
4.54k
  virtual ~Matcher() = default;
72
73
  /**
74
   * @return the matcher's index in the match tree vector (see above).
75
   */
76
4.54k
  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
312
  const MatchStatus& matchStatus(const MatchStatusVector& statuses) const {
142
312
    return statuses[my_index_];
143
312
  }
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
                  Server::Configuration::CommonFactoryContext& context);
157
158
/**
159
 * Base class for logic matchers that need to forward update calls to child matchers.
160
 */
161
class LogicMatcherBase : public Matcher {
162
public:
163
  using Matcher::Matcher;
164
165
1.83k
  void onNewStream(MatchStatusVector& statuses) const override {
166
1.83k
    updateLocalStatus(statuses,
167
4.46k
                      [](Matcher& m, MatchStatusVector& statuses) { m.onNewStream(statuses); });
168
1.83k
  }
169
  void onHttpRequestHeaders(const Http::RequestHeaderMap& request_headers,
170
1.69k
                            MatchStatusVector& statuses) const override {
171
4.12k
    updateLocalStatus(statuses, [&request_headers](Matcher& m, MatchStatusVector& statuses) {
172
4.12k
      m.onHttpRequestHeaders(request_headers, statuses);
173
4.12k
    });
174
1.69k
  }
175
  void onHttpRequestTrailers(const Http::RequestTrailerMap& request_trailers,
176
33
                             MatchStatusVector& statuses) const override {
177
111
    updateLocalStatus(statuses, [&request_trailers](Matcher& m, MatchStatusVector& statuses) {
178
111
      m.onHttpRequestTrailers(request_trailers, statuses);
179
111
    });
180
33
  }
181
  void onHttpResponseHeaders(const Http::ResponseHeaderMap& response_headers,
182
1.55k
                             MatchStatusVector& statuses) const override {
183
3.76k
    updateLocalStatus(statuses, [&response_headers](Matcher& m, MatchStatusVector& statuses) {
184
3.76k
      m.onHttpResponseHeaders(response_headers, statuses);
185
3.76k
    });
186
1.55k
  }
187
  void onHttpResponseTrailers(const Http::ResponseTrailerMap& response_trailers,
188
303
                              MatchStatusVector& statuses) const override {
189
897
    updateLocalStatus(statuses, [&response_trailers](Matcher& m, MatchStatusVector& statuses) {
190
897
      m.onHttpResponseTrailers(response_trailers, statuses);
191
897
    });
192
303
  }
193
1.89k
  void onRequestBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
194
5.61k
    updateLocalStatus(statuses, [&data](Matcher& m, MatchStatusVector& statuses) {
195
5.61k
      m.onRequestBody(data, statuses);
196
5.61k
    });
197
1.89k
  }
198
3.20k
  void onResponseBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
199
9.14k
    updateLocalStatus(statuses, [&data](Matcher& m, MatchStatusVector& statuses) {
200
9.14k
      m.onResponseBody(data, statuses);
201
9.14k
    });
202
3.20k
  }
203
204
protected:
205
  using UpdateFunctor = std::function<void(Matcher&, MatchStatusVector&)>;
206
  virtual void updateLocalStatus(MatchStatusVector& statuses,
207
                                 const UpdateFunctor& functor) const PURE;
208
};
209
210
/**
211
 * Matcher for implementing set logic.
212
 */
213
class SetLogicMatcher : public LogicMatcherBase {
214
public:
215
  enum class Type { And, Or };
216
217
  SetLogicMatcher(const envoy::config::common::matcher::v3::MatchPredicate::MatchSet& configs,
218
                  std::vector<MatcherPtr>& matchers, Type type,
219
                  Server::Configuration::CommonFactoryContext& context);
220
221
private:
222
  void updateLocalStatus(MatchStatusVector& statuses, const UpdateFunctor& functor) const override;
223
224
  std::vector<MatcherPtr>& matchers_;
225
  std::vector<size_t> indexes_;
226
  const Type type_;
227
};
228
229
/**
230
 * Not matcher.
231
 */
232
class NotMatcher : public LogicMatcherBase {
233
public:
234
  NotMatcher(const envoy::config::common::matcher::v3::MatchPredicate& config,
235
             std::vector<MatcherPtr>& matchers,
236
             Server::Configuration::CommonFactoryContext& context);
237
238
private:
239
  void updateLocalStatus(MatchStatusVector& statuses, const UpdateFunctor& functor) const override;
240
241
  std::vector<MatcherPtr>& matchers_;
242
  const size_t not_index_;
243
};
244
245
/**
246
 * A base class for a matcher that generally wants to return default values, but might override
247
 * a single update function.
248
 */
249
class SimpleMatcher : public Matcher {
250
public:
251
  using Matcher::Matcher;
252
253
1.77k
  void onNewStream(MatchStatusVector&) const override {}
254
2.29k
  void onHttpRequestHeaders(const Http::RequestHeaderMap&, MatchStatusVector&) const override {}
255
78
  void onHttpRequestTrailers(const Http::RequestTrailerMap&, MatchStatusVector&) const override {}
256
1.60k
  void onHttpResponseHeaders(const Http::ResponseHeaderMap&, MatchStatusVector&) const override {}
257
138
  void onHttpResponseTrailers(const Http::ResponseTrailerMap&, MatchStatusVector&) const override {}
258
5.04k
  void onRequestBody(const Buffer::Instance&, MatchStatusVector&) override {}
259
5.99k
  void onResponseBody(const Buffer::Instance&, MatchStatusVector&) override {}
260
};
261
262
/**
263
 * Any matcher (always matches).
264
 */
265
class AnyMatcher : public SimpleMatcher {
266
public:
267
  using SimpleMatcher::SimpleMatcher;
268
269
628
  void onNewStream(MatchStatusVector& statuses) const override {
270
628
    statuses[my_index_].matches_ = true;
271
628
    statuses[my_index_].might_change_status_ = false;
272
628
  }
273
};
274
275
/**
276
 * Base class for the various HTTP header matchers.
277
 */
278
class HttpHeaderMatcherBase : public SimpleMatcher {
279
public:
280
  HttpHeaderMatcherBase(const envoy::config::common::matcher::v3::HttpHeadersMatch& config,
281
                        const std::vector<MatcherPtr>& matchers,
282
                        Server::Configuration::CommonFactoryContext& context);
283
284
protected:
285
  void matchHeaders(const Http::HeaderMap& headers, MatchStatusVector& statuses) const;
286
287
  const std::vector<Http::HeaderUtility::HeaderDataPtr> headers_to_match_;
288
};
289
290
/**
291
 * HTTP request headers matcher.
292
 */
293
class HttpRequestHeadersMatcher : public HttpHeaderMatcherBase {
294
public:
295
  using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
296
297
  void onHttpRequestHeaders(const Http::RequestHeaderMap& request_headers,
298
212
                            MatchStatusVector& statuses) const override {
299
212
    matchHeaders(request_headers, statuses);
300
212
  }
301
};
302
303
/**
304
 * HTTP request trailers matcher.
305
 */
306
class HttpRequestTrailersMatcher : public HttpHeaderMatcherBase {
307
public:
308
  using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
309
310
  void onHttpRequestTrailers(const Http::RequestTrailerMap& request_trailers,
311
10
                             MatchStatusVector& statuses) const override {
312
10
    matchHeaders(request_trailers, statuses);
313
10
  }
314
};
315
316
/**
317
 * HTTP response headers matcher.
318
 */
319
class HttpResponseHeadersMatcher : public HttpHeaderMatcherBase {
320
public:
321
  using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
322
323
  void onHttpResponseHeaders(const Http::ResponseHeaderMap& response_headers,
324
683
                             MatchStatusVector& statuses) const override {
325
683
    matchHeaders(response_headers, statuses);
326
683
  }
327
};
328
329
/**
330
 * HTTP response trailers matcher.
331
 */
332
class HttpResponseTrailersMatcher : public HttpHeaderMatcherBase {
333
public:
334
  using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
335
336
  void onHttpResponseTrailers(const Http::ResponseTrailerMap& response_trailers,
337
491
                              MatchStatusVector& statuses) const override {
338
491
    matchHeaders(response_trailers, statuses);
339
491
  }
340
};
341
342
/**
343
 * Base class for body matchers.
344
 */
345
class HttpBodyMatcherBase : public SimpleMatcher {
346
public:
347
310
  HttpBodyMatcherBase(const std::vector<MatcherPtr>& matchers) : SimpleMatcher(matchers) {}
348
349
protected:
350
  // Limit search to specified number of bytes.
351
  // Value equal to zero means no limit.
352
  uint32_t limit_{};
353
};
354
355
/**
356
 * Context is used by HttpGenericBodyMatcher to:
357
 * - track how many bytes has been processed
358
 * - track patterns which have been found
359
 * - store last several seen bytes of the HTTP body (when pattern starts at the end of previous body
360
 *   chunk and continues at the beginning of the next body chunk)
361
 */
362
class HttpGenericBodyMatcherCtx : public MatcherCtx {
363
public:
364
  HttpGenericBodyMatcherCtx(const std::shared_ptr<std::vector<std::string>>& patterns,
365
                            size_t overlap_size)
366
310
      : patterns_(patterns) {
367
    // Initialize overlap_ buffer's capacity to fit the longest pattern - 1.
368
    // The length of the longest pattern is known and passed here as overlap_size.
369
310
    patterns_index_.resize(patterns_->size());
370
310
    std::iota(patterns_index_.begin(), patterns_index_.end(), 0);
371
310
    overlap_.reserve(overlap_size);
372
310
    capacity_ = overlap_size;
373
310
  }
374
310
  ~HttpGenericBodyMatcherCtx() override = default;
375
376
  // The context is initialized per each http request. The patterns_
377
  // shared pointer attaches to matcher's list of patterns, so patterns
378
  // can be referenced without copying data.
379
  const std::shared_ptr<const std::vector<std::string>> patterns_;
380
  // List stores indexes of patterns in patterns_ shared memory which
381
  // still need to be located in the body. When a pattern is found
382
  // its index is removed from the list.
383
  // When all patterns have been found, the list is empty.
384
  std::list<uint32_t> patterns_index_;
385
  // Buffer to store the last bytes from previous body chunk(s).
386
  // It will store only as many bytes as is the length of the longest
387
  // pattern to be found minus 1.
388
  // It is necessary to locate patterns which are spread across 2 or more
389
  // body chunks.
390
  std::vector<char> overlap_;
391
  // capacity_ tells how many bytes should be buffered. overlap_'s initial
392
  // capacity is set to the length of the longest pattern - 1. As patterns
393
  // are found, there is a possibility that not as many bytes are required to be buffered.
394
  // It must be tracked outside of vector, because vector::reserve does not
395
  // change capacity when new value is lower than current capacity.
396
  uint32_t capacity_{};
397
  // processed_bytes_ tracks how many bytes of HTTP body have been processed.
398
  uint32_t processed_bytes_{};
399
};
400
401
class HttpGenericBodyMatcher : public HttpBodyMatcherBase {
402
public:
403
  HttpGenericBodyMatcher(const envoy::config::common::matcher::v3::HttpGenericBodyMatch& config,
404
                         const std::vector<MatcherPtr>& matchers);
405
406
protected:
407
  void onBody(const Buffer::Instance&, MatchStatusVector&);
408
310
  void onNewStream(MatchStatusVector& statuses) const override {
409
    // Allocate a new context used for the new stream.
410
310
    statuses[my_index_].ctx_ =
411
310
        std::make_unique<HttpGenericBodyMatcherCtx>(patterns_, overlap_size_);
412
310
    statuses[my_index_].matches_ = false;
413
310
    statuses[my_index_].might_change_status_ = true;
414
310
  }
415
  bool locatePatternAcrossChunks(const std::string& pattern, const Buffer::Instance& data,
416
                                 const HttpGenericBodyMatcherCtx* ctx);
417
  void bufferLastBytes(const Buffer::Instance& data, HttpGenericBodyMatcherCtx* ctx);
418
419
  size_t calcLongestPatternSize(const std::list<uint32_t>& indexes) const;
420
  void resizeOverlapBuffer(HttpGenericBodyMatcherCtx* ctx);
421
422
private:
423
  // The following fields are initialized based on matcher config and are used
424
  // by all HTTP matchers.
425
  // List of strings which body must contain to get match.
426
  std::shared_ptr<std::vector<std::string>> patterns_;
427
  // Stores the length of the longest pattern.
428
  size_t overlap_size_{};
429
};
430
431
class HttpRequestGenericBodyMatcher : public HttpGenericBodyMatcher {
432
public:
433
  using HttpGenericBodyMatcher::HttpGenericBodyMatcher;
434
435
180
  void onRequestBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
436
180
    onBody(data, statuses);
437
180
  }
438
};
439
440
class HttpResponseGenericBodyMatcher : public HttpGenericBodyMatcher {
441
public:
442
  using HttpGenericBodyMatcher::HttpGenericBodyMatcher;
443
444
1.90k
  void onResponseBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
445
1.90k
    onBody(data, statuses);
446
1.90k
  }
447
};
448
449
} // namespace Matcher
450
} // namespace Common
451
} // namespace Extensions
452
} // namespace Envoy