1
#include "source/common/http/header_utility.h"
2

            
3
#include "envoy/config/route/v3/route_components.pb.h"
4

            
5
#include "source/common/common/json_escape_string.h"
6
#include "source/common/common/matchers.h"
7
#include "source/common/common/regex.h"
8
#include "source/common/common/utility.h"
9
#include "source/common/http/character_set_validation.h"
10
#include "source/common/http/header_map_impl.h"
11
#include "source/common/http/utility.h"
12
#include "source/common/protobuf/utility.h"
13
#include "source/common/runtime/runtime_features.h"
14

            
15
#include "absl/strings/match.h"
16

            
17
#ifdef ENVOY_NGHTTP2
18
#include "nghttp2/nghttp2.h"
19
#endif
20
#ifdef ENVOY_ENABLE_HTTP_DATAGRAMS
21
#include "quiche/common/structured_headers.h"
22
#endif
23
#include "quiche/http2/adapter/header_validator.h"
24

            
25
namespace Envoy {
26
namespace Http {
27

            
28
struct SharedResponseCodeDetailsValues {
29
  const absl::string_view InvalidAuthority = "http.invalid_authority";
30
  const absl::string_view ConnectUnsupported = "http.connect_not_supported";
31
  const absl::string_view InvalidMethod = "http.invalid_method";
32
  const absl::string_view InvalidPath = "http.invalid_path";
33
  const absl::string_view InvalidScheme = "http.invalid_scheme";
34
};
35

            
36
using SharedResponseCodeDetails = ConstSingleton<SharedResponseCodeDetailsValues>;
37

            
38
bool HeaderUtility::matchHeaders(const HeaderMap& request_headers,
39
93907
                                 const std::vector<HeaderDataPtr>& config_headers) {
40
  // No headers to match is considered a match.
41
93907
  if (!config_headers.empty()) {
42
4864
    for (const HeaderDataPtr& cfg_header_data : config_headers) {
43
4864
      if (!cfg_header_data->matchesHeaders(request_headers)) {
44
1880
        return false;
45
1880
      }
46
4864
    }
47
4712
  }
48

            
49
92027
  return true;
50
93907
}
51

            
52
HeaderUtility::GetAllOfHeaderAsStringResult
53
HeaderUtility::getAllOfHeaderAsString(const HeaderMap::GetResult& header_value,
54
361
                                      absl::string_view separator) {
55
361
  GetAllOfHeaderAsStringResult result;
56
  // In this case we concatenate all found headers using a delimiter before performing the
57
  // final match. We use an InlinedVector of absl::string_view to invoke the optimized join
58
  // algorithm. This requires a copying phase before we invoke join. The 3 used as the inline
59
  // size has been arbitrarily chosen.
60
  // TODO(mattklein123): Do we need to normalize any whitespace here?
61
361
  absl::InlinedVector<absl::string_view, 3> string_view_vector;
62
361
  string_view_vector.reserve(header_value.size());
63
1104
  for (size_t i = 0; i < header_value.size(); i++) {
64
743
    string_view_vector.push_back(header_value[i]->value().getStringView());
65
743
  }
66
361
  result.result_backing_string_ = absl::StrJoin(string_view_vector, separator);
67

            
68
361
  return result;
69
361
}
70

            
71
HeaderUtility::GetAllOfHeaderAsStringResult
72
HeaderUtility::getAllOfHeaderAsString(const HeaderMap& headers, const Http::LowerCaseString& key,
73
12610
                                      absl::string_view separator) {
74
12610
  GetAllOfHeaderAsStringResult result;
75
12610
  const auto header_value = headers.get(key);
76

            
77
12610
  if (header_value.empty()) {
78
    // Empty for clarity. Avoid handling the empty case in the block below if the runtime feature
79
    // is disabled.
80
12152
  } else if (header_value.size() == 1) {
81
11077
    result.result_ = header_value[0]->value().getStringView();
82
11094
  } else {
83
351
    return getAllOfHeaderAsString(header_value, separator);
84
351
  }
85

            
86
12259
  return result;
87
12610
}
88

            
89
HeaderUtility::HeaderDataPtr
90
HeaderUtility::createHeaderData(const envoy::config::route::v3::HeaderMatcher& config,
91
1953
                                Server::Configuration::CommonFactoryContext& factory_context) {
92
1953
  switch (config.header_match_specifier_case()) {
93
3
  case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase::kExactMatch:
94
3
    return std::make_unique<HeaderDataExactMatch>(config);
95
    break;
96
3
  case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase::kSafeRegexMatch:
97
3
    return THROW_OR_RETURN_VALUE(HeaderDataRegexMatch::create(config, factory_context),
98
                                 std::unique_ptr<HeaderDataRegexMatch>);
99
    break;
100
242
  case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase::kRangeMatch:
101
242
    return std::make_unique<HeaderDataRangeMatch>(config);
102
    break;
103
31
  case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase::kPresentMatch:
104
31
    return std::make_unique<HeaderDataPresentMatch>(config);
105
    break;
106
4
  case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase::kPrefixMatch:
107
4
    return std::make_unique<HeaderDataPrefixMatch>(config);
108
    break;
109
4
  case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase::kSuffixMatch:
110
4
    return std::make_unique<HeaderDataSuffixMatch>(config);
111
    break;
112
4
  case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase::kContainsMatch:
113
4
    return std::make_unique<HeaderDataContainsMatch>(config);
114
    break;
115
1626
  case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase::kStringMatch:
116
1626
    return std::make_unique<HeaderDataStringMatch>(config, factory_context);
117
    break;
118
36
  case envoy::config::route::v3::HeaderMatcher::HeaderMatchSpecifierCase::
119
36
      HEADER_MATCH_SPECIFIER_NOT_SET:
120
36
    FALLTHRU;
121
36
  default:
122
36
    return std::make_unique<HeaderDataPresentMatch>(config, true);
123
    break;
124
1953
  }
125
1953
}
126

            
127
1156293
bool HeaderUtility::headerValueIsValid(const absl::string_view header_value) {
128
1156293
  return http2::adapter::HeaderValidator::IsValidHeaderValue(header_value,
129
1156293
                                                             http2::adapter::ObsTextOption::kAllow);
130
1156293
}
131

            
132
1046569
bool HeaderUtility::headerNameIsValid(absl::string_view header_key) {
133
1046569
  if (!header_key.empty() && header_key[0] == ':') {
134
423419
    header_key.remove_prefix(1);
135
423419
    if (header_key.empty()) {
136
5
      return false;
137
5
    }
138
423419
  }
139
  // For all other header use HTTP/1 semantics. The only difference from HTTP/2 is that
140
  // uppercase characters are allowed. This allows HTTP filters to add header with mixed
141
  // case names. The HTTP/1 codec will send as is, as uppercase characters are allowed.
142
  // However the HTTP/2 codec will NOT convert these to lowercase when serializing the
143
  // header map, thus producing an invalid request.
144
  // TODO(yanavlasov): make validation in HTTP/2 case stricter.
145
1046564
  bool is_valid = true;
146
9755548
  for (auto iter = header_key.begin(); iter != header_key.end() && is_valid; ++iter) {
147
8708984
    is_valid &= testCharInTable(kGenericHeaderNameCharTable, *iter);
148
8708984
  }
149
1046564
  return is_valid;
150
1046569
}
151

            
152
211
bool HeaderUtility::headerNameContainsUnderscore(const absl::string_view header_name) {
153
211
  return header_name.find('_') != absl::string_view::npos;
154
211
}
155

            
156
130774
bool HeaderUtility::authorityIsValid(const absl::string_view header_value) {
157
  // This function validates the authority header for both HTTP/1 and HTTP/2.
158
  // Note the HTTP/1 spec allows "user-info@host:port" for the authority, whereas
159
  // the HTTP/2 spec only allows "host:port". Thus, this function permits all the
160
  // HTTP/2 valid characters (similar to oghttp2's implementation) and the "@" character.
161
  // Once UHV is used, this function should be removed, and the HTTP/1 and HTTP/2
162
  // authority validations should be different.
163
130774
  static constexpr char ValidAuthorityChars[] = {
164
130774
      0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,
165
130774
      0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,
166
130774
      0 /* BS   */, 0 /* HT   */, 0 /* LF   */, 0 /* VT   */,
167
130774
      0 /* FF   */, 0 /* CR   */, 0 /* SO   */, 0 /* SI   */,
168
130774
      0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
169
130774
      0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */,
170
130774
      0 /* CAN  */, 0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */,
171
130774
      0 /* FS   */, 0 /* GS   */, 0 /* RS   */, 0 /* US   */,
172
130774
      0 /* SPC  */, 1 /* !    */, 0 /* "    */, 0 /* #    */,
173
130774
      1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,
174
130774
      1 /* (    */, 1 /* )    */, 1 /* *    */, 1 /* +    */,
175
130774
      1 /* ,    */, 1 /* -    */, 1 /* . */,    0 /* /    */,
176
130774
      1 /* 0    */, 1 /* 1    */, 1 /* 2    */, 1 /* 3    */,
177
130774
      1 /* 4    */, 1 /* 5    */, 1 /* 6    */, 1 /* 7    */,
178
130774
      1 /* 8    */, 1 /* 9    */, 1 /* :    */, 1 /* ;    */,
179
130774
      0 /* <    */, 1 /* =    */, 0 /* >    */, 0 /* ?    */,
180
130774
      1 /* @    */, 1 /* A    */, 1 /* B    */, 1 /* C    */,
181
130774
      1 /* D    */, 1 /* E    */, 1 /* F    */, 1 /* G    */,
182
130774
      1 /* H    */, 1 /* I    */, 1 /* J    */, 1 /* K    */,
183
130774
      1 /* L    */, 1 /* M    */, 1 /* N    */, 1 /* O    */,
184
130774
      1 /* P    */, 1 /* Q    */, 1 /* R    */, 1 /* S    */,
185
130774
      1 /* T    */, 1 /* U    */, 1 /* V    */, 1 /* W    */,
186
130774
      1 /* X    */, 1 /* Y    */, 1 /* Z    */, 1 /* [    */,
187
130774
      0 /* \    */, 1 /* ]    */, 0 /* ^    */, 1 /* _    */,
188
130774
      0 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
189
130774
      1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */,
190
130774
      1 /* h    */, 1 /* i    */, 1 /* j    */, 1 /* k    */,
191
130774
      1 /* l    */, 1 /* m    */, 1 /* n    */, 1 /* o    */,
192
130774
      1 /* p    */, 1 /* q    */, 1 /* r    */, 1 /* s    */,
193
130774
      1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
194
130774
      1 /* x    */, 1 /* y    */, 1 /* z    */, 0 /* {    */,
195
130774
      0 /* |    */, 0 /* }    */, 1 /* ~    */, 0 /* DEL  */,
196
130774
      0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
197
130774
      0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
198
130774
      0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
199
130774
      0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
200
130774
      0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
201
130774
      0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
202
130774
      0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
203
130774
      0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
204
130774
      0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
205
130774
      0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
206
130774
      0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
207
130774
      0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
208
130774
      0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
209
130774
      0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
210
130774
      0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
211
130774
      0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
212
130774
      0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
213
130774
      0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
214
130774
      0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
215
130774
      0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
216
130774
      0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
217
130774
      0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
218
130774
      0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
219
130774
      0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
220
130774
      0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
221
130774
      0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
222
130774
      0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
223
130774
      0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
224
130774
      0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
225
130774
      0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
226
130774
      0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
227
130774
      0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
228
130774
  };
229

            
230
680526
  for (const uint8_t c : header_value) {
231
680526
    if (!ValidAuthorityChars[c]) {
232
6
      return false;
233
6
    }
234
680526
  }
235
130768
  return true;
236
130774
}
237

            
238
85739
bool HeaderUtility::isSpecial1xx(const ResponseHeaderMap& response_headers) {
239
85739
  return response_headers.Status()->value() == "100" ||
240
85739
         response_headers.Status()->value() == "102" ||
241
85739
         response_headers.Status()->value() == "103" || response_headers.Status()->value() == "104";
242
85739
}
243

            
244
374702
bool HeaderUtility::isConnect(const RequestHeaderMap& headers) {
245
374702
  return headers.Method() && headers.Method()->value() == Http::Headers::get().MethodValues.Connect;
246
374702
}
247

            
248
98206
bool HeaderUtility::isConnectUdpRequest(const RequestHeaderMap& headers) {
249
98206
  return headers.Upgrade() && absl::EqualsIgnoreCase(headers.getUpgradeValue(),
250
468
                                                     Http::Headers::get().UpgradeValues.ConnectUdp);
251
98206
}
252

            
253
56
bool HeaderUtility::isConnectUdpResponse(const ResponseHeaderMap& headers) {
254
  // In connect-udp case, Envoy will transform the H2 headers to H1 upgrade headers.
255
  // A valid response should have SwitchingProtocol status and connect-udp upgrade.
256
56
  return headers.Upgrade() && Utility::getResponseStatus(headers) == 101 &&
257
56
         absl::EqualsIgnoreCase(headers.getUpgradeValue(),
258
41
                                Http::Headers::get().UpgradeValues.ConnectUdp);
259
56
}
260

            
261
bool HeaderUtility::isConnectResponse(const RequestHeaderMap* request_headers,
262
47177
                                      const ResponseHeaderMap& response_headers) {
263
47177
  return request_headers && isConnect(*request_headers) &&
264
47177
         static_cast<Http::Code>(Http::Utility::getResponseStatus(response_headers)) ==
265
302
             Http::Code::OK;
266
47177
}
267

            
268
76
bool HeaderUtility::rewriteAuthorityForConnectUdp(RequestHeaderMap& headers) {
269
  // Per RFC 9298, the URI template must only contain ASCII characters in the range 0x21-0x7E.
270
76
  absl::string_view path = headers.getPathValue();
271
3001
  for (char c : path) {
272
3001
    unsigned char ascii_code = static_cast<unsigned char>(c);
273
3001
    if (ascii_code < 0x21 || ascii_code > 0x7e) {
274
      ENVOY_LOG_MISC(warn, "CONNECT-UDP request with a bad character in the path {}", path);
275
      return false;
276
    }
277
3001
  }
278

            
279
  // Extract target host and port from path using default template.
280
76
  if (!absl::StartsWith(path, "/.well-known/masque/udp/")) {
281
7
    ENVOY_LOG_MISC(warn, "CONNECT-UDP request path is not a well-known URI: {}", path);
282
7
    return false;
283
7
  }
284

            
285
69
  std::vector<absl::string_view> path_split = absl::StrSplit(path, '/');
286
69
  if (path_split.size() != 7 || path_split[4].empty() || path_split[5].empty() ||
287
69
      !path_split[6].empty()) {
288
11
    ENVOY_LOG_MISC(warn, "CONNECT-UDP request with a malformed URI template in the path {}", path);
289
11
    return false;
290
11
  }
291

            
292
  // Utility::PercentEncoding::decode never returns an empty string if the input argument is not
293
  // empty.
294
58
  std::string target_host = Utility::PercentEncoding::decode(path_split[4]);
295
  // Per RFC 9298, IPv6 Zone ID is not supported.
296
58
  if (target_host.find('%') != std::string::npos) {
297
5
    ENVOY_LOG_MISC(warn, "CONNECT-UDP request with a non-escpaed char (%) in the path {}", path);
298
5
    return false;
299
5
  }
300
53
  std::string target_port = Utility::PercentEncoding::decode(path_split[5]);
301

            
302
  // If the host is an IPv6 address, surround the address with square brackets.
303
53
  in6_addr sin6_addr;
304
53
  bool is_ipv6 = (inet_pton(AF_INET6, target_host.c_str(), &sin6_addr) == 1);
305
53
  std::string new_host =
306
53
      absl::StrCat((is_ipv6 ? absl::StrCat("[", target_host, "]") : target_host), ":", target_port);
307
53
  headers.setHost(new_host);
308

            
309
53
  return true;
310
58
}
311

            
312
#ifdef ENVOY_ENABLE_HTTP_DATAGRAMS
313
7351
bool HeaderUtility::isCapsuleProtocol(const RequestOrResponseHeaderMap& headers) {
314
7351
  Http::HeaderMap::GetResult capsule_protocol =
315
7351
      headers.get(Envoy::Http::LowerCaseString("Capsule-Protocol"));
316
  // When there are multiple Capsule-Protocol header entries, it returns false. RFC 9297 specifies
317
  // that non-boolean value types must be ignored. If there are multiple header entries, the value
318
  // type becomes a List so the header field must be ignored.
319
7351
  if (capsule_protocol.size() != 1) {
320
7304
    return false;
321
7304
  }
322
  // Parses the header value and extracts the boolean value ignoring parameters.
323
47
  absl::optional<quiche::structured_headers::ParameterizedItem> header_item =
324
47
      quiche::structured_headers::ParseItem(capsule_protocol[0]->value().getStringView());
325
47
  return header_item && header_item->item.is_boolean() && header_item->item.GetBoolean();
326
7351
}
327
#endif
328

            
329
41940
bool HeaderUtility::requestShouldHaveNoBody(const RequestHeaderMap& headers) {
330
41940
  return (headers.Method() &&
331
41940
          (headers.Method()->value() == Http::Headers::get().MethodValues.Get ||
332
41940
           headers.Method()->value() == Http::Headers::get().MethodValues.Head ||
333
41940
           headers.Method()->value() == Http::Headers::get().MethodValues.Delete ||
334
41940
           headers.Method()->value() == Http::Headers::get().MethodValues.Trace ||
335
41940
           headers.Method()->value() == Http::Headers::get().MethodValues.Connect));
336
41940
}
337

            
338
77221
bool HeaderUtility::isEnvoyInternalRequest(const RequestHeaderMap& headers) {
339
77221
  const HeaderEntry* internal_request_header = headers.EnvoyInternalRequest();
340
77221
  return internal_request_header != nullptr &&
341
77221
         internal_request_header->value() == Headers::get().EnvoyInternalRequestValues.True;
342
77221
}
343

            
344
34
void HeaderUtility::stripTrailingHostDot(RequestHeaderMap& headers) {
345
34
  auto host = headers.getHostValue();
346
  // If the host ends in a period, remove it.
347
34
  auto dot_index = host.rfind('.');
348
34
  if (dot_index == std::string::npos) {
349
8
    return;
350
27
  } else if (dot_index == (host.size() - 1)) {
351
19
    host.remove_suffix(1);
352
19
    headers.setHost(host);
353
19
    return;
354
19
  }
355
  // If the dot is just before a colon, it must be preceding the port number.
356
  // IPv6 addresses may contain colons or dots, but the dot will never directly
357
  // precede the colon, so this check should be sufficient to detect a trailing port number.
358
7
  if (host[dot_index + 1] == ':') {
359
3
    headers.setHost(absl::StrCat(host.substr(0, dot_index), host.substr(dot_index + 1)));
360
3
  }
361
7
}
362

            
363
355
bool HeaderUtility::hostHasPort(absl::string_view original_host) {
364
355
  const absl::string_view::size_type port_start = getPortStart(original_host);
365
355
  const absl::string_view port_str = original_host.substr(port_start + 1);
366
355
  if (port_start == absl::string_view::npos) {
367
165
    return false;
368
165
  }
369
190
  uint32_t port = 0;
370
190
  if (!absl::SimpleAtoi(port_str, &port)) {
371
4
    return false;
372
4
  }
373
186
  return true;
374
190
}
375

            
376
absl::optional<uint32_t> HeaderUtility::stripPortFromHost(RequestHeaderMap& headers,
377
38
                                                          absl::optional<uint32_t> listener_port) {
378
38
  const absl::string_view original_host = headers.getHostValue();
379
38
  const absl::string_view::size_type port_start = getPortStart(original_host);
380
38
  if (port_start == absl::string_view::npos) {
381
10
    return absl::nullopt;
382
10
  }
383
28
  const absl::string_view port_str = original_host.substr(port_start + 1);
384
28
  uint32_t port = 0;
385
28
  if (!absl::SimpleAtoi(port_str, &port)) {
386
3
    return absl::nullopt;
387
3
  }
388
25
  if (listener_port.has_value() && port != listener_port) {
389
    // We would strip ports only if it is specified and they are the same, as local port of the
390
    // listener.
391
3
    return absl::nullopt;
392
3
  }
393
22
  const absl::string_view host = original_host.substr(0, port_start);
394
22
  headers.setHost(host);
395
22
  return port;
396
25
}
397

            
398
24
void HeaderUtility::stripPortFromHost(std::string& host) {
399
24
  const absl::string_view::size_type port_start = getPortStart(host);
400
24
  if (port_start == absl::string_view::npos) {
401
8
    return;
402
8
  }
403
16
  host = host.substr(0, port_start);
404
16
}
405

            
406
44686
absl::string_view::size_type HeaderUtility::getPortStart(absl::string_view host) {
407
44686
  const absl::string_view::size_type port_start = host.rfind(':');
408
44686
  if (port_start == absl::string_view::npos) {
409
43860
    return absl::string_view::npos;
410
43860
  }
411
  // According to RFC3986 v6 address is always enclosed in "[]". section 3.2.2.
412
826
  const auto v6_end_index = host.rfind(']');
413
826
  if (v6_end_index == absl::string_view::npos || v6_end_index < port_start) {
414
821
    if ((port_start + 1) > host.size()) {
415
      return absl::string_view::npos;
416
    }
417
821
    return port_start;
418
821
  }
419
5
  return absl::string_view::npos;
420
826
}
421

            
422
800196
constexpr bool isInvalidToken(unsigned char c) {
423
800196
  if (c == '!' || c == '|' || c == '~' || c == '*' || c == '+' || c == '-' || c == '.' ||
424
      // #, $, %, &, '
425
800196
      (c >= '#' && c <= '\'') ||
426
      // [0-9]
427
800196
      (c >= '0' && c <= '9') ||
428
      // [A-Z]
429
800196
      (c >= 'A' && c <= 'Z') ||
430
      // ^, _, `, [a-z]
431
800196
      (c >= '^' && c <= 'z')) {
432
800196
    return false;
433
800196
  }
434
  return true;
435
800196
}
436

            
437
absl::optional<std::reference_wrapper<const absl::string_view>>
438
131880
HeaderUtility::requestHeadersValid(const RequestHeaderMap& headers) {
439
  // Make sure the host is valid.
440
131880
  if (headers.Host() && !HeaderUtility::authorityIsValid(headers.Host()->value().getStringView())) {
441
3
    return SharedResponseCodeDetails::get().InvalidAuthority;
442
3
  }
443
131877
  if (headers.Method()) {
444
131877
    absl::string_view method = headers.Method()->value().getStringView();
445
131877
    if (method.empty() || std::any_of(method.begin(), method.end(), isInvalidToken)) {
446
3
      return SharedResponseCodeDetails::get().InvalidMethod;
447
3
    }
448
131877
  }
449
131874
  if (headers.Scheme() && absl::StrContains(headers.Scheme()->value().getStringView(), ",")) {
450
3
    return SharedResponseCodeDetails::get().InvalidScheme;
451
3
  }
452
131871
  return absl::nullopt;
453
131874
}
454

            
455
bool HeaderUtility::shouldCloseConnection(Http::Protocol protocol,
456
117080
                                          const RequestOrResponseHeaderMap& headers) {
457
  // HTTP/1.0 defaults to single-use connections. Make sure the connection will be closed unless
458
  // Keep-Alive is present.
459
117080
  if (protocol == Protocol::Http10 &&
460
117080
      (!headers.Connection() ||
461
15
       !Envoy::StringUtil::caseFindToken(headers.Connection()->value().getStringView(), ",",
462
12
                                         Http::Headers::get().ConnectionValues.KeepAlive))) {
463
11
    return true;
464
11
  }
465

            
466
117069
  if (protocol == Protocol::Http11 && headers.Connection() &&
467
117069
      Envoy::StringUtil::caseFindToken(headers.Connection()->value().getStringView(), ",",
468
129
                                       Http::Headers::get().ConnectionValues.Close)) {
469
19
    return true;
470
19
  }
471

            
472
  // Note: Proxy-Connection is not a standard header, but is supported here
473
  // since it is supported by http-parser the underlying parser for http
474
  // requests.
475
117050
  if (protocol < Protocol::Http2 && headers.ProxyConnection() &&
476
117050
      Envoy::StringUtil::caseFindToken(headers.ProxyConnection()->value().getStringView(), ",",
477
4
                                       Http::Headers::get().ConnectionValues.Close)) {
478
4
    return true;
479
4
  }
480
117046
  return false;
481
117050
}
482

            
483
101655
Http::Status HeaderUtility::checkRequiredRequestHeaders(const Http::RequestHeaderMap& headers) {
484
101655
  if (!headers.Method()) {
485
16
    return absl::InvalidArgumentError(
486
16
        absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Method.get()));
487
16
  }
488
101639
  bool is_connect = Http::HeaderUtility::isConnect(headers);
489
101639
  if (is_connect) {
490
1738
    if (!headers.Host()) {
491
      // Host header must be present for CONNECT request.
492
12
      return absl::InvalidArgumentError(
493
12
          absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Host.get()));
494
12
    }
495
1726
    if (headers.Path() && !headers.Protocol()) {
496
      // Path and Protocol header should only be present for CONNECT for upgrade style CONNECT.
497
1
      return absl::InvalidArgumentError(
498
1
          absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Protocol.get()));
499
1
    }
500
1725
    if (!headers.Path() && headers.Protocol()) {
501
      // Path and Protocol header should only be present for CONNECT for upgrade style CONNECT.
502
1
      return absl::InvalidArgumentError(
503
1
          absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Path.get()));
504
1
    }
505
101416
  } else {
506
99901
    if (!headers.Path()) {
507
      // :path header must be present for non-CONNECT requests.
508
13
      return absl::InvalidArgumentError(
509
13
          absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Path.get()));
510
13
    }
511
99901
  }
512
101612
  return Http::okStatus();
513
101639
}
514

            
515
101612
Http::Status HeaderUtility::checkValidRequestHeaders(const Http::RequestHeaderMap& headers) {
516
101612
  if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.validate_upstream_headers")) {
517
8954
    return Http::okStatus();
518
8954
  }
519

            
520
92658
  const HeaderEntry* invalid_entry = nullptr;
521
92658
  bool invalid_key = false;
522
778413
  headers.iterate([&invalid_entry, &invalid_key](const HeaderEntry& header) -> HeaderMap::Iterate {
523
778413
    if (!HeaderUtility::headerNameIsValid(header.key().getStringView())) {
524
5
      invalid_entry = &header;
525
5
      invalid_key = true;
526
5
      return HeaderMap::Iterate::Break;
527
5
    }
528

            
529
778408
    if (!HeaderUtility::headerValueIsValid(header.value().getStringView())) {
530
9
      invalid_entry = &header;
531
9
      invalid_key = false;
532
9
      return HeaderMap::Iterate::Break;
533
9
    }
534

            
535
778399
    return HeaderMap::Iterate::Continue;
536
778408
  });
537

            
538
92658
  if (invalid_entry) {
539
    // The header key may contain non-printable characters. Escape the key so that the error
540
    // details can be safely presented.
541
14
    const absl::string_view key = invalid_entry->key().getStringView();
542
14
    uint64_t extra_length = JsonEscaper::extraSpace(key);
543
14
    const std::string escaped_key = JsonEscaper::escapeString(key, extra_length);
544

            
545
14
    return absl::InvalidArgumentError(
546
14
        absl::StrCat("invalid header ", invalid_key ? "name: " : "value for: ", escaped_key));
547
14
  }
548
92644
  return Http::okStatus();
549
92658
}
550

            
551
87213
Http::Status HeaderUtility::checkRequiredResponseHeaders(const Http::ResponseHeaderMap& headers) {
552
87213
  const absl::optional<uint64_t> status = Utility::getResponseStatusOrNullopt(headers);
553
87213
  if (!status.has_value()) {
554
37
    return absl::InvalidArgumentError(
555
37
        absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Status.get()));
556
37
  }
557
87176
  return Http::okStatus();
558
87213
}
559

            
560
559
bool HeaderUtility::isRemovableHeader(absl::string_view header) {
561
559
  return (header.empty() || header[0] != ':') &&
562
559
         !absl::EqualsIgnoreCase(header, Headers::get().HostLegacy.get());
563
559
}
564

            
565
3408
bool HeaderUtility::isModifiableHeader(absl::string_view header) {
566
3408
  return (header.empty() || header[0] != ':') &&
567
3408
         !absl::EqualsIgnoreCase(header, Headers::get().HostLegacy.get());
568
3408
}
569

            
570
HeaderUtility::HeaderValidationResult HeaderUtility::checkHeaderNameForUnderscores(
571
    absl::string_view header_name,
572
    envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
573
        headers_with_underscores_action,
574
177670
    HeaderValidatorStats& stats) {
575
177670
  if (headers_with_underscores_action == envoy::config::core::v3::HttpProtocolOptions::ALLOW ||
576
177670
      !HeaderUtility::headerNameContainsUnderscore(header_name)) {
577
177660
    return HeaderValidationResult::ACCEPT;
578
177660
  }
579
10
  if (headers_with_underscores_action ==
580
10
      envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER) {
581
7
    ENVOY_LOG_MISC(debug, "Dropping header with invalid characters in its name: {}", header_name);
582
7
    stats.incDroppedHeadersWithUnderscores();
583
7
    return HeaderValidationResult::DROP;
584
7
  }
585
3
  ENVOY_LOG_MISC(debug, "Rejecting request due to header name with underscores: {}", header_name);
586
3
  stats.incRequestsRejectedWithUnderscoresInHeaders();
587
3
  return HeaderUtility::HeaderValidationResult::REJECT;
588
10
}
589

            
590
HeaderUtility::HeaderValidationResult
591
HeaderUtility::validateContentLength(absl::string_view header_value,
592
                                     bool override_stream_error_on_invalid_http_message,
593
512
                                     bool& should_close_connection, size_t& content_length_output) {
594
512
  should_close_connection = false;
595
512
  std::vector<absl::string_view> values = absl::StrSplit(header_value, ',');
596
512
  absl::optional<uint64_t> content_length;
597
515
  for (const absl::string_view& value : values) {
598
515
    uint64_t new_value;
599
515
    if (!absl::SimpleAtoi(value, &new_value) ||
600
515
        !std::all_of(value.begin(), value.end(), absl::ascii_isdigit)) {
601
1
      ENVOY_LOG_MISC(debug, "Content length was either unparseable or negative");
602
1
      should_close_connection = !override_stream_error_on_invalid_http_message;
603
1
      return HeaderValidationResult::REJECT;
604
1
    }
605
514
    if (!content_length.has_value()) {
606
511
      content_length = new_value;
607
511
      continue;
608
511
    }
609
3
    if (new_value != content_length.value()) {
610
2
      ENVOY_LOG_MISC(
611
2
          debug,
612
2
          "Parsed content length {} is inconsistent with previously detected content length {}",
613
2
          new_value, content_length.value());
614
2
      should_close_connection = !override_stream_error_on_invalid_http_message;
615
2
      return HeaderValidationResult::REJECT;
616
2
    }
617
3
  }
618
509
  content_length_output = content_length.value();
619
509
  return HeaderValidationResult::ACCEPT;
620
512
}
621

            
622
std::vector<absl::string_view>
623
118
HeaderUtility::parseCommaDelimitedHeader(absl::string_view header_value) {
624
118
  std::vector<absl::string_view> values;
625
180
  for (absl::string_view s : absl::StrSplit(header_value, ',')) {
626
165
    absl::string_view token = absl::StripAsciiWhitespace(s);
627
165
    if (token.empty()) {
628
16
      continue;
629
16
    }
630
149
    values.emplace_back(token);
631
149
  }
632
118
  return values;
633
118
}
634

            
635
19
absl::string_view HeaderUtility::getSemicolonDelimitedAttribute(absl::string_view value) {
636
19
  return absl::StripAsciiWhitespace(StringUtil::cropRight(value, ";"));
637
19
}
638

            
639
std::string HeaderUtility::addEncodingToAcceptEncoding(absl::string_view accept_encoding_header,
640
26
                                                       absl::string_view encoding) {
641
  // Append the content encoding only if it isn't already present in the
642
  // accept_encoding header. If it is present with a q-value ("gzip;q=0.3"),
643
  // remove the q-value to indicate that the content encoding setting that we
644
  // add has max priority (i.e. q-value 1.0).
645
26
  std::vector<absl::string_view> newContentEncodings;
646
26
  std::vector<absl::string_view> contentEncodings =
647
26
      Http::HeaderUtility::parseCommaDelimitedHeader(accept_encoding_header);
648
29
  for (absl::string_view contentEncoding : contentEncodings) {
649
14
    absl::string_view strippedEncoding =
650
14
        Http::HeaderUtility::getSemicolonDelimitedAttribute(contentEncoding);
651
14
    if (strippedEncoding != encoding) {
652
      // Add back all content encodings back except for the content encoding that we want to
653
      // add. For example, if content encoding is "gzip", this filters out encodings "gzip" and
654
      // "gzip;q=0.6".
655
7
      newContentEncodings.push_back(contentEncoding);
656
7
    }
657
14
  }
658
  // Finally add a single instance of our content encoding.
659
26
  newContentEncodings.push_back(encoding);
660
26
  return absl::StrJoin(newContentEncodings, ",");
661
26
}
662

            
663
3488
bool HeaderUtility::isStandardConnectRequest(const Http::RequestHeaderMap& headers) {
664
3488
  return headers.getMethodValue() == Http::Headers::get().MethodValues.Connect &&
665
3488
         headers.getProtocolValue().empty();
666
3488
}
667

            
668
3407
bool HeaderUtility::isExtendedH2ConnectRequest(const Http::RequestHeaderMap& headers) {
669
3407
  return headers.getMethodValue() == Http::Headers::get().MethodValues.Connect &&
670
3407
         !headers.getProtocolValue().empty();
671
3407
}
672

            
673
552629
bool HeaderUtility::isPseudoHeader(absl::string_view header_name) {
674
552629
  return !header_name.empty() && header_name[0] == ':';
675
552629
}
676

            
677
} // namespace Http
678
} // namespace Envoy