LCOV - code coverage report
Current view: top level - source/common/http/http2 - codec_impl.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 1070 1500 71.3 %
Date: 2024-01-05 06:35:25 Functions: 110 146 75.3 %

          Line data    Source code
       1             : #include "source/common/http/http2/codec_impl.h"
       2             : 
       3             : #include <algorithm>
       4             : #include <cstdint>
       5             : #include <memory>
       6             : #include <ostream>
       7             : #include <vector>
       8             : 
       9             : #include "envoy/event/dispatcher.h"
      10             : #include "envoy/http/codes.h"
      11             : #include "envoy/http/header_map.h"
      12             : #include "envoy/network/connection.h"
      13             : 
      14             : #include "source/common/common/assert.h"
      15             : #include "source/common/common/cleanup.h"
      16             : #include "source/common/common/dump_state_utils.h"
      17             : #include "source/common/common/enum_to_int.h"
      18             : #include "source/common/common/fmt.h"
      19             : #include "source/common/common/safe_memcpy.h"
      20             : #include "source/common/common/scope_tracker.h"
      21             : #include "source/common/common/utility.h"
      22             : #include "source/common/http/codes.h"
      23             : #include "source/common/http/exception.h"
      24             : #include "source/common/http/header_utility.h"
      25             : #include "source/common/http/headers.h"
      26             : #include "source/common/http/http2/codec_stats.h"
      27             : #include "source/common/http/utility.h"
      28             : #include "source/common/runtime/runtime_features.h"
      29             : 
      30             : #include "absl/cleanup/cleanup.h"
      31             : #include "absl/container/fixed_array.h"
      32             : #include "quiche/http2/adapter/callback_visitor.h"
      33             : #include "quiche/http2/adapter/nghttp2_adapter.h"
      34             : #include "quiche/http2/adapter/oghttp2_adapter.h"
      35             : 
      36             : namespace Envoy {
      37             : namespace Http {
      38             : namespace Http2 {
      39             : 
      40             : // Changes or additions to details should be reflected in
      41             : // docs/root/configuration/http/http_conn_man/response_code_details.rst
      42             : class Http2ResponseCodeDetailValues {
      43             : public:
      44             :   // Invalid HTTP header field was received and stream is going to be
      45             :   // closed.
      46             :   const absl::string_view ng_http2_err_http_header_ = "http2.invalid.header.field";
      47             :   // Violation in HTTP messaging rule.
      48             :   const absl::string_view ng_http2_err_http_messaging_ = "http2.violation.of.messaging.rule";
      49             :   // none of the above
      50             :   const absl::string_view ng_http2_err_unknown_ = "http2.unknown.nghttp2.error";
      51             :   // The number of headers (or trailers) exceeded the configured limits
      52             :   const absl::string_view too_many_headers = "http2.too_many_headers";
      53             :   // Envoy detected an HTTP/2 frame flood from the server.
      54             :   const absl::string_view outbound_frame_flood = "http2.outbound_frames_flood";
      55             :   // Envoy detected an inbound HTTP/2 frame flood.
      56             :   const absl::string_view inbound_empty_frame_flood = "http2.inbound_empty_frames_flood";
      57             :   // Envoy was configured to drop requests with header keys beginning with underscores.
      58             :   const absl::string_view invalid_underscore = "http2.unexpected_underscore";
      59             :   // The peer refused the stream.
      60             :   const absl::string_view remote_refused = "http2.remote_refuse";
      61             :   // The peer reset the stream.
      62             :   const absl::string_view remote_reset = "http2.remote_reset";
      63             : 
      64         128 :   const absl::string_view errorDetails(int error_code) const {
      65         128 :     switch (error_code) {
      66          61 :     case NGHTTP2_ERR_HTTP_HEADER:
      67          61 :       return ng_http2_err_http_header_;
      68          67 :     case NGHTTP2_ERR_HTTP_MESSAGING:
      69          67 :       return ng_http2_err_http_messaging_;
      70           0 :     default:
      71           0 :       return ng_http2_err_unknown_;
      72         128 :     }
      73         128 :   }
      74             : };
      75             : 
      76         133 : int reasonToReset(StreamResetReason reason) {
      77         133 :   switch (reason) {
      78           4 :   case StreamResetReason::LocalRefusedStreamReset:
      79           4 :     return NGHTTP2_REFUSED_STREAM;
      80           0 :   case StreamResetReason::ConnectError:
      81           0 :     return NGHTTP2_CONNECT_ERROR;
      82         129 :   default:
      83         129 :     return NGHTTP2_NO_ERROR;
      84         133 :   }
      85         133 : }
      86             : 
      87             : using Http2ResponseCodeDetails = ConstSingleton<Http2ResponseCodeDetailValues>;
      88             : 
      89         903 : ReceivedSettingsImpl::ReceivedSettingsImpl(const nghttp2_settings& settings) {
      90        1420 :   for (uint32_t i = 0; i < settings.niv; ++i) {
      91         804 :     if (settings.iv[i].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
      92         287 :       concurrent_stream_limit_ = settings.iv[i].value;
      93         287 :       break;
      94         287 :     }
      95         804 :   }
      96         903 : }
      97             : 
      98             : bool Utility::reconstituteCrumbledCookies(const HeaderString& key, const HeaderString& value,
      99       24931 :                                           HeaderString& cookies) {
     100       24931 :   if (key != Headers::get().Cookie.get().c_str()) {
     101        6172 :     return false;
     102        6172 :   }
     103             : 
     104       18759 :   if (!cookies.empty()) {
     105       18739 :     cookies.append("; ", 2);
     106       18739 :   }
     107             : 
     108       18759 :   const absl::string_view value_view = value.getStringView();
     109       18759 :   cookies.append(value_view.data(), value_view.size());
     110       18759 :   return true;
     111       24931 : }
     112             : 
     113             : ConnectionImpl::Http2Callbacks ConnectionImpl::http2_callbacks_;
     114             : 
     115             : std::unique_ptr<http2::adapter::Http2Adapter>
     116             : ProdNghttp2SessionFactory::create(const nghttp2_session_callbacks* callbacks,
     117             :                                   ConnectionImpl* connection,
     118         305 :                                   const http2::adapter::OgHttp2Adapter::Options& options) {
     119         305 :   auto visitor = std::make_unique<http2::adapter::CallbackVisitor>(
     120         305 :       http2::adapter::Perspective::kClient, *callbacks, connection);
     121         305 :   std::unique_ptr<http2::adapter::Http2Adapter> adapter =
     122         305 :       http2::adapter::OgHttp2Adapter::Create(*visitor, options);
     123         305 :   connection->setVisitor(std::move(visitor));
     124         305 :   return adapter;
     125         305 : }
     126             : 
     127             : std::unique_ptr<http2::adapter::Http2Adapter>
     128             : ProdNghttp2SessionFactory::create(const nghttp2_session_callbacks* callbacks,
     129          56 :                                   ConnectionImpl* connection, const nghttp2_option* options) {
     130          56 :   auto visitor = std::make_unique<http2::adapter::CallbackVisitor>(
     131          56 :       http2::adapter::Perspective::kClient, *callbacks, connection);
     132          56 :   auto adapter = http2::adapter::NgHttp2Adapter::CreateClientAdapter(*visitor, options);
     133          56 :   auto stream_close_listener = [p = adapter.get()](http2::adapter::Http2StreamId stream_id) {
     134           9 :     p->RemoveStream(stream_id);
     135           9 :   };
     136          56 :   visitor->set_stream_close_listener(std::move(stream_close_listener));
     137          56 :   connection->setVisitor(std::move(visitor));
     138          56 :   return adapter;
     139          56 : }
     140             : 
     141             : void ProdNghttp2SessionFactory::init(ConnectionImpl* connection,
     142         361 :                                      const envoy::config::core::v3::Http2ProtocolOptions& options) {
     143         361 :   connection->sendSettings(options, true);
     144         361 : }
     145             : 
     146             : /**
     147             :  * Helper to remove const during a cast. nghttp2 takes non-const pointers for headers even though
     148             :  * it copies them.
     149             :  */
     150           0 : template <typename T> static T* removeConst(const void* object) {
     151           0 :   return const_cast<T*>(reinterpret_cast<const T*>(object));
     152           0 : }
     153             : 
     154             : ConnectionImpl::StreamImpl::StreamImpl(ConnectionImpl& parent, uint32_t buffer_limit)
     155             :     : MultiplexedStreamImplBase(parent.connection_.dispatcher()), parent_(parent),
     156             :       pending_recv_data_(parent_.connection_.dispatcher().getWatermarkFactory().createBuffer(
     157           0 :           [this]() -> void { this->pendingRecvBufferLowWatermark(); },
     158           0 :           [this]() -> void { this->pendingRecvBufferHighWatermark(); },
     159           0 :           []() -> void { /* TODO(adisuissa): Handle overflow watermark */ })),
     160             :       pending_send_data_(parent_.connection_.dispatcher().getWatermarkFactory().createBuffer(
     161          70 :           [this]() -> void { this->pendingSendBufferLowWatermark(); },
     162          74 :           [this]() -> void { this->pendingSendBufferHighWatermark(); },
     163           0 :           []() -> void { /* TODO(adisuissa): Handle overflow watermark */ })),
     164             :       local_end_stream_sent_(false), remote_end_stream_(false), remote_rst_(false),
     165             :       data_deferred_(false), received_noninformational_headers_(false),
     166             :       pending_receive_buffer_high_watermark_called_(false),
     167             :       pending_send_buffer_high_watermark_called_(false), reset_due_to_messaging_error_(false),
     168             :       defer_processing_backedup_streams_(
     169             :           Runtime::runtimeFeatureEnabled(Runtime::defer_processing_backedup_streams)),
     170        1974 :       extend_stream_lifetime_flag_(false) {
     171        1974 :   parent_.stats_.streams_active_.inc();
     172        1974 :   if (buffer_limit > 0) {
     173        1974 :     setWriteBufferWatermarks(buffer_limit);
     174        1974 :   }
     175        1974 :   stream_manager_.defer_processing_segment_size_ = parent.connection_.bufferLimit();
     176        1974 : }
     177             : 
     178        1974 : void ConnectionImpl::StreamImpl::destroy() {
     179             :   // Cancel any pending buffered data callback for the stream.
     180        1974 :   process_buffered_data_callback_.reset();
     181             : 
     182        1974 :   MultiplexedStreamImplBase::destroy();
     183        1974 :   parent_.stats_.streams_active_.dec();
     184        1974 :   parent_.stats_.pending_send_bytes_.sub(pending_send_data_->length());
     185        1974 : }
     186             : 
     187        1499 : void ConnectionImpl::ServerStreamImpl::destroy() {
     188             :   // Only the downstream stream should clear the downstream of the
     189             :   // memory account.
     190             :   // This occurs in destroy as we want to ensure the Stream does not get
     191             :   // reset called on it from the account.
     192             :   //
     193             :   // There are cases where a corresponding upstream stream dtor might
     194             :   // be called, but the downstream stream isn't going to terminate soon
     195             :   // such as StreamDecoderFilterCallbacks::recreateStream().
     196        1499 :   if (buffer_memory_account_) {
     197           0 :     buffer_memory_account_->clearDownstream();
     198           0 :   }
     199             : 
     200        1499 :   StreamImpl::destroy();
     201        1499 : }
     202             : 
     203           0 : static void insertHeader(std::vector<nghttp2_nv>& headers, const HeaderEntry& header) {
     204           0 :   uint8_t flags = 0;
     205           0 :   if (header.key().isReference()) {
     206           0 :     flags |= NGHTTP2_NV_FLAG_NO_COPY_NAME;
     207           0 :   }
     208           0 :   if (header.value().isReference()) {
     209           0 :     flags |= NGHTTP2_NV_FLAG_NO_COPY_VALUE;
     210           0 :   }
     211           0 :   const absl::string_view header_key = header.key().getStringView();
     212           0 :   const absl::string_view header_value = header.value().getStringView();
     213           0 :   headers.push_back({removeConst<uint8_t>(header_key.data()),
     214           0 :                      removeConst<uint8_t>(header_value.data()), header_key.size(),
     215           0 :                      header_value.size(), flags});
     216           0 : }
     217             : 
     218             : void ConnectionImpl::StreamImpl::buildHeaders(std::vector<nghttp2_nv>& final_headers,
     219           0 :                                               const HeaderMap& headers) {
     220           0 :   final_headers.reserve(headers.size());
     221           0 :   headers.iterate([&final_headers](const HeaderEntry& header) -> HeaderMap::Iterate {
     222           0 :     insertHeader(final_headers, header);
     223           0 :     return HeaderMap::Iterate::Continue;
     224           0 :   });
     225           0 : }
     226             : 
     227        7088 : http2::adapter::HeaderRep getRep(const HeaderString& str) {
     228        7088 :   if (str.isReference()) {
     229        4023 :     return str.getStringView();
     230        4493 :   } else {
     231        3065 :     return std::string(str.getStringView());
     232        3065 :   }
     233        7088 : }
     234             : 
     235             : std::vector<http2::adapter::Header>
     236         743 : ConnectionImpl::StreamImpl::buildHeaders(const HeaderMap& headers) {
     237         743 :   std::vector<http2::adapter::Header> out;
     238         743 :   out.reserve(headers.size());
     239        3544 :   headers.iterate([&out](const HeaderEntry& header) -> HeaderMap::Iterate {
     240        3544 :     out.push_back({getRep(header.key()), getRep(header.value())});
     241        3544 :     return HeaderMap::Iterate::Continue;
     242        3544 :   });
     243         743 :   return out;
     244         743 : }
     245             : 
     246           8 : void ConnectionImpl::ServerStreamImpl::encode1xxHeaders(const ResponseHeaderMap& headers) {
     247           8 :   ASSERT(HeaderUtility::isSpecial1xx(headers));
     248           8 :   encodeHeaders(headers, false);
     249           8 : }
     250             : 
     251         727 : void ConnectionImpl::StreamImpl::encodeHeadersBase(const HeaderMap& headers, bool end_stream) {
     252         727 :   local_end_stream_ = end_stream;
     253         727 :   submitHeaders(headers, end_stream);
     254         727 :   if (parent_.sendPendingFramesAndHandleError()) {
     255             :     // Intended to check through coverage that this error case is tested
     256           0 :     return;
     257           0 :   }
     258         727 : }
     259             : 
     260             : Status ConnectionImpl::ClientStreamImpl::encodeHeaders(const RequestHeaderMap& headers,
     261         475 :                                                        bool end_stream) {
     262         475 :   parent_.updateActiveStreamsOnEncode(*this);
     263         475 : #ifndef ENVOY_ENABLE_UHV
     264             :   // Headers are now validated by UHV before encoding by the codec. Two checks below are not needed
     265             :   // when UHV is enabled.
     266             :   //
     267             :   // Required headers must be present. This can only happen by some erroneous processing after the
     268             :   // downstream codecs decode.
     269         475 :   RETURN_IF_ERROR(HeaderUtility::checkRequiredRequestHeaders(headers));
     270             :   // Verify that a filter hasn't added an invalid header key or value.
     271         469 :   RETURN_IF_ERROR(HeaderUtility::checkValidRequestHeaders(headers));
     272             :   // Extended CONNECT to H/1 upgrade transformation has moved to UHV
     273             :   // This must exist outside of the scope of isUpgrade as the underlying memory is
     274             :   // needed until encodeHeadersBase has been called.
     275         459 :   Http::RequestHeaderMapPtr modified_headers;
     276         459 :   if (Http::Utility::isUpgrade(headers)) {
     277           4 :     modified_headers = createHeaderMap<RequestHeaderMapImpl>(headers);
     278           4 :     upgrade_type_ = std::string(headers.getUpgradeValue());
     279           4 :     Http::Utility::transformUpgradeRequestFromH1toH2(*modified_headers);
     280           4 :     encodeHeadersBase(*modified_headers, end_stream);
     281         455 :   } else if (headers.Method() && headers.Method()->value() == "CONNECT") {
     282           0 :     modified_headers = createHeaderMap<RequestHeaderMapImpl>(headers);
     283           0 :     modified_headers->removeScheme();
     284           0 :     modified_headers->removePath();
     285           0 :     modified_headers->removeProtocol();
     286           0 :     encodeHeadersBase(*modified_headers, end_stream);
     287         455 :   } else {
     288         455 :     encodeHeadersBase(headers, end_stream);
     289         455 :   }
     290             : #else
     291             :   encodeHeadersBase(headers, end_stream);
     292             : #endif
     293         459 :   return okStatus();
     294         469 : }
     295             : 
     296             : void ConnectionImpl::ServerStreamImpl::encodeHeaders(const ResponseHeaderMap& headers,
     297         268 :                                                      bool end_stream) {
     298         268 :   parent_.updateActiveStreamsOnEncode(*this);
     299             :   // The contract is that client codecs must ensure that :status is present.
     300         268 :   ASSERT(headers.Status() != nullptr);
     301             : 
     302         268 : #ifndef ENVOY_ENABLE_UHV
     303             :   // Extended CONNECT to H/1 upgrade transformation has moved to UHV
     304             :   // This must exist outside of the scope of isUpgrade as the underlying memory is
     305             :   // needed until encodeHeadersBase has been called.
     306         268 :   Http::ResponseHeaderMapPtr modified_headers;
     307         268 :   if (Http::Utility::isUpgrade(headers)) {
     308           2 :     modified_headers = createHeaderMap<ResponseHeaderMapImpl>(headers);
     309           2 :     Http::Utility::transformUpgradeResponseFromH1toH2(*modified_headers);
     310           2 :     encodeHeadersBase(*modified_headers, end_stream);
     311         266 :   } else {
     312         266 :     encodeHeadersBase(headers, end_stream);
     313         266 :   }
     314             : #else
     315             :   encodeHeadersBase(headers, end_stream);
     316             : #endif
     317         268 : }
     318             : 
     319          58 : void ConnectionImpl::StreamImpl::encodeTrailersBase(const HeaderMap& trailers) {
     320          58 :   parent_.updateActiveStreamsOnEncode(*this);
     321          58 :   ASSERT(!local_end_stream_);
     322          58 :   local_end_stream_ = true;
     323          58 :   if (pending_send_data_->length() > 0) {
     324             :     // In this case we want trailers to come after we release all pending body data that is
     325             :     // waiting on window updates. We need to save the trailers so that we can emit them later.
     326             :     // However, for empty trailers, we don't need to to save the trailers.
     327           2 :     ASSERT(!pending_trailers_to_encode_);
     328           2 :     const bool skip_encoding_empty_trailers = trailers.empty();
     329           2 :     if (!skip_encoding_empty_trailers) {
     330           0 :       pending_trailers_to_encode_ = cloneTrailers(trailers);
     331           0 :       onLocalEndStream();
     332           0 :     }
     333          56 :   } else {
     334          56 :     submitTrailers(trailers);
     335          56 :     if (parent_.sendPendingFramesAndHandleError()) {
     336             :       // Intended to check through coverage that this error case is tested
     337           0 :       return;
     338           0 :     }
     339          56 :   }
     340          58 : }
     341             : 
     342           8 : void ConnectionImpl::StreamImpl::encodeMetadata(const MetadataMapVector& metadata_map_vector) {
     343           8 :   parent_.updateActiveStreamsOnEncode(*this);
     344           8 :   ASSERT(parent_.allow_metadata_);
     345           8 :   NewMetadataEncoder& metadata_encoder = getMetadataEncoder();
     346           8 :   auto sources_vec = metadata_encoder.createSources(metadata_map_vector);
     347           8 :   for (auto& source : sources_vec) {
     348           8 :     parent_.adapter_->SubmitMetadata(stream_id_, 16 * 1024, std::move(source));
     349           8 :   }
     350             : 
     351           8 :   if (parent_.sendPendingFramesAndHandleError()) {
     352             :     // Intended to check through coverage that this error case is tested
     353           0 :     return;
     354           0 :   }
     355           8 : }
     356             : 
     357          16 : void ConnectionImpl::StreamImpl::processBufferedData() {
     358          16 :   ENVOY_CONN_LOG(debug, "Stream {} processing buffered data.", parent_.connection_, stream_id_);
     359             : 
     360             :   // Restore crash dump context when processing buffered data.
     361          16 :   Event::Dispatcher& dispatcher = parent_.connection_.dispatcher();
     362             :   // This method is only called from a callback placed directly on the
     363             :   // dispatcher, as such the dispatcher shouldn't have any tracked objects.
     364          16 :   ASSERT(dispatcher.trackedObjectStackIsEmpty());
     365          16 :   Envoy::ScopeTrackedObjectStack stack;
     366          16 :   stack.add(parent_.connection_);
     367             : 
     368          16 :   absl::Cleanup clear_current_stream_id = [this]() { parent_.current_stream_id_.reset(); };
     369             :   // TODO(kbaichoo): When we add support to *ConnectionImpl::getStream* for
     370             :   // deferred closed streams we can use their stream id here.
     371          16 :   if (!stream_manager_.buffered_on_stream_close_) {
     372           0 :     ASSERT(!parent_.current_stream_id_.has_value());
     373           0 :     parent_.current_stream_id_ = stream_id_;
     374           0 :   }
     375             : 
     376          16 :   stack.add(parent_);
     377          16 :   ScopeTrackerScopeState scope{&stack, dispatcher};
     378             : 
     379          16 :   if (stream_manager_.body_buffered_ && continueProcessingBufferedData()) {
     380          16 :     decodeData();
     381          16 :   }
     382             : 
     383          16 :   if (stream_manager_.trailers_buffered_ && !stream_manager_.body_buffered_ &&
     384          16 :       continueProcessingBufferedData()) {
     385           0 :     decodeTrailers();
     386           0 :     ASSERT(!stream_manager_.trailers_buffered_);
     387           0 :   }
     388             : 
     389             :   // Reset cases are handled by resetStream and directly invoke onStreamClose,
     390             :   // which consumes the buffered_on_stream_close_ so we don't invoke
     391             :   // onStreamClose twice.
     392          16 :   if (stream_manager_.buffered_on_stream_close_ && !stream_manager_.hasBufferedBodyOrTrailers()) {
     393          16 :     ASSERT(!reset_reason_.has_value());
     394          16 :     ENVOY_CONN_LOG(debug, "invoking onStreamClose for stream: {} via processBufferedData",
     395          16 :                    parent_.connection_, stream_id_);
     396             :     // We only buffer the onStreamClose if we had no errors.
     397          16 :     if (Status status = parent_.onStreamClose(this, 0); !status.ok()) {
     398           0 :       ENVOY_CONN_LOG(debug, "error invoking onStreamClose: {}", parent_.connection_,
     399           0 :                      status.message());
     400           0 :     }
     401          16 :   }
     402          16 : }
     403             : 
     404          37 : void ConnectionImpl::StreamImpl::grantPeerAdditionalStreamWindow() {
     405          37 :   parent_.adapter_->MarkDataConsumedForStream(stream_id_, unconsumed_bytes_);
     406          37 :   unconsumed_bytes_ = 0;
     407          37 :   if (parent_.sendPendingFramesAndHandleError()) {
     408             :     // Intended to check through coverage that this error case is tested
     409           0 :     return;
     410           0 :   }
     411          37 : }
     412             : 
     413          82 : void ConnectionImpl::StreamImpl::readDisable(bool disable) {
     414          82 :   ENVOY_CONN_LOG(debug, "Stream {} {}, unconsumed_bytes {} read_disable_count {}",
     415          82 :                  parent_.connection_, stream_id_, (disable ? "disabled" : "enabled"),
     416          82 :                  unconsumed_bytes_, read_disable_count_);
     417          82 :   if (disable) {
     418          45 :     ++read_disable_count_;
     419          45 :   } else {
     420          37 :     ASSERT(read_disable_count_ > 0);
     421          37 :     --read_disable_count_;
     422          37 :     if (!buffersOverrun()) {
     423          37 :       scheduleProcessingOfBufferedData(false);
     424          37 :       if (shouldAllowPeerAdditionalStreamWindow()) {
     425          37 :         grantPeerAdditionalStreamWindow();
     426          37 :       }
     427          37 :     }
     428          37 :   }
     429          82 : }
     430             : 
     431          37 : void ConnectionImpl::StreamImpl::scheduleProcessingOfBufferedData(bool schedule_next_iteration) {
     432          37 :   if (defer_processing_backedup_streams_ && stream_manager_.hasBufferedBodyOrTrailers()) {
     433          16 :     if (!process_buffered_data_callback_) {
     434          16 :       process_buffered_data_callback_ = parent_.connection_.dispatcher().createSchedulableCallback(
     435          16 :           [this]() { processBufferedData(); });
     436          16 :     }
     437             : 
     438             :     // We schedule processing to occur in another callback to avoid
     439             :     // reentrant and deep call stacks.
     440          16 :     if (schedule_next_iteration) {
     441           0 :       process_buffered_data_callback_->scheduleCallbackNextIteration();
     442          16 :     } else {
     443          16 :       process_buffered_data_callback_->scheduleCallbackCurrentIteration();
     444          16 :     }
     445          16 :   }
     446          37 : }
     447             : 
     448           0 : void ConnectionImpl::StreamImpl::pendingRecvBufferHighWatermark() {
     449             :   // If `defer_processing_backedup_streams_`, read disabling here can become
     450             :   // dangerous as it can prevent us from processing buffered data.
     451           0 :   if (!defer_processing_backedup_streams_) {
     452           0 :     ENVOY_CONN_LOG(debug, "recv buffer over limit ", parent_.connection_);
     453           0 :     ASSERT(!pending_receive_buffer_high_watermark_called_);
     454           0 :     pending_receive_buffer_high_watermark_called_ = true;
     455           0 :     readDisable(true);
     456           0 :   }
     457           0 : }
     458             : 
     459           0 : void ConnectionImpl::StreamImpl::pendingRecvBufferLowWatermark() {
     460             :   // If `defer_processing_backedup_streams_`, we don't read disable on
     461             :   // high watermark, so we shouldn't read disable here.
     462           0 :   if (defer_processing_backedup_streams_) {
     463           0 :     if (shouldAllowPeerAdditionalStreamWindow()) {
     464             :       // We should grant additional stream window here, in case the
     465             :       // `pending_recv_buffer_` was blocking flow control updates
     466             :       // from going to the peer.
     467           0 :       grantPeerAdditionalStreamWindow();
     468           0 :     }
     469           0 :   } else {
     470           0 :     ENVOY_CONN_LOG(debug, "recv buffer under limit ", parent_.connection_);
     471           0 :     ASSERT(pending_receive_buffer_high_watermark_called_);
     472           0 :     pending_receive_buffer_high_watermark_called_ = false;
     473           0 :     readDisable(false);
     474           0 :   }
     475           0 : }
     476             : 
     477       22198 : void ConnectionImpl::StreamImpl::decodeData() {
     478       22198 :   if (defer_processing_backedup_streams_ && buffersOverrun()) {
     479          28 :     ENVOY_CONN_LOG(trace, "Stream {} buffering decodeData() call.", parent_.connection_,
     480          28 :                    stream_id_);
     481          28 :     stream_manager_.body_buffered_ = true;
     482          28 :     return;
     483          28 :   }
     484             : 
     485             :   // Some buffered body will be consumed. If there remains buffered body after
     486             :   // this call, set this to true.
     487       22170 :   stream_manager_.body_buffered_ = false;
     488             : 
     489       22170 :   bool already_drained_data = false;
     490             :   // It's possible that we are waiting to send a deferred reset, so only raise data if local
     491             :   // is not complete.
     492       22170 :   if (!deferred_reset_) {
     493             :     // We should decode data in chunks only if we have defer processing enabled
     494             :     // with a non-zero defer_processing_segment_size, and the buffer holds more
     495             :     // data than the defer_processing_segment_size. Otherwise, push the
     496             :     // entire buffer through.
     497       22169 :     const bool decode_data_in_chunk =
     498       22169 :         defer_processing_backedup_streams_ && stream_manager_.decodeAsChunks() &&
     499       22169 :         pending_recv_data_->length() > stream_manager_.defer_processing_segment_size_;
     500             : 
     501       22169 :     if (decode_data_in_chunk) {
     502           0 :       Buffer::OwnedImpl chunk_buffer;
     503             :       // TODO(kbaichoo): Consider implementing an approximate move for chunking.
     504           0 :       chunk_buffer.move(*pending_recv_data_, stream_manager_.defer_processing_segment_size_);
     505             : 
     506             :       // With the current implementation this should always be true,
     507             :       // though this can change with approximation.
     508           0 :       stream_manager_.body_buffered_ = true;
     509           0 :       ASSERT(pending_recv_data_->length() > 0);
     510             : 
     511           0 :       decoder().decodeData(chunk_buffer, sendEndStream());
     512           0 :       already_drained_data = true;
     513             : 
     514           0 :       if (!buffersOverrun()) {
     515           0 :         scheduleProcessingOfBufferedData(true);
     516           0 :       }
     517       22169 :     } else {
     518             :       // Send the entire buffer through.
     519       22169 :       decoder().decodeData(*pending_recv_data_, sendEndStream());
     520       22169 :     }
     521       22169 :   }
     522             : 
     523       22171 :   if (!already_drained_data) {
     524       22171 :     pending_recv_data_->drain(pending_recv_data_->length());
     525       22171 :   }
     526       22170 : }
     527             : 
     528         183 : void ConnectionImpl::ClientStreamImpl::decodeHeaders() {
     529         183 :   auto& headers = absl::get<ResponseHeaderMapPtr>(headers_or_trailers_);
     530         183 : #ifndef ENVOY_ENABLE_UHV
     531         183 :   const uint64_t status = Http::Utility::getResponseStatus(*headers);
     532             : 
     533             :   // Extended CONNECT to H/1 upgrade transformation has moved to UHV
     534         183 :   if (!upgrade_type_.empty() && headers->Status()) {
     535           2 :     Http::Utility::transformUpgradeResponseFromH2toH1(*headers, upgrade_type_);
     536           2 :   }
     537             : #else
     538             :   // In UHV mode the :status header at this point can be malformed, as it is validated
     539             :   // later on in the response_decoder_.decodeHeaders() call.
     540             :   // Account for this here.
     541             :   absl::optional<uint64_t> status_opt = Http::Utility::getResponseStatusOrNullopt(*headers);
     542             :   if (!status_opt.has_value()) {
     543             :     // In case the status is invalid or missing, the response_decoder_.decodeHeaders() will fail the
     544             :     // request
     545             :     response_decoder_.decodeHeaders(std::move(headers), sendEndStream());
     546             :     return;
     547             :   }
     548             :   const uint64_t status = status_opt.value();
     549             : #endif
     550             :   // Non-informational headers are non-1xx OR 101-SwitchingProtocols, since 101 implies that further
     551             :   // proxying is on an upgrade path.
     552             :   // TODO(#29071) determine how to handle 101, since it is not supported by HTTP/2
     553         183 :   received_noninformational_headers_ =
     554         183 :       !CodeUtility::is1xx(status) || status == enumToInt(Http::Code::SwitchingProtocols);
     555             : 
     556         183 :   if (HeaderUtility::isSpecial1xx(*headers)) {
     557           7 :     ASSERT(!remote_end_stream_);
     558           7 :     response_decoder_.decode1xxHeaders(std::move(headers));
     559         176 :   } else {
     560         176 :     response_decoder_.decodeHeaders(std::move(headers), sendEndStream());
     561         176 :   }
     562         183 : }
     563             : 
     564          20 : bool ConnectionImpl::StreamImpl::maybeDeferDecodeTrailers() {
     565          20 :   ASSERT(!deferred_reset_.has_value());
     566             :   // Buffer trailers if we're deferring processing and not flushing all data
     567             :   // through and either
     568             :   // 1) Buffers are overrun
     569             :   // 2) There's buffered body which should get processed before these trailers
     570             :   //    to avoid losing data.
     571          20 :   if (defer_processing_backedup_streams_ && (buffersOverrun() || stream_manager_.body_buffered_)) {
     572           4 :     stream_manager_.trailers_buffered_ = true;
     573           4 :     ENVOY_CONN_LOG(trace, "Stream {} buffering decodeTrailers() call.", parent_.connection_,
     574           4 :                    stream_id_);
     575           4 :     return true;
     576           4 :   }
     577             : 
     578          16 :   return false;
     579          20 : }
     580             : 
     581          10 : void ConnectionImpl::ClientStreamImpl::decodeTrailers() {
     582          10 :   if (maybeDeferDecodeTrailers()) {
     583           4 :     return;
     584           4 :   }
     585             : 
     586             :   // Consume any buffered trailers.
     587           6 :   stream_manager_.trailers_buffered_ = false;
     588             : 
     589           6 :   response_decoder_.decodeTrailers(
     590           6 :       std::move(absl::get<ResponseTrailerMapPtr>(headers_or_trailers_)));
     591           6 : }
     592             : 
     593         312 : void ConnectionImpl::ServerStreamImpl::decodeHeaders() {
     594         312 :   auto& headers = absl::get<RequestHeaderMapSharedPtr>(headers_or_trailers_);
     595         312 : #ifndef ENVOY_ENABLE_UHV
     596             :   // Extended CONNECT to H/1 upgrade transformation has moved to UHV
     597         312 :   if (Http::Utility::isH2UpgradeRequest(*headers)) {
     598           0 :     Http::Utility::transformUpgradeRequestFromH2toH1(*headers);
     599           0 :   }
     600         312 : #endif
     601         312 :   request_decoder_->decodeHeaders(std::move(headers), sendEndStream());
     602         312 : }
     603             : 
     604          10 : void ConnectionImpl::ServerStreamImpl::decodeTrailers() {
     605          10 :   if (maybeDeferDecodeTrailers()) {
     606           0 :     return;
     607           0 :   }
     608             : 
     609             :   // Consume any buffered trailers.
     610          10 :   stream_manager_.trailers_buffered_ = false;
     611             : 
     612          10 :   request_decoder_->decodeTrailers(
     613          10 :       std::move(absl::get<RequestTrailerMapPtr>(headers_or_trailers_)));
     614          10 : }
     615             : 
     616          74 : void ConnectionImpl::StreamImpl::pendingSendBufferHighWatermark() {
     617          74 :   ENVOY_CONN_LOG(debug, "send buffer over limit ", parent_.connection_);
     618          74 :   ASSERT(!pending_send_buffer_high_watermark_called_);
     619          74 :   pending_send_buffer_high_watermark_called_ = true;
     620          74 :   runHighWatermarkCallbacks();
     621          74 : }
     622             : 
     623          70 : void ConnectionImpl::StreamImpl::pendingSendBufferLowWatermark() {
     624          70 :   ENVOY_CONN_LOG(debug, "send buffer under limit ", parent_.connection_);
     625          70 :   ASSERT(pending_send_buffer_high_watermark_called_);
     626          70 :   pending_send_buffer_high_watermark_called_ = false;
     627          70 :   runLowWatermarkCallbacks();
     628          70 : }
     629             : 
     630       24931 : void ConnectionImpl::StreamImpl::saveHeader(HeaderString&& name, HeaderString&& value) {
     631       24931 :   if (!Utility::reconstituteCrumbledCookies(name, value, cookies_)) {
     632        6172 :     headers().addViaMove(std::move(name), std::move(value));
     633        6172 :   }
     634       24931 : }
     635             : 
     636          56 : void ConnectionImpl::StreamImpl::submitTrailers(const HeaderMap& trailers) {
     637          56 :   ASSERT(local_end_stream_);
     638          56 :   const bool skip_encoding_empty_trailers = trailers.empty();
     639          56 :   if (skip_encoding_empty_trailers) {
     640          40 :     ENVOY_CONN_LOG(debug, "skipping submitting trailers", parent_.connection_);
     641             : 
     642             :     // Instead of submitting empty trailers, we send empty data instead.
     643          40 :     Buffer::OwnedImpl empty_buffer;
     644          40 :     encodeDataHelper(empty_buffer, /*end_stream=*/true, skip_encoding_empty_trailers);
     645          40 :     return;
     646          40 :   }
     647             : 
     648          16 :   std::vector<http2::adapter::Header> final_headers = buildHeaders(trailers);
     649          16 :   parent_.adapter_->SubmitTrailer(stream_id_, final_headers);
     650          16 : }
     651             : 
     652             : std::pair<int64_t, bool>
     653       23432 : ConnectionImpl::StreamDataFrameSource::SelectPayloadLength(size_t max_length) {
     654       23432 :   if (stream_.pending_send_data_->length() == 0 && !stream_.local_end_stream_) {
     655        1098 :     ASSERT(!stream_.data_deferred_);
     656        1098 :     stream_.data_deferred_ = true;
     657        1098 :     return {kBlocked, false};
     658       22417 :   } else {
     659       22334 :     const size_t length = std::min<size_t>(max_length, stream_.pending_send_data_->length());
     660       22334 :     bool end_data = false;
     661       22334 :     if (stream_.local_end_stream_ && length == stream_.pending_send_data_->length()) {
     662         237 :       end_data = true;
     663         237 :       if (stream_.pending_trailers_to_encode_) {
     664           0 :         stream_.submitTrailers(*stream_.pending_trailers_to_encode_);
     665           0 :         stream_.pending_trailers_to_encode_.reset();
     666         237 :       } else {
     667         237 :         send_fin_ = true;
     668         237 :       }
     669         237 :     }
     670       22334 :     return {static_cast<int64_t>(length), end_data};
     671       22334 :   }
     672       23432 : }
     673             : 
     674             : bool ConnectionImpl::StreamDataFrameSource::Send(absl::string_view frame_header,
     675       22334 :                                                  size_t payload_length) {
     676       22334 :   stream_.parent_.protocol_constraints_.incrementOutboundDataFrameCount();
     677             : 
     678       22334 :   Buffer::OwnedImpl output;
     679       22334 :   stream_.parent_.addOutboundFrameFragment(
     680       22334 :       output, reinterpret_cast<const uint8_t*>(frame_header.data()), frame_header.size());
     681       22334 :   if (!stream_.parent_.protocol_constraints_.checkOutboundFrameLimits().ok()) {
     682           0 :     ENVOY_CONN_LOG(debug, "error sending data frame: Too many frames in the outbound queue",
     683           0 :                    stream_.parent_.connection_);
     684           0 :     stream_.setDetails(Http2ResponseCodeDetails::get().outbound_frame_flood);
     685           0 :   }
     686             : 
     687       22334 :   stream_.parent_.stats_.pending_send_bytes_.sub(payload_length);
     688       22334 :   output.move(*stream_.pending_send_data_, payload_length);
     689       22334 :   stream_.parent_.connection_.write(output, false);
     690       22334 :   return true;
     691       22334 : }
     692             : 
     693         459 : void ConnectionImpl::ClientStreamImpl::submitHeaders(const HeaderMap& headers, bool end_stream) {
     694         459 :   ASSERT(stream_id_ == -1);
     695         459 :   stream_id_ = parent_.adapter_->SubmitRequest(
     696         459 :       buildHeaders(headers), end_stream ? nullptr : std::make_unique<StreamDataFrameSource>(*this),
     697         459 :       base());
     698         459 :   ASSERT(stream_id_ > 0);
     699         459 : }
     700             : 
     701         268 : void ConnectionImpl::ServerStreamImpl::submitHeaders(const HeaderMap& headers, bool end_stream) {
     702         268 :   ASSERT(stream_id_ != -1);
     703         268 :   parent_.adapter_->SubmitResponse(stream_id_, buildHeaders(headers),
     704         268 :                                    end_stream ? nullptr
     705         268 :                                               : std::make_unique<StreamDataFrameSource>(*this));
     706         268 : }
     707             : 
     708           0 : void ConnectionImpl::StreamImpl::onPendingFlushTimer() {
     709           0 :   ENVOY_CONN_LOG(debug, "pending stream flush timeout", parent_.connection_);
     710           0 :   MultiplexedStreamImplBase::onPendingFlushTimer();
     711           0 :   parent_.stats_.tx_flush_timeout_.inc();
     712           0 :   ASSERT(local_end_stream_ && !local_end_stream_sent_);
     713             :   // This will emit a reset frame for this stream and close the stream locally.
     714             :   // Only the stream adapter's reset callback should run as other higher layers
     715             :   // think the stream is already finished.
     716           0 :   resetStreamWorker(StreamResetReason::LocalReset);
     717           0 :   if (parent_.sendPendingFramesAndHandleError()) {
     718             :     // Intended to check through coverage that this error case is tested
     719           0 :     return;
     720           0 :   }
     721           0 : }
     722             : 
     723        1389 : void ConnectionImpl::StreamImpl::encodeData(Buffer::Instance& data, bool end_stream) {
     724        1389 :   parent_.updateActiveStreamsOnEncode(*this);
     725        1389 :   ASSERT(!local_end_stream_);
     726        1389 :   encodeDataHelper(data, end_stream,
     727             :                    /*skip_encoding_empty_trailers=*/
     728        1389 :                    false);
     729        1389 : }
     730             : 
     731             : void ConnectionImpl::StreamImpl::encodeDataHelper(Buffer::Instance& data, bool end_stream,
     732        1429 :                                                   bool skip_encoding_empty_trailers) {
     733        1429 :   if (skip_encoding_empty_trailers) {
     734          40 :     ASSERT(data.length() == 0 && end_stream);
     735          40 :   }
     736             : 
     737        1429 :   local_end_stream_ = end_stream;
     738        1429 :   parent_.stats_.pending_send_bytes_.add(data.length());
     739        1429 :   pending_send_data_->move(data);
     740        1429 :   if (data_deferred_) {
     741         916 :     bool success = parent_.adapter_->ResumeStream(stream_id_);
     742         916 :     ASSERT(success);
     743             : 
     744         916 :     data_deferred_ = false;
     745         916 :   }
     746             : 
     747        1429 :   if (parent_.sendPendingFramesAndHandleError()) {
     748             :     // Intended to check through coverage that this error case is tested
     749           0 :     return;
     750           0 :   }
     751        1429 :   if (local_end_stream_) {
     752         267 :     onLocalEndStream();
     753         267 :   }
     754        1429 : }
     755             : 
     756          15 : void ConnectionImpl::ServerStreamImpl::resetStream(StreamResetReason reason) {
     757             :   // Clear the downstream on the account since we're resetting the downstream.
     758          15 :   if (buffer_memory_account_) {
     759           0 :     buffer_memory_account_->clearDownstream();
     760           0 :   }
     761             : 
     762          15 :   StreamImpl::resetStream(reason);
     763          15 : }
     764             : 
     765         136 : void ConnectionImpl::StreamImpl::resetStream(StreamResetReason reason) {
     766         136 :   reset_reason_ = reason;
     767             : 
     768             :   // Higher layers expect calling resetStream() to immediately raise reset callbacks.
     769         136 :   runResetCallbacks(reason);
     770             : 
     771             :   // If we've bufferedOnStreamClose for this stream, we shouldn't propagate this
     772             :   // reset as nghttp2 will have forgotten about the stream.
     773         136 :   if (stream_manager_.buffered_on_stream_close_) {
     774           0 :     ENVOY_CONN_LOG(
     775           0 :         trace, "Stopped propagating reset to nghttp2 as we've buffered onStreamClose for stream {}",
     776           0 :         parent_.connection_, stream_id_);
     777             :     // The stream didn't originally have an NGHTTP2 error, since we buffered
     778             :     // its stream close.
     779           0 :     if (Status status = parent_.onStreamClose(this, 0); !status.ok()) {
     780           0 :       ENVOY_CONN_LOG(debug, "error invoking onStreamClose: {}", parent_.connection_,
     781           0 :                      status.message());
     782           0 :     }
     783           0 :     return;
     784           0 :   }
     785             : 
     786             :   // If we submit a reset, nghttp2 will cancel outbound frames that have not yet been sent.
     787             :   // We want these frames to go out so we defer the reset until we send all of the frames that
     788             :   // end the local stream. However, if we're resetting the stream due to
     789             :   // overload, we should reset the stream as soon as possible to free used
     790             :   // resources.
     791         136 :   if (useDeferredReset() && local_end_stream_ && !local_end_stream_sent_ &&
     792         136 :       reason != StreamResetReason::OverloadManager) {
     793           2 :     ASSERT(parent_.getStreamUnchecked(stream_id_) != nullptr);
     794           2 :     parent_.pending_deferred_reset_streams_.emplace(stream_id_, this);
     795           2 :     deferred_reset_ = reason;
     796           2 :     ENVOY_CONN_LOG(trace, "deferred reset stream", parent_.connection_);
     797         136 :   } else {
     798         134 :     resetStreamWorker(reason);
     799         134 :   }
     800             : 
     801             :   // We must still call sendPendingFrames() in both the deferred and not deferred path. This forces
     802             :   // the cleanup logic to run which will reset the stream in all cases if all data frames could not
     803             :   // be sent.
     804         136 :   if (parent_.sendPendingFramesAndHandleError()) {
     805             :     // Intended to check through coverage that this error case is tested
     806           0 :     return;
     807           0 :   }
     808         136 : }
     809             : 
     810         134 : void ConnectionImpl::StreamImpl::resetStreamWorker(StreamResetReason reason) {
     811         134 :   if (stream_id_ == -1) {
     812             :     // Handle the case where client streams are reset before headers are created.
     813           1 :     return;
     814           1 :   }
     815         133 :   if (codec_callbacks_) {
     816           0 :     codec_callbacks_->onCodecLowLevelReset();
     817           0 :   }
     818         133 :   parent_.adapter_->SubmitRst(stream_id_,
     819         133 :                               static_cast<http2::adapter::Http2ErrorCode>(reasonToReset(reason)));
     820         133 : }
     821             : 
     822           8 : NewMetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoder() {
     823           8 :   if (metadata_encoder_ == nullptr) {
     824           6 :     metadata_encoder_ = std::make_unique<NewMetadataEncoder>();
     825           6 :   }
     826           8 :   return *metadata_encoder_;
     827           8 : }
     828             : 
     829          50 : MetadataDecoder& ConnectionImpl::StreamImpl::getMetadataDecoder() {
     830          50 :   if (metadata_decoder_ == nullptr) {
     831           6 :     auto cb = [this](MetadataMapPtr&& metadata_map_ptr) {
     832           6 :       this->onMetadataDecoded(std::move(metadata_map_ptr));
     833           6 :     };
     834           4 :     metadata_decoder_ = std::make_unique<MetadataDecoder>(cb);
     835           4 :   }
     836          50 :   return *metadata_decoder_;
     837          50 : }
     838             : 
     839           6 : void ConnectionImpl::StreamImpl::onMetadataDecoded(MetadataMapPtr&& metadata_map_ptr) {
     840             :   // Empty metadata maps should not be decoded.
     841           6 :   if (metadata_map_ptr->empty()) {
     842           0 :     ENVOY_CONN_LOG(debug, "decode metadata called with empty map, skipping", parent_.connection_);
     843           0 :     parent_.stats_.metadata_empty_frames_.inc();
     844           6 :   } else {
     845           6 :     decoder().decodeMetadata(std::move(metadata_map_ptr));
     846           6 :   }
     847           6 : }
     848             : 
     849         360 : void ConnectionImpl::StreamImpl::setAccount(Buffer::BufferMemoryAccountSharedPtr account) {
     850         360 :   buffer_memory_account_ = account;
     851         360 :   pending_recv_data_->bindAccount(buffer_memory_account_);
     852         360 :   pending_send_data_->bindAccount(buffer_memory_account_);
     853         360 : }
     854             : 
     855             : ConnectionImpl::ConnectionImpl(Network::Connection& connection, CodecStats& stats,
     856             :                                Random::RandomGenerator& random_generator,
     857             :                                const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
     858             :                                const uint32_t max_headers_kb, const uint32_t max_headers_count)
     859             :     : stats_(stats), connection_(connection), max_headers_kb_(max_headers_kb),
     860             :       max_headers_count_(max_headers_count),
     861             :       per_stream_buffer_limit_(http2_options.initial_stream_window_size().value()),
     862             :       stream_error_on_invalid_http_messaging_(
     863             :           http2_options.override_stream_error_on_invalid_http_message().value()),
     864             :       protocol_constraints_(stats, http2_options), dispatching_(false), raised_goaway_(false),
     865             :       random_(random_generator),
     866        2860 :       last_received_data_time_(connection_.dispatcher().timeSource().monotonicTime()) {
     867        2860 :   if (http2_options.has_use_oghttp2_codec()) {
     868           0 :     use_oghttp2_library_ = http2_options.use_oghttp2_codec().value();
     869        2860 :   } else {
     870        2860 :     use_oghttp2_library_ =
     871        2860 :         Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_use_oghttp2");
     872        2860 :   }
     873        2860 :   if (http2_options.has_connection_keepalive()) {
     874           0 :     keepalive_interval_ = std::chrono::milliseconds(
     875           0 :         PROTOBUF_GET_MS_OR_DEFAULT(http2_options.connection_keepalive(), interval, 0));
     876           0 :     keepalive_timeout_ = std::chrono::milliseconds(
     877           0 :         PROTOBUF_GET_MS_REQUIRED(http2_options.connection_keepalive(), timeout));
     878           0 :     keepalive_interval_jitter_percent_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(
     879           0 :         http2_options.connection_keepalive(), interval_jitter, 15.0);
     880             : 
     881           0 :     if (keepalive_interval_.count() > 0) {
     882           0 :       keepalive_send_timer_ = connection.dispatcher().createTimer([this]() { sendKeepalive(); });
     883           0 :     }
     884           0 :     keepalive_timeout_timer_ =
     885           0 :         connection.dispatcher().createTimer([this]() { onKeepaliveResponseTimeout(); });
     886             : 
     887             :     // This call schedules the initial interval, with jitter.
     888           0 :     onKeepaliveResponse();
     889           0 :   }
     890        2860 : }
     891             : 
     892        2860 : ConnectionImpl::~ConnectionImpl() {
     893        2881 :   for (const auto& stream : active_streams_) {
     894        1613 :     stream->destroy();
     895        1613 :   }
     896        2860 : }
     897             : 
     898           0 : void ConnectionImpl::sendKeepalive() {
     899           0 :   ASSERT(keepalive_timeout_timer_);
     900           0 :   if (keepalive_timeout_timer_->enabled()) {
     901           0 :     ENVOY_CONN_LOG(trace, "Skipping PING: already awaiting PING ACK", connection_);
     902           0 :     return;
     903           0 :   }
     904             : 
     905             :   // Include the current time as the payload to help with debugging.
     906           0 :   SystemTime now = connection_.dispatcher().timeSource().systemTime();
     907           0 :   uint64_t ms_since_epoch =
     908           0 :       std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
     909           0 :   ENVOY_CONN_LOG(trace, "Sending keepalive PING {}", connection_, ms_since_epoch);
     910             : 
     911           0 :   adapter_->SubmitPing(ms_since_epoch);
     912             : 
     913           0 :   if (sendPendingFramesAndHandleError()) {
     914             :     // Intended to check through coverage that this error case is tested
     915           0 :     return;
     916           0 :   }
     917           0 :   keepalive_timeout_timer_->enableTimer(keepalive_timeout_);
     918           0 : }
     919             : 
     920           0 : void ConnectionImpl::onKeepaliveResponse() {
     921             :   // Check the timers for nullptr in case the peer sent an unsolicited PING ACK.
     922           0 :   if (keepalive_timeout_timer_ != nullptr) {
     923           0 :     keepalive_timeout_timer_->disableTimer();
     924           0 :   }
     925           0 :   if (keepalive_send_timer_ != nullptr && keepalive_interval_.count()) {
     926           0 :     uint64_t interval_ms = keepalive_interval_.count();
     927           0 :     const uint64_t jitter_percent_mod = keepalive_interval_jitter_percent_ * interval_ms / 100;
     928           0 :     if (jitter_percent_mod > 0) {
     929           0 :       interval_ms += random_.random() % jitter_percent_mod;
     930           0 :     }
     931           0 :     keepalive_send_timer_->enableTimer(std::chrono::milliseconds(interval_ms));
     932           0 :   }
     933           0 : }
     934             : 
     935           0 : void ConnectionImpl::onKeepaliveResponseTimeout() {
     936           0 :   ENVOY_CONN_LOG_EVENT(debug, "h2_ping_timeout", "Closing connection due to keepalive timeout",
     937           0 :                        connection_);
     938           0 :   stats_.keepalive_timeout_.inc();
     939           0 :   connection_.close(Network::ConnectionCloseType::NoFlush,
     940           0 :                     StreamInfo::LocalCloseReasons::get().Http2PingTimeout);
     941           0 : }
     942             : 
     943           0 : bool ConnectionImpl::slowContainsStreamId(int32_t stream_id) const {
     944           0 :   for (const auto& stream : active_streams_) {
     945           0 :     if (stream->stream_id_ == stream_id) {
     946           0 :       return true;
     947           0 :     }
     948           0 :   }
     949             : 
     950           0 :   return false;
     951           0 : }
     952             : 
     953       45212 : Http::Status ConnectionImpl::dispatch(Buffer::Instance& data) {
     954       45212 :   ScopeTrackerScopeState scope(this, connection_.dispatcher());
     955       45212 :   ENVOY_CONN_LOG(trace, "dispatching {} bytes", connection_, data.length());
     956             :   // Make sure that dispatching_ is set to false after dispatching, even when
     957             :   // ConnectionImpl::dispatch returns early or throws an exception (consider removing if there is a
     958             :   // single return after exception removal (#10878)).
     959       45212 :   Cleanup cleanup([this]() {
     960       45212 :     dispatching_ = false;
     961       45212 :     current_slice_ = nullptr;
     962       45212 :     current_stream_id_.reset();
     963       45212 :   });
     964       45212 :   last_received_data_time_ = connection_.dispatcher().timeSource().monotonicTime();
     965       62379 :   for (const Buffer::RawSlice& slice : data.getRawSlices()) {
     966       62379 :     current_slice_ = &slice;
     967       62379 :     dispatching_ = true;
     968       62379 :     ssize_t rc;
     969       62379 :     rc = adapter_->ProcessBytes(absl::string_view(static_cast<char*>(slice.mem_), slice.len_));
     970       62379 :     if (!codec_callback_status_.ok()) {
     971           5 :       return codec_callback_status_;
     972           5 :     }
     973             :     // This error is returned when nghttp2 library detected a frame flood by one of its
     974             :     // internal mechanisms. Most flood protection is done by Envoy's codec and this error
     975             :     // should never be returned. However it is handled here in case nghttp2 has some flood
     976             :     // protections that Envoy's codec does not have.
     977       62374 :     if (rc == NGHTTP2_ERR_FLOODED) {
     978           0 :       return bufferFloodError(
     979           0 :           "Flooding was detected in this HTTP/2 session, and it must be closed");
     980           0 :     }
     981       62374 :     if (rc != static_cast<ssize_t>(slice.len_)) {
     982         226 :       return codecProtocolError(nghttp2_strerror(rc));
     983         226 :     }
     984             : 
     985       62148 :     current_slice_ = nullptr;
     986       62148 :     dispatching_ = false;
     987       62148 :     current_stream_id_.reset();
     988       62148 :   }
     989             : 
     990       44981 :   ENVOY_CONN_LOG(trace, "dispatched {} bytes", connection_, data.length());
     991       44981 :   data.drain(data.length());
     992             : 
     993             :   // Decoding incoming frames can generate outbound frames so flush pending.
     994       44981 :   return sendPendingFrames();
     995       45212 : }
     996             : 
     997           0 : const ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) const {
     998             :   // Delegate to the non-const version.
     999           0 :   return const_cast<ConnectionImpl*>(this)->getStream(stream_id);
    1000           0 : }
    1001             : 
    1002       38904 : ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) {
    1003       38904 :   StreamImpl* stream = getStreamUnchecked(stream_id);
    1004       38904 :   SLOW_ASSERT(stream != nullptr || !slowContainsStreamId(stream_id));
    1005       38904 :   return stream;
    1006       38904 : }
    1007             : 
    1008           0 : const ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) const {
    1009             :   // Delegate to the non-const version.
    1010           0 :   return const_cast<ConnectionImpl*>(this)->getStreamUnchecked(stream_id);
    1011           0 : }
    1012             : 
    1013      158583 : ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) {
    1014      158583 :   return static_cast<StreamImpl*>(adapter_->GetStreamUserData(stream_id));
    1015      158583 : }
    1016             : 
    1017       38874 : int ConnectionImpl::onData(int32_t stream_id, const uint8_t* data, size_t len) {
    1018       38874 :   ASSERT(connection_.state() == Network::Connection::State::Open);
    1019       38874 :   StreamImpl* stream = getStream(stream_id);
    1020             :   // If this results in buffering too much data, the watermark buffer will call
    1021             :   // pendingRecvBufferHighWatermark, resulting in ++read_disable_count_
    1022       38874 :   stream->pending_recv_data_->add(data, len);
    1023             :   // Update the window to the peer unless some consumer of this stream's data has hit a flow control
    1024             :   // limit and disabled reads on this stream
    1025       38876 :   if (stream->shouldAllowPeerAdditionalStreamWindow()) {
    1026       38856 :     adapter_->MarkDataConsumedForStream(stream_id, len);
    1027       38362 :   } else {
    1028          20 :     stream->unconsumed_bytes_ += len;
    1029          20 :   }
    1030       38874 :   return 0;
    1031       38874 : }
    1032             : 
    1033          94 : void ConnectionImpl::goAway() {
    1034          94 :   adapter_->SubmitGoAway(adapter_->GetHighestReceivedStreamId(),
    1035          94 :                          http2::adapter::Http2ErrorCode::HTTP2_NO_ERROR, "");
    1036          94 :   stats_.goaway_sent_.inc();
    1037          94 :   if (sendPendingFramesAndHandleError()) {
    1038             :     // Intended to check through coverage that this error case is tested
    1039           0 :     return;
    1040           0 :   }
    1041          94 : }
    1042             : 
    1043           0 : void ConnectionImpl::shutdownNotice() {
    1044           0 :   adapter_->SubmitShutdownNotice();
    1045             : 
    1046           0 :   if (sendPendingFramesAndHandleError()) {
    1047             :     // Intended to check through coverage that this error case is tested
    1048           0 :     return;
    1049           0 :   }
    1050           0 : }
    1051             : 
    1052           0 : Status ConnectionImpl::protocolErrorForTest() {
    1053           0 :   adapter_->SubmitGoAway(adapter_->GetHighestReceivedStreamId(),
    1054           0 :                          http2::adapter::Http2ErrorCode::PROTOCOL_ERROR, "");
    1055             : 
    1056           0 :   return sendPendingFrames();
    1057           0 : }
    1058             : 
    1059             : Status ConnectionImpl::onBeforeFrameReceived(int32_t stream_id, size_t length, uint8_t type,
    1060       45183 :                                              uint8_t flags) {
    1061       45183 :   ENVOY_CONN_LOG(trace, "about to recv frame type={}, flags={}, stream_id={}", connection_,
    1062       45183 :                  static_cast<uint64_t>(type), static_cast<uint64_t>(flags), stream_id);
    1063       45183 :   ASSERT(connection_.state() == Network::Connection::State::Open);
    1064             : 
    1065       45183 :   current_stream_id_ = stream_id;
    1066             :   // Track all the frames without padding here, since this is the only callback we receive
    1067             :   // for some of them (e.g. CONTINUATION frame, frames sent on closed streams, etc.).
    1068             :   // HEADERS frame is tracked in onBeginHeaders(), DATA frame is tracked in onFrameReceived().
    1069       45183 :   auto status = okStatus();
    1070       45183 :   if (type != NGHTTP2_HEADERS && type != NGHTTP2_DATA) {
    1071       20974 :     status = trackInboundFrames(stream_id, length, type, flags, 0);
    1072       20974 :   }
    1073             : 
    1074       45183 :   return status;
    1075       45183 : }
    1076             : 
    1077             : ABSL_MUST_USE_RESULT
    1078          15 : enum GoAwayErrorCode ngHttp2ErrorCodeToErrorCode(uint32_t code) noexcept {
    1079          15 :   switch (code) {
    1080          10 :   case NGHTTP2_NO_ERROR:
    1081          10 :     return GoAwayErrorCode::NoError;
    1082           5 :   default:
    1083           5 :     return GoAwayErrorCode::Other;
    1084          15 :   }
    1085          15 : }
    1086             : 
    1087       42685 : Status ConnectionImpl::onFrameReceived(const nghttp2_frame* frame) {
    1088       42685 :   ENVOY_CONN_LOG(trace, "recv frame type={}", connection_, static_cast<uint64_t>(frame->hd.type));
    1089       42685 :   ASSERT(connection_.state() == Network::Connection::State::Open);
    1090             : 
    1091             :   // onFrameReceived() is called with a complete HEADERS frame assembled from all the HEADERS
    1092             :   // and CONTINUATION frames, but we track them separately: HEADERS frames in onBeginHeaders()
    1093             :   // and CONTINUATION frames in onBeforeFrameReceived().
    1094       42685 :   ASSERT(frame->hd.type != NGHTTP2_CONTINUATION);
    1095             : 
    1096       42685 :   if ((frame->hd.type == NGHTTP2_PING) && (frame->ping.hd.flags & NGHTTP2_FLAG_ACK)) {
    1097             :     // The ``opaque_data`` should be exactly what was sent in the ping, which is
    1098             :     // was the current time when the ping was sent. This can be useful while debugging
    1099             :     // to match the ping and ack.
    1100           0 :     uint64_t data;
    1101           0 :     safeMemcpy(&data, &(frame->ping.opaque_data));
    1102           0 :     ENVOY_CONN_LOG(trace, "recv PING ACK {}", connection_, data);
    1103             : 
    1104           0 :     onKeepaliveResponse();
    1105           0 :     return okStatus();
    1106           0 :   }
    1107             : 
    1108             :   // In slow networks, HOL blocking can prevent the ping response from coming in a reasonable
    1109             :   // amount of time. To avoid HOL blocking influence, if we receive *any* frame extend the
    1110             :   // timeout for another timeout period. This will still timeout the connection if there is no
    1111             :   // activity, but if there is frame activity we assume the connection is still healthy and the
    1112             :   // PING ACK may be delayed behind other frames.
    1113       42685 :   if (keepalive_timeout_timer_ != nullptr && keepalive_timeout_timer_->enabled()) {
    1114           0 :     keepalive_timeout_timer_->enableTimer(keepalive_timeout_);
    1115           0 :   }
    1116             : 
    1117       42685 :   if (frame->hd.type == NGHTTP2_DATA) {
    1118       22182 :     RETURN_IF_ERROR(trackInboundFrames(frame->hd.stream_id, frame->hd.length, frame->hd.type,
    1119       22182 :                                        frame->hd.flags, frame->data.padlen));
    1120       22182 :   }
    1121             : 
    1122             :   // Only raise GOAWAY once, since we don't currently expose stream information. Shutdown
    1123             :   // notifications are the same as a normal GOAWAY.
    1124             :   // TODO: handle multiple GOAWAY frames.
    1125       42685 :   if (frame->hd.type == NGHTTP2_GOAWAY && !raised_goaway_) {
    1126          15 :     ASSERT(frame->hd.stream_id == 0);
    1127          15 :     raised_goaway_ = true;
    1128          15 :     callbacks().onGoAway(ngHttp2ErrorCodeToErrorCode(frame->goaway.error_code));
    1129          15 :     return okStatus();
    1130          15 :   }
    1131             : 
    1132       42670 :   if (frame->hd.type == NGHTTP2_SETTINGS && frame->hd.flags == NGHTTP2_FLAG_NONE) {
    1133        2872 :     onSettings(frame->settings);
    1134        2872 :   }
    1135             : 
    1136       42670 :   StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
    1137       42670 :   if (!stream) {
    1138       12615 :     return okStatus();
    1139       12615 :   }
    1140             : 
    1141             :   // Track bytes sent and received.
    1142       30057 :   if (frame->hd.type != METADATA_FRAME_TYPE) {
    1143       30016 :     stream->bytes_meter_->addWireBytesReceived(frame->hd.length + H2_FRAME_HEADER_SIZE);
    1144       30016 :   }
    1145       30055 :   if (frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_CONTINUATION) {
    1146         515 :     stream->bytes_meter_->addHeaderBytesReceived(frame->hd.length + H2_FRAME_HEADER_SIZE);
    1147         515 :   }
    1148             : 
    1149       30055 :   switch (frame->hd.type) {
    1150         515 :   case NGHTTP2_HEADERS: {
    1151         515 :     stream->remote_end_stream_ = frame->hd.flags & NGHTTP2_FLAG_END_STREAM;
    1152         515 :     if (!stream->cookies_.empty()) {
    1153          18 :       HeaderString key(Headers::get().Cookie);
    1154          18 :       stream->headers().addViaMove(std::move(key), std::move(stream->cookies_));
    1155          18 :     }
    1156             : 
    1157         515 :     switch (frame->headers.cat) {
    1158         179 :     case NGHTTP2_HCAT_RESPONSE:
    1159         491 :     case NGHTTP2_HCAT_REQUEST: {
    1160         491 :       stream->decodeHeaders();
    1161         491 :       break;
    1162         179 :     }
    1163             : 
    1164          24 :     case NGHTTP2_HCAT_HEADERS: {
    1165             :       // It's possible that we are waiting to send a deferred reset, so only raise headers/trailers
    1166             :       // if local is not complete.
    1167          24 :       if (!stream->deferred_reset_) {
    1168          24 :         if (adapter_->IsServerSession() || stream->received_noninformational_headers_) {
    1169          20 :           ASSERT(stream->remote_end_stream_);
    1170          20 :           stream->decodeTrailers();
    1171          20 :         } else {
    1172             :           // We're a client session and still waiting for non-informational headers.
    1173           4 :           stream->decodeHeaders();
    1174           4 :         }
    1175          24 :       }
    1176          24 :       break;
    1177         179 :     }
    1178             : 
    1179           0 :     default:
    1180             :       // We do not currently support push.
    1181           0 :       ENVOY_BUG(false, "push not supported");
    1182         515 :     }
    1183             : 
    1184         515 :     break;
    1185         515 :   }
    1186       22356 :   case NGHTTP2_DATA: {
    1187       22180 :     stream->remote_end_stream_ = frame->hd.flags & NGHTTP2_FLAG_END_STREAM;
    1188       22180 :     stream->decodeData();
    1189       22180 :     break;
    1190         515 :   }
    1191           5 :   case NGHTTP2_RST_STREAM: {
    1192           5 :     ENVOY_CONN_LOG(trace, "remote reset: {}", connection_, frame->rst_stream.error_code);
    1193           5 :     stream->remote_rst_ = true;
    1194           5 :     stats_.rx_reset_.inc();
    1195           5 :     break;
    1196         515 :   }
    1197       30055 :   }
    1198             : 
    1199       30058 :   return okStatus();
    1200       30055 : }
    1201             : 
    1202             : int ConnectionImpl::onFrameSend(int32_t stream_id, size_t length, uint8_t type, uint8_t flags,
    1203       47039 :                                 uint32_t error_code) {
    1204             :   // The codec library does not cleanly give us a way to determine whether we received invalid
    1205             :   // data from our peer. Sometimes it raises the invalid frame callback, and sometimes it does not.
    1206             :   // In all cases however it will attempt to send a GOAWAY frame with an error status. If we see
    1207             :   // an outgoing frame of this type, we will return an error code so that we can abort execution.
    1208       47039 :   ENVOY_CONN_LOG(trace, "sent frame type={}, stream_id={}, length={}", connection_,
    1209       47039 :                  static_cast<uint64_t>(type), stream_id, length);
    1210       47039 :   StreamImpl* stream = getStreamUnchecked(stream_id);
    1211       47039 :   if (stream != nullptr) {
    1212       30343 :     if (type != METADATA_FRAME_TYPE) {
    1213       30335 :       stream->bytes_meter_->addWireBytesSent(length + H2_FRAME_HEADER_SIZE);
    1214       30335 :     }
    1215       30343 :     if (type == NGHTTP2_HEADERS || type == NGHTTP2_CONTINUATION) {
    1216         671 :       stream->bytes_meter_->addHeaderBytesSent(length + H2_FRAME_HEADER_SIZE);
    1217         671 :     }
    1218       30343 :   }
    1219       47039 :   switch (type) {
    1220        1982 :   case NGHTTP2_GOAWAY: {
    1221        1982 :     ENVOY_CONN_LOG(debug, "sent goaway code={}", connection_, error_code);
    1222        1982 :     if (error_code != NGHTTP2_NO_ERROR) {
    1223             :       // TODO(mattklein123): Returning this error code abandons standard nghttp2 frame accounting.
    1224             :       // As such, it is not reliable to call sendPendingFrames() again after this and we assume
    1225             :       // that the connection is going to get torn down immediately. One byproduct of this is that
    1226             :       // we need to cancel all pending flush stream timeouts since they can race with connection
    1227             :       // teardown. As part of the work to remove exceptions we should aim to clean up all of this
    1228             :       // error handling logic and only handle this type of case at the end of dispatch.
    1229        1888 :       for (auto& stream : active_streams_) {
    1230        1164 :         stream->disarmStreamIdleTimer();
    1231        1164 :       }
    1232        1888 :       return NGHTTP2_ERR_CALLBACK_FAILURE;
    1233        1888 :     }
    1234          94 :     break;
    1235        1982 :   }
    1236             : 
    1237         149 :   case NGHTTP2_RST_STREAM: {
    1238          84 :     ENVOY_CONN_LOG(debug, "sent reset code={}", connection_, error_code);
    1239          84 :     stats_.tx_reset_.inc();
    1240          84 :     break;
    1241        1982 :   }
    1242             : 
    1243         671 :   case NGHTTP2_HEADERS:
    1244       23005 :   case NGHTTP2_DATA: {
    1245             :     // This should be the case since we're sending these frames. It's possible
    1246             :     // that codec fuzzers would incorrectly send frames for non-existent streams
    1247             :     // which is why this is not an assert.
    1248       23005 :     if (stream != nullptr) {
    1249       23005 :       const bool end_stream_sent = flags & NGHTTP2_FLAG_END_STREAM;
    1250       23005 :       stream->local_end_stream_sent_ = end_stream_sent;
    1251       23005 :       if (end_stream_sent) {
    1252         467 :         stream->onEndStreamEncoded();
    1253         467 :       }
    1254       23005 :     }
    1255       23005 :     break;
    1256         671 :   }
    1257       47039 :   }
    1258             : 
    1259       45151 :   return 0;
    1260       47039 : }
    1261             : 
    1262          16 : int ConnectionImpl::onError(absl::string_view error) {
    1263          16 :   ENVOY_CONN_LOG(debug, "invalid http2: {}", connection_, error);
    1264          16 :   return 0;
    1265          16 : }
    1266             : 
    1267         182 : int ConnectionImpl::onInvalidFrame(int32_t stream_id, int error_code) {
    1268         182 :   ENVOY_CONN_LOG(debug, "invalid frame: {} on stream {}", connection_, nghttp2_strerror(error_code),
    1269         182 :                  stream_id);
    1270             : 
    1271             :   // Set details of error_code in the stream whenever we have one.
    1272         182 :   StreamImpl* stream = getStreamUnchecked(stream_id);
    1273         182 :   if (stream != nullptr) {
    1274         128 :     stream->setDetails(Http2ResponseCodeDetails::get().errorDetails(error_code));
    1275         128 :   }
    1276             : 
    1277         182 :   switch (error_code) {
    1278           0 :   case NGHTTP2_ERR_REFUSED_STREAM:
    1279             : 
    1280           0 :     stats_.stream_refused_errors_.inc();
    1281           0 :     return 0;
    1282             : 
    1283          61 :   case NGHTTP2_ERR_HTTP_HEADER:
    1284         128 :   case NGHTTP2_ERR_HTTP_MESSAGING:
    1285         128 :     stats_.rx_messaging_error_.inc();
    1286         128 :     if (stream_error_on_invalid_http_messaging_) {
    1287             :       // The stream is about to be closed due to an invalid header or messaging. Don't kill the
    1288             :       // entire connection if one stream has bad headers or messaging.
    1289           0 :       if (stream != nullptr) {
    1290             :         // See comment below in onStreamClose() for why we do this.
    1291           0 :         stream->reset_due_to_messaging_error_ = true;
    1292           0 :       }
    1293           0 :       return 0;
    1294           0 :     }
    1295         128 :     break;
    1296             : 
    1297         128 :   case NGHTTP2_ERR_FLOW_CONTROL:
    1298          54 :   case NGHTTP2_ERR_PROTO:
    1299          54 :   case NGHTTP2_ERR_STREAM_CLOSED:
    1300             :     // Known error conditions that should trigger connection close.
    1301          54 :     break;
    1302             : 
    1303           0 :   default:
    1304             :     // Unknown error conditions. Trigger ENVOY_BUG and connection close.
    1305           0 :     ENVOY_BUG(false, absl::StrCat("Unexpected error_code: ", error_code));
    1306           0 :     break;
    1307         182 :   }
    1308             : 
    1309             :   // Cause dispatch to return with an error code.
    1310         182 :   return NGHTTP2_ERR_CALLBACK_FAILURE;
    1311         182 : }
    1312             : 
    1313             : int ConnectionImpl::onBeforeFrameSend(int32_t /*stream_id*/, size_t /*length*/, uint8_t type,
    1314       24705 :                                       uint8_t flags) {
    1315       24705 :   ENVOY_CONN_LOG(trace, "about to send frame type={}, flags={}", connection_,
    1316       24705 :                  static_cast<uint64_t>(type), static_cast<uint64_t>(flags));
    1317       24705 :   ASSERT(!is_outbound_flood_monitored_control_frame_);
    1318             :   // Flag flood monitored outbound control frames.
    1319       24705 :   is_outbound_flood_monitored_control_frame_ =
    1320       24705 :       ((type == NGHTTP2_PING || type == NGHTTP2_SETTINGS) && flags & NGHTTP2_FLAG_ACK) ||
    1321       24705 :       type == NGHTTP2_RST_STREAM;
    1322       24705 :   return 0;
    1323       24705 : }
    1324             : 
    1325             : void ConnectionImpl::addOutboundFrameFragment(Buffer::OwnedImpl& output, const uint8_t* data,
    1326       47392 :                                               size_t length) {
    1327             :   // Reset the outbound frame type (set in the onBeforeFrameSend callback) since the
    1328             :   // onBeforeFrameSend callback is not called for DATA frames.
    1329       47392 :   bool is_outbound_flood_monitored_control_frame = false;
    1330       47392 :   std::swap(is_outbound_flood_monitored_control_frame, is_outbound_flood_monitored_control_frame_);
    1331       47392 :   auto releasor =
    1332       47392 :       protocol_constraints_.incrementOutboundFrameCount(is_outbound_flood_monitored_control_frame);
    1333       47392 :   output.add(data, length);
    1334       47392 :   output.addDrainTracker(releasor);
    1335       47392 : }
    1336             : 
    1337       25058 : ssize_t ConnectionImpl::onSend(const uint8_t* data, size_t length) {
    1338       25058 :   ENVOY_CONN_LOG(trace, "send data: bytes={}", connection_, length);
    1339       25058 :   Buffer::OwnedImpl buffer;
    1340       25058 :   addOutboundFrameFragment(buffer, data, length);
    1341             : 
    1342             :   // While the buffer is transient the fragment it contains will be moved into the
    1343             :   // write_buffer_ of the underlying connection_ by the write method below.
    1344             :   // This creates lifetime dependency between the write_buffer_ of the underlying connection
    1345             :   // and the codec object. Specifically the write_buffer_ MUST be either fully drained or
    1346             :   // deleted before the codec object is deleted. This is presently guaranteed by the
    1347             :   // destruction order of the Network::ConnectionImpl object where write_buffer_ is
    1348             :   // destroyed before the filter_manager_ which owns the codec through Http::ConnectionManagerImpl.
    1349       25058 :   connection_.write(buffer, false);
    1350       25058 :   return length;
    1351       25058 : }
    1352             : 
    1353         393 : Status ConnectionImpl::onStreamClose(StreamImpl* stream, uint32_t error_code) {
    1354         393 :   if (stream) {
    1355         381 :     const int32_t stream_id = stream->stream_id_;
    1356             : 
    1357             :     // Consume buffered on stream_close.
    1358         381 :     if (stream->stream_manager_.buffered_on_stream_close_) {
    1359          16 :       stream->stream_manager_.buffered_on_stream_close_ = false;
    1360          16 :       stats_.deferred_stream_close_.dec();
    1361          16 :     }
    1362             : 
    1363         381 :     ENVOY_CONN_LOG(debug, "stream {} closed: {}", connection_, stream_id, error_code);
    1364             : 
    1365             :     // Even if we have received both the remote_end_stream and the
    1366             :     // local_end_stream (e.g. we have all the data for the response), if we've
    1367             :     // received a remote reset we should reset the stream.
    1368             :     // We only do so currently for server side streams by checking for
    1369             :     // extend_stream_lifetime_flag_ as its observers all unregisters stream
    1370             :     // callbacks.
    1371         381 :     bool should_reset_stream = !stream->remote_end_stream_ || !stream->local_end_stream_;
    1372         381 :     if (stream->extend_stream_lifetime_flag_) {
    1373         153 :       should_reset_stream = should_reset_stream || stream->remote_rst_;
    1374         153 :     }
    1375             : 
    1376         381 :     if (should_reset_stream) {
    1377          72 :       StreamResetReason reason;
    1378          72 :       if (stream->reset_due_to_messaging_error_) {
    1379             :         // Unfortunately, the nghttp2 API makes it incredibly difficult to clearly understand
    1380             :         // the flow of resets. I.e., did the reset originate locally? Was it remote? Here,
    1381             :         // we attempt to track cases in which we sent a reset locally due to an invalid frame
    1382             :         // received from the remote. We only do that in two cases currently (HTTP messaging layer
    1383             :         // errors from https://tools.ietf.org/html/rfc7540#section-8 which nghttp2 is very strict
    1384             :         // about). In other cases we treat invalid frames as a protocol error and just kill
    1385             :         // the connection.
    1386             : 
    1387             :         // Get ClientConnectionImpl or ServerConnectionImpl specific stream reset reason,
    1388             :         // depending whether the connection is upstream or downstream.
    1389           0 :         reason = getMessagingErrorResetReason();
    1390          72 :       } else {
    1391          72 :         if (error_code == NGHTTP2_REFUSED_STREAM) {
    1392           6 :           reason = StreamResetReason::RemoteRefusedStreamReset;
    1393           6 :           stream->setDetails(Http2ResponseCodeDetails::get().remote_refused);
    1394          66 :         } else {
    1395          66 :           if (error_code == NGHTTP2_CONNECT_ERROR) {
    1396           0 :             reason = StreamResetReason::ConnectError;
    1397          66 :           } else {
    1398          66 :             reason = StreamResetReason::RemoteReset;
    1399          66 :           }
    1400          66 :           stream->setDetails(Http2ResponseCodeDetails::get().remote_reset);
    1401          66 :         }
    1402          72 :       }
    1403             : 
    1404          72 :       stream->runResetCallbacks(reason);
    1405             : 
    1406         319 :     } else if (stream->defer_processing_backedup_streams_ && !stream->reset_reason_.has_value() &&
    1407         309 :                stream->stream_manager_.hasBufferedBodyOrTrailers()) {
    1408          20 :       ASSERT(error_code == NGHTTP2_NO_ERROR);
    1409          20 :       ENVOY_CONN_LOG(debug, "buffered onStreamClose for stream: {}", connection_, stream_id);
    1410             :       // Buffer the call, rely on the stream->process_buffered_data_callback_
    1411             :       // to end up invoking.
    1412          20 :       stream->stream_manager_.buffered_on_stream_close_ = true;
    1413          20 :       stats_.deferred_stream_close_.inc();
    1414          20 :       return okStatus();
    1415          20 :     }
    1416             : 
    1417         361 :     stream->destroy();
    1418         361 :     current_stream_id_.reset();
    1419             :     // TODO(antoniovicente) Test coverage for onCloseStream before deferred reset handling happens.
    1420         361 :     pending_deferred_reset_streams_.erase(stream->stream_id_);
    1421             : 
    1422         361 :     connection_.dispatcher().deferredDelete(stream->removeFromList(active_streams_));
    1423             :     // Any unconsumed data must be consumed before the stream is deleted.
    1424             :     // nghttp2 does not appear to track this internally, and any stream deleted
    1425             :     // with outstanding window will contribute to a slow connection-window leak.
    1426         361 :     ENVOY_CONN_LOG(debug, "Recouping {} bytes of flow control window for stream {}.", connection_,
    1427         361 :                    stream->unconsumed_bytes_, stream_id);
    1428         361 :     adapter_->MarkDataConsumedForStream(stream_id, stream->unconsumed_bytes_);
    1429         361 :     stream->unconsumed_bytes_ = 0;
    1430         361 :     adapter_->SetStreamUserData(stream->stream_id_, nullptr);
    1431         361 :   }
    1432             : 
    1433         373 :   return okStatus();
    1434         393 : }
    1435             : 
    1436         377 : Status ConnectionImpl::onStreamClose(int32_t stream_id, uint32_t error_code) {
    1437         377 :   return onStreamClose(getStreamUnchecked(stream_id), error_code);
    1438         377 : }
    1439             : 
    1440         318 : int ConnectionImpl::onMetadataReceived(int32_t stream_id, const uint8_t* data, size_t len) {
    1441         318 :   ENVOY_CONN_LOG(trace, "recv {} bytes METADATA", connection_, len);
    1442             : 
    1443         318 :   StreamImpl* stream = getStreamUnchecked(stream_id);
    1444         318 :   if (!stream || stream->remote_end_stream_) {
    1445         274 :     if (!stream) {
    1446         159 :       ENVOY_CONN_LOG(debug, "no stream for stream_id {} while receiving METADATA", connection_,
    1447         159 :                      stream_id);
    1448         159 :     }
    1449         274 :     return 0;
    1450         274 :   }
    1451             : 
    1452          44 :   bool success = stream->getMetadataDecoder().receiveMetadata(data, len);
    1453          44 :   return success ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
    1454         318 : }
    1455             : 
    1456          76 : int ConnectionImpl::onMetadataFrameComplete(int32_t stream_id, bool end_metadata) {
    1457          76 :   ENVOY_CONN_LOG(trace, "recv METADATA frame on stream {}, end_metadata: {}", connection_,
    1458          76 :                  stream_id, end_metadata);
    1459             : 
    1460          76 :   StreamImpl* stream = getStreamUnchecked(stream_id);
    1461          76 :   if (!stream || stream->remote_end_stream_) {
    1462          70 :     if (!stream) {
    1463          35 :       ENVOY_CONN_LOG(debug, "no stream for stream_id {} while completing METADATA", connection_,
    1464          35 :                      stream_id);
    1465          35 :     }
    1466          70 :     return 0;
    1467          70 :   }
    1468             : 
    1469           6 :   bool result = stream->getMetadataDecoder().onMetadataFrameComplete(end_metadata);
    1470           6 :   return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
    1471          76 : }
    1472             : 
    1473             : int ConnectionImpl::saveHeader(const nghttp2_frame* frame, HeaderString&& name,
    1474       24931 :                                HeaderString&& value) {
    1475       24931 :   StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
    1476       24931 :   if (!stream) {
    1477             :     // We have seen 1 or 2 crashes where we get a headers callback but there is no associated
    1478             :     // stream data. I honestly am not sure how this can happen. However, from reading the nghttp2
    1479             :     // code it looks possible that inflate_header_block() can safely inflate headers for an already
    1480             :     // closed stream, but will still call the headers callback. Since that seems possible, we should
    1481             :     // ignore this case here.
    1482             :     // TODO(mattklein123): Figure out a test case that can hit this.
    1483           0 :     stats_.headers_cb_no_stream_.inc();
    1484           0 :     return 0;
    1485           0 :   }
    1486             : 
    1487             :   // TODO(10646): Switch to use HeaderUtility::checkHeaderNameForUnderscores().
    1488       24931 :   auto should_return = checkHeaderNameForUnderscores(name.getStringView());
    1489       24931 :   if (should_return) {
    1490           0 :     stream->setDetails(Http2ResponseCodeDetails::get().invalid_underscore);
    1491           0 :     name.clear();
    1492           0 :     value.clear();
    1493           0 :     return should_return.value();
    1494           0 :   }
    1495             : 
    1496       24931 :   stream->saveHeader(std::move(name), std::move(value));
    1497             : 
    1498       24931 :   if (stream->headers().byteSize() > max_headers_kb_ * 1024 ||
    1499       24931 :       stream->headers().size() > max_headers_count_) {
    1500           6 :     stream->setDetails(Http2ResponseCodeDetails::get().too_many_headers);
    1501           6 :     stats_.header_overflow_.inc();
    1502             :     // This will cause the library to reset/close the stream.
    1503           6 :     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
    1504       24925 :   } else {
    1505       24925 :     return 0;
    1506       24925 :   }
    1507       24931 : }
    1508             : 
    1509       47467 : Status ConnectionImpl::sendPendingFrames() {
    1510       47467 :   if (dispatching_ || connection_.state() == Network::Connection::State::Closed) {
    1511         635 :     return okStatus();
    1512         635 :   }
    1513             : 
    1514       46832 :   const int rc = adapter_->Send();
    1515       46832 :   if (rc != 0) {
    1516        1888 :     ASSERT(rc == NGHTTP2_ERR_CALLBACK_FAILURE);
    1517        1888 :     return codecProtocolError(nghttp2_strerror(rc));
    1518        1888 :   }
    1519             : 
    1520             :   // See ConnectionImpl::StreamImpl::resetStream() for why we do this. This is an uncommon event,
    1521             :   // so iterating through every stream to find the ones that have a deferred reset is not a big
    1522             :   // deal. Furthermore, queueing a reset frame does not actually invoke the close stream callback.
    1523             :   // This is only done when the reset frame is sent. Thus, it's safe to work directly with the
    1524             :   // stream map.
    1525             :   // NOTE: The way we handle deferred reset is essentially best effort. If we intend to do a
    1526             :   //       deferred reset, we try to finish the stream, including writing any pending data frames.
    1527             :   //       If we cannot do this (potentially due to not enough window), we just reset the stream.
    1528             :   //       In general this behavior occurs only when we are trying to send immediate error messages
    1529             :   //       to short circuit requests. In the best effort case, we complete the stream before
    1530             :   //       resetting. In other cases, we just do the reset now which will blow away pending data
    1531             :   //       frames and release any memory associated with the stream.
    1532       44944 :   if (!pending_deferred_reset_streams_.empty()) {
    1533           0 :     while (!pending_deferred_reset_streams_.empty()) {
    1534           0 :       auto it = pending_deferred_reset_streams_.begin();
    1535           0 :       auto* stream = it->second;
    1536             :       // Sanity check: the stream's id matches the map key.
    1537           0 :       ASSERT(it->first == stream->stream_id_);
    1538           0 :       pending_deferred_reset_streams_.erase(it);
    1539           0 :       ASSERT(stream->deferred_reset_);
    1540           0 :       stream->resetStreamWorker(stream->deferred_reset_.value());
    1541           0 :     }
    1542           0 :     RETURN_IF_ERROR(sendPendingFrames());
    1543           0 :   }
    1544             : 
    1545             :   // After all pending frames have been written into the outbound buffer check if any of
    1546             :   // protocol constraints had been violated.
    1547       44944 :   Status status = protocol_constraints_.checkOutboundFrameLimits();
    1548       44944 :   if (!status.ok()) {
    1549           0 :     ENVOY_CONN_LOG(debug, "error sending frames: Too many frames in the outbound queue.",
    1550           0 :                    connection_);
    1551           0 :   }
    1552       44944 :   return status;
    1553       44944 : }
    1554             : 
    1555        2487 : bool ConnectionImpl::sendPendingFramesAndHandleError() {
    1556        2487 :   if (!sendPendingFrames().ok()) {
    1557           0 :     scheduleProtocolConstraintViolationCallback();
    1558           0 :     return true;
    1559           0 :   }
    1560        2487 :   return false;
    1561        2487 : }
    1562             : 
    1563             : void ConnectionImpl::sendSettingsHelper(
    1564        2860 :     const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) {
    1565        2860 :   absl::InlinedVector<http2::adapter::Http2Setting, 10> settings;
    1566        2860 :   auto insertParameter = [&settings](const http2::adapter::Http2Setting& entry) mutable -> bool {
    1567             :     // Consider using a set as an intermediate data structure, rather than this ad-hoc
    1568             :     // deduplication.
    1569           0 :     const auto it = std::find_if(
    1570           0 :         settings.cbegin(), settings.cend(),
    1571           0 :         [&entry](const http2::adapter::Http2Setting& existing) { return entry.id == existing.id; });
    1572           0 :     if (it != settings.end()) {
    1573           0 :       return false;
    1574           0 :     }
    1575           0 :     settings.push_back(entry);
    1576           0 :     return true;
    1577           0 :   };
    1578             : 
    1579             :   // Universally disable receiving push promise frames as we don't currently
    1580             :   // support them. nghttp2 will fail the connection if the other side still
    1581             :   // sends them.
    1582             :   // TODO(mattklein123): Remove this when we correctly proxy push promise.
    1583             :   // NOTE: This is a special case with respect to custom parameter overrides in
    1584             :   // that server push is not supported and therefore not end user configurable.
    1585        2860 :   if (disable_push) {
    1586         361 :     settings.push_back({static_cast<int32_t>(http2::adapter::ENABLE_PUSH), disable_push ? 0U : 1U});
    1587         361 :   }
    1588             : 
    1589        2860 :   for (const auto& it : http2_options.custom_settings_parameters()) {
    1590           0 :     ASSERT(it.identifier().value() <= std::numeric_limits<uint16_t>::max());
    1591           0 :     const bool result =
    1592           0 :         insertParameter({static_cast<http2::adapter::Http2SettingsId>(it.identifier().value()),
    1593           0 :                          it.value().value()});
    1594           0 :     ASSERT(result);
    1595           0 :     ENVOY_CONN_LOG(debug, "adding custom settings parameter with id {:#x} to {}", connection_,
    1596           0 :                    it.identifier().value(), it.value().value());
    1597           0 :   }
    1598             : 
    1599             :   // Insert named parameters.
    1600        2860 :   settings.insert(
    1601        2860 :       settings.end(),
    1602        2860 :       {{http2::adapter::HEADER_TABLE_SIZE, http2_options.hpack_table_size().value()},
    1603        2860 :        {http2::adapter::ENABLE_CONNECT_PROTOCOL, http2_options.allow_connect()},
    1604        2860 :        {http2::adapter::MAX_CONCURRENT_STREAMS, http2_options.max_concurrent_streams().value()},
    1605        2860 :        {http2::adapter::INITIAL_WINDOW_SIZE, http2_options.initial_stream_window_size().value()}});
    1606        2860 :   adapter_->SubmitSettings(settings);
    1607        2860 : }
    1608             : 
    1609             : void ConnectionImpl::sendSettings(
    1610        2860 :     const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) {
    1611        2860 :   sendSettingsHelper(http2_options, disable_push);
    1612             : 
    1613        2860 :   const uint32_t initial_connection_window_size =
    1614        2860 :       http2_options.initial_connection_window_size().value();
    1615             :   // Increase connection window size up to our default size.
    1616        2860 :   if (initial_connection_window_size != NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE) {
    1617        2636 :     ENVOY_CONN_LOG(debug, "updating connection-level initial window size to {}", connection_,
    1618        2636 :                    initial_connection_window_size);
    1619        2636 :     adapter_->SubmitWindowUpdate(0, initial_connection_window_size -
    1620        2636 :                                         NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE);
    1621        2636 :   }
    1622        2860 : }
    1623             : 
    1624       90018 : int ConnectionImpl::setAndCheckCodecCallbackStatus(Status&& status) {
    1625             :   // Keep the error status that caused the original failure. Subsequent
    1626             :   // error statuses are silently discarded.
    1627       90018 :   codec_callback_status_.Update(std::move(status));
    1628       90018 :   if (codec_callback_status_.ok() && connection_.state() != Network::Connection::State::Open) {
    1629           5 :     codec_callback_status_ = codecProtocolError("Connection was closed while dispatching frames");
    1630           5 :   }
    1631             : 
    1632       90018 :   return codec_callback_status_.ok() ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
    1633       90018 : }
    1634             : 
    1635           0 : void ConnectionImpl::scheduleProtocolConstraintViolationCallback() {
    1636           0 :   if (!protocol_constraint_violation_callback_) {
    1637           0 :     protocol_constraint_violation_callback_ = connection_.dispatcher().createSchedulableCallback(
    1638           0 :         [this]() { onProtocolConstraintViolation(); });
    1639           0 :     protocol_constraint_violation_callback_->scheduleCallbackCurrentIteration();
    1640           0 :   }
    1641           0 : }
    1642             : 
    1643           0 : void ConnectionImpl::onProtocolConstraintViolation() {
    1644             :   // Flooded outbound queue implies that peer is not reading and it does not
    1645             :   // make sense to try to flush pending bytes.
    1646           0 :   connection_.close(Envoy::Network::ConnectionCloseType::NoFlush,
    1647           0 :                     StreamInfo::LocalCloseReasons::get().Http2ConnectionProtocolViolation);
    1648           0 : }
    1649             : 
    1650           0 : void ConnectionImpl::onUnderlyingConnectionBelowWriteBufferLowWatermark() {
    1651           0 :   if (Runtime::runtimeFeatureEnabled(Runtime::defer_processing_backedup_streams)) {
    1652             :     // Notify the streams based on least recently encoding to the connection.
    1653           0 :     for (auto it = active_streams_.rbegin(); it != active_streams_.rend(); ++it) {
    1654           0 :       (*it)->runLowWatermarkCallbacks();
    1655           0 :     }
    1656           0 :   } else {
    1657           0 :     for (auto& stream : active_streams_) {
    1658           0 :       stream->runLowWatermarkCallbacks();
    1659           0 :     }
    1660           0 :   }
    1661           0 : }
    1662             : 
    1663          69 : ConnectionImpl::Http2Callbacks::Http2Callbacks() {
    1664          69 :   nghttp2_session_callbacks_new(&callbacks_);
    1665          69 :   nghttp2_session_callbacks_set_send_callback(
    1666          69 :       callbacks_,
    1667       25116 :       [](nghttp2_session*, const uint8_t* data, size_t length, int, void* user_data) -> ssize_t {
    1668       25058 :         return static_cast<ConnectionImpl*>(user_data)->onSend(data, length);
    1669       25058 :       });
    1670             : 
    1671          69 :   nghttp2_session_callbacks_set_on_begin_headers_callback(
    1672        1822 :       callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int {
    1673        1764 :         auto status = static_cast<ConnectionImpl*>(user_data)->onBeginHeaders(frame);
    1674        1764 :         return static_cast<ConnectionImpl*>(user_data)->setAndCheckCodecCallbackStatus(
    1675        1764 :             std::move(status));
    1676        1764 :       });
    1677             : 
    1678          69 :   nghttp2_session_callbacks_set_on_header_callback(
    1679          69 :       callbacks_,
    1680          69 :       [](nghttp2_session*, const nghttp2_frame* frame, const uint8_t* raw_name, size_t name_length,
    1681       24990 :          const uint8_t* raw_value, size_t value_length, uint8_t, void* user_data) -> int {
    1682             :         // TODO PERF: Can reference count here to avoid copies.
    1683       24931 :         HeaderString name;
    1684       24931 :         name.setCopy(reinterpret_cast<const char*>(raw_name), name_length);
    1685       24931 :         HeaderString value;
    1686       24931 :         value.setCopy(reinterpret_cast<const char*>(raw_value), value_length);
    1687       24931 :         return static_cast<ConnectionImpl*>(user_data)->onHeader(frame, std::move(name),
    1688       24931 :                                                                  std::move(value));
    1689       24931 :       });
    1690             : 
    1691          69 :   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
    1692          69 :       callbacks_,
    1693          69 :       [](nghttp2_session*, uint8_t, int32_t stream_id, const uint8_t* data, size_t len,
    1694       38939 :          void* user_data) -> int {
    1695       38874 :         return static_cast<ConnectionImpl*>(user_data)->onData(stream_id, data, len);
    1696       38874 :       });
    1697             : 
    1698          69 :   nghttp2_session_callbacks_set_on_begin_frame_callback(
    1699       45244 :       callbacks_, [](nghttp2_session*, const nghttp2_frame_hd* hd, void* user_data) -> int {
    1700       45186 :         auto status = static_cast<ConnectionImpl*>(user_data)->onBeforeFrameReceived(
    1701       45186 :             hd->stream_id, hd->length, hd->type, hd->flags);
    1702       45186 :         return static_cast<ConnectionImpl*>(user_data)->setAndCheckCodecCallbackStatus(
    1703       45186 :             std::move(status));
    1704       45186 :       });
    1705             : 
    1706          69 :   nghttp2_session_callbacks_set_on_frame_recv_callback(
    1707       42743 :       callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int {
    1708       42685 :         auto status = static_cast<ConnectionImpl*>(user_data)->onFrameReceived(frame);
    1709       42685 :         return static_cast<ConnectionImpl*>(user_data)->setAndCheckCodecCallbackStatus(
    1710       42685 :             std::move(status));
    1711       42685 :       });
    1712             : 
    1713          69 :   nghttp2_session_callbacks_set_on_stream_close_callback(
    1714          69 :       callbacks_,
    1715         438 :       [](nghttp2_session*, int32_t stream_id, uint32_t error_code, void* user_data) -> int {
    1716         377 :         auto status = static_cast<ConnectionImpl*>(user_data)->onStreamClose(stream_id, error_code);
    1717         377 :         return static_cast<ConnectionImpl*>(user_data)->setAndCheckCodecCallbackStatus(
    1718         377 :             std::move(status));
    1719         377 :       });
    1720             : 
    1721          69 :   nghttp2_session_callbacks_set_on_frame_send_callback(
    1722       47097 :       callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int {
    1723       47039 :         uint32_t error_code = 0;
    1724       47039 :         switch (frame->hd.type) {
    1725        1982 :         case NGHTTP2_GOAWAY:
    1726        1982 :           error_code = frame->goaway.error_code;
    1727        1982 :           break;
    1728          84 :         case NGHTTP2_RST_STREAM:
    1729          84 :           error_code = frame->rst_stream.error_code;
    1730          84 :           break;
    1731       47039 :         }
    1732       47039 :         return static_cast<ConnectionImpl*>(user_data)->onFrameSend(
    1733       47039 :             frame->hd.stream_id, frame->hd.length, frame->hd.type, frame->hd.flags, error_code);
    1734       47039 :       });
    1735             : 
    1736          69 :   nghttp2_session_callbacks_set_before_frame_send_callback(
    1737       24763 :       callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int {
    1738       24705 :         return static_cast<ConnectionImpl*>(user_data)->onBeforeFrameSend(
    1739       24705 :             frame->hd.stream_id, frame->hd.length, frame->hd.type, frame->hd.flags);
    1740       24705 :       });
    1741             : 
    1742          69 :   nghttp2_session_callbacks_set_on_frame_not_send_callback(
    1743          69 :       callbacks_, [](nghttp2_session*, const nghttp2_frame*, int, void*) -> int {
    1744             :         // We used to always return failure here but it looks now this can get called if the other
    1745             :         // side sends GOAWAY and we are trying to send a SETTINGS ACK. Just ignore this for now.
    1746           0 :         return 0;
    1747           0 :       });
    1748             : 
    1749          69 :   nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
    1750          69 :       callbacks_,
    1751         245 :       [](nghttp2_session*, const nghttp2_frame* frame, int error_code, void* user_data) -> int {
    1752         182 :         return static_cast<ConnectionImpl*>(user_data)->onInvalidFrame(frame->hd.stream_id,
    1753         182 :                                                                        error_code);
    1754         182 :       });
    1755             : 
    1756          69 :   nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
    1757          69 :       callbacks_,
    1758          69 :       [](nghttp2_session*, const nghttp2_frame_hd* hd, const uint8_t* data, size_t len,
    1759         382 :          void* user_data) -> int {
    1760         318 :         ASSERT(hd->length >= len);
    1761         318 :         return static_cast<ConnectionImpl*>(user_data)->onMetadataReceived(hd->stream_id, data,
    1762         318 :                                                                            len);
    1763         318 :       });
    1764             : 
    1765          69 :   nghttp2_session_callbacks_set_unpack_extension_callback(
    1766         140 :       callbacks_, [](nghttp2_session*, void**, const nghttp2_frame_hd* hd, void* user_data) -> int {
    1767          76 :         return static_cast<ConnectionImpl*>(user_data)->onMetadataFrameComplete(
    1768          76 :             hd->stream_id, hd->flags == END_METADATA_FLAG);
    1769          76 :       });
    1770             : 
    1771          69 :   nghttp2_session_callbacks_set_error_callback2(
    1772          83 :       callbacks_, [](nghttp2_session*, int, const char* msg, size_t len, void* user_data) -> int {
    1773          16 :         return static_cast<ConnectionImpl*>(user_data)->onError(absl::string_view(msg, len));
    1774          16 :       });
    1775          69 : }
    1776             : 
    1777           0 : ConnectionImpl::Http2Callbacks::~Http2Callbacks() { nghttp2_session_callbacks_del(callbacks_); }
    1778             : 
    1779             : ConnectionImpl::Http2Options::Http2Options(
    1780        2860 :     const envoy::config::core::v3::Http2ProtocolOptions& http2_options, uint32_t max_headers_kb) {
    1781        2860 :   og_options_.perspective = http2::adapter::Perspective::kServer;
    1782        2860 :   og_options_.max_hpack_encoding_table_capacity = http2_options.hpack_table_size().value();
    1783        2860 :   og_options_.max_header_list_bytes = max_headers_kb * 1024;
    1784        2860 :   og_options_.max_header_field_size = max_headers_kb * 1024;
    1785        2860 :   og_options_.allow_extended_connect = http2_options.allow_connect();
    1786        2860 :   og_options_.allow_different_host_and_authority = true;
    1787             : 
    1788             : #ifdef ENVOY_ENABLE_UHV
    1789             :   // UHV - disable header validations in oghttp2
    1790             :   og_options_.validate_http_headers = false;
    1791             : #endif
    1792             : 
    1793        2860 :   nghttp2_option_new(&options_);
    1794             :   // Currently we do not do anything with stream priority. Setting the following option prevents
    1795             :   // nghttp2 from keeping around closed streams for use during stream priority dependency graph
    1796             :   // calculations. This saves a tremendous amount of memory in cases where there are a large
    1797             :   // number of kept alive HTTP/2 connections.
    1798        2860 :   nghttp2_option_set_no_closed_streams(options_, 1);
    1799        2860 :   nghttp2_option_set_no_auto_window_update(options_, 1);
    1800             : 
    1801             :   // RFC9113 invalidates trailing whitespace in header values but this is a new validation which
    1802             :   // can break existing deployments.
    1803             :   // Disable this validation for now.
    1804        2860 :   nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(options_, 1);
    1805             : 
    1806             :   // The max send header block length is configured to an arbitrarily high number so as to never
    1807             :   // trigger the check within nghttp2, as we check request headers length in
    1808             :   // codec_impl::saveHeader.
    1809        2860 :   nghttp2_option_set_max_send_header_block_length(options_, 0x2000000);
    1810             : 
    1811        2860 :   if (http2_options.hpack_table_size().value() != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
    1812         224 :     nghttp2_option_set_max_deflate_dynamic_table_size(options_,
    1813         224 :                                                       http2_options.hpack_table_size().value());
    1814         224 :   }
    1815             : 
    1816        2860 :   if (http2_options.allow_metadata()) {
    1817         812 :     nghttp2_option_set_user_recv_extension_type(options_, METADATA_FRAME_TYPE);
    1818        2787 :   } else {
    1819        2048 :     ENVOY_LOG(trace, "Codec does not have Metadata frame support.");
    1820        2048 :   }
    1821             : 
    1822             :   // nghttp2 v1.39.2 lowered the internal flood protection limit from 10K to 1K of ACK frames.
    1823             :   // This new limit may cause the internal nghttp2 mitigation to trigger more often (as it
    1824             :   // requires just 9K of incoming bytes for smallest 9 byte SETTINGS frame), bypassing the same
    1825             :   // mitigation and its associated behavior in the envoy HTTP/2 codec. Since envoy does not rely
    1826             :   // on this mitigation, set back to the old 10K number to avoid any changes in the HTTP/2 codec
    1827             :   // behavior.
    1828        2860 :   nghttp2_option_set_max_outbound_ack(options_, 10000);
    1829        2860 : }
    1830             : 
    1831        2860 : ConnectionImpl::Http2Options::~Http2Options() { nghttp2_option_del(options_); }
    1832             : 
    1833             : ConnectionImpl::ClientHttp2Options::ClientHttp2Options(
    1834             :     const envoy::config::core::v3::Http2ProtocolOptions& http2_options, uint32_t max_headers_kb)
    1835         361 :     : Http2Options(http2_options, max_headers_kb) {
    1836         361 :   og_options_.perspective = http2::adapter::Perspective::kClient;
    1837         361 :   og_options_.remote_max_concurrent_streams =
    1838         361 :       ::Envoy::Http2::Utility::OptionsLimits::DEFAULT_MAX_CONCURRENT_STREAMS;
    1839             :   // Temporarily disable initial max streams limit/protection, since we might want to create
    1840             :   // more than 100 streams before receiving the HTTP/2 SETTINGS frame from the server.
    1841             :   //
    1842             :   // TODO(PiotrSikora): remove this once multiple upstream connections or queuing are implemented.
    1843         361 :   nghttp2_option_set_peer_max_concurrent_streams(
    1844         361 :       options_, ::Envoy::Http2::Utility::OptionsLimits::DEFAULT_MAX_CONCURRENT_STREAMS);
    1845         361 : }
    1846             : 
    1847           0 : void ConnectionImpl::dumpState(std::ostream& os, int indent_level) const {
    1848           0 :   const char* spaces = spacesForLevel(indent_level);
    1849           0 :   os << spaces << "Http2::ConnectionImpl " << this << DUMP_MEMBER(max_headers_kb_)
    1850           0 :      << DUMP_MEMBER(max_headers_count_) << DUMP_MEMBER(per_stream_buffer_limit_)
    1851           0 :      << DUMP_MEMBER(allow_metadata_) << DUMP_MEMBER(stream_error_on_invalid_http_messaging_)
    1852           0 :      << DUMP_MEMBER(is_outbound_flood_monitored_control_frame_) << DUMP_MEMBER(dispatching_)
    1853           0 :      << DUMP_MEMBER(raised_goaway_) << DUMP_MEMBER(pending_deferred_reset_streams_.size()) << '\n';
    1854             : 
    1855             :   // Dump the protocol constraints
    1856           0 :   DUMP_DETAILS(&protocol_constraints_);
    1857             : 
    1858             :   // Dump either a targeted stream or several of the active streams.
    1859           0 :   dumpStreams(os, indent_level);
    1860             : 
    1861             :   // Dump the active slice
    1862           0 :   if (current_slice_ == nullptr) {
    1863             :     // No current slice, use macro for consistent formatting.
    1864           0 :     os << spaces << "current_slice_: null\n";
    1865           0 :   } else {
    1866           0 :     auto slice_view =
    1867           0 :         absl::string_view(static_cast<const char*>(current_slice_->mem_), current_slice_->len_);
    1868             : 
    1869           0 :     os << spaces << "current slice length: " << slice_view.length() << " contents: \"";
    1870           0 :     StringUtil::escapeToOstream(os, slice_view);
    1871           0 :     os << "\"\n";
    1872           0 :   }
    1873           0 : }
    1874             : 
    1875           0 : void ConnectionImpl::dumpStreams(std::ostream& os, int indent_level) const {
    1876           0 :   const char* spaces = spacesForLevel(indent_level);
    1877             : 
    1878             :   // Try to dump details for the current stream.
    1879             :   // If none, dump a subset of our active streams.
    1880           0 :   os << spaces << "Number of active streams: " << active_streams_.size()
    1881           0 :      << DUMP_OPTIONAL_MEMBER(current_stream_id_);
    1882             : 
    1883           0 :   if (current_stream_id_.has_value()) {
    1884           0 :     os << " Dumping current stream:\n";
    1885           0 :     const ConnectionImpl::StreamImpl* stream = getStream(current_stream_id_.value());
    1886           0 :     DUMP_DETAILS(stream);
    1887           0 :   } else {
    1888           0 :     os << " Dumping " << std::min<size_t>(25, active_streams_.size()) << " Active Streams:\n";
    1889           0 :     size_t count = 0;
    1890           0 :     for (auto& stream : active_streams_) {
    1891           0 :       DUMP_DETAILS(stream);
    1892           0 :       if (++count >= 25) {
    1893           0 :         break;
    1894           0 :       }
    1895           0 :     }
    1896           0 :   }
    1897           0 : }
    1898             : 
    1899           0 : void ClientConnectionImpl::dumpStreams(std::ostream& os, int indent_level) const {
    1900           0 :   ConnectionImpl::dumpStreams(os, indent_level);
    1901             : 
    1902           0 :   if (!current_stream_id_.has_value()) {
    1903           0 :     return;
    1904           0 :   }
    1905             : 
    1906             :   // Try to dump the downstream request information, corresponding to the
    1907             :   // stream we were processing.
    1908           0 :   const char* spaces = spacesForLevel(indent_level);
    1909           0 :   os << spaces << "Dumping corresponding downstream request for upstream stream "
    1910           0 :      << current_stream_id_.value() << ":\n";
    1911             : 
    1912           0 :   const ClientStreamImpl* client_stream =
    1913           0 :       static_cast<const ClientStreamImpl*>(getStreamUnchecked(current_stream_id_.value()));
    1914           0 :   if (client_stream) {
    1915           0 :     client_stream->response_decoder_.dumpState(os, indent_level + 1);
    1916           0 :   } else {
    1917           0 :     os << spaces
    1918           0 :        << " Failed to get the upstream stream with stream id: " << current_stream_id_.value()
    1919           0 :        << " Unable to dump downstream request.\n";
    1920           0 :   }
    1921           0 : }
    1922             : 
    1923           0 : void ConnectionImpl::StreamImpl::dumpState(std::ostream& os, int indent_level) const {
    1924           0 :   const char* spaces = spacesForLevel(indent_level);
    1925           0 :   os << spaces << "ConnectionImpl::StreamImpl " << this << DUMP_MEMBER(stream_id_)
    1926           0 :      << DUMP_MEMBER(unconsumed_bytes_) << DUMP_MEMBER(read_disable_count_)
    1927           0 :      << DUMP_MEMBER(local_end_stream_) << DUMP_MEMBER(local_end_stream_sent_)
    1928           0 :      << DUMP_MEMBER(remote_end_stream_) << DUMP_MEMBER(data_deferred_)
    1929           0 :      << DUMP_MEMBER(received_noninformational_headers_)
    1930           0 :      << DUMP_MEMBER(pending_receive_buffer_high_watermark_called_)
    1931           0 :      << DUMP_MEMBER(pending_send_buffer_high_watermark_called_)
    1932           0 :      << DUMP_MEMBER(reset_due_to_messaging_error_)
    1933           0 :      << DUMP_MEMBER_AS(cookies_, cookies_.getStringView());
    1934             : 
    1935           0 :   DUMP_DETAILS(pending_trailers_to_encode_);
    1936           0 : }
    1937             : 
    1938           0 : void ConnectionImpl::ClientStreamImpl::dumpState(std::ostream& os, int indent_level) const {
    1939           0 :   const char* spaces = spacesForLevel(indent_level);
    1940           0 :   StreamImpl::dumpState(os, indent_level);
    1941             : 
    1942             :   // Dump header map
    1943           0 :   if (absl::holds_alternative<ResponseHeaderMapPtr>(headers_or_trailers_)) {
    1944           0 :     DUMP_DETAILS(absl::get<ResponseHeaderMapPtr>(headers_or_trailers_));
    1945           0 :   } else {
    1946           0 :     DUMP_DETAILS(absl::get<ResponseTrailerMapPtr>(headers_or_trailers_));
    1947           0 :   }
    1948           0 : }
    1949             : 
    1950           0 : void ConnectionImpl::ServerStreamImpl::dumpState(std::ostream& os, int indent_level) const {
    1951           0 :   const char* spaces = spacesForLevel(indent_level);
    1952           0 :   StreamImpl::dumpState(os, indent_level);
    1953             : 
    1954             :   // Dump header map
    1955           0 :   if (absl::holds_alternative<RequestHeaderMapSharedPtr>(headers_or_trailers_)) {
    1956           0 :     DUMP_DETAILS(absl::get<RequestHeaderMapSharedPtr>(headers_or_trailers_));
    1957           0 :   } else {
    1958           0 :     DUMP_DETAILS(absl::get<RequestTrailerMapPtr>(headers_or_trailers_));
    1959           0 :   }
    1960           0 : }
    1961             : 
    1962             : ClientConnectionImpl::ClientConnectionImpl(
    1963             :     Network::Connection& connection, Http::ConnectionCallbacks& callbacks, CodecStats& stats,
    1964             :     Random::RandomGenerator& random_generator,
    1965             :     const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
    1966             :     const uint32_t max_response_headers_kb, const uint32_t max_response_headers_count,
    1967             :     Http2SessionFactory& http2_session_factory)
    1968             :     : ConnectionImpl(connection, stats, random_generator, http2_options, max_response_headers_kb,
    1969             :                      max_response_headers_count),
    1970         361 :       callbacks_(callbacks) {
    1971         361 :   ClientHttp2Options client_http2_options(http2_options, max_response_headers_kb);
    1972         361 :   if (use_oghttp2_library_) {
    1973         305 :     adapter_ = http2_session_factory.create(http2_callbacks_.callbacks(), base(),
    1974         305 :                                             client_http2_options.ogOptions());
    1975         305 :   } else {
    1976          56 :     adapter_ = http2_session_factory.create(http2_callbacks_.callbacks(), base(),
    1977          56 :                                             client_http2_options.options());
    1978          56 :   }
    1979         361 :   http2_session_factory.init(base(), http2_options);
    1980         361 :   allow_metadata_ = http2_options.allow_metadata();
    1981         361 :   idle_session_requires_ping_interval_ = std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(
    1982         361 :       http2_options.connection_keepalive(), connection_idle_interval, 0));
    1983         361 : }
    1984             : 
    1985         475 : RequestEncoder& ClientConnectionImpl::newStream(ResponseDecoder& decoder) {
    1986             :   // If the connection has been idle long enough to trigger a ping, send one
    1987             :   // ahead of creating the stream.
    1988         475 :   if (idle_session_requires_ping_interval_.count() != 0 &&
    1989         475 :       (connection_.dispatcher().timeSource().monotonicTime() - lastReceivedDataTime() >
    1990           0 :        idle_session_requires_ping_interval_)) {
    1991           0 :     sendKeepalive();
    1992           0 :   }
    1993             : 
    1994         475 :   ClientStreamImplPtr stream(new ClientStreamImpl(*this, per_stream_buffer_limit_, decoder));
    1995             :   // If the connection is currently above the high watermark, make sure to inform the new stream.
    1996             :   // The connection can not pass this on automatically as it has no awareness that a new stream is
    1997             :   // created.
    1998         475 :   if (connection_.aboveHighWatermark()) {
    1999           0 :     stream->runHighWatermarkCallbacks();
    2000           0 :   }
    2001         475 :   ClientStreamImpl& stream_ref = *stream;
    2002         475 :   LinkedList::moveIntoList(std::move(stream), active_streams_);
    2003         475 :   protocol_constraints_.incrementOpenedStreamCount();
    2004         475 :   return stream_ref;
    2005         475 : }
    2006             : 
    2007         249 : Status ClientConnectionImpl::onBeginHeaders(const nghttp2_frame* frame) {
    2008             :   // The client code explicitly does not currently support push promise.
    2009         249 :   RELEASE_ASSERT(frame->hd.type == NGHTTP2_HEADERS, "");
    2010         249 :   RELEASE_ASSERT(frame->headers.cat == NGHTTP2_HCAT_RESPONSE ||
    2011         249 :                      frame->headers.cat == NGHTTP2_HCAT_HEADERS,
    2012         249 :                  "");
    2013         249 :   RETURN_IF_ERROR(trackInboundFrames(frame->hd.stream_id, frame->hd.length, frame->hd.type,
    2014         249 :                                      frame->hd.flags, frame->headers.padlen));
    2015         249 :   if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
    2016          14 :     StreamImpl* stream = getStream(frame->hd.stream_id);
    2017          14 :     stream->allocTrailers();
    2018          14 :   }
    2019             : 
    2020         249 :   return okStatus();
    2021         249 : }
    2022             : 
    2023             : int ClientConnectionImpl::onHeader(const nghttp2_frame* frame, HeaderString&& name,
    2024       20847 :                                    HeaderString&& value) {
    2025             :   // The client code explicitly does not currently support push promise.
    2026       20847 :   ASSERT(frame->hd.type == NGHTTP2_HEADERS);
    2027       20847 :   ASSERT(frame->headers.cat == NGHTTP2_HCAT_RESPONSE || frame->headers.cat == NGHTTP2_HCAT_HEADERS);
    2028       20847 :   ASSERT(connection_.state() == Network::Connection::State::Open);
    2029       20847 :   return saveHeader(frame, std::move(name), std::move(value));
    2030       20847 : }
    2031             : 
    2032             : // TODO(yanavlasov): move to the base class once the runtime flag is removed.
    2033             : Status ClientConnectionImpl::trackInboundFrames(int32_t stream_id, size_t length, uint8_t type,
    2034       16056 :                                                 uint8_t flags, uint32_t padding_length) {
    2035       16056 :   Status result;
    2036       16056 :   ENVOY_CONN_LOG(trace, "track inbound frame type={} flags={} length={} padding_length={}",
    2037       16056 :                  connection_, static_cast<uint64_t>(type), static_cast<uint64_t>(flags),
    2038       16056 :                  static_cast<uint64_t>(length), padding_length);
    2039             : 
    2040       16056 :   result = protocol_constraints_.trackInboundFrames(length, type, flags, padding_length);
    2041       16056 :   if (!result.ok()) {
    2042           0 :     ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_,
    2043           0 :                    result.message());
    2044           0 :     if (isInboundFramesWithEmptyPayloadError(result)) {
    2045           0 :       ConnectionImpl::StreamImpl* stream = getStreamUnchecked(stream_id);
    2046           0 :       if (stream) {
    2047           0 :         stream->setDetails(Http2ResponseCodeDetails::get().inbound_empty_frame_flood);
    2048           0 :       }
    2049             :       // Above if is defensive, because the stream has just been created and therefore always
    2050             :       // exists.
    2051           0 :     }
    2052           0 :   }
    2053       16056 :   return result;
    2054       16056 : }
    2055             : 
    2056           0 : StreamResetReason ClientConnectionImpl::getMessagingErrorResetReason() const {
    2057           0 :   connection_.streamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamProtocolError);
    2058             : 
    2059           0 :   return StreamResetReason::ProtocolError;
    2060           0 : }
    2061             : 
    2062             : ServerConnectionImpl::ServerConnectionImpl(
    2063             :     Network::Connection& connection, Http::ServerConnectionCallbacks& callbacks, CodecStats& stats,
    2064             :     Random::RandomGenerator& random_generator,
    2065             :     const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
    2066             :     const uint32_t max_request_headers_kb, const uint32_t max_request_headers_count,
    2067             :     envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
    2068             :         headers_with_underscores_action,
    2069             :     Server::OverloadManager& overload_manager)
    2070             :     : ConnectionImpl(connection, stats, random_generator, http2_options, max_request_headers_kb,
    2071             :                      max_request_headers_count),
    2072             :       callbacks_(callbacks), headers_with_underscores_action_(headers_with_underscores_action),
    2073             :       should_send_go_away_on_dispatch_(overload_manager.getLoadShedPoint(
    2074        2499 :           "envoy.load_shed_points.http2_server_go_away_on_dispatch")) {
    2075        2499 :   ENVOY_LOG_ONCE_IF(trace, should_send_go_away_on_dispatch_ == nullptr,
    2076        2499 :                     "LoadShedPoint envoy.load_shed_points.http2_server_go_away_on_dispatch is not "
    2077        2499 :                     "found. Is it configured?");
    2078        2499 :   Http2Options h2_options(http2_options, max_request_headers_kb);
    2079             : 
    2080        2499 :   auto visitor = std::make_unique<http2::adapter::CallbackVisitor>(
    2081        2499 :       http2::adapter::Perspective::kServer, *http2_callbacks_.callbacks(), base());
    2082        2499 :   if (use_oghttp2_library_) {
    2083        2276 :     visitor_ = std::move(visitor);
    2084        2276 :     adapter_ = http2::adapter::OgHttp2Adapter::Create(*visitor_, h2_options.ogOptions());
    2085        2276 :   } else {
    2086         223 :     auto adapter =
    2087         223 :         http2::adapter::NgHttp2Adapter::CreateServerAdapter(*visitor, h2_options.options());
    2088         223 :     auto stream_close_listener = [p = adapter.get()](http2::adapter::Http2StreamId stream_id) {
    2089           9 :       p->RemoveStream(stream_id);
    2090           9 :     };
    2091         223 :     visitor->set_stream_close_listener(std::move(stream_close_listener));
    2092         223 :     visitor_ = std::move(visitor);
    2093         223 :     adapter_ = std::move(adapter);
    2094         223 :   }
    2095        2499 :   sendSettings(http2_options, false);
    2096        2499 :   allow_metadata_ = http2_options.allow_metadata();
    2097        2499 : }
    2098             : 
    2099        1515 : Status ServerConnectionImpl::onBeginHeaders(const nghttp2_frame* frame) {
    2100             :   // For a server connection, we should never get push promise frames.
    2101        1515 :   ASSERT(frame->hd.type == NGHTTP2_HEADERS);
    2102        1515 :   ASSERT(connection_.state() == Network::Connection::State::Open);
    2103        1515 :   RETURN_IF_ERROR(trackInboundFrames(frame->hd.stream_id, frame->hd.length, frame->hd.type,
    2104        1515 :                                      frame->hd.flags, frame->headers.padlen));
    2105             : 
    2106        1515 :   if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
    2107          16 :     stats_.trailers_.inc();
    2108          16 :     ASSERT(frame->headers.cat == NGHTTP2_HCAT_HEADERS);
    2109             : 
    2110          16 :     StreamImpl* stream = getStream(frame->hd.stream_id);
    2111          16 :     stream->allocTrailers();
    2112          16 :     return okStatus();
    2113          16 :   }
    2114             : 
    2115        1499 :   ServerStreamImplPtr stream(new ServerStreamImpl(*this, per_stream_buffer_limit_));
    2116        1499 :   if (connection_.aboveHighWatermark()) {
    2117           0 :     stream->runHighWatermarkCallbacks();
    2118           0 :   }
    2119        1499 :   stream->setRequestDecoder(callbacks_.newStream(*stream));
    2120        1499 :   stream->stream_id_ = frame->hd.stream_id;
    2121        1499 :   LinkedList::moveIntoList(std::move(stream), active_streams_);
    2122        1499 :   adapter_->SetStreamUserData(frame->hd.stream_id, active_streams_.front().get());
    2123        1499 :   protocol_constraints_.incrementOpenedStreamCount();
    2124        1499 :   return okStatus();
    2125        1515 : }
    2126             : 
    2127             : int ServerConnectionImpl::onHeader(const nghttp2_frame* frame, HeaderString&& name,
    2128        4084 :                                    HeaderString&& value) {
    2129             :   // For a server connection, we should never get push promise frames.
    2130        4084 :   ASSERT(frame->hd.type == NGHTTP2_HEADERS);
    2131        4084 :   ASSERT(frame->headers.cat == NGHTTP2_HCAT_REQUEST || frame->headers.cat == NGHTTP2_HCAT_HEADERS);
    2132        4084 :   if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_discard_host_header")) {
    2133        4084 :     StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
    2134        4084 :     if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) {
    2135             :       // Check if there is already the :authority header
    2136           1 :       const auto result = stream->headers().get(Http::Headers::get().Host);
    2137           1 :       if (!result.empty()) {
    2138             :         // Discard the host header value
    2139           0 :         return 0;
    2140           0 :       }
    2141             :       // Otherwise use host value as :authority
    2142           1 :     }
    2143        4084 :   }
    2144        4084 :   return saveHeader(frame, std::move(name), std::move(value));
    2145        4084 : }
    2146             : 
    2147             : Status ServerConnectionImpl::trackInboundFrames(int32_t stream_id, size_t length, uint8_t type,
    2148       28865 :                                                 uint8_t flags, uint32_t padding_length) {
    2149       28865 :   ENVOY_CONN_LOG(trace, "track inbound frame type={} flags={} length={} padding_length={}",
    2150       28865 :                  connection_, static_cast<uint64_t>(type), static_cast<uint64_t>(flags),
    2151       28865 :                  static_cast<uint64_t>(length), padding_length);
    2152             : 
    2153       28865 :   auto result = protocol_constraints_.trackInboundFrames(length, type, flags, padding_length);
    2154       28865 :   if (!result.ok()) {
    2155           0 :     ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_,
    2156           0 :                    result.message());
    2157           0 :     if (isInboundFramesWithEmptyPayloadError(result)) {
    2158           0 :       ConnectionImpl::StreamImpl* stream = getStreamUnchecked(stream_id);
    2159           0 :       if (stream) {
    2160           0 :         stream->setDetails(Http2ResponseCodeDetails::get().inbound_empty_frame_flood);
    2161           0 :       }
    2162             :       // Above if is defensive, because the stream has just been created and therefore always
    2163             :       // exists.
    2164           0 :     }
    2165           0 :   }
    2166       28865 :   return result;
    2167       28865 : }
    2168             : 
    2169       29693 : Http::Status ServerConnectionImpl::dispatch(Buffer::Instance& data) {
    2170             :   // Make sure downstream outbound queue was not flooded by the upstream frames.
    2171       29693 :   RETURN_IF_ERROR(protocol_constraints_.checkOutboundFrameLimits());
    2172       29693 :   if (should_send_go_away_on_dispatch_ != nullptr && !sent_go_away_on_dispatch_ &&
    2173       29693 :       should_send_go_away_on_dispatch_->shouldShedLoad()) {
    2174           0 :     ConnectionImpl::goAway();
    2175           0 :     sent_go_away_on_dispatch_ = true;
    2176           0 :   }
    2177       29693 :   return ConnectionImpl::dispatch(data);
    2178       29693 : }
    2179             : 
    2180             : absl::optional<int> ServerConnectionImpl::checkHeaderNameForUnderscores(
    2181        4084 :     [[maybe_unused]] absl::string_view header_name) {
    2182        4084 : #ifndef ENVOY_ENABLE_UHV
    2183             :   // This check has been moved to UHV
    2184        4084 :   if (headers_with_underscores_action_ != envoy::config::core::v3::HttpProtocolOptions::ALLOW &&
    2185        4084 :       Http::HeaderUtility::headerNameContainsUnderscore(header_name)) {
    2186           0 :     if (headers_with_underscores_action_ ==
    2187           0 :         envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER) {
    2188           0 :       ENVOY_CONN_LOG(debug, "Dropping header with invalid characters in its name: {}", connection_,
    2189           0 :                      header_name);
    2190           0 :       stats_.incDroppedHeadersWithUnderscores();
    2191           0 :       return 0;
    2192           0 :     }
    2193           0 :     ENVOY_CONN_LOG(debug, "Rejecting request due to header name with underscores: {}", connection_,
    2194           0 :                    header_name);
    2195           0 :     stats_.incRequestsRejectedWithUnderscoresInHeaders();
    2196           0 :     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
    2197           0 :   }
    2198             : #else
    2199             :   // Workaround for gcc not understanding [[maybe_unused]] for class members.
    2200             :   (void)headers_with_underscores_action_;
    2201             : #endif
    2202        4084 :   return absl::nullopt;
    2203        4084 : }
    2204             : 
    2205             : } // namespace Http2
    2206             : } // namespace Http
    2207             : } // namespace Envoy

Generated by: LCOV version 1.15