Line data Source code
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 0 : const std::string ResponseFlagUtils::toString(const StreamInfo& stream_info) { 16 0 : return toString(stream_info, true); 17 0 : } 18 : 19 612 : const std::string ResponseFlagUtils::toShortString(const StreamInfo& stream_info) { 20 612 : return toString(stream_info, false); 21 612 : } 22 : 23 612 : 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 612 : absl::InlinedVector<absl::string_view, 16> flag_strings_vec; 27 17136 : for (const auto& [flag_strings, flag] : ALL_RESPONSE_STRINGS_FLAGS) { 28 17136 : if (stream_info.hasResponseFlag(flag)) { 29 244 : flag_strings_vec.push_back(use_long_name ? flag_strings.long_string_ 30 244 : : flag_strings.short_string_); 31 244 : } 32 17136 : } 33 612 : if (flag_strings_vec.empty()) { 34 368 : return std::string(NONE); 35 368 : } 36 244 : return absl::StrJoin(flag_strings_vec, ","); 37 612 : } 38 : 39 0 : absl::flat_hash_map<std::string, ResponseFlag> ResponseFlagUtils::getFlagMap() { 40 0 : static_assert(ResponseFlag::LastFlag == 0x8000000, 41 0 : "A flag has been added. Add the new flag to ALL_RESPONSE_STRINGS_FLAGS."); 42 0 : absl::flat_hash_map<std::string, ResponseFlag> res; 43 0 : for (auto [flag_strings, flag] : ResponseFlagUtils::ALL_RESPONSE_STRINGS_FLAGS) { 44 0 : res.emplace(flag_strings.short_string_, flag); 45 0 : } 46 0 : return res; 47 0 : } 48 : 49 0 : absl::optional<ResponseFlag> ResponseFlagUtils::toResponseFlag(absl::string_view flag) { 50 : // This `MapType` is introduce because CONSTRUCT_ON_FIRST_USE doesn't like template. 51 0 : using MapType = absl::flat_hash_map<std::string, ResponseFlag>; 52 0 : const auto& flag_map = []() { 53 0 : CONSTRUCT_ON_FIRST_USE(MapType, ResponseFlagUtils::getFlagMap()); 54 0 : }(); 55 0 : const auto& it = flag_map.find(flag); 56 0 : if (it != flag_map.end()) { 57 0 : return absl::make_optional<ResponseFlag>(it->second); 58 0 : } 59 0 : return absl::nullopt; 60 0 : } 61 : 62 3 : OptRef<const UpstreamTiming> getUpstreamTiming(const StreamInfo& stream_info) { 63 3 : OptRef<const UpstreamInfo> info = stream_info.upstreamInfo(); 64 3 : if (!info.has_value()) { 65 0 : return {}; 66 0 : } 67 3 : return info.value().get().upstreamTiming(); 68 3 : } 69 : 70 : absl::optional<std::chrono::nanoseconds> duration(const absl::optional<MonotonicTime>& time, 71 3 : const StreamInfo& stream_info) { 72 3 : if (!time.has_value()) { 73 3 : return absl::nullopt; 74 3 : } 75 0 : return std::chrono::duration_cast<std::chrono::nanoseconds>(time.value() - 76 0 : stream_info.startTimeMonotonic()); 77 3 : } 78 : 79 0 : absl::optional<std::chrono::nanoseconds> TimingUtility::firstUpstreamTxByteSent() { 80 0 : OptRef<const UpstreamTiming> timing = getUpstreamTiming(stream_info_); 81 0 : if (!timing) { 82 0 : return absl::nullopt; 83 0 : } 84 0 : return duration(timing.value().get().first_upstream_tx_byte_sent_, stream_info_); 85 0 : } 86 : 87 3 : absl::optional<std::chrono::nanoseconds> TimingUtility::lastUpstreamTxByteSent() { 88 3 : OptRef<const UpstreamTiming> timing = getUpstreamTiming(stream_info_); 89 3 : if (!timing) { 90 0 : return absl::nullopt; 91 0 : } 92 3 : return duration(timing.value().get().last_upstream_tx_byte_sent_, stream_info_); 93 3 : } 94 : 95 0 : absl::optional<std::chrono::nanoseconds> TimingUtility::firstUpstreamRxByteReceived() { 96 0 : OptRef<const UpstreamTiming> timing = getUpstreamTiming(stream_info_); 97 0 : if (!timing) { 98 0 : return absl::nullopt; 99 0 : } 100 0 : return duration(timing.value().get().first_upstream_rx_byte_received_, stream_info_); 101 0 : } 102 : 103 0 : absl::optional<std::chrono::nanoseconds> TimingUtility::lastUpstreamRxByteReceived() { 104 0 : OptRef<const UpstreamTiming> timing = getUpstreamTiming(stream_info_); 105 0 : if (!timing) { 106 0 : return absl::nullopt; 107 0 : } 108 0 : return duration(timing.value().get().last_upstream_rx_byte_received_, stream_info_); 109 0 : } 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 0 : absl::optional<std::chrono::nanoseconds> TimingUtility::firstDownstreamTxByteSent() { 120 0 : OptRef<const DownstreamTiming> timing = stream_info_.downstreamTiming(); 121 0 : if (!timing) { 122 0 : return absl::nullopt; 123 0 : } 124 0 : return duration(timing.value().get().firstDownstreamTxByteSent(), stream_info_); 125 0 : } 126 : 127 0 : absl::optional<std::chrono::nanoseconds> TimingUtility::lastDownstreamTxByteSent() { 128 0 : OptRef<const DownstreamTiming> timing = stream_info_.downstreamTiming(); 129 0 : if (!timing) { 130 0 : return absl::nullopt; 131 0 : } 132 0 : return duration(timing.value().get().lastDownstreamTxByteSent(), stream_info_); 133 0 : } 134 : 135 0 : absl::optional<std::chrono::nanoseconds> TimingUtility::lastDownstreamRxByteReceived() { 136 0 : OptRef<const DownstreamTiming> timing = stream_info_.downstreamTiming(); 137 0 : if (!timing) { 138 0 : return absl::nullopt; 139 0 : } 140 0 : return duration(timing.value().get().lastDownstreamRxByteReceived(), stream_info_); 141 0 : } 142 : 143 0 : absl::optional<std::chrono::nanoseconds> TimingUtility::downstreamHandshakeComplete() { 144 0 : OptRef<const DownstreamTiming> timing = stream_info_.downstreamTiming(); 145 0 : if (!timing) { 146 0 : return absl::nullopt; 147 0 : } 148 0 : return duration(timing.value().get().downstreamHandshakeComplete(), stream_info_); 149 0 : } 150 : 151 0 : absl::optional<std::chrono::nanoseconds> TimingUtility::lastDownstreamAckReceived() { 152 0 : OptRef<const DownstreamTiming> timing = stream_info_.downstreamTiming(); 153 0 : if (!timing) { 154 0 : return absl::nullopt; 155 0 : } 156 0 : return duration(timing.value().get().lastDownstreamAckReceived(), stream_info_); 157 0 : } 158 : 159 : const std::string& 160 56 : Utility::formatDownstreamAddressNoPort(const Network::Address::Instance& address) { 161 56 : if (address.type() == Network::Address::Type::Ip) { 162 3 : return address.ip()->addressAsString(); 163 53 : } else { 164 53 : return address.asString(); 165 53 : } 166 56 : } 167 : 168 : const std::string 169 0 : Utility::formatDownstreamAddressJustPort(const Network::Address::Instance& address) { 170 0 : std::string port; 171 0 : if (address.type() == Network::Address::Type::Ip) { 172 0 : port = std::to_string(address.ip()->port()); 173 0 : } 174 0 : return port; 175 0 : } 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 922 : ProxyStatusConfig* proxy_status_config) { 239 922 : if (proxy_status_config == nullptr) { 240 922 : return std::string(server_name); 241 922 : } 242 : // For the proxy name, the config specified either a preset proxy name or a literal proxy name. 243 0 : switch (proxy_status_config->proxy_name_case()) { 244 0 : case envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager:: 245 0 : ProxyStatusConfig::ProxyNameCase::kLiteralProxyName: { 246 0 : return std::string(proxy_status_config->literal_proxy_name()); 247 0 : } 248 0 : case envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager:: 249 0 : ProxyStatusConfig::ProxyNameCase::kUseNodeId: { 250 0 : return std::string(node_id); 251 0 : } 252 0 : case envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager:: 253 0 : ProxyStatusConfig::ProxyNameCase::PROXY_NAME_NOT_SET: 254 0 : default: { 255 0 : return std::string(server_name); 256 0 : } 257 0 : } 258 0 : } 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 0 : 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 0 : if (stream_info.hasResponseFlag(ResponseFlag::FailedLocalHealthCheck)) { 366 0 : return ProxyStatusError::DestinationUnavailable; 367 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::NoHealthyUpstream)) { 368 0 : return ProxyStatusError::DestinationUnavailable; 369 0 : } 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 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::LocalReset)) { 376 0 : return ProxyStatusError::ConnectionTimeout; 377 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamRemoteReset)) { 378 0 : return ProxyStatusError::ConnectionTerminated; 379 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamConnectionFailure)) { 380 0 : return ProxyStatusError::ConnectionRefused; 381 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamConnectionTermination)) { 382 0 : return ProxyStatusError::ConnectionTerminated; 383 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamOverflow)) { 384 0 : return ProxyStatusError::ConnectionLimitReached; 385 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::NoRouteFound)) { 386 0 : return ProxyStatusError::DestinationNotFound; 387 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::RateLimited)) { 388 0 : return ProxyStatusError::ConnectionLimitReached; 389 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::RateLimitServiceError)) { 390 0 : return ProxyStatusError::ConnectionLimitReached; 391 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamRetryLimitExceeded)) { 392 0 : return ProxyStatusError::DestinationUnavailable; 393 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::StreamIdleTimeout)) { 394 0 : return ProxyStatusError::HttpResponseTimeout; 395 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::InvalidEnvoyRequestHeaders)) { 396 0 : return ProxyStatusError::HttpRequestError; 397 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::DownstreamProtocolError)) { 398 0 : return ProxyStatusError::HttpRequestError; 399 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamMaxStreamDurationReached)) { 400 0 : return ProxyStatusError::HttpResponseTimeout; 401 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::NoFilterConfigFound)) { 402 0 : return ProxyStatusError::ProxyConfigurationError; 403 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::UpstreamProtocolError)) { 404 0 : return ProxyStatusError::HttpProtocolError; 405 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::NoClusterFound)) { 406 0 : return ProxyStatusError::DestinationUnavailable; 407 0 : } else if (stream_info.hasResponseFlag(ResponseFlag::DnsResolutionFailed)) { 408 0 : return ProxyStatusError::DnsError; 409 0 : } else { 410 0 : return absl::nullopt; 411 0 : } 412 0 : } 413 : 414 : } // namespace StreamInfo 415 : } // namespace Envoy