/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 |