Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/common/http/header_utility.h
Line
Count
Source (jump to first uncovered line)
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/http/status.h"
15
#include "source/common/protobuf/protobuf.h"
16
17
namespace Envoy {
18
namespace Http {
19
20
/**
21
 * Classes and methods for manipulating and checking HTTP headers.
22
 */
23
class HeaderUtility {
24
public:
25
  enum class HeaderMatchType {
26
    Value,
27
    Regex,
28
    Range,
29
    Present,
30
    Prefix,
31
    Suffix,
32
    Contains,
33
    StringMatch
34
  };
35
36
  /**
37
   * Get all header values as a single string. Multiple headers are concatenated with ','.
38
   */
39
  class GetAllOfHeaderAsStringResult {
40
  public:
41
    // The ultimate result of the concatenation. If absl::nullopt, no header values were found.
42
    // If the final string required a string allocation, the memory is held in
43
    // backingString(). This allows zero allocation in the common case of a single header
44
    // value.
45
404k
    absl::optional<absl::string_view> result() const {
46
      // This is safe for move/copy of this class as the backing string will be moved or copied.
47
      // Otherwise result_ is valid. The assert verifies that both are empty or only 1 is set.
48
404k
      ASSERT((!result_.has_value() && result_backing_string_.empty()) ||
49
404k
             (result_.has_value() ^ !result_backing_string_.empty()));
50
404k
      return !result_backing_string_.empty() ? result_backing_string_ : result_;
51
404k
    }
52
53
171k
    const std::string& backingString() const { return result_backing_string_; }
54
55
  private:
56
    absl::optional<absl::string_view> result_;
57
    // Valid only if result_ relies on memory allocation that must live beyond the call. See above.
58
    std::string result_backing_string_;
59
60
    friend class HeaderUtility;
61
  };
62
  static GetAllOfHeaderAsStringResult getAllOfHeaderAsString(const HeaderMap::GetResult& header,
63
                                                             absl::string_view separator = ",");
64
  static GetAllOfHeaderAsStringResult getAllOfHeaderAsString(const HeaderMap& headers,
65
                                                             const Http::LowerCaseString& key,
66
                                                             absl::string_view separator = ",");
67
68
  // A HeaderData specifies one of exact value or regex or range element
69
  // to match in a request's header, specified in the header_match_type_ member.
70
  // It is the runtime equivalent of the HeaderMatchSpecifier proto in RDS API.
71
  struct HeaderData : public HeaderMatcher {
72
    HeaderData(const envoy::config::route::v3::HeaderMatcher& config);
73
74
    const LowerCaseString name_;
75
    HeaderMatchType header_match_type_;
76
    std::string value_;
77
    Regex::CompiledMatcherPtr regex_;
78
    envoy::type::v3::Int64Range range_;
79
    Matchers::StringMatcherPtr string_match_;
80
    const bool invert_match_;
81
    const bool treat_missing_as_empty_;
82
    bool present_;
83
84
    // HeaderMatcher
85
0
    bool matchesHeaders(const HeaderMap& headers) const override {
86
0
      return HeaderUtility::matchHeaders(headers, *this);
87
0
    };
88
  };
89
90
  using HeaderDataPtr = std::unique_ptr<HeaderData>;
91
92
  /**
93
   * Build a vector of HeaderDataPtr given input config.
94
   */
95
  static std::vector<HeaderUtility::HeaderDataPtr> buildHeaderDataVector(
96
103k
      const Protobuf::RepeatedPtrField<envoy::config::route::v3::HeaderMatcher>& header_matchers) {
97
103k
    std::vector<HeaderUtility::HeaderDataPtr> ret;
98
103k
    for (const auto& header_matcher : header_matchers) {
99
19.3k
      ret.emplace_back(std::make_unique<HeaderUtility::HeaderData>(header_matcher));
100
19.3k
    }
101
103k
    return ret;
102
103k
  }
103
104
  /**
105
   * Build a vector of HeaderMatcherSharedPtr given input config.
106
   */
107
  static std::vector<Http::HeaderMatcherSharedPtr> buildHeaderMatcherVector(
108
60.6k
      const Protobuf::RepeatedPtrField<envoy::config::route::v3::HeaderMatcher>& header_matchers) {
109
60.6k
    std::vector<Http::HeaderMatcherSharedPtr> ret;
110
60.6k
    for (const auto& header_matcher : header_matchers) {
111
23.4k
      ret.emplace_back(std::make_shared<HeaderUtility::HeaderData>(header_matcher));
112
23.4k
    }
113
60.6k
    return ret;
114
60.6k
  }
115
116
  /**
117
   * See if the headers specified in the config are present in a request.
118
   * @param request_headers supplies the headers from the request.
119
   * @param config_headers supplies the list of configured header conditions on which to match.
120
   * @return bool true if all the headers (and values) in the config_headers are found in the
121
   *         request_headers. If no config_headers are specified, returns true.
122
   */
123
  static bool matchHeaders(const HeaderMap& request_headers,
124
                           const std::vector<HeaderDataPtr>& config_headers);
125
126
  static bool matchHeaders(const HeaderMap& request_headers, const HeaderData& config_header);
127
128
  /**
129
   * Validates that a header value is valid, according to RFC 7230, section 3.2.
130
   * http://tools.ietf.org/html/rfc7230#section-3.2
131
   * @return bool true if the header values are valid, according to the aforementioned RFC.
132
   */
133
  static bool headerValueIsValid(const absl::string_view header_value);
134
135
  /**
136
   * Validates that a header name is valid, according to RFC 7230, section 3.2.
137
   * http://tools.ietf.org/html/rfc7230#section-3.2
138
   * @return bool true if the header name is valid, according to the aforementioned RFC.
139
   */
140
  static bool headerNameIsValid(const absl::string_view header_key);
141
142
  /**
143
   * Checks if header name contains underscore characters.
144
   * Underscore character is allowed in header names by the RFC-7230 and this check is implemented
145
   * as a security measure due to systems that treat '_' and '-' as interchangeable. Envoy by
146
   * default allows headers with underscore characters.
147
   * @return bool true if header name contains underscore characters.
148
   */
149
  static bool headerNameContainsUnderscore(const absl::string_view header_name);
150
151
  /**
152
   * Validates that the characters in the authority are valid.
153
   * @return bool true if the header values are valid, false otherwise.
154
   */
155
  static bool authorityIsValid(const absl::string_view authority_value);
156
157
  /**
158
   * @brief return if the 1xx should be handled by the [encode|decode]1xx calls.
159
   */
160
  static bool isSpecial1xx(const ResponseHeaderMap& response_headers);
161
162
  /**
163
   * @brief a helper function to determine if the headers represent a CONNECT request.
164
   */
165
  static bool isConnect(const RequestHeaderMap& headers);
166
167
  /**
168
   * @brief a helper function to determine if the headers represent a CONNECT-UDP request.
169
   */
170
  static bool isConnectUdpRequest(const RequestHeaderMap& headers);
171
172
  /**
173
   * @brief a helper function to determine if the headers represent a CONNECT-UDP response.
174
   */
175
  static bool isConnectUdpResponse(const ResponseHeaderMap& headers);
176
177
  /**
178
   * @brief a helper function to determine if the headers represent an accepted CONNECT response.
179
   */
180
  static bool isConnectResponse(const RequestHeaderMap* request_headers,
181
                                const ResponseHeaderMap& response_headers);
182
183
  /**
184
   * @brief Rewrites the authority header field by parsing the path using the default CONNECT-UDP
185
   * URI template. Returns true if the parsing was successful, otherwise returns false.
186
   */
187
  static bool rewriteAuthorityForConnectUdp(RequestHeaderMap& headers);
188
189
#ifdef ENVOY_ENABLE_HTTP_DATAGRAMS
190
  /**
191
   * @brief Returns true if the Capsule-Protocol header field (RFC 9297) is set to true. If the
192
   * header field is included multiple times, returns false as per RFC 9297.
193
   */
194
  static bool isCapsuleProtocol(const RequestOrResponseHeaderMap& headers);
195
#endif
196
197
  static bool requestShouldHaveNoBody(const RequestHeaderMap& headers);
198
199
  /**
200
   * @brief a helper function to determine if the headers represent an envoy internal request
201
   */
202
  static bool isEnvoyInternalRequest(const RequestHeaderMap& headers);
203
204
  /**
205
   * Determines if request headers pass Envoy validity checks.
206
   * @param headers to validate
207
   * @return details of the error if an error is present, otherwise absl::nullopt
208
   */
209
  static absl::optional<std::reference_wrapper<const absl::string_view>>
210
  requestHeadersValid(const RequestHeaderMap& headers);
211
212
  /**
213
   * Determines if the response should be framed by Connection: Close based on protocol
214
   * and headers.
215
   * @param protocol the protocol of the request
216
   * @param headers the request or response headers
217
   * @return if the response should be framed by Connection: Close
218
   */
219
  static bool shouldCloseConnection(Http::Protocol protocol,
220
                                    const RequestOrResponseHeaderMap& headers);
221
222
  /**
223
   * @brief Remove the trailing host dot from host/authority header.
224
   */
225
  static void stripTrailingHostDot(RequestHeaderMap& headers);
226
227
  /**
228
   * @return bool true if the provided host has a port, false otherwise.
229
   */
230
  static bool hostHasPort(absl::string_view host);
231
232
  /**
233
   * @brief Remove the port part from host/authority header if it is equal to provided port.
234
   * @return absl::optional<uint32_t> containing the port, if removed, else absl::nullopt.
235
   * If port is not passed, port part from host/authority header is removed.
236
   */
237
  static absl::optional<uint32_t> stripPortFromHost(RequestHeaderMap& headers,
238
                                                    absl::optional<uint32_t> listener_port);
239
240
  /**
241
   * @brief Return the index of the port, or npos if the host has no port
242
   *
243
   * Note this does not do validity checks on the port, it just finds the
244
   * trailing : which is not a part of an IP address.
245
   */
246
  static absl::string_view::size_type getPortStart(absl::string_view host);
247
248
  /* Does a common header check ensuring required request headers are present.
249
   * Required request headers include :method header, :path for non-CONNECT requests, and
250
   * host/authority for HTTP/1.1 or CONNECT requests.
251
   * @return Status containing the result. If failed, message includes details on which header was
252
   * missing.
253
   */
254
  static Http::Status checkRequiredRequestHeaders(const Http::RequestHeaderMap& headers);
255
256
  /* Does a common header check ensuring required response headers are present.
257
   * Current required response headers only includes :status.
258
   * @return Status containing the result. If failed, message includes details on which header was
259
   * missing.
260
   */
261
  static Http::Status checkRequiredResponseHeaders(const Http::ResponseHeaderMap& headers);
262
263
  /* Does a common header check ensuring that header keys and values are valid and do not contain
264
   * forbidden characters (e.g. valid HTTP header keys/values should never contain embedded NULLs
265
   * or new lines.)
266
   * @return Status containing the result. If failed, message includes details on which header key
267
   * or value was invalid.
268
   */
269
  static Http::Status checkValidRequestHeaders(const Http::RequestHeaderMap& headers);
270
271
  /**
272
   * Returns true if a header may be safely removed without causing additional
273
   * problems. Effectively, header names beginning with ":" and the "host" header
274
   * may not be removed.
275
   */
276
  static bool isRemovableHeader(absl::string_view header);
277
278
  /**
279
   * Returns true if a header may be safely modified without causing additional
280
   * problems. Currently header names beginning with ":" and the "host" header
281
   * may not be modified.
282
   */
283
  static bool isModifiableHeader(absl::string_view header);
284
285
  enum class HeaderValidationResult {
286
    ACCEPT = 0,
287
    DROP,
288
    REJECT,
289
  };
290
291
  /**
292
   * Check if the given header_name has underscore.
293
   * Return HeaderValidationResult and populate the given counters based on
294
   * headers_with_underscores_action.
295
   */
296
  static HeaderValidationResult checkHeaderNameForUnderscores(
297
      absl::string_view header_name,
298
      envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
299
          headers_with_underscores_action,
300
      HeaderValidatorStats& stats);
301
302
  /**
303
   * Check if header_value represents a valid value for HTTP content-length header.
304
   * Return HeaderValidationResult and populate content_length_output if the value is valid,
305
   * otherwise populate should_close_connection according to
306
   * override_stream_error_on_invalid_http_message.
307
   */
308
  static HeaderValidationResult
309
  validateContentLength(absl::string_view header_value,
310
                        bool override_stream_error_on_invalid_http_message,
311
                        bool& should_close_connection, size_t& content_length_output);
312
313
  /**
314
   * Parse a comma-separated header string to the individual tokens. Discard empty tokens
315
   * and whitespace. Return a vector of the comma-separated tokens.
316
   */
317
  static std::vector<absl::string_view> parseCommaDelimitedHeader(absl::string_view header_value);
318
319
  /**
320
   * Return the part of attribute before first ';'-sign. For example,
321
   * "foo;bar=1" would return "foo".
322
   */
323
  static absl::string_view getSemicolonDelimitedAttribute(absl::string_view value);
324
325
  /**
326
   * Return a new AcceptEncoding header string vector.
327
   */
328
  static std::string addEncodingToAcceptEncoding(absl::string_view accept_encoding_header,
329
                                                 absl::string_view encoding);
330
331
  /**
332
   * Return `true` if the request is a standard HTTP CONNECT.
333
   * HTTP/1 RFC: https://datatracker.ietf.org/doc/html/rfc9110#section-9.3.6
334
   * HTTP/2 RFC: https://datatracker.ietf.org/doc/html/rfc9113#section-8.5
335
   */
336
  static bool isStandardConnectRequest(const Http::RequestHeaderMap& headers);
337
338
  /**
339
   * Return `true` if the request is an extended HTTP/2 CONNECT.
340
   * according to https://datatracker.ietf.org/doc/html/rfc8441#section-4
341
   */
342
  static bool isExtendedH2ConnectRequest(const Http::RequestHeaderMap& headers);
343
344
  /**
345
   * Return true if the given header name is a pseudo header.
346
   */
347
  static bool isPseudoHeader(absl::string_view header_name);
348
};
349
350
} // namespace Http
351
} // namespace Envoy