LCOV - code coverage report
Current view: top level - source/common/http - conn_manager_utility.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 198 414 47.8 %
Date: 2024-01-05 06:35:25 Functions: 11 11 100.0 %

          Line data    Source code
       1             : #include "source/common/http/conn_manager_utility.h"
       2             : 
       3             : #include <atomic>
       4             : #include <cstdint>
       5             : #include <string>
       6             : 
       7             : #include "envoy/type/v3/percent.pb.h"
       8             : 
       9             : #include "source/common/common/empty_string.h"
      10             : #include "source/common/common/enum_to_int.h"
      11             : #include "source/common/common/utility.h"
      12             : #include "source/common/http/conn_manager_config.h"
      13             : #include "source/common/http/header_utility.h"
      14             : #include "source/common/http/headers.h"
      15             : #include "source/common/http/http1/codec_impl.h"
      16             : #include "source/common/http/http2/codec_impl.h"
      17             : #include "source/common/http/path_utility.h"
      18             : #include "source/common/http/utility.h"
      19             : #include "source/common/network/utility.h"
      20             : #include "source/common/runtime/runtime_features.h"
      21             : #include "source/common/stream_info/utility.h"
      22             : #include "source/common/tracing/http_tracer_impl.h"
      23             : 
      24             : #include "absl/strings/str_cat.h"
      25             : #include "absl/strings/str_join.h"
      26             : 
      27             : namespace Envoy {
      28             : namespace Http {
      29             : namespace {
      30             : 
      31         302 : absl::string_view getScheme(absl::string_view forwarded_proto, bool is_ssl) {
      32         302 :   if (Utility::schemeIsValid(forwarded_proto)) {
      33         302 :     return forwarded_proto;
      34         302 :   }
      35           0 :   return is_ssl ? Headers::get().SchemeValues.Https : Headers::get().SchemeValues.Http;
      36         302 : }
      37             : 
      38             : } // namespace
      39             : std::string ConnectionManagerUtility::determineNextProtocol(Network::Connection& connection,
      40         101 :                                                             const Buffer::Instance& data) {
      41         101 :   const std::string next_protocol = connection.nextProtocol();
      42         101 :   if (!next_protocol.empty()) {
      43           0 :     return next_protocol;
      44           0 :   }
      45             : 
      46             :   // See if the data we have so far shows the HTTP/2 prefix. We ignore the case where someone sends
      47             :   // us the first few bytes of the HTTP/2 prefix since in all public cases we use SSL/ALPN. For
      48             :   // internal cases this should practically never happen.
      49         101 :   if (data.startsWith(Http2::CLIENT_MAGIC_PREFIX)) {
      50           0 :     return Utility::AlpnNames::get().Http2;
      51           0 :   }
      52             : 
      53         101 :   return "";
      54         101 : }
      55             : 
      56             : ServerConnectionPtr ConnectionManagerUtility::autoCreateCodec(
      57             :     Network::Connection& connection, const Buffer::Instance& data,
      58             :     ServerConnectionCallbacks& callbacks, Stats::Scope& scope, Random::RandomGenerator& random,
      59             :     Http1::CodecStats::AtomicPtr& http1_codec_stats,
      60             :     Http2::CodecStats::AtomicPtr& http2_codec_stats, const Http1Settings& http1_settings,
      61             :     const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
      62             :     uint32_t max_request_headers_kb, uint32_t max_request_headers_count,
      63             :     envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
      64             :         headers_with_underscores_action,
      65         101 :     Server::OverloadManager& overload_manager) {
      66         101 :   if (determineNextProtocol(connection, data) == Utility::AlpnNames::get().Http2) {
      67           0 :     Http2::CodecStats& stats = Http2::CodecStats::atomicGet(http2_codec_stats, scope);
      68           0 :     return std::make_unique<Http2::ServerConnectionImpl>(
      69           0 :         connection, callbacks, stats, random, http2_options, max_request_headers_kb,
      70           0 :         max_request_headers_count, headers_with_underscores_action, overload_manager);
      71         101 :   } else {
      72         101 :     Http1::CodecStats& stats = Http1::CodecStats::atomicGet(http1_codec_stats, scope);
      73         101 :     return std::make_unique<Http1::ServerConnectionImpl>(
      74         101 :         connection, stats, callbacks, http1_settings, max_request_headers_kb,
      75         101 :         max_request_headers_count, headers_with_underscores_action, overload_manager);
      76         101 :   }
      77         101 : }
      78             : 
      79             : ConnectionManagerUtility::MutateRequestHeadersResult ConnectionManagerUtility::mutateRequestHeaders(
      80             :     RequestHeaderMap& request_headers, Network::Connection& connection,
      81             :     ConnectionManagerConfig& config, const Router::Config& route_config,
      82         507 :     const LocalInfo::LocalInfo& local_info, const StreamInfo::StreamInfo& stream_info) {
      83             : 
      84         507 :   for (const auto& extension : config.earlyHeaderMutationExtensions()) {
      85           0 :     if (!extension->mutate(request_headers, stream_info)) {
      86           0 :       break;
      87           0 :     }
      88           0 :   }
      89             : 
      90             :   // If this is a Upgrade request, do not remove the Connection and Upgrade headers,
      91             :   // as we forward them verbatim to the upstream hosts.
      92         507 :   if (!Utility::isUpgrade(request_headers)) {
      93         506 :     request_headers.removeConnection();
      94         506 :     request_headers.removeUpgrade();
      95         506 :     if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.sanitize_te")) {
      96         506 :       request_headers.removeTE();
      97         506 :     }
      98         506 :   }
      99             : 
     100             :   // Clean proxy headers.
     101         507 :   request_headers.removeEnvoyInternalRequest();
     102         507 :   request_headers.removeKeepAlive();
     103         507 :   request_headers.removeProxyConnection();
     104         507 :   request_headers.removeTransferEncoding();
     105             : 
     106             :   // Sanitize referer field if exists.
     107         507 :   auto result = request_headers.get(Http::CustomHeaders::get().Referer);
     108         507 :   if (!result.empty()) {
     109           0 :     if (result.size() > 1 || !Utility::isValidRefererValue(result[0]->value().getStringView())) {
     110             :       // A request header shouldn't have multiple referer field.
     111           0 :       request_headers.remove(Http::CustomHeaders::get().Referer);
     112           0 :     }
     113           0 :   }
     114             : 
     115             :   // If we are "using remote address" this means that we create/append to XFF with our immediate
     116             :   // peer. Cases where we don't "use remote address" include trusted double proxy where we expect
     117             :   // our peer to have already properly set XFF, etc.
     118         507 :   Network::Address::InstanceConstSharedPtr final_remote_address;
     119         507 :   bool allow_trusted_address_checks = false;
     120         507 :   const uint32_t xff_num_trusted_hops = config.xffNumTrustedHops();
     121             : 
     122         507 :   if (config.useRemoteAddress()) {
     123         120 :     allow_trusted_address_checks = request_headers.ForwardedFor() == nullptr;
     124             :     // If there are any trusted proxies in front of this Envoy instance (as indicated by
     125             :     // the xff_num_trusted_hops configuration option), get the trusted client address
     126             :     // from the XFF before we append to XFF.
     127         120 :     if (xff_num_trusted_hops > 0) {
     128           0 :       final_remote_address =
     129           0 :           Utility::getLastAddressFromXFF(request_headers, xff_num_trusted_hops - 1).address_;
     130           0 :     }
     131             :     // If there aren't any trusted proxies in front of this Envoy instance, or there
     132             :     // are but they didn't populate XFF properly, the trusted client address is the
     133             :     // source address of the immediate downstream's connection to us.
     134         120 :     if (final_remote_address == nullptr) {
     135         120 :       final_remote_address = connection.connectionInfoProvider().remoteAddress();
     136         120 :     }
     137         120 :     if (!config.skipXffAppend()) {
     138         120 :       if (Network::Utility::isLoopbackAddress(
     139         120 :               *connection.connectionInfoProvider().remoteAddress())) {
     140          98 :         Utility::appendXff(request_headers, config.localAddress());
     141         120 :       } else {
     142          22 :         Utility::appendXff(request_headers, *connection.connectionInfoProvider().remoteAddress());
     143          22 :       }
     144         120 :     }
     145             :     // If the prior hop is not a trusted proxy, overwrite any
     146             :     // x-forwarded-proto/x-forwarded-port value it set as untrusted. Alternately if no
     147             :     // x-forwarded-proto/x-forwarded-port header exists, add one if configured.
     148         120 :     if (xff_num_trusted_hops == 0 || request_headers.ForwardedProto() == nullptr) {
     149         120 :       request_headers.setReferenceForwardedProto(
     150         120 :           connection.ssl() ? Headers::get().SchemeValues.Https : Headers::get().SchemeValues.Http);
     151         120 :     }
     152         120 :     if (config.appendXForwardedPort() &&
     153         120 :         (xff_num_trusted_hops == 0 || request_headers.ForwardedPort() == nullptr)) {
     154           0 :       const Envoy::Network::Address::Ip* ip =
     155           0 :           connection.streamInfo().downstreamAddressProvider().localAddress()->ip();
     156           0 :       if (ip) {
     157           0 :         request_headers.setForwardedPort(ip->port());
     158           0 :       }
     159           0 :     }
     160         449 :   } else {
     161             :     // If we are not using remote address, attempt to pull a valid IPv4 or IPv6 address out of XFF
     162             :     // or through an extension. An extension might be needed when XFF doesn't work (e.g. an
     163             :     // irregular network).
     164             :     //
     165             :     // If we find one, it will be used as the downstream address for logging. It may or may not be
     166             :     // used for determining internal/external status (see below).
     167         387 :     OriginalIPDetectionParams params = {request_headers,
     168         387 :                                         connection.connectionInfoProvider().remoteAddress()};
     169         387 :     for (const auto& detection_extension : config.originalIpDetectionExtensions()) {
     170         387 :       const auto result = detection_extension->detect(params);
     171             : 
     172         387 :       if (result.reject_options.has_value()) {
     173           0 :         return {nullptr, result.reject_options};
     174           0 :       }
     175             : 
     176         387 :       if (result.detected_remote_address) {
     177         179 :         final_remote_address = result.detected_remote_address;
     178         179 :         allow_trusted_address_checks = result.allow_trusted_address_checks;
     179         179 :         break;
     180         179 :       }
     181         387 :     }
     182         387 :   }
     183             : 
     184             :   // If the x-forwarded-proto header is not set, set it here, since Envoy uses it for determining
     185             :   // scheme and communicating it upstream.
     186         507 :   if (!request_headers.ForwardedProto()) {
     187         387 :     request_headers.setReferenceForwardedProto(connection.ssl() ? Headers::get().SchemeValues.Https
     188         387 :                                                                 : Headers::get().SchemeValues.Http);
     189         387 :   }
     190             : 
     191             :   // Usually, the x-forwarded-port header comes with x-forwarded-proto header. If the
     192             :   // x-forwarded-proto header is not set, set it here if append-x-forwarded-port is configured.
     193         507 :   if (config.appendXForwardedPort() && !request_headers.ForwardedPort()) {
     194           0 :     const Envoy::Network::Address::Ip* ip =
     195           0 :         connection.streamInfo().downstreamAddressProvider().localAddress()->ip();
     196           0 :     if (ip) {
     197           0 :       request_headers.setForwardedPort(ip->port());
     198           0 :     }
     199           0 :   }
     200             : 
     201         507 :   if (config.schemeToSet().has_value()) {
     202           0 :     request_headers.setScheme(config.schemeToSet().value());
     203           0 :     request_headers.setForwardedProto(config.schemeToSet().value());
     204           0 :   }
     205             : 
     206             :   // If :scheme is not set, sets :scheme based on X-Forwarded-Proto if a valid scheme,
     207             :   // else encryption level.
     208             :   // X-Forwarded-Proto and :scheme may still differ if different values are sent from downstream.
     209         507 :   if (!request_headers.Scheme()) {
     210         302 :     request_headers.setScheme(
     211         302 :         getScheme(request_headers.getForwardedProtoValue(), connection.ssl() != nullptr));
     212         302 :   }
     213         507 :   if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.lowercase_scheme")) {
     214         507 :     request_headers.setScheme(absl::AsciiStrToLower(request_headers.getSchemeValue()));
     215         507 :   }
     216             : 
     217             :   // At this point we can determine whether this is an internal or external request. The
     218             :   // determination of internal status uses the following:
     219             :   // 1) After remote address/XFF appending, the XFF header must contain a *single* address.
     220             :   // 2) The single address must be an internal address.
     221             :   // 3) If configured to not use remote address, but no XFF header is available, even if the real
     222             :   //    remote is internal, the request is considered external.
     223             :   // HUGE WARNING: The way we do this is not optimal but is how it worked "from the beginning" so
     224             :   //               we can't change it at this point. In the future we will likely need to add
     225             :   //               additional inference modes and make this mode legacy.
     226         507 :   const bool internal_request =
     227         507 :       allow_trusted_address_checks && final_remote_address != nullptr &&
     228         507 :       config.internalAddressConfig().isInternalAddress(*final_remote_address);
     229             : 
     230             :   // After determining internal request status, if there is no final remote address, due to no XFF,
     231             :   // busted XFF, etc., use the direct connection remote address for logging.
     232         507 :   if (final_remote_address == nullptr) {
     233         208 :     final_remote_address = connection.connectionInfoProvider().remoteAddress();
     234         208 :   }
     235             : 
     236             :   // Edge request is the request from external clients to front Envoy.
     237             :   // Request from front Envoy to the internal service will be treated as not edge request.
     238         507 :   const bool edge_request = !internal_request && config.useRemoteAddress();
     239             : 
     240             :   // If internal request, set header and do other internal only modifications.
     241         507 :   if (internal_request) {
     242         179 :     request_headers.setReferenceEnvoyInternalRequest(
     243         179 :         Headers::get().EnvoyInternalRequestValues.True);
     244         475 :   } else {
     245         328 :     cleanInternalHeaders(request_headers, edge_request, route_config.internalOnlyHeaders());
     246         328 :   }
     247             : 
     248         507 :   if (config.userAgent()) {
     249           0 :     request_headers.setEnvoyDownstreamServiceCluster(config.userAgent().value());
     250           0 :     const HeaderEntry* user_agent_header = request_headers.UserAgent();
     251           0 :     if (!user_agent_header || user_agent_header->value().empty()) {
     252             :       // Following setReference() is safe because user agent is constant for the life of the
     253             :       // listener.
     254           0 :       request_headers.setReferenceUserAgent(config.userAgent().value());
     255           0 :     }
     256             : 
     257             :     // TODO(htuch): should this be under the config.userAgent() condition or in the outer scope?
     258           0 :     if (!local_info.nodeName().empty()) {
     259             :       // Following setReference() is safe because local info is constant for the life of the server.
     260           0 :       request_headers.setReferenceEnvoyDownstreamServiceNode(local_info.nodeName());
     261           0 :     }
     262           0 :   }
     263             : 
     264         507 :   if (!config.via().empty()) {
     265           0 :     Utility::appendVia(request_headers, config.via());
     266           0 :   }
     267             : 
     268             :   // If we are an external request, AND we are "using remote address" (see above), we set
     269             :   // x-envoy-external-address since this is our first ingress point into the trusted network.
     270         507 :   if (edge_request && final_remote_address->type() == Network::Address::Type::Ip) {
     271         120 :     request_headers.setEnvoyExternalAddress(final_remote_address->ip()->addressAsString());
     272         120 :   }
     273             : 
     274             :   // Generate x-request-id for all edge requests, or if there is none.
     275         507 :   if (config.generateRequestId()) {
     276         409 :     auto rid_extension = config.requestIDExtension();
     277             :     // Unconditionally set a request ID if we are allowed to override it from
     278             :     // the edge. Otherwise just ensure it is set.
     279         409 :     const bool force_set = !config.preserveExternalRequestId() && edge_request;
     280         409 :     rid_extension->set(request_headers, force_set);
     281         409 :   }
     282             : 
     283         507 :   if (connection.connecting() && request_headers.get(Headers::get().EarlyData).empty()) {
     284             :     // Add an Early-Data header to indicate that this is a 0-RTT request according to
     285             :     // https://datatracker.ietf.org/doc/html/rfc8470#section-5.1.
     286           0 :     HeaderString value;
     287           0 :     value.setCopy("1");
     288           0 :     request_headers.addViaMove(HeaderString(Headers::get().EarlyData), std::move(value));
     289           0 :   }
     290         507 :   mutateXfccRequestHeader(request_headers, connection, config);
     291             : 
     292         507 :   return {final_remote_address, absl::nullopt};
     293         507 : }
     294             : 
     295             : void ConnectionManagerUtility::cleanInternalHeaders(
     296             :     RequestHeaderMap& request_headers, bool edge_request,
     297         328 :     const std::list<Http::LowerCaseString>& internal_only_headers) {
     298         328 :   if (edge_request) {
     299             :     // Headers to be stripped from edge requests, i.e. to sanitize so
     300             :     // clients can't inject values.
     301         120 :     request_headers.removeEnvoyDecoratorOperation();
     302         120 :     request_headers.removeEnvoyDownstreamServiceCluster();
     303         120 :     request_headers.removeEnvoyDownstreamServiceNode();
     304         120 :     request_headers.removeEnvoyOriginalPath();
     305         120 :   }
     306             : 
     307             :   // Headers to be stripped from edge *and* intermediate-hop external requests.
     308             :   // TODO: some of these should only be stripped at edge, i.e. moved into
     309             :   // the block above.
     310         328 :   request_headers.removeEnvoyRetriableStatusCodes();
     311         328 :   request_headers.removeEnvoyRetriableHeaderNames();
     312         328 :   request_headers.removeEnvoyRetryOn();
     313         328 :   request_headers.removeEnvoyRetryGrpcOn();
     314         328 :   request_headers.removeEnvoyMaxRetries();
     315         328 :   request_headers.removeEnvoyUpstreamAltStatName();
     316         328 :   request_headers.removeEnvoyUpstreamRequestTimeoutMs();
     317         328 :   request_headers.removeEnvoyUpstreamRequestPerTryTimeoutMs();
     318         328 :   request_headers.removeEnvoyUpstreamRequestTimeoutAltResponse();
     319         328 :   request_headers.removeEnvoyExpectedRequestTimeoutMs();
     320         328 :   request_headers.removeEnvoyForceTrace();
     321         328 :   request_headers.removeEnvoyIpTags();
     322         328 :   request_headers.removeEnvoyOriginalUrl();
     323         328 :   request_headers.removeEnvoyHedgeOnPerTryTimeout();
     324             : 
     325         328 :   for (const LowerCaseString& header : internal_only_headers) {
     326           0 :     request_headers.remove(header);
     327           0 :   }
     328         328 : }
     329             : 
     330             : Tracing::Reason ConnectionManagerUtility::mutateTracingRequestHeader(
     331             :     RequestHeaderMap& request_headers, Runtime::Loader& runtime, ConnectionManagerConfig& config,
     332         507 :     const Router::Route* route) {
     333         507 :   Tracing::Reason final_reason = Tracing::Reason::NotTraceable;
     334         507 :   if (!config.tracingConfig()) {
     335         507 :     return final_reason;
     336         507 :   }
     337             : 
     338           0 :   auto rid_extension = config.requestIDExtension();
     339           0 :   if (!rid_extension->useRequestIdForTraceSampling()) {
     340           0 :     return Tracing::Reason::Sampling;
     341           0 :   }
     342           0 :   const auto rid_to_integer = rid_extension->getInteger(request_headers);
     343             :   // Skip if request-id is corrupted, or non-existent
     344           0 :   if (!rid_to_integer.has_value()) {
     345           0 :     return final_reason;
     346           0 :   }
     347           0 :   const uint64_t result = rid_to_integer.value() % 10000;
     348             : 
     349           0 :   const envoy::type::v3::FractionalPercent* client_sampling =
     350           0 :       &config.tracingConfig()->client_sampling_;
     351           0 :   const envoy::type::v3::FractionalPercent* random_sampling =
     352           0 :       &config.tracingConfig()->random_sampling_;
     353           0 :   const envoy::type::v3::FractionalPercent* overall_sampling =
     354           0 :       &config.tracingConfig()->overall_sampling_;
     355             : 
     356           0 :   if (route && route->tracingConfig()) {
     357           0 :     client_sampling = &route->tracingConfig()->getClientSampling();
     358           0 :     random_sampling = &route->tracingConfig()->getRandomSampling();
     359           0 :     overall_sampling = &route->tracingConfig()->getOverallSampling();
     360           0 :   }
     361             : 
     362             :   // Do not apply tracing transformations if we are currently tracing.
     363           0 :   final_reason = rid_extension->getTraceReason(request_headers);
     364           0 :   if (Tracing::Reason::NotTraceable == final_reason) {
     365           0 :     if (request_headers.ClientTraceId() &&
     366           0 :         runtime.snapshot().featureEnabled("tracing.client_enabled", *client_sampling)) {
     367           0 :       final_reason = Tracing::Reason::ClientForced;
     368           0 :       rid_extension->setTraceReason(request_headers, final_reason);
     369           0 :     } else if (request_headers.EnvoyForceTrace()) {
     370           0 :       final_reason = Tracing::Reason::ServiceForced;
     371           0 :       rid_extension->setTraceReason(request_headers, final_reason);
     372           0 :     } else if (runtime.snapshot().featureEnabled("tracing.random_sampling", *random_sampling,
     373           0 :                                                  result)) {
     374           0 :       final_reason = Tracing::Reason::Sampling;
     375           0 :       rid_extension->setTraceReason(request_headers, final_reason);
     376           0 :     }
     377           0 :   }
     378             : 
     379           0 :   if (final_reason != Tracing::Reason::NotTraceable &&
     380           0 :       !runtime.snapshot().featureEnabled("tracing.global_enabled", *overall_sampling, result)) {
     381           0 :     final_reason = Tracing::Reason::NotTraceable;
     382           0 :     rid_extension->setTraceReason(request_headers, final_reason);
     383           0 :   }
     384             : 
     385           0 :   return final_reason;
     386           0 : }
     387             : 
     388             : void ConnectionManagerUtility::mutateXfccRequestHeader(RequestHeaderMap& request_headers,
     389             :                                                        Network::Connection& connection,
     390         507 :                                                        ConnectionManagerConfig& config) {
     391             :   // When AlwaysForwardOnly is set, always forward the XFCC header without modification.
     392         507 :   if (config.forwardClientCert() == ForwardClientCertType::AlwaysForwardOnly) {
     393           0 :     return;
     394           0 :   }
     395             :   // When Sanitize is set, or the connection is not mutual TLS, remove the XFCC header.
     396         507 :   if (config.forwardClientCert() == ForwardClientCertType::Sanitize ||
     397         507 :       !(connection.ssl() && connection.ssl()->peerCertificatePresented())) {
     398         507 :     request_headers.removeForwardedClientCert();
     399         507 :     return;
     400         507 :   }
     401             : 
     402             :   // When ForwardOnly is set, always forward the XFCC header without modification.
     403           0 :   if (config.forwardClientCert() == ForwardClientCertType::ForwardOnly) {
     404           0 :     return;
     405           0 :   }
     406             : 
     407             :   // TODO(myidpt): Handle the special characters in By and URI fields.
     408             :   // TODO: Optimize client_cert_details based on perf analysis (direct string appending may be more
     409             :   // preferable).
     410           0 :   std::vector<std::string> client_cert_details;
     411             :   // When AppendForward or SanitizeSet is set, the client certificate information should be set into
     412             :   // the XFCC header.
     413           0 :   if (config.forwardClientCert() == ForwardClientCertType::AppendForward ||
     414           0 :       config.forwardClientCert() == ForwardClientCertType::SanitizeSet) {
     415           0 :     const auto uri_sans_local_cert = connection.ssl()->uriSanLocalCertificate();
     416           0 :     if (!uri_sans_local_cert.empty()) {
     417           0 :       for (const std::string& uri : uri_sans_local_cert) {
     418           0 :         client_cert_details.push_back(absl::StrCat("By=", uri));
     419           0 :       }
     420           0 :     }
     421           0 :     const std::string cert_digest = connection.ssl()->sha256PeerCertificateDigest();
     422           0 :     if (!cert_digest.empty()) {
     423           0 :       client_cert_details.push_back(absl::StrCat("Hash=", cert_digest));
     424           0 :     }
     425           0 :     for (const auto& detail : config.setCurrentClientCertDetails()) {
     426           0 :       switch (detail) {
     427           0 :       case ClientCertDetailsType::Cert: {
     428           0 :         const std::string peer_cert = connection.ssl()->urlEncodedPemEncodedPeerCertificate();
     429           0 :         if (!peer_cert.empty()) {
     430           0 :           client_cert_details.push_back(absl::StrCat("Cert=\"", peer_cert, "\""));
     431           0 :         }
     432           0 :         break;
     433           0 :       }
     434           0 :       case ClientCertDetailsType::Chain: {
     435           0 :         const std::string peer_chain = connection.ssl()->urlEncodedPemEncodedPeerCertificateChain();
     436           0 :         if (!peer_chain.empty()) {
     437           0 :           client_cert_details.push_back(absl::StrCat("Chain=\"", peer_chain, "\""));
     438           0 :         }
     439           0 :         break;
     440           0 :       }
     441           0 :       case ClientCertDetailsType::Subject:
     442             :         // The "Subject" key still exists even if the subject is empty.
     443           0 :         client_cert_details.push_back(
     444           0 :             absl::StrCat("Subject=\"", connection.ssl()->subjectPeerCertificate(), "\""));
     445           0 :         break;
     446           0 :       case ClientCertDetailsType::URI: {
     447             :         // The "URI" key still exists even if the URI is empty.
     448           0 :         const auto sans = connection.ssl()->uriSanPeerCertificate();
     449           0 :         if (!sans.empty()) {
     450           0 :           for (const std::string& uri : sans) {
     451           0 :             client_cert_details.push_back(absl::StrCat("URI=", uri));
     452           0 :           }
     453           0 :         } else {
     454           0 :           client_cert_details.push_back("URI=");
     455           0 :         }
     456           0 :         break;
     457           0 :       }
     458           0 :       case ClientCertDetailsType::DNS: {
     459           0 :         auto dns_sans = connection.ssl()->dnsSansPeerCertificate();
     460           0 :         if (!dns_sans.empty()) {
     461           0 :           for (const std::string& dns : dns_sans) {
     462           0 :             client_cert_details.push_back(absl::StrCat("DNS=", dns));
     463           0 :           }
     464           0 :         }
     465           0 :         break;
     466           0 :       }
     467           0 :       }
     468           0 :     }
     469           0 :   }
     470             : 
     471           0 :   const std::string client_cert_details_str = absl::StrJoin(client_cert_details, ";");
     472             : 
     473           0 :   ENVOY_BUG(config.forwardClientCert() == ForwardClientCertType::AppendForward ||
     474           0 :                 config.forwardClientCert() == ForwardClientCertType::SanitizeSet,
     475           0 :             "error in client cert logic");
     476           0 :   if (config.forwardClientCert() == ForwardClientCertType::AppendForward) {
     477           0 :     request_headers.appendForwardedClientCert(client_cert_details_str, ",");
     478           0 :   } else if (config.forwardClientCert() == ForwardClientCertType::SanitizeSet) {
     479           0 :     request_headers.setForwardedClientCert(client_cert_details_str);
     480           0 :   }
     481           0 : }
     482             : 
     483             : void ConnectionManagerUtility::mutateResponseHeaders(ResponseHeaderMap& response_headers,
     484             :                                                      const RequestHeaderMap* request_headers,
     485             :                                                      ConnectionManagerConfig& config,
     486             :                                                      const std::string& via,
     487             :                                                      const StreamInfo::StreamInfo& stream_info,
     488             :                                                      absl::string_view proxy_name,
     489         611 :                                                      bool clear_hop_by_hop) {
     490         611 :   if (request_headers != nullptr && Utility::isUpgrade(*request_headers) &&
     491         611 :       Utility::isUpgrade(response_headers)) {
     492             :     // As in mutateRequestHeaders, Upgrade responses have special handling.
     493             :     //
     494             :     // Unlike mutateRequestHeaders there is no explicit protocol check. If Envoy is proxying an
     495             :     // upgrade response it has already passed the protocol checks.
     496           4 :     const bool no_body =
     497           4 :         (!response_headers.TransferEncoding() && !response_headers.ContentLength());
     498             : 
     499           4 :     const bool is_1xx = CodeUtility::is1xx(Utility::getResponseStatus(response_headers));
     500             : 
     501             :     // We are explicitly forbidden from setting content-length for 1xx responses
     502             :     // (RFC7230, Section 3.3.2). We ignore 204 because this is an upgrade.
     503           4 :     if (no_body && !is_1xx) {
     504           2 :       response_headers.setContentLength(uint64_t(0));
     505           2 :     }
     506         607 :   } else {
     507             :     // Only clear these hop by hop headers for non-upgrade connections.
     508         607 :     if (clear_hop_by_hop) {
     509         607 :       response_headers.removeConnection();
     510         607 :       response_headers.removeUpgrade();
     511         607 :     }
     512         607 :   }
     513         611 :   if (clear_hop_by_hop) {
     514         611 :     response_headers.removeTransferEncoding();
     515         611 :     response_headers.removeKeepAlive();
     516         611 :     response_headers.removeProxyConnection();
     517         611 :   }
     518             : 
     519         611 :   if (request_headers != nullptr &&
     520         611 :       (config.alwaysSetRequestIdInResponse() || request_headers->EnvoyForceTrace())) {
     521           0 :     config.requestIDExtension()->setInResponse(response_headers, *request_headers);
     522           0 :   }
     523         611 :   if (!via.empty()) {
     524           1 :     Utility::appendVia(response_headers, via);
     525           1 :   }
     526             : 
     527         611 :   setProxyStatusHeader(response_headers, config, stream_info, proxy_name);
     528         611 : }
     529             : 
     530             : void ConnectionManagerUtility::setProxyStatusHeader(ResponseHeaderMap& response_headers,
     531             :                                                     const ConnectionManagerConfig& config,
     532             :                                                     const StreamInfo::StreamInfo& stream_info,
     533         611 :                                                     absl::string_view proxy_name) {
     534         611 :   if (auto* proxy_status_config = config.proxyStatusConfig(); proxy_status_config != nullptr) {
     535             :     // Writing the Proxy-Status header is gated on the existence of
     536             :     // |proxy_status_config|. The |details| field and other internals are generated in
     537             :     // fromStreamInfo().
     538           0 :     if (absl::optional<StreamInfo::ProxyStatusError> proxy_status =
     539           0 :             StreamInfo::ProxyStatusUtils::fromStreamInfo(stream_info);
     540           0 :         proxy_status.has_value()) {
     541           0 :       response_headers.appendProxyStatus(
     542           0 :           StreamInfo::ProxyStatusUtils::makeProxyStatusHeader(stream_info, *proxy_status,
     543           0 :                                                               proxy_name, *proxy_status_config),
     544           0 :           ", ");
     545             :       // Apply the recommended response code, if configured and applicable.
     546           0 :       if (proxy_status_config->set_recommended_response_code()) {
     547           0 :         if (absl::optional<Http::Code> response_code =
     548           0 :                 StreamInfo::ProxyStatusUtils::recommendedHttpStatusCode(*proxy_status);
     549           0 :             response_code.has_value()) {
     550           0 :           response_headers.setStatus(std::to_string(enumToInt(*response_code)));
     551           0 :         }
     552           0 :       }
     553           0 :     }
     554           0 :   }
     555         611 : }
     556             : 
     557             : ConnectionManagerUtility::NormalizePathAction
     558             : ConnectionManagerUtility::maybeNormalizePath(RequestHeaderMap& request_headers,
     559         507 :                                              const ConnectionManagerConfig& config) {
     560         507 :   if (!request_headers.Path()) {
     561           0 :     return NormalizePathAction::Continue; // It's as valid as it is going to get.
     562           0 :   }
     563             : 
     564         507 :   auto fragment_pos = request_headers.getPathValue().find('#');
     565         507 :   if (fragment_pos != absl::string_view::npos) {
     566           0 :     if (Runtime::runtimeFeatureEnabled(
     567           0 :             "envoy.reloadable_features.http_reject_path_with_fragment")) {
     568           0 :       return NormalizePathAction::Reject;
     569           0 :     }
     570             :     // Check runtime override and throw away fragment from URI path
     571           0 :     request_headers.setPath(request_headers.getPathValue().substr(0, fragment_pos));
     572           0 :   }
     573             : 
     574         507 :   NormalizePathAction final_action = NormalizePathAction::Continue;
     575         507 :   const auto escaped_slashes_action = config.pathWithEscapedSlashesAction();
     576         507 :   ASSERT(escaped_slashes_action != envoy::extensions::filters::network::http_connection_manager::
     577         507 :                                        v3::HttpConnectionManager::IMPLEMENTATION_SPECIFIC_DEFAULT);
     578         507 :   if (escaped_slashes_action != envoy::extensions::filters::network::http_connection_manager::v3::
     579         507 :                                     HttpConnectionManager::KEEP_UNCHANGED) {
     580           0 :     auto escaped_slashes_result = PathUtil::unescapeSlashes(request_headers);
     581           0 :     if (escaped_slashes_result == PathUtil::UnescapeSlashesResult::FoundAndUnescaped) {
     582           0 :       if (escaped_slashes_action == envoy::extensions::filters::network::http_connection_manager::
     583           0 :                                         v3::HttpConnectionManager::REJECT_REQUEST) {
     584           0 :         return NormalizePathAction::Reject;
     585           0 :       } else if (escaped_slashes_action ==
     586           0 :                  envoy::extensions::filters::network::http_connection_manager::v3::
     587           0 :                      HttpConnectionManager::UNESCAPE_AND_REDIRECT) {
     588           0 :         final_action = NormalizePathAction::Redirect;
     589           0 :       } else {
     590           0 :         ASSERT(escaped_slashes_action ==
     591           0 :                envoy::extensions::filters::network::http_connection_manager::v3::
     592           0 :                    HttpConnectionManager::UNESCAPE_AND_FORWARD);
     593           0 :       }
     594           0 :     }
     595           0 :   }
     596             : 
     597         507 :   if (config.shouldNormalizePath() && !PathUtil::canonicalPath(request_headers)) {
     598           0 :     return NormalizePathAction::Reject;
     599           0 :   }
     600             : 
     601             :   // Merge slashes after path normalization to catch potential edge cases with percent encoding.
     602         507 :   if (config.shouldMergeSlashes()) {
     603          98 :     PathUtil::mergeSlashes(request_headers);
     604          98 :   }
     605             : 
     606         507 :   return final_action;
     607         507 : }
     608             : 
     609             : absl::optional<uint32_t>
     610             : ConnectionManagerUtility::maybeNormalizeHost(RequestHeaderMap& request_headers,
     611         507 :                                              const ConnectionManagerConfig& config, uint32_t port) {
     612         507 :   if (config.shouldStripTrailingHostDot()) {
     613           0 :     HeaderUtility::stripTrailingHostDot(request_headers);
     614           0 :   }
     615         507 :   if (config.stripPortType() == Http::StripPortType::Any) {
     616           0 :     return HeaderUtility::stripPortFromHost(request_headers, absl::nullopt);
     617         507 :   } else if (config.stripPortType() == Http::StripPortType::MatchingHost) {
     618           0 :     return HeaderUtility::stripPortFromHost(request_headers, port);
     619           0 :   }
     620         507 :   return absl::nullopt;
     621         507 : }
     622             : 
     623             : } // namespace Http
     624             : } // namespace Envoy

Generated by: LCOV version 1.15