1
#pragma once
2

            
3
#include <vector>
4

            
5
#include "envoy/common/matchers.h"
6
#include "envoy/common/regex.h"
7
#include "envoy/config/core/v3/protocol.pb.h"
8
#include "envoy/config/route/v3/route_components.pb.h"
9
#include "envoy/http/header_map.h"
10
#include "envoy/http/header_validator.h"
11
#include "envoy/http/protocol.h"
12
#include "envoy/type/v3/range.pb.h"
13

            
14
#include "source/common/common/matchers.h"
15
#include "source/common/common/regex.h"
16
#include "source/common/http/status.h"
17
#include "source/common/protobuf/protobuf.h"
18

            
19
namespace Envoy {
20
namespace Http {
21

            
22
/**
23
 * Classes and methods for manipulating and checking HTTP headers.
24
 */
25
class HeaderUtility {
26
public:
27
  enum class HeaderMatchType {
28
    Value,
29
    Regex,
30
    Range,
31
    Present,
32
    Prefix,
33
    Suffix,
34
    Contains,
35
    StringMatch
36
  };
37

            
38
  /**
39
   * Get all header values as a single string. Multiple headers are concatenated with ','.
40
   */
41
  class GetAllOfHeaderAsStringResult {
42
  public:
43
    // The ultimate result of the concatenation. If absl::nullopt, no header values were found.
44
    // If the final string required a string allocation, the memory is held in
45
    // backingString(). This allows zero allocation in the common case of a single header
46
    // value.
47
22446
    absl::optional<absl::string_view> result() const {
48
      // This is safe for move/copy of this class as the backing string will be moved or copied.
49
      // Otherwise result_ is valid. The assert verifies that both are empty or only 1 is set.
50
22446
      ASSERT((!result_.has_value() && result_backing_string_.empty()) ||
51
22446
             (result_.has_value() ^ !result_backing_string_.empty()));
52
22446
      return !result_backing_string_.empty() ? result_backing_string_ : result_;
53
22446
    }
54

            
55
134
    const std::string& backingString() const { return result_backing_string_; }
56

            
57
  private:
58
    absl::optional<absl::string_view> result_;
59
    // Valid only if result_ relies on memory allocation that must live beyond the call. See above.
60
    std::string result_backing_string_;
61

            
62
    friend class HeaderUtility;
63
  };
64
  static GetAllOfHeaderAsStringResult getAllOfHeaderAsString(const HeaderMap::GetResult& header,
65
                                                             absl::string_view separator = ",");
66
  static GetAllOfHeaderAsStringResult getAllOfHeaderAsString(const HeaderMap& headers,
67
                                                             const Http::LowerCaseString& key,
68
                                                             absl::string_view separator = ",");
69

            
70
  // Corresponds to the present_match from the HeaderMatchSpecifier proto in the RDS API.
71
  class HeaderDataPresentMatch : public HeaderMatcher {
72
  public:
73
    HeaderDataPresentMatch(const envoy::config::route::v3::HeaderMatcher& config)
74
31
        : HeaderDataPresentMatch(config, config.present_match()) {}
75

            
76
    // Creates a new present-matcher with an explicit setting of the present field.
77
    HeaderDataPresentMatch(const envoy::config::route::v3::HeaderMatcher& config,
78
                           bool explicit_present)
79
67
        : name_(config.name()), invert_match_(config.invert_match()),
80
67
          treat_missing_as_empty_(config.treat_missing_header_as_empty()),
81
67
          present_(explicit_present) {}
82

            
83
155
    bool matchesHeaders(const HeaderMap& request_headers) const override {
84
155
      const auto header_value = getAllOfHeaderAsString(request_headers, name_);
85

            
86
      // If treat_missing_as_empty is false, and the header_value is empty,
87
      // return true iff invert_match is the same as present_.
88
155
      if (!header_value.result().has_value() && !treat_missing_as_empty_) {
89
75
        return invert_match_ == present_;
90
75
      }
91

            
92
80
      return present_ != invert_match_;
93
155
    };
94

            
95
6
    bool matchesHeadersIndividually(const HeaderMap& request_headers) const override {
96
6
      return matchesHeaders(request_headers);
97
6
    };
98

            
99
  private:
100
    const LowerCaseString name_;
101
    const bool invert_match_;
102
    const bool treat_missing_as_empty_;
103
    const bool present_;
104
  };
105

            
106
  // A shared behavior for HeaderDataMatcher types (exact, regex, etc.) that
107
  // correspond to the HeaderMatchSpecifier proto in RDS API.
108
  class HeaderDataBaseImpl : public HeaderMatcher {
109
  public:
110
    HeaderDataBaseImpl(const envoy::config::route::v3::HeaderMatcher& config)
111
1886
        : name_(config.name()), invert_match_(config.invert_match()),
112
1886
          treat_missing_as_empty_(config.treat_missing_header_as_empty()) {}
113

            
114
    // HeaderMatcher
115
5017
    bool matchesHeaders(const HeaderMap& request_headers) const override {
116
5017
      const auto header_value = getAllOfHeaderAsString(request_headers, name_);
117
      // If treat_missing_as_empty_ is false and there is no header value, most
118
      // matchers (other than HeaderDataPresentMatch) will just return false here.
119
5017
      if (!treat_missing_as_empty_ && !header_value.result().has_value()) {
120
959
        return false;
121
959
      }
122

            
123
      // If the header does not have value and the result is not returned in the
124
      // code above, it means treat_missing_as_empty_ is set to true and we should
125
      // treat the header value as empty.
126
4058
      absl::string_view value =
127
4058
          header_value.result().has_value() ? header_value.result().value() : EMPTY_STRING;
128
      // Execute the specific matcher's code and invert if invert_match_ is set.
129
4058
      return specificMatchesHeaders(value) != invert_match_;
130
5017
    };
131

            
132
    // Matches each header value individually.
133
68
    bool matchesHeadersIndividually(const HeaderMap& request_headers) const override {
134
68
      const auto header_values = request_headers.get(name_);
135

            
136
68
      if (header_values.empty()) {
137
6
        if (!treat_missing_as_empty_) {
138
1
          return false;
139
1
        }
140
        // treat_missing_as_empty_ is true, match against empty string
141
5
        return specificMatchesHeaders(EMPTY_STRING) != invert_match_;
142
6
      }
143

            
144
      // Validate each header value individually
145
94
      for (size_t i = 0; i < header_values.size(); ++i) {
146
65
        absl::string_view value = header_values[i]->value().getStringView();
147
65
        bool matches = specificMatchesHeaders(value);
148
65
        if (!invert_match_ && matches) {
149
32
          return true;
150
32
        }
151
33
        if (invert_match_ && matches) {
152
1
          return false;
153
1
        }
154
33
      }
155

            
156
      // For normal match: no value matched, return false
157
      // For invert_match: no value matched the pattern, return true
158
29
      return invert_match_;
159
62
    }
160

            
161
  protected:
162
    // A matcher specific implementation to match the given header_value.
163
    virtual bool specificMatchesHeaders(absl::string_view header_value) const PURE;
164
    const LowerCaseString name_;
165
    const bool invert_match_;
166
    const bool treat_missing_as_empty_;
167
  };
168

            
169
  // Corresponds to the exact_match from the HeaderMatchSpecifier proto in the RDS API.
170
  class HeaderDataExactMatch : public HeaderDataBaseImpl {
171
  public:
172
    HeaderDataExactMatch(const envoy::config::route::v3::HeaderMatcher& config)
173
3
        : HeaderDataBaseImpl(config), expected_value_(config.exact_match()) {}
174

            
175
  private:
176
7
    bool specificMatchesHeaders(absl::string_view header_value) const override {
177
7
      return expected_value_.empty() || header_value == expected_value_;
178
7
    };
179
    const std::string expected_value_;
180
  };
181

            
182
  // Corresponds to the safe_regex_match from the HeaderMatchSpecifier proto in the RDS API.
183
  class HeaderDataRegexMatch : public HeaderDataBaseImpl {
184
  public:
185
    static absl::StatusOr<std::unique_ptr<HeaderDataRegexMatch>>
186
    create(const envoy::config::route::v3::HeaderMatcher& config,
187
3
           Server::Configuration::CommonFactoryContext& factory_context) {
188
3
      auto regex_or_error =
189
3
          Regex::Utility::parseRegex(config.safe_regex_match(), factory_context.regexEngine());
190
3
      RETURN_IF_NOT_OK_REF(regex_or_error.status());
191
3
      return std::unique_ptr<HeaderDataRegexMatch>(
192
3
          new HeaderDataRegexMatch(config, std::move(*regex_or_error)));
193
3
    }
194

            
195
  protected:
196
    HeaderDataRegexMatch(const envoy::config::route::v3::HeaderMatcher& config,
197
                         Regex::CompiledMatcherPtr&& regex)
198
3
        : HeaderDataBaseImpl(config), regex_(std::move(regex)) {}
199

            
200
  private:
201
7
    bool specificMatchesHeaders(absl::string_view header_value) const override {
202
7
      return regex_->match(header_value);
203
7
    };
204
    const Regex::CompiledMatcherPtr regex_;
205
  };
206

            
207
  // Corresponds to the range_match from the HeaderMatchSpecifier proto in the RDS API.
208
  class HeaderDataRangeMatch : public HeaderDataBaseImpl {
209
  public:
210
    HeaderDataRangeMatch(const envoy::config::route::v3::HeaderMatcher& config)
211
242
        : HeaderDataBaseImpl(config), range_start_(config.range_match().start()),
212
242
          range_end_(config.range_match().end()) {}
213

            
214
  private:
215
86
    bool specificMatchesHeaders(absl::string_view header_value) const override {
216
86
      int64_t header_int_value = 0;
217
86
      return absl::SimpleAtoi(header_value, &header_int_value) &&
218
86
             header_int_value >= range_start_ && header_int_value < range_end_;
219
86
    };
220

            
221
    const int64_t range_start_;
222
    const int64_t range_end_;
223
  };
224

            
225
  // Corresponds to the prefix_match from the HeaderMatchSpecifier proto in the RDS API.
226
  class HeaderDataPrefixMatch : public HeaderDataBaseImpl {
227
  public:
228
    HeaderDataPrefixMatch(const envoy::config::route::v3::HeaderMatcher& config)
229
4
        : HeaderDataBaseImpl(config), prefix_(config.prefix_match()) {}
230

            
231
  private:
232
10
    bool specificMatchesHeaders(absl::string_view header_value) const override {
233
10
      return absl::StartsWith(header_value, prefix_);
234
10
    };
235
    const std::string prefix_;
236
  };
237

            
238
  // Corresponds to the suffix_match from the HeaderMatchSpecifier proto in the RDS API.
239
  class HeaderDataSuffixMatch : public HeaderDataBaseImpl {
240
  public:
241
    HeaderDataSuffixMatch(const envoy::config::route::v3::HeaderMatcher& config)
242
4
        : HeaderDataBaseImpl(config), suffix_(config.suffix_match()) {}
243

            
244
  private:
245
10
    bool specificMatchesHeaders(absl::string_view header_value) const override {
246
10
      return absl::EndsWith(header_value, suffix_);
247
10
    };
248
    const std::string suffix_;
249
  };
250

            
251
  // Corresponds to the contains_match from the HeaderMatchSpecifier proto in the RDS API.
252
  class HeaderDataContainsMatch : public HeaderDataBaseImpl {
253
  public:
254
    HeaderDataContainsMatch(const envoy::config::route::v3::HeaderMatcher& config)
255
4
        : HeaderDataBaseImpl(config), expected_substr_(config.contains_match()) {}
256

            
257
  private:
258
10
    bool specificMatchesHeaders(absl::string_view header_value) const override {
259
10
      return absl::StrContains(header_value, expected_substr_);
260
10
    };
261
    const std::string expected_substr_;
262
  };
263

            
264
  // Corresponds to the string_match from the HeaderMatchSpecifier proto in the RDS API.
265
  class HeaderDataStringMatch : public HeaderDataBaseImpl {
266
  public:
267
    HeaderDataStringMatch(const envoy::config::route::v3::HeaderMatcher& config,
268
                          Server::Configuration::CommonFactoryContext& factory_context)
269
1626
        : HeaderDataBaseImpl(config), string_match_(std::make_unique<Matchers::StringMatcherImpl>(
270
1626
                                          config.string_match(), factory_context)) {}
271

            
272
  private:
273
3998
    bool specificMatchesHeaders(absl::string_view header_value) const override {
274
3998
      return string_match_->match(header_value);
275
3998
    };
276
    const Matchers::StringMatcherPtr string_match_;
277
  };
278

            
279
  using HeaderDataPtr = std::unique_ptr<HeaderMatcher>;
280

            
281
  /**
282
   * Creates a Header Data matcher data structure according to the given HeaderMatcher config.
283
   * HeaderMatcher will consist of:
284
   *   header_match_specifier which can be any one of exact_match, regex_match, range_match,
285
   *   present_match, prefix_match or suffix_match.
286
   *   Each of these also can be inverted with the invert_match option.
287
   *   Absence of these options implies empty header value match based on header presence.
288
   *   a.exact_match: value will be used for exact string matching.
289
   *   b.regex_match: Match will succeed if header value matches the value specified here.
290
   *   c.range_match: Match will succeed if header value lies within the range specified
291
   *     here, using half open interval semantics [start,end).
292
   *   d.present_match: Match will succeed if the header is present.
293
   *   f.prefix_match: Match will succeed if header value matches the prefix value specified here.
294
   *   g.suffix_match: Match will succeed if header value matches the suffix value specified here.
295
   */
296
  static HeaderDataPtr
297
  createHeaderData(const envoy::config::route::v3::HeaderMatcher& config,
298
                   Server::Configuration::CommonFactoryContext& factory_context);
299

            
300
  /**
301
   * Build a vector of HeaderDataPtr given input config.
302
   */
303
  static std::vector<HeaderUtility::HeaderDataPtr> buildHeaderDataVector(
304
      const Protobuf::RepeatedPtrField<envoy::config::route::v3::HeaderMatcher>& header_matchers,
305
14445
      Server::Configuration::CommonFactoryContext& context) {
306
14445
    std::vector<HeaderUtility::HeaderDataPtr> ret;
307
14445
    ret.reserve(header_matchers.size());
308
14666
    for (const auto& header_matcher : header_matchers) {
309
1383
      ret.emplace_back(HeaderUtility::createHeaderData(header_matcher, context));
310
1383
    }
311
14445
    return ret;
312
14445
  }
313

            
314
  /**
315
   * Build a vector of HeaderMatcherSharedPtr given input config.
316
   */
317
  static std::vector<Http::HeaderMatcherSharedPtr> buildHeaderMatcherVector(
318
      const Protobuf::RepeatedPtrField<envoy::config::route::v3::HeaderMatcher>& header_matchers,
319
462
      Server::Configuration::CommonFactoryContext& context) {
320
462
    std::vector<Http::HeaderMatcherSharedPtr> ret;
321
462
    ret.reserve(header_matchers.size());
322
466
    for (const auto& header_matcher : header_matchers) {
323
10
      ret.emplace_back(HeaderUtility::createHeaderData(header_matcher, context));
324
10
    }
325
462
    return ret;
326
462
  }
327

            
328
  /**
329
   * See if the headers specified in the config are present in a request.
330
   * @param request_headers supplies the headers from the request.
331
   * @param config_headers supplies the list of configured header conditions on which to match.
332
   * @return bool true if all the headers (and values) in the config_headers are found in the
333
   *         request_headers. If no config_headers are specified, returns true.
334
   */
335
  static bool matchHeaders(const HeaderMap& request_headers,
336
                           const std::vector<HeaderDataPtr>& config_headers);
337

            
338
  /**
339
   * Validates that a header value is valid, according to RFC 7230, section 3.2.
340
   * http://tools.ietf.org/html/rfc7230#section-3.2
341
   * @return bool true if the header values are valid, according to the aforementioned RFC.
342
   */
343
  static bool headerValueIsValid(const absl::string_view header_value);
344

            
345
  /**
346
   * Validates that a header name is valid, according to RFC 7230, section 3.2.
347
   * http://tools.ietf.org/html/rfc7230#section-3.2
348
   * @return bool true if the header name is valid, according to the aforementioned RFC.
349
   */
350
  static bool headerNameIsValid(absl::string_view header_key);
351

            
352
  /**
353
   * Checks if header name contains underscore characters.
354
   * Underscore character is allowed in header names by the RFC-7230 and this check is implemented
355
   * as a security measure due to systems that treat '_' and '-' as interchangeable. Envoy by
356
   * default allows headers with underscore characters.
357
   * @return bool true if header name contains underscore characters.
358
   */
359
  static bool headerNameContainsUnderscore(const absl::string_view header_name);
360

            
361
  /**
362
   * Validates that the characters in the authority are valid.
363
   * @return bool true if the header values are valid, false otherwise.
364
   */
365
  static bool authorityIsValid(const absl::string_view authority_value);
366

            
367
  /**
368
   * @brief return if the 1xx should be handled by the [encode|decode]1xx calls.
369
   */
370
  static bool isSpecial1xx(const ResponseHeaderMap& response_headers);
371

            
372
  /**
373
   * @brief a helper function to determine if the headers represent a CONNECT request.
374
   */
375
  static bool isConnect(const RequestHeaderMap& headers);
376

            
377
  /**
378
   * @brief a helper function to determine if the headers represent a CONNECT-UDP request.
379
   */
380
  static bool isConnectUdpRequest(const RequestHeaderMap& headers);
381

            
382
  /**
383
   * @brief a helper function to determine if the headers represent a CONNECT-UDP response.
384
   */
385
  static bool isConnectUdpResponse(const ResponseHeaderMap& headers);
386

            
387
  /**
388
   * @brief a helper function to determine if the headers represent an accepted CONNECT response.
389
   */
390
  static bool isConnectResponse(const RequestHeaderMap* request_headers,
391
                                const ResponseHeaderMap& response_headers);
392

            
393
  /**
394
   * @brief Rewrites the authority header field by parsing the path using the default CONNECT-UDP
395
   * URI template. Returns true if the parsing was successful, otherwise returns false.
396
   */
397
  static bool rewriteAuthorityForConnectUdp(RequestHeaderMap& headers);
398

            
399
#ifdef ENVOY_ENABLE_HTTP_DATAGRAMS
400
  /**
401
   * @brief Returns true if the Capsule-Protocol header field (RFC 9297) is set to true. If the
402
   * header field is included multiple times, returns false as per RFC 9297.
403
   */
404
  static bool isCapsuleProtocol(const RequestOrResponseHeaderMap& headers);
405
#endif
406

            
407
  static bool requestShouldHaveNoBody(const RequestHeaderMap& headers);
408

            
409
  /**
410
   * @brief a helper function to determine if the headers represent an envoy internal request
411
   */
412
  static bool isEnvoyInternalRequest(const RequestHeaderMap& headers);
413

            
414
  /**
415
   * Determines if request headers pass Envoy validity checks.
416
   * @param headers to validate
417
   * @return details of the error if an error is present, otherwise absl::nullopt
418
   */
419
  static absl::optional<std::reference_wrapper<const absl::string_view>>
420
  requestHeadersValid(const RequestHeaderMap& headers);
421

            
422
  /**
423
   * Determines if the response should be framed by Connection: Close based on protocol
424
   * and headers.
425
   * @param protocol the protocol of the request
426
   * @param headers the request or response headers
427
   * @return if the response should be framed by Connection: Close
428
   */
429
  static bool shouldCloseConnection(Http::Protocol protocol,
430
                                    const RequestOrResponseHeaderMap& headers);
431

            
432
  /**
433
   * @brief Remove the trailing host dot from host/authority header.
434
   */
435
  static void stripTrailingHostDot(RequestHeaderMap& headers);
436

            
437
  /**
438
   * @return bool true if the provided host has a port, false otherwise.
439
   */
440
  static bool hostHasPort(absl::string_view host);
441

            
442
  /**
443
   * @brief Remove the port part from host/authority header if it is equal to provided port.
444
   * @return absl::optional<uint32_t> containing the port, if removed, else absl::nullopt.
445
   * If port is not passed, port part from host/authority header is removed.
446
   */
447
  static absl::optional<uint32_t> stripPortFromHost(RequestHeaderMap& headers,
448
                                                    absl::optional<uint32_t> listener_port);
449

            
450
  /**
451
   * @brief Remove the port part from host if it exists.
452
   */
453
  static void stripPortFromHost(std::string& host);
454

            
455
  /**
456
   * @brief Return the index of the port, or npos if the host has no port
457
   *
458
   * Note this does not do validity checks on the port, it just finds the
459
   * trailing : which is not a part of an IP address.
460
   */
461
  static absl::string_view::size_type getPortStart(absl::string_view host);
462

            
463
  /* Does a common header check ensuring required request headers are present.
464
   * Required request headers include :method header, :path for non-CONNECT requests, and
465
   * host/authority for HTTP/1.1 or CONNECT requests.
466
   * @return Status containing the result. If failed, message includes details on which header was
467
   * missing.
468
   */
469
  static Http::Status checkRequiredRequestHeaders(const Http::RequestHeaderMap& headers);
470

            
471
  /* Does a common header check ensuring required response headers are present.
472
   * Current required response headers only includes :status.
473
   * @return Status containing the result. If failed, message includes details on which header was
474
   * missing.
475
   */
476
  static Http::Status checkRequiredResponseHeaders(const Http::ResponseHeaderMap& headers);
477

            
478
  /* Does a common header check ensuring that header keys and values are valid and do not contain
479
   * forbidden characters (e.g. valid HTTP header keys/values should never contain embedded NULLs
480
   * or new lines.)
481
   * @return Status containing the result. If failed, message includes details on which header key
482
   * or value was invalid.
483
   */
484
  static Http::Status checkValidRequestHeaders(const Http::RequestHeaderMap& headers);
485

            
486
  /**
487
   * Returns true if a header may be safely removed without causing additional
488
   * problems. Effectively, header names beginning with ":" and the "host" header
489
   * may not be removed.
490
   */
491
  static bool isRemovableHeader(absl::string_view header);
492

            
493
  /**
494
   * Returns true if a header may be safely modified without causing additional
495
   * problems. Currently header names beginning with ":" and the "host" header
496
   * may not be modified.
497
   */
498
  static bool isModifiableHeader(absl::string_view header);
499

            
500
  enum class HeaderValidationResult {
501
    ACCEPT = 0,
502
    DROP,
503
    REJECT,
504
  };
505

            
506
  /**
507
   * Check if the given header_name has underscore.
508
   * Return HeaderValidationResult and populate the given counters based on
509
   * headers_with_underscores_action.
510
   */
511
  static HeaderValidationResult checkHeaderNameForUnderscores(
512
      absl::string_view header_name,
513
      envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
514
          headers_with_underscores_action,
515
      HeaderValidatorStats& stats);
516

            
517
  /**
518
   * Check if header_value represents a valid value for HTTP content-length header.
519
   * Return HeaderValidationResult and populate content_length_output if the value is valid,
520
   * otherwise populate should_close_connection according to
521
   * override_stream_error_on_invalid_http_message.
522
   */
523
  static HeaderValidationResult
524
  validateContentLength(absl::string_view header_value,
525
                        bool override_stream_error_on_invalid_http_message,
526
                        bool& should_close_connection, size_t& content_length_output);
527

            
528
  /**
529
   * Parse a comma-separated header string to the individual tokens. Discard empty tokens
530
   * and whitespace. Return a vector of the comma-separated tokens.
531
   */
532
  static std::vector<absl::string_view> parseCommaDelimitedHeader(absl::string_view header_value);
533

            
534
  /**
535
   * Return the part of attribute before first ';'-sign. For example,
536
   * "foo;bar=1" would return "foo".
537
   */
538
  static absl::string_view getSemicolonDelimitedAttribute(absl::string_view value);
539

            
540
  /**
541
   * Return a new AcceptEncoding header string vector.
542
   */
543
  static std::string addEncodingToAcceptEncoding(absl::string_view accept_encoding_header,
544
                                                 absl::string_view encoding);
545

            
546
  /**
547
   * Return `true` if the request is a standard HTTP CONNECT.
548
   * HTTP/1 RFC: https://datatracker.ietf.org/doc/html/rfc9110#section-9.3.6
549
   * HTTP/2 RFC: https://datatracker.ietf.org/doc/html/rfc9113#section-8.5
550
   */
551
  static bool isStandardConnectRequest(const Http::RequestHeaderMap& headers);
552

            
553
  /**
554
   * Return `true` if the request is an extended HTTP/2 CONNECT.
555
   * according to https://datatracker.ietf.org/doc/html/rfc8441#section-4
556
   */
557
  static bool isExtendedH2ConnectRequest(const Http::RequestHeaderMap& headers);
558

            
559
  /**
560
   * Return true if the given header name is a pseudo header.
561
   */
562
  static bool isPseudoHeader(absl::string_view header_name);
563
};
564

            
565
} // namespace Http
566
} // namespace Envoy