Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/common/stream_info/utility.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/common/stream_info/utility.h"
2
3
#include <string>
4
5
#include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h"
6
7
#include "source/common/http/default_server_string.h"
8
#include "source/common/runtime/runtime_features.h"
9
10
#include "absl/strings/str_format.h"
11
12
namespace Envoy {
13
namespace StreamInfo {
14
15
31
const std::string ResponseFlagUtils::toString(const StreamInfo& stream_info) {
16
31
  return toString(stream_info, true);
17
31
}
18
19
8.17k
const std::string ResponseFlagUtils::toShortString(const StreamInfo& stream_info) {
20
8.17k
  return toString(stream_info, false);
21
8.17k
}
22
23
8.20k
const std::string ResponseFlagUtils::toString(const StreamInfo& stream_info, bool use_long_name) {
24
  // We don't expect more than 4 flags are set. Relax to 16 since the vector is allocated on stack
25
  // anyway.
26
8.20k
  absl::InlinedVector<absl::string_view, 16> flag_strings_vec;
27
213k
  for (const auto& [flag_strings, flag] : ALL_RESPONSE_STRINGS_FLAGS) {
28
213k
    if (stream_info.hasResponseFlag(flag)) {
29
576
      flag_strings_vec.push_back(use_long_name ? flag_strings.long_string_
30
576
                                               : flag_strings.short_string_);
31
576
    }
32
213k
  }
33
8.20k
  if (flag_strings_vec.empty()) {
34
7.63k
    return std::string(NONE);
35
7.63k
  }
36
576
  return absl::StrJoin(flag_strings_vec, ",");
37
8.20k
}
38
39
4
absl::flat_hash_map<std::string, ResponseFlag> ResponseFlagUtils::getFlagMap() {
40
4
  static_assert(ResponseFlag::LastFlag == 0x4000000,
41
4
                "A flag has been added. Add the new flag to ALL_RESPONSE_STRINGS_FLAGS.");
42
4
  absl::flat_hash_map<std::string, ResponseFlag> res;
43
104
  for (auto [flag_strings, flag] : ResponseFlagUtils::ALL_RESPONSE_STRINGS_FLAGS) {
44
104
    res.emplace(flag_strings.short_string_, flag);
45
104
  }
46
4
  return res;
47
4
}
48
49
20.9k
absl::optional<ResponseFlag> ResponseFlagUtils::toResponseFlag(absl::string_view flag) {
50
  // This `MapType` is introduce because CONSTRUCT_ON_FIRST_USE doesn't like template.
51
20.9k
  using MapType = absl::flat_hash_map<std::string, ResponseFlag>;
52
20.9k
  const auto& flag_map = []() {
53
20.9k
    CONSTRUCT_ON_FIRST_USE(MapType, ResponseFlagUtils::getFlagMap());
54
20.9k
  }();
55
20.9k
  const auto& it = flag_map.find(flag);
56
20.9k
  if (it != flag_map.end()) {
57
20.9k
    return absl::make_optional<ResponseFlag>(it->second);
58
20.9k
  }
59
0
  return absl::nullopt;
60
20.9k
}
61
62
2.60k
OptRef<const UpstreamTiming> getUpstreamTiming(const StreamInfo& stream_info) {
63
2.60k
  OptRef<const UpstreamInfo> info = stream_info.upstreamInfo();
64
2.60k
  if (!info.has_value()) {
65
273
    return {};
66
273
  }
67
2.33k
  return info.value().get().upstreamTiming();
68
2.60k
}
69
70
absl::optional<std::chrono::nanoseconds> duration(const absl::optional<MonotonicTime>& time,
71
3.71k
                                                  const StreamInfo& stream_info) {
72
3.71k
  if (!time.has_value()) {
73
3.67k
    return absl::nullopt;
74
3.67k
  }
75
34
  return std::chrono::duration_cast<std::chrono::nanoseconds>(time.value() -
76
34
                                                              stream_info.startTimeMonotonic());
77
3.71k
}
78
79
17
absl::optional<std::chrono::nanoseconds> TimingUtility::firstUpstreamTxByteSent() {
80
17
  OptRef<const UpstreamTiming> timing = getUpstreamTiming(stream_info_);
81
17
  if (!timing) {
82
17
    return absl::nullopt;
83
17
  }
84
0
  return duration(timing.value().get().first_upstream_tx_byte_sent_, stream_info_);
85
17
}
86
87
888
absl::optional<std::chrono::nanoseconds> TimingUtility::lastUpstreamTxByteSent() {
88
888
  OptRef<const UpstreamTiming> timing = getUpstreamTiming(stream_info_);
89
888
  if (!timing) {
90
222
    return absl::nullopt;
91
222
  }
92
666
  return duration(timing.value().get().last_upstream_tx_byte_sent_, stream_info_);
93
888
}
94
95
1.68k
absl::optional<std::chrono::nanoseconds> TimingUtility::firstUpstreamRxByteReceived() {
96
1.68k
  OptRef<const UpstreamTiming> timing = getUpstreamTiming(stream_info_);
97
1.68k
  if (!timing) {
98
17
    return absl::nullopt;
99
17
  }
100
1.66k
  return duration(timing.value().get().first_upstream_rx_byte_received_, stream_info_);
101
1.68k
}
102
103
17
absl::optional<std::chrono::nanoseconds> TimingUtility::lastUpstreamRxByteReceived() {
104
17
  OptRef<const UpstreamTiming> timing = getUpstreamTiming(stream_info_);
105
17
  if (!timing) {
106
17
    return absl::nullopt;
107
17
  }
108
0
  return duration(timing.value().get().last_upstream_rx_byte_received_, stream_info_);
109
17
}
110
111
0
absl::optional<std::chrono::nanoseconds> TimingUtility::upstreamHandshakeComplete() {
112
0
  OptRef<const UpstreamTiming> timing = getUpstreamTiming(stream_info_);
113
0
  if (!timing) {
114
0
    return absl::nullopt;
115
0
  }
116
0
  return duration(timing.value().get().upstreamHandshakeComplete(), stream_info_);
117
0
}
118
119
17
absl::optional<std::chrono::nanoseconds> TimingUtility::firstDownstreamTxByteSent() {
120
17
  OptRef<const DownstreamTiming> timing = stream_info_.downstreamTiming();
121
17
  if (!timing) {
122
0
    return absl::nullopt;
123
0
  }
124
17
  return duration(timing.value().get().firstDownstreamTxByteSent(), stream_info_);
125
17
}
126
127
1.39k
absl::optional<std::chrono::nanoseconds> TimingUtility::lastDownstreamTxByteSent() {
128
1.39k
  OptRef<const DownstreamTiming> timing = stream_info_.downstreamTiming();
129
1.39k
  if (!timing) {
130
1.31k
    return absl::nullopt;
131
1.31k
  }
132
83
  return duration(timing.value().get().lastDownstreamTxByteSent(), stream_info_);
133
1.39k
}
134
135
1.30k
absl::optional<std::chrono::nanoseconds> TimingUtility::lastDownstreamRxByteReceived() {
136
1.30k
  OptRef<const DownstreamTiming> timing = stream_info_.downstreamTiming();
137
1.30k
  if (!timing) {
138
257
    return absl::nullopt;
139
257
  }
140
1.05k
  return duration(timing.value().get().lastDownstreamRxByteReceived(), stream_info_);
141
1.30k
}
142
143
573
absl::optional<std::chrono::nanoseconds> TimingUtility::downstreamHandshakeComplete() {
144
573
  OptRef<const DownstreamTiming> timing = stream_info_.downstreamTiming();
145
573
  if (!timing) {
146
377
    return absl::nullopt;
147
377
  }
148
196
  return duration(timing.value().get().downstreamHandshakeComplete(), stream_info_);
149
573
}
150
151
101
absl::optional<std::chrono::nanoseconds> TimingUtility::lastDownstreamAckReceived() {
152
101
  OptRef<const DownstreamTiming> timing = stream_info_.downstreamTiming();
153
101
  if (!timing) {
154
66
    return absl::nullopt;
155
66
  }
156
35
  return duration(timing.value().get().lastDownstreamAckReceived(), stream_info_);
157
101
}
158
159
const std::string&
160
4.51k
Utility::formatDownstreamAddressNoPort(const Network::Address::Instance& address) {
161
4.51k
  if (address.type() == Network::Address::Type::Ip) {
162
4.26k
    return address.ip()->addressAsString();
163
4.26k
  } else {
164
247
    return address.asString();
165
247
  }
166
4.51k
}
167
168
const std::string
169
14.8k
Utility::formatDownstreamAddressJustPort(const Network::Address::Instance& address) {
170
14.8k
  std::string port;
171
14.8k
  if (address.type() == Network::Address::Type::Ip) {
172
14.3k
    port = std::to_string(address.ip()->port());
173
14.3k
  }
174
14.8k
  return port;
175
14.8k
}
176
177
absl::optional<uint32_t>
178
0
Utility::extractDownstreamAddressJustPort(const Network::Address::Instance& address) {
179
0
  if (address.type() == Network::Address::Type::Ip) {
180
0
    return address.ip()->port();
181
0
  }
182
0
  return {};
183
0
}
184
185
const absl::optional<Http::Code>
186
0
ProxyStatusUtils::recommendedHttpStatusCode(const ProxyStatusError proxy_status) {
187
  // This switch statement was derived from the mapping from proxy error type to
188
  // recommended HTTP status code in
189
  // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-proxy-status-05#section-2.3 and below.
190
  //
191
  // TODO(ambuc): Replace this with the non-draft URL when finalized.
192
0
  switch (proxy_status) {
193
0
  case ProxyStatusError::DnsTimeout:
194
0
  case ProxyStatusError::ConnectionTimeout:
195
0
  case ProxyStatusError::ConnectionReadTimeout:
196
0
  case ProxyStatusError::ConnectionWriteTimeout:
197
0
  case ProxyStatusError::HttpResponseTimeout:
198
0
    return Http::Code::GatewayTimeout; // 504
199
0
  case ProxyStatusError::DnsError:
200
0
  case ProxyStatusError::DestinationIpProhibited:
201
0
  case ProxyStatusError::DestinationIpUnroutable:
202
0
  case ProxyStatusError::ConnectionRefused:
203
0
  case ProxyStatusError::ConnectionTerminated:
204
0
  case ProxyStatusError::TlsProtocolError:
205
0
  case ProxyStatusError::TlsCertificateError:
206
0
  case ProxyStatusError::TlsAlertReceived:
207
0
  case ProxyStatusError::HttpResponseIncomplete:
208
0
  case ProxyStatusError::HttpResponseHeaderSectionSize:
209
0
  case ProxyStatusError::HttpResponseHeaderSize:
210
0
  case ProxyStatusError::HttpResponseBodySize:
211
0
  case ProxyStatusError::HttpResponseTrailerSectionSize:
212
0
  case ProxyStatusError::HttpResponseTrailerSize:
213
0
  case ProxyStatusError::HttpResponseTransferCoding:
214
0
  case ProxyStatusError::HttpResponseContentCoding:
215
0
  case ProxyStatusError::HttpUpgradeFailed:
216
0
  case ProxyStatusError::HttpProtocolError:
217
0
  case ProxyStatusError::ProxyLoopDetected:
218
0
    return Http::Code::BadGateway; // 502
219
0
  case ProxyStatusError::DestinationNotFound:
220
0
  case ProxyStatusError::ProxyInternalError:
221
0
  case ProxyStatusError::ProxyConfigurationError:
222
0
    return Http::Code::InternalServerError; // 500
223
0
  case ProxyStatusError::DestinationUnavailable:
224
0
  case ProxyStatusError::ConnectionLimitReached:
225
0
    return Http::Code::ServiceUnavailable; // 503
226
0
  case ProxyStatusError::HttpRequestDenied:
227
0
    return Http::Code::Forbidden; // 403
228
0
  case ProxyStatusError::ProxyInternalResponse:
229
0
  case ProxyStatusError::HttpRequestError:
230
0
  default:
231
0
    return absl::nullopt;
232
0
  }
233
0
}
234
235
const std::string ProxyStatusUtils::makeProxyName(
236
    absl::string_view node_id, absl::string_view server_name,
237
    const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
238
11.7k
        ProxyStatusConfig* proxy_status_config) {
239
11.7k
  if (proxy_status_config == nullptr) {
240
11.3k
    return std::string(server_name);
241
11.3k
  }
242
  // For the proxy name, the config specified either a preset proxy name or a literal proxy name.
243
413
  switch (proxy_status_config->proxy_name_case()) {
244
63
  case envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
245
63
      ProxyStatusConfig::ProxyNameCase::kLiteralProxyName: {
246
63
    return std::string(proxy_status_config->literal_proxy_name());
247
0
  }
248
97
  case envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
249
97
      ProxyStatusConfig::ProxyNameCase::kUseNodeId: {
250
97
    return std::string(node_id);
251
0
  }
252
253
  case envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
253
253
      ProxyStatusConfig::ProxyNameCase::PROXY_NAME_NOT_SET:
254
253
  default: {
255
253
    return std::string(server_name);
256
253
  }
257
413
  }
258
413
}
259
260
const std::string ProxyStatusUtils::makeProxyStatusHeader(
261
    const StreamInfo& stream_info, const ProxyStatusError error, absl::string_view proxy_name,
262
    const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
263
0
        ProxyStatusConfig& proxy_status_config) {
264
0
  std::vector<std::string> retval = {};
265
266
0
  retval.push_back(std::string(proxy_name));
267
268
0
  retval.push_back(absl::StrFormat("error=%s", proxyStatusErrorToString(error)));
269
270
0
  if (!proxy_status_config.remove_details() && stream_info.responseCodeDetails().has_value()) {
271
0
    std::vector<std::string> details = {};
272
0
    details.push_back(stream_info.responseCodeDetails().value());
273
0
    if (!proxy_status_config.remove_connection_termination_details() &&
274
0
        stream_info.connectionTerminationDetails().has_value()) {
275
0
      details.push_back(stream_info.connectionTerminationDetails().value());
276
0
    }
277
0
    if (!proxy_status_config.remove_response_flags() && stream_info.hasAnyResponseFlag()) {
278
0
      details.push_back(ResponseFlagUtils::toShortString(stream_info));
279
0
    }
280
0
    retval.push_back(
281
0
        absl::StrFormat("details=\"%s\"", StringUtil::escape(absl::StrJoin(details, "; "))));
282
0
  }
283
284
0
  return absl::StrJoin(retval, "; ");
285
0
}
286
287
const absl::string_view
288
0
ProxyStatusUtils::proxyStatusErrorToString(const ProxyStatusError proxy_status) {
289
0
  switch (proxy_status) {
290
0
  case ProxyStatusError::DnsTimeout:
291
0
    return DNS_TIMEOUT;
292
0
  case ProxyStatusError::DnsError:
293
0
    return DNS_ERROR;
294
0
  case ProxyStatusError::DestinationNotFound:
295
0
    return DESTINATION_NOT_FOUND;
296
0
  case ProxyStatusError::DestinationUnavailable:
297
0
    return DESTINATION_UNAVAILABLE;
298
0
  case ProxyStatusError::DestinationIpProhibited:
299
0
    return DESTINATION_IP_PROHIBITED;
300
0
  case ProxyStatusError::DestinationIpUnroutable:
301
0
    return DESTINATION_IP_UNROUTABLE;
302
0
  case ProxyStatusError::ConnectionRefused:
303
0
    return CONNECTION_REFUSED;
304
0
  case ProxyStatusError::ConnectionTerminated:
305
0
    return CONNECTION_TERMINATED;
306
0
  case ProxyStatusError::ConnectionTimeout:
307
0
    return CONNECTION_TIMEOUT;
308
0
  case ProxyStatusError::ConnectionReadTimeout:
309
0
    return CONNECTION_READ_TIMEOUT;
310
0
  case ProxyStatusError::ConnectionWriteTimeout:
311
0
    return CONNECTION_WRITE_TIMEOUT;
312
0
  case ProxyStatusError::ConnectionLimitReached:
313
0
    return CONNECTION_LIMIT_REACHED;
314
0
  case ProxyStatusError::TlsProtocolError:
315
0
    return TLS_PROTOCOL_ERROR;
316
0
  case ProxyStatusError::TlsCertificateError:
317
0
    return TLS_CERTIFICATE_ERROR;
318
0
  case ProxyStatusError::TlsAlertReceived:
319
0
    return TLS_ALERT_RECEIVED;
320
0
  case ProxyStatusError::HttpRequestError:
321
0
    return HTTP_REQUEST_ERROR;
322
0
  case ProxyStatusError::HttpRequestDenied:
323
0
    return HTTP_REQUEST_DENIED;
324
0
  case ProxyStatusError::HttpResponseIncomplete:
325
0
    return HTTP_RESPONSE_INCOMPLETE;
326
0
  case ProxyStatusError::HttpResponseHeaderSectionSize:
327
0
    return HTTP_RESPONSE_HEADER_SECTION_SIZE;
328
0
  case ProxyStatusError::HttpResponseHeaderSize:
329
0
    return HTTP_RESPONSE_HEADER_SIZE;
330
0
  case ProxyStatusError::HttpResponseBodySize:
331
0
    return HTTP_RESPONSE_BODY_SIZE;
332
0
  case ProxyStatusError::HttpResponseTrailerSectionSize:
333
0
    return HTTP_RESPONSE_TRAILER_SECTION_SIZE;
334
0
  case ProxyStatusError::HttpResponseTrailerSize:
335
0
    return HTTP_RESPONSE_TRAILER_SIZE;
336
0
  case ProxyStatusError::HttpResponseTransferCoding:
337
0
    return HTTP_RESPONSE_TRANSFER_CODING;
338
0
  case ProxyStatusError::HttpResponseContentCoding:
339
0
    return HTTP_RESPONSE_CONTENT_CODING;
340
0
  case ProxyStatusError::HttpResponseTimeout:
341
0
    return HTTP_RESPONSE_TIMEOUT;
342
0
  case ProxyStatusError::HttpUpgradeFailed:
343
0
    return HTTP_UPGRADE_FAILED;
344
0
  case ProxyStatusError::HttpProtocolError:
345
0
    return HTTP_PROTOCOL_ERROR;
346
0
  case ProxyStatusError::ProxyInternalResponse:
347
0
    return PROXY_INTERNAL_RESPONSE;
348
0
  case ProxyStatusError::ProxyInternalError:
349
0
    return PROXY_INTERNAL_ERROR;
350
0
  case ProxyStatusError::ProxyConfigurationError:
351
0
    return PROXY_CONFIGURATION_ERROR;
352
0
  case ProxyStatusError::ProxyLoopDetected:
353
0
    return PROXY_LOOP_DETECTED;
354
0
  default:
355
0
    return "-";
356
0
  }
357
0
}
358
359
const absl::optional<ProxyStatusError>
360
149
ProxyStatusUtils::fromStreamInfo(const StreamInfo& stream_info) {
361
  // NB: This mapping from Envoy-specific ResponseFlag enum to Proxy-Status
362
  // error enum is lossy, since ResponseFlag is really a bitset of many
363
  // ResponseFlag enums. Here, we search the list of all known ResponseFlag values in
364
  // enum order, returning the first matching ProxyStatusError.
365
149
  if (stream_info.hasResponseFlag(ResponseFlag::FailedLocalHealthCheck)) {
366
0
    return ProxyStatusError::DestinationUnavailable;
367
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::NoHealthyUpstream)) {
368
0
    return ProxyStatusError::DestinationUnavailable;
369
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamRequestTimeout)) {
370
0
    if (!Runtime::runtimeFeatureEnabled(
371
0
            "envoy.reloadable_features.proxy_status_upstream_request_timeout")) {
372
0
      return ProxyStatusError::ConnectionTimeout;
373
0
    }
374
0
    return ProxyStatusError::HttpResponseTimeout;
375
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::LocalReset)) {
376
0
    return ProxyStatusError::ConnectionTimeout;
377
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamRemoteReset)) {
378
0
    return ProxyStatusError::ConnectionTerminated;
379
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamConnectionFailure)) {
380
0
    return ProxyStatusError::ConnectionRefused;
381
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamConnectionTermination)) {
382
0
    return ProxyStatusError::ConnectionTerminated;
383
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamOverflow)) {
384
0
    return ProxyStatusError::ConnectionLimitReached;
385
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::NoRouteFound)) {
386
0
    return ProxyStatusError::DestinationNotFound;
387
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::RateLimited)) {
388
0
    return ProxyStatusError::ConnectionLimitReached;
389
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::RateLimitServiceError)) {
390
0
    return ProxyStatusError::ConnectionLimitReached;
391
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamRetryLimitExceeded)) {
392
0
    return ProxyStatusError::DestinationUnavailable;
393
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::StreamIdleTimeout)) {
394
0
    return ProxyStatusError::HttpResponseTimeout;
395
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::InvalidEnvoyRequestHeaders)) {
396
0
    return ProxyStatusError::HttpRequestError;
397
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::DownstreamProtocolError)) {
398
0
    return ProxyStatusError::HttpRequestError;
399
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamMaxStreamDurationReached)) {
400
0
    return ProxyStatusError::HttpResponseTimeout;
401
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::NoFilterConfigFound)) {
402
0
    return ProxyStatusError::ProxyConfigurationError;
403
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamProtocolError)) {
404
0
    return ProxyStatusError::HttpProtocolError;
405
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::NoClusterFound)) {
406
0
    return ProxyStatusError::DestinationUnavailable;
407
149
  } else if (stream_info.hasResponseFlag(ResponseFlag::DnsResolutionFailed)) {
408
0
    return ProxyStatusError::DnsError;
409
149
  } else {
410
149
    return absl::nullopt;
411
149
  }
412
149
}
413
414
} // namespace StreamInfo
415
} // namespace Envoy