LCOV - code coverage report
Current view: top level - source/common/http - codec_client.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 147 161 91.3 %
Date: 2024-01-05 06:35:25 Functions: 15 16 93.8 %

          Line data    Source code
       1             : #include "source/common/http/codec_client.h"
       2             : 
       3             : #include <cstdint>
       4             : #include <memory>
       5             : 
       6             : #include "envoy/http/codec.h"
       7             : 
       8             : #include "source/common/common/enum_to_int.h"
       9             : #include "source/common/config/utility.h"
      10             : #include "source/common/http/exception.h"
      11             : #include "source/common/http/http1/codec_impl.h"
      12             : #include "source/common/http/http2/codec_impl.h"
      13             : #include "source/common/http/status.h"
      14             : #include "source/common/http/utility.h"
      15             : 
      16             : #ifdef ENVOY_ENABLE_QUIC
      17             : #include "source/common/quic/client_codec_impl.h"
      18             : #endif
      19             : 
      20             : namespace Envoy {
      21             : namespace Http {
      22             : 
      23             : CodecClient::CodecClient(CodecType type, Network::ClientConnectionPtr&& connection,
      24             :                          Upstream::HostDescriptionConstSharedPtr host,
      25             :                          Event::Dispatcher& dispatcher)
      26             :     : type_(type), host_(host), connection_(std::move(connection)),
      27         365 :       idle_timeout_(host_->cluster().idleTimeout()) {
      28         365 :   if (type_ != CodecType::HTTP3) {
      29             :     // Make sure upstream connections process data and then the FIN, rather than processing
      30             :     // TCP disconnects immediately. (see https://github.com/envoyproxy/envoy/issues/1679 for
      31             :     // details)
      32         365 :     connection_->detectEarlyCloseWhenReadDisabled(false);
      33         365 :   }
      34         365 :   connection_->addConnectionCallbacks(*this);
      35         365 :   connection_->addReadFilter(Network::ReadFilterSharedPtr{new CodecReadFilter(*this)});
      36             : 
      37         365 :   if (idle_timeout_) {
      38         173 :     idle_timer_ = dispatcher.createTimer([this]() -> void { onIdleTimeout(); });
      39         173 :     enableIdleTimer();
      40         173 :   }
      41             : 
      42             :   // We just universally set no delay on connections. Theoretically we might at some point want
      43             :   // to make this configurable.
      44         365 :   connection_->noDelay(true);
      45         365 : }
      46             : 
      47         365 : void CodecClient::connect() {
      48         365 :   ASSERT(!connect_called_);
      49         365 :   connect_called_ = true;
      50         365 :   ASSERT(codec_ != nullptr);
      51             :   // In general, codecs are handed new not-yet-connected connections, but in the
      52             :   // case of ALPN, the codec may be handed an already connected connection.
      53         365 :   if (!connection_->connecting()) {
      54          54 :     ASSERT(connection_->state() == Network::Connection::State::Open);
      55          54 :     connected_ = true;
      56         365 :   } else {
      57         311 :     ENVOY_CONN_LOG(debug, "connecting", *connection_);
      58         311 :     connection_->connect();
      59         311 :   }
      60         365 : }
      61             : 
      62         271 : void CodecClient::close(Network::ConnectionCloseType type) { connection_->close(type); }
      63             : 
      64         398 : void CodecClient::deleteRequest(ActiveRequest& request) {
      65         398 :   connection_->dispatcher().deferredDelete(request.removeFromList(active_requests_));
      66         398 :   if (codec_client_callbacks_) {
      67         188 :     codec_client_callbacks_->onStreamDestroy();
      68         188 :   }
      69         398 :   if (numActiveRequests() == 0) {
      70         398 :     enableIdleTimer();
      71         398 :   }
      72         398 : }
      73             : 
      74         398 : RequestEncoder& CodecClient::newStream(ResponseDecoder& response_decoder) {
      75         398 :   ActiveRequestPtr request(new ActiveRequest(*this, response_decoder));
      76         398 :   request->setEncoder(codec_->newStream(*request));
      77         398 :   LinkedList::moveIntoList(std::move(request), active_requests_);
      78             : 
      79         398 :   auto upstream_info = connection_->streamInfo().upstreamInfo();
      80         398 :   upstream_info->setUpstreamNumStreams(upstream_info->upstreamNumStreams() + 1);
      81             : 
      82         398 :   disableIdleTimer();
      83         398 :   return *active_requests_.front();
      84         398 : }
      85             : 
      86         669 : void CodecClient::onEvent(Network::ConnectionEvent event) {
      87         669 :   if (event == Network::ConnectionEvent::Connected) {
      88         304 :     ENVOY_CONN_LOG(debug, "connected", *connection_);
      89         304 :     connected_ = true;
      90         304 :     return;
      91         304 :   }
      92             : 
      93         365 :   if (event == Network::ConnectionEvent::RemoteClose) {
      94         110 :     remote_closed_ = true;
      95         110 :   }
      96             : 
      97             :   // HTTP/1 can signal end of response by disconnecting. We need to handle that case.
      98         365 :   if (type_ == CodecType::HTTP1 && event == Network::ConnectionEvent::RemoteClose &&
      99         365 :       !active_requests_.empty()) {
     100           8 :     Buffer::OwnedImpl empty;
     101           8 :     onData(empty);
     102           8 :   }
     103             : 
     104         365 :   if (event == Network::ConnectionEvent::RemoteClose ||
     105         365 :       event == Network::ConnectionEvent::LocalClose) {
     106         365 :     ENVOY_CONN_LOG(debug, "disconnect. resetting {} pending requests", *connection_,
     107         365 :                    active_requests_.size());
     108         365 :     disableIdleTimer();
     109         365 :     idle_timer_.reset();
     110         365 :     StreamResetReason reason = event == Network::ConnectionEvent::RemoteClose
     111         365 :                                    ? StreamResetReason::RemoteConnectionFailure
     112         365 :                                    : StreamResetReason::LocalConnectionFailure;
     113         365 :     if (connected_) {
     114         357 :       reason = StreamResetReason::ConnectionTermination;
     115         357 :       if (protocol_error_) {
     116          63 :         reason = StreamResetReason::ProtocolError;
     117          63 :         connection_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamProtocolError);
     118          63 :       }
     119         357 :     }
     120         468 :     while (!active_requests_.empty()) {
     121             :       // Fake resetting all active streams so that reset() callbacks get invoked.
     122         103 :       active_requests_.front()->getStream().resetStream(reason);
     123         103 :     }
     124         365 :   }
     125         365 : }
     126             : 
     127         260 : void CodecClient::responsePreDecodeComplete(ActiveRequest& request) {
     128         260 :   ENVOY_CONN_LOG(debug, "response complete", *connection_);
     129         260 :   if (codec_client_callbacks_) {
     130         105 :     codec_client_callbacks_->onStreamPreDecodeComplete();
     131         105 :   }
     132         260 :   request.decode_complete_ = true;
     133         260 :   if (request.encode_complete_ || !request.wait_encode_complete_) {
     134         235 :     completeRequest(request);
     135         235 :   } else {
     136          25 :     ENVOY_CONN_LOG(debug, "waiting for encode to complete", *connection_);
     137          25 :   }
     138         260 : }
     139             : 
     140         343 : void CodecClient::requestEncodeComplete(ActiveRequest& request) {
     141         343 :   ENVOY_CONN_LOG(debug, "encode complete", *connection_);
     142         343 :   request.encode_complete_ = true;
     143         343 :   if (request.decode_complete_) {
     144           0 :     completeRequest(request);
     145           0 :   }
     146         343 : }
     147             : 
     148         235 : void CodecClient::completeRequest(ActiveRequest& request) {
     149         235 :   deleteRequest(request);
     150             : 
     151             :   // HTTP/2 can send us a reset after a complete response if the request was not complete. Users
     152             :   // of CodecClient will deal with the premature response case and we should not handle any
     153             :   // further reset notification.
     154         235 :   request.removeEncoderCallbacks();
     155         235 : }
     156             : 
     157         163 : void CodecClient::onReset(ActiveRequest& request, StreamResetReason reason) {
     158         163 :   ENVOY_CONN_LOG(debug, "request reset", *connection_);
     159         163 :   if (codec_client_callbacks_) {
     160         108 :     codec_client_callbacks_->onStreamReset(reason);
     161         108 :   }
     162             : 
     163         163 :   deleteRequest(request);
     164         163 : }
     165             : 
     166         683 : void CodecClient::onData(Buffer::Instance& data) {
     167         683 :   const Status status = codec_->dispatch(data);
     168             : 
     169         683 :   if (!status.ok()) {
     170          68 :     ENVOY_CONN_LOG(debug, "Error dispatching received data: {}", *connection_, status.message());
     171             : 
     172             :     // Don't count 408 responses where we have no active requests as protocol errors
     173          68 :     if (!isPrematureResponseError(status) ||
     174          68 :         (!active_requests_.empty() ||
     175          68 :          getPrematureResponseHttpCode(status) != Code::RequestTimeout)) {
     176          68 :       host_->cluster().trafficStats()->upstream_cx_protocol_error_.inc();
     177          68 :       protocol_error_ = true;
     178          68 :     }
     179          68 :     close();
     180          68 :   }
     181             : 
     182             :   // All data should be consumed at this point if the connection remains open.
     183         683 :   ASSERT(data.length() == 0 || connection_->state() != Network::Connection::State::Open,
     184         683 :          absl::StrCat("extraneous bytes after response complete: ", data.length()));
     185         683 : }
     186             : 
     187         398 : Status CodecClient::ActiveRequest::encodeHeaders(const RequestHeaderMap& headers, bool end_stream) {
     188             : #ifdef ENVOY_ENABLE_UHV
     189             :   if (header_validator_) {
     190             :     bool failure = false;
     191             :     std::string failure_details;
     192             :     auto transformation_result = header_validator_->transformRequestHeaders(headers);
     193             :     if (!transformation_result.status.ok()) {
     194             :       ENVOY_CONN_LOG(debug, "Request header transformation failed: {}\n{}", *parent_.connection_,
     195             :                      transformation_result.status.details(), headers);
     196             :       failure = true;
     197             :       failure_details = std::string(transformation_result.status.details());
     198             :     } else {
     199             :       // Validate header map after request encoder transformations
     200             :       const ::Envoy::Http::HeaderValidator::ValidationResult validation_result =
     201             :           header_validator_->validateRequestHeaders(
     202             :               transformation_result.new_headers ? *transformation_result.new_headers : headers);
     203             :       if (!validation_result.ok()) {
     204             :         ENVOY_CONN_LOG(debug, "Request header validation failed: {}\n{}", *parent_.connection_,
     205             :                        validation_result.details(),
     206             :                        transformation_result.new_headers ? *transformation_result.new_headers
     207             :                                                          : headers);
     208             :         failure = true;
     209             :         failure_details = std::string(validation_result.details());
     210             :       }
     211             :     }
     212             :     if (failure) {
     213             :       return absl::InvalidArgumentError(
     214             :           absl::StrCat("header validation failed: ", failure_details));
     215             :     }
     216             :     return RequestEncoderWrapper::encodeHeaders(
     217             :         transformation_result.new_headers ? *transformation_result.new_headers : headers,
     218             :         end_stream);
     219             :   }
     220             : #endif
     221         398 :   return RequestEncoderWrapper::encodeHeaders(headers, end_stream);
     222         398 : }
     223             : 
     224         305 : void CodecClient::ActiveRequest::decodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream) {
     225             : #ifdef ENVOY_ENABLE_UHV
     226             :   if (header_validator_) {
     227             :     const ::Envoy::Http::HeaderValidator::ValidationResult validation_result =
     228             :         header_validator_->validateResponseHeaders(*headers);
     229             :     bool failure = !validation_result.ok();
     230             :     std::string failure_details(validation_result.details());
     231             :     if (!failure) {
     232             :       const ::Envoy::Http::ClientHeaderValidator::TransformationResult transformation_result =
     233             :           header_validator_->transformResponseHeaders(*headers);
     234             :       failure = !transformation_result.ok();
     235             :       failure_details = std::string(validation_result.details());
     236             :     }
     237             :     if (failure) {
     238             :       ENVOY_CONN_LOG(debug, "Response header validation failed: {}\n{}", *parent_.connection_,
     239             :                      failure_details, *headers);
     240             :       if ((parent_.codec_->protocol() == Protocol::Http2 &&
     241             :            !parent_.host_->cluster()
     242             :                 .http2Options()
     243             :                 .override_stream_error_on_invalid_http_message()
     244             :                 .value()) ||
     245             :           (parent_.codec_->protocol() == Protocol::Http3 &&
     246             :            !parent_.host_->cluster()
     247             :                 .http3Options()
     248             :                 .override_stream_error_on_invalid_http_message()
     249             :                 .value())) {
     250             :         parent_.protocol_error_ = true;
     251             :         parent_.close();
     252             :       } else {
     253             :         inner_encoder_->getStream().resetStream(StreamResetReason::ProtocolError);
     254             :       }
     255             :       return;
     256             :     }
     257             :   }
     258             : #endif
     259         305 :   ResponseDecoderWrapper::decodeHeaders(std::move(headers), end_stream);
     260         305 : }
     261             : 
     262             : CodecClientProd::CodecClientProd(CodecType type, Network::ClientConnectionPtr&& connection,
     263             :                                  Upstream::HostDescriptionConstSharedPtr host,
     264             :                                  Event::Dispatcher& dispatcher,
     265             :                                  Random::RandomGenerator& random_generator,
     266             :                                  const Network::TransportSocketOptionsConstSharedPtr& options)
     267             :     : NoConnectCodecClientProd(type, std::move(connection), host, dispatcher, random_generator,
     268         311 :                                options) {
     269         311 :   connect();
     270         311 : }
     271             : 
     272             : NoConnectCodecClientProd::NoConnectCodecClientProd(
     273             :     CodecType type, Network::ClientConnectionPtr&& connection,
     274             :     Upstream::HostDescriptionConstSharedPtr host, Event::Dispatcher& dispatcher,
     275             :     Random::RandomGenerator& random_generator,
     276             :     const Network::TransportSocketOptionsConstSharedPtr& options)
     277         311 :     : CodecClient(type, std::move(connection), host, dispatcher) {
     278         311 :   switch (type) {
     279         166 :   case CodecType::HTTP1: {
     280             :     // If the transport socket indicates this is being proxied, inform the HTTP/1.1 codec. It will
     281             :     // send fully qualified URLs iff the underlying transport is plaintext.
     282         166 :     bool proxied = false;
     283         166 :     if (options && options->http11ProxyInfo().has_value()) {
     284           0 :       proxied = true;
     285           0 :     }
     286         166 :     codec_ = std::make_unique<Http1::ClientConnectionImpl>(
     287         166 :         *connection_, host->cluster().http1CodecStats(), *this, host->cluster().http1Settings(),
     288         166 :         host->cluster().maxResponseHeadersCount(), proxied);
     289         166 :     break;
     290           0 :   }
     291         145 :   case CodecType::HTTP2:
     292         145 :     codec_ = std::make_unique<Http2::ClientConnectionImpl>(
     293         145 :         *connection_, *this, host->cluster().http2CodecStats(), random_generator,
     294         145 :         host->cluster().http2Options(), Http::DEFAULT_MAX_REQUEST_HEADERS_KB,
     295         145 :         host->cluster().maxResponseHeadersCount(), Http2::ProdNghttp2SessionFactory::get());
     296         145 :     break;
     297           0 :   case CodecType::HTTP3: {
     298           0 : #ifdef ENVOY_ENABLE_QUIC
     299           0 :     auto& quic_session = dynamic_cast<Quic::EnvoyQuicClientSession&>(*connection_);
     300           0 :     codec_ = std::make_unique<Quic::QuicHttpClientConnectionImpl>(
     301           0 :         quic_session, *this, host->cluster().http3CodecStats(), host->cluster().http3Options(),
     302           0 :         Http::DEFAULT_MAX_REQUEST_HEADERS_KB, host->cluster().maxResponseHeadersCount());
     303             :     // Initialize the session after max request header size is changed in above http client
     304             :     // connection creation.
     305           0 :     quic_session.Initialize();
     306           0 :     break;
     307             : #else
     308             :     // Should be blocked by configuration checking at an earlier point.
     309             :     PANIC("unexpected");
     310             : #endif
     311           0 :   }
     312         311 :   }
     313         311 : }
     314             : 
     315             : } // namespace Http
     316             : } // namespace Envoy

Generated by: LCOV version 1.15