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
179
  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
287
    bool operator==(const MatchStatus& rhs) const {
51
287
      return matches_ == rhs.matches_ && might_change_status_ == rhs.might_change_status_;
52
287
    }
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
271
      : my_index_(matchers.size() - 1) {}
70

            
71
271
  virtual ~Matcher() = default;
72

            
73
  /**
74
   * @return the matcher's index in the match tree vector (see above).
75
   */
76
255
  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 Envoy::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 Envoy::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 Envoy::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 Envoy::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
463
  const MatchStatus& matchStatus(const MatchStatusVector& statuses) const {
142
463
    return statuses[my_index_];
143
463
  }
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
26
  void onNewStream(MatchStatusVector& statuses) const override {
166
26
    updateLocalStatus(statuses,
167
50
                      [](Matcher& m, MatchStatusVector& statuses) { m.onNewStream(statuses); });
168
26
  }
169
  void onHttpRequestHeaders(const Envoy::Http::RequestHeaderMap& request_headers,
170
14
                            MatchStatusVector& statuses) const override {
171
26
    updateLocalStatus(statuses, [&request_headers](Matcher& m, MatchStatusVector& statuses) {
172
25
      m.onHttpRequestHeaders(request_headers, statuses);
173
25
    });
174
14
  }
175
  void onHttpRequestTrailers(const Envoy::Http::RequestTrailerMap& request_trailers,
176
5
                             MatchStatusVector& statuses) const override {
177
8
    updateLocalStatus(statuses, [&request_trailers](Matcher& m, MatchStatusVector& statuses) {
178
7
      m.onHttpRequestTrailers(request_trailers, statuses);
179
7
    });
180
5
  }
181
  void onHttpResponseHeaders(const Envoy::Http::ResponseHeaderMap& response_headers,
182
26
                             MatchStatusVector& statuses) const override {
183
50
    updateLocalStatus(statuses, [&response_headers](Matcher& m, MatchStatusVector& statuses) {
184
49
      m.onHttpResponseHeaders(response_headers, statuses);
185
49
    });
186
26
  }
187
  void onHttpResponseTrailers(const Envoy::Http::ResponseTrailerMap& response_trailers,
188
5
                              MatchStatusVector& statuses) const override {
189
8
    updateLocalStatus(statuses, [&response_trailers](Matcher& m, MatchStatusVector& statuses) {
190
6
      m.onHttpResponseTrailers(response_trailers, statuses);
191
6
    });
192
5
  }
193
3
  void onRequestBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
194
6
    updateLocalStatus(statuses, [&data](Matcher& m, MatchStatusVector& statuses) {
195
6
      m.onRequestBody(data, statuses);
196
6
    });
197
3
  }
198
4
  void onResponseBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
199
8
    updateLocalStatus(statuses, [&data](Matcher& m, MatchStatusVector& statuses) {
200
8
      m.onResponseBody(data, statuses);
201
8
    });
202
4
  }
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
60
  void onNewStream(MatchStatusVector&) const override {}
254
  void onHttpRequestHeaders(const Envoy::Http::RequestHeaderMap&,
255
50
                            MatchStatusVector&) const override {}
256
  void onHttpRequestTrailers(const Envoy::Http::RequestTrailerMap&,
257
10
                             MatchStatusVector&) const override {}
258
  void onHttpResponseHeaders(const Envoy::Http::ResponseHeaderMap&,
259
52
                             MatchStatusVector&) const override {}
260
  void onHttpResponseTrailers(const Envoy::Http::ResponseTrailerMap&,
261
9
                              MatchStatusVector&) const override {}
262
34
  void onRequestBody(const Buffer::Instance&, MatchStatusVector&) override {}
263
35
  void onResponseBody(const Buffer::Instance&, MatchStatusVector&) override {}
264
};
265

            
266
/**
267
 * Any matcher (always matches).
268
 */
269
class AnyMatcher : public SimpleMatcher {
270
public:
271
  using SimpleMatcher::SimpleMatcher;
272

            
273
39
  void onNewStream(MatchStatusVector& statuses) const override {
274
39
    statuses[my_index_].matches_ = true;
275
39
    statuses[my_index_].might_change_status_ = false;
276
39
  }
277
};
278

            
279
/**
280
 * Base class for the various HTTP header matchers.
281
 */
282
class HttpHeaderMatcherBase : public SimpleMatcher {
283
public:
284
  HttpHeaderMatcherBase(const envoy::config::common::matcher::v3::HttpHeadersMatch& config,
285
                        const std::vector<MatcherPtr>& matchers,
286
                        Server::Configuration::CommonFactoryContext& context);
287

            
288
protected:
289
  void matchHeaders(const Envoy::Http::HeaderMap& headers, MatchStatusVector& statuses) const;
290

            
291
  const std::vector<Envoy::Http::HeaderUtility::HeaderDataPtr> headers_to_match_;
292
};
293

            
294
/**
295
 * HTTP request headers matcher.
296
 */
297
class HttpRequestHeadersMatcher : public HttpHeaderMatcherBase {
298
public:
299
  using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
300

            
301
  void onHttpRequestHeaders(const Envoy::Http::RequestHeaderMap& request_headers,
302
10
                            MatchStatusVector& statuses) const override {
303
10
    matchHeaders(request_headers, statuses);
304
10
  }
305
};
306

            
307
/**
308
 * HTTP request trailers matcher.
309
 */
310
class HttpRequestTrailersMatcher : public HttpHeaderMatcherBase {
311
public:
312
  using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
313

            
314
  void onHttpRequestTrailers(const Envoy::Http::RequestTrailerMap& request_trailers,
315
3
                             MatchStatusVector& statuses) const override {
316
3
    matchHeaders(request_trailers, statuses);
317
3
  }
318
};
319

            
320
/**
321
 * HTTP response headers matcher.
322
 */
323
class HttpResponseHeadersMatcher : public HttpHeaderMatcherBase {
324
public:
325
  using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
326

            
327
  void onHttpResponseHeaders(const Envoy::Http::ResponseHeaderMap& response_headers,
328
44
                             MatchStatusVector& statuses) const override {
329
44
    matchHeaders(response_headers, statuses);
330
44
  }
331
};
332

            
333
/**
334
 * HTTP response trailers matcher.
335
 */
336
class HttpResponseTrailersMatcher : public HttpHeaderMatcherBase {
337
public:
338
  using HttpHeaderMatcherBase::HttpHeaderMatcherBase;
339

            
340
  void onHttpResponseTrailers(const Envoy::Http::ResponseTrailerMap& response_trailers,
341
3
                              MatchStatusVector& statuses) const override {
342
3
    matchHeaders(response_trailers, statuses);
343
3
  }
344
};
345

            
346
/**
347
 * Base class for body matchers.
348
 */
349
class HttpBodyMatcherBase : public SimpleMatcher {
350
public:
351
175
  HttpBodyMatcherBase(const std::vector<MatcherPtr>& matchers) : SimpleMatcher(matchers) {}
352

            
353
protected:
354
  // Limit search to specified number of bytes.
355
  // Value equal to zero means no limit.
356
  uint32_t limit_{};
357
};
358

            
359
/**
360
 * Context is used by HttpGenericBodyMatcher to:
361
 * - track how many bytes has been processed
362
 * - track patterns which have been found
363
 * - store last several seen bytes of the HTTP body (when pattern starts at the end of previous body
364
 *   chunk and continues at the beginning of the next body chunk)
365
 */
366
class HttpGenericBodyMatcherCtx : public MatcherCtx {
367
public:
368
  HttpGenericBodyMatcherCtx(const std::shared_ptr<std::vector<std::string>>& patterns,
369
                            size_t overlap_size)
370
179
      : patterns_(patterns) {
371
    // Initialize overlap_ buffer's capacity to fit the longest pattern - 1.
372
    // The length of the longest pattern is known and passed here as overlap_size.
373
179
    patterns_index_.resize(patterns_->size());
374
179
    std::iota(patterns_index_.begin(), patterns_index_.end(), 0);
375
179
    overlap_.reserve(overlap_size);
376
179
    capacity_ = overlap_size;
377
179
  }
378
179
  ~HttpGenericBodyMatcherCtx() override = default;
379

            
380
  // The context is initialized per each http request. The patterns_
381
  // shared pointer attaches to matcher's list of patterns, so patterns
382
  // can be referenced without copying data.
383
  const std::shared_ptr<const std::vector<std::string>> patterns_;
384
  // List stores indexes of patterns in patterns_ shared memory which
385
  // still need to be located in the body. When a pattern is found
386
  // its index is removed from the list.
387
  // When all patterns have been found, the list is empty.
388
  std::list<uint32_t> patterns_index_;
389
  // Buffer to store the last bytes from previous body chunk(s).
390
  // It will store only as many bytes as is the length of the longest
391
  // pattern to be found minus 1.
392
  // It is necessary to locate patterns which are spread across 2 or more
393
  // body chunks.
394
  std::vector<char> overlap_;
395
  // capacity_ tells how many bytes should be buffered. overlap_'s initial
396
  // capacity is set to the length of the longest pattern - 1. As patterns
397
  // are found, there is a possibility that not as many bytes are required to be buffered.
398
  // It must be tracked outside of vector, because vector::reserve does not
399
  // change capacity when new value is lower than current capacity.
400
  uint32_t capacity_{};
401
  // processed_bytes_ tracks how many bytes of HTTP body have been processed.
402
  uint32_t processed_bytes_{};
403
};
404

            
405
class HttpGenericBodyMatcher : public HttpBodyMatcherBase {
406
public:
407
  HttpGenericBodyMatcher(const envoy::config::common::matcher::v3::HttpGenericBodyMatch& config,
408
                         const std::vector<MatcherPtr>& matchers);
409

            
410
protected:
411
  void onBody(const Buffer::Instance&, MatchStatusVector&);
412
179
  void onNewStream(MatchStatusVector& statuses) const override {
413
    // Allocate a new context used for the new stream.
414
179
    statuses[my_index_].ctx_ =
415
179
        std::make_unique<HttpGenericBodyMatcherCtx>(patterns_, overlap_size_);
416
179
    statuses[my_index_].matches_ = false;
417
179
    statuses[my_index_].might_change_status_ = true;
418
179
  }
419
  bool locatePatternAcrossChunks(const std::string& pattern, const Buffer::Instance& data,
420
                                 const HttpGenericBodyMatcherCtx* ctx);
421
  void bufferLastBytes(const Buffer::Instance& data, HttpGenericBodyMatcherCtx* ctx);
422

            
423
  size_t calcLongestPatternSize(const std::list<uint32_t>& indexes) const;
424
  void resizeOverlapBuffer(HttpGenericBodyMatcherCtx* ctx);
425

            
426
private:
427
  // The following fields are initialized based on matcher config and are used
428
  // by all HTTP matchers.
429
  // List of strings which body must contain to get match.
430
  std::shared_ptr<std::vector<std::string>> patterns_;
431
  // Stores the length of the longest pattern.
432
  size_t overlap_size_{};
433
};
434

            
435
class HttpRequestGenericBodyMatcher : public HttpGenericBodyMatcher {
436
public:
437
  using HttpGenericBodyMatcher::HttpGenericBodyMatcher;
438

            
439
300
  void onRequestBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
440
300
    onBody(data, statuses);
441
300
  }
442
};
443

            
444
class HttpResponseGenericBodyMatcher : public HttpGenericBodyMatcher {
445
public:
446
  using HttpGenericBodyMatcher::HttpGenericBodyMatcher;
447

            
448
99
  void onResponseBody(const Buffer::Instance& data, MatchStatusVector& statuses) override {
449
99
    onBody(data, statuses);
450
99
  }
451
};
452

            
453
} // namespace Matcher
454
} // namespace Common
455
} // namespace Extensions
456
} // namespace Envoy