LCOV - code coverage report
Current view: top level - source/common/http/http2 - codec_impl.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 116 144 80.6 %
Date: 2024-01-05 06:35:25 Functions: 48 63 76.2 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <cstdint>
       4             : #include <functional>
       5             : #include <list>
       6             : #include <memory>
       7             : #include <ostream>
       8             : #include <string>
       9             : #include <vector>
      10             : 
      11             : #include "envoy/buffer/buffer.h"
      12             : #include "envoy/common/random_generator.h"
      13             : #include "envoy/common/scope_tracker.h"
      14             : #include "envoy/config/core/v3/protocol.pb.h"
      15             : #include "envoy/event/deferred_deletable.h"
      16             : #include "envoy/http/codec.h"
      17             : #include "envoy/network/connection.h"
      18             : 
      19             : #include "source/common/buffer/buffer_impl.h"
      20             : #include "source/common/buffer/watermark_buffer.h"
      21             : #include "source/common/common/assert.h"
      22             : #include "source/common/common/linked_object.h"
      23             : #include "source/common/common/logger.h"
      24             : #include "source/common/common/statusor.h"
      25             : #include "source/common/common/thread.h"
      26             : #include "source/common/http/codec_helper.h"
      27             : #include "source/common/http/header_map_impl.h"
      28             : #include "source/common/http/http2/codec_stats.h"
      29             : #include "source/common/http/http2/metadata_decoder.h"
      30             : #include "source/common/http/http2/metadata_encoder.h"
      31             : #include "source/common/http/http2/protocol_constraints.h"
      32             : #include "source/common/http/status.h"
      33             : #include "source/common/http/utility.h"
      34             : 
      35             : #include "absl/types/optional.h"
      36             : #include "nghttp2/nghttp2.h"
      37             : #include "quiche/http2/adapter/http2_adapter.h"
      38             : #include "quiche/http2/adapter/oghttp2_adapter.h"
      39             : 
      40             : namespace Envoy {
      41             : namespace Http {
      42             : namespace Http2 {
      43             : 
      44             : class Http2CodecImplTestFixture;
      45             : 
      46             : // This is not the full client magic, but it's the smallest size that should be able to
      47             : // differentiate between HTTP/1 and HTTP/2.
      48             : const std::string CLIENT_MAGIC_PREFIX = "PRI * HTTP/2";
      49             : constexpr uint64_t H2_FRAME_HEADER_SIZE = 9;
      50             : 
      51             : class ReceivedSettingsImpl : public ReceivedSettings {
      52             : public:
      53             :   explicit ReceivedSettingsImpl(const nghttp2_settings& settings);
      54             : 
      55             :   // ReceivedSettings
      56         106 :   const absl::optional<uint32_t>& maxConcurrentStreams() const override {
      57         106 :     return concurrent_stream_limit_;
      58         106 :   }
      59             : 
      60             : private:
      61             :   absl::optional<uint32_t> concurrent_stream_limit_{};
      62             : };
      63             : 
      64             : class Utility {
      65             : public:
      66             :   /**
      67             :    * Deal with https://tools.ietf.org/html/rfc7540#section-8.1.2.5
      68             :    * @param key supplies the incoming header key.
      69             :    * @param value supplies the incoming header value.
      70             :    * @param cookies supplies the header string to fill if this is a cookie header that needs to be
      71             :    *                rebuilt.
      72             :    */
      73             :   static bool reconstituteCrumbledCookies(const HeaderString& key, const HeaderString& value,
      74             :                                           HeaderString& cookies);
      75             : };
      76             : 
      77             : class ConnectionImpl;
      78             : 
      79             : // Abstract factory. Used to enable injection of factories for testing.
      80             : class Http2SessionFactory {
      81             : public:
      82             :   using ConnectionImplType = ConnectionImpl;
      83           0 :   virtual ~Http2SessionFactory() = default;
      84             : 
      85             :   // Returns a new HTTP/2 session to be used with |connection|.
      86             :   virtual std::unique_ptr<http2::adapter::Http2Adapter>
      87             :   create(const nghttp2_session_callbacks* callbacks, ConnectionImplType* connection,
      88             :          const http2::adapter::OgHttp2Adapter::Options& options) PURE;
      89             : 
      90             :   // Returns a new HTTP/2 session to be used with |connection|.
      91             :   virtual std::unique_ptr<http2::adapter::Http2Adapter>
      92             :   create(const nghttp2_session_callbacks* callbacks, ConnectionImplType* connection,
      93             :          const nghttp2_option* options) PURE;
      94             : 
      95             :   // Initializes the |session|.
      96             :   virtual void init(ConnectionImplType* connection,
      97             :                     const envoy::config::core::v3::Http2ProtocolOptions& options) PURE;
      98             : };
      99             : 
     100             : class ProdNghttp2SessionFactory : public Http2SessionFactory {
     101             : public:
     102             :   std::unique_ptr<http2::adapter::Http2Adapter>
     103             :   create(const nghttp2_session_callbacks* callbacks, ConnectionImpl* connection,
     104             :          const http2::adapter::OgHttp2Adapter::Options& options) override;
     105             : 
     106             :   std::unique_ptr<http2::adapter::Http2Adapter> create(const nghttp2_session_callbacks* callbacks,
     107             :                                                        ConnectionImpl* connection,
     108             :                                                        const nghttp2_option* options) override;
     109             : 
     110             :   void init(ConnectionImpl* connection,
     111             :             const envoy::config::core::v3::Http2ProtocolOptions& options) override;
     112             : 
     113             :   // Returns a global factory instance. Note that this is possible because no
     114             :   // internal state is maintained; the thread safety of create() and init()'s
     115             :   // side effects is guaranteed by Envoy's worker based threading model.
     116         361 :   static ProdNghttp2SessionFactory& get() {
     117         361 :     static ProdNghttp2SessionFactory* instance = new ProdNghttp2SessionFactory();
     118         361 :     return *instance;
     119         361 :   }
     120             : };
     121             : 
     122             : /**
     123             :  * Base class for HTTP/2 client and server codecs.
     124             :  */
     125             : class ConnectionImpl : public virtual Connection,
     126             :                        protected Logger::Loggable<Logger::Id::http2>,
     127             :                        public ScopeTrackedObject {
     128             : public:
     129             :   ConnectionImpl(Network::Connection& connection, CodecStats& stats,
     130             :                  Random::RandomGenerator& random_generator,
     131             :                  const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
     132             :                  const uint32_t max_headers_kb, const uint32_t max_headers_count);
     133             : 
     134             :   ~ConnectionImpl() override;
     135             : 
     136             :   // Http::Connection
     137             :   // NOTE: the `dispatch` method is also overridden in the ServerConnectionImpl class
     138             :   Http::Status dispatch(Buffer::Instance& data) override;
     139             :   void goAway() override;
     140        3318 :   Protocol protocol() override { return Protocol::Http2; }
     141             :   void shutdownNotice() override;
     142             :   Status protocolErrorForTest(); // Used in tests to simulate errors.
     143           0 :   bool wantsToWrite() override { return adapter_->want_write(); }
     144             :   // Propagate network connection watermark events to each stream on the connection.
     145           0 :   void onUnderlyingConnectionAboveWriteBufferHighWatermark() override {
     146           0 :     for (auto& stream : active_streams_) {
     147           0 :       stream->runHighWatermarkCallbacks();
     148           0 :     }
     149           0 :   }
     150             :   void onUnderlyingConnectionBelowWriteBufferLowWatermark() override;
     151             : 
     152         361 :   void setVisitor(std::unique_ptr<http2::adapter::Http2VisitorInterface> visitor) {
     153         361 :     visitor_ = std::move(visitor);
     154         361 :   }
     155             : 
     156             :   // ScopeTrackedObject
     157             :   void dumpState(std::ostream& os, int indent_level) const override;
     158             : 
     159             : protected:
     160             :   friend class ProdNghttp2SessionFactory;
     161             : 
     162             :   /**
     163             :    * Wrapper for static nghttp2 callback dispatchers.
     164             :    */
     165             :   class Http2Callbacks {
     166             :   public:
     167             :     Http2Callbacks();
     168             :     ~Http2Callbacks();
     169             : 
     170        2860 :     const nghttp2_session_callbacks* callbacks() { return callbacks_; }
     171             : 
     172             :   private:
     173             :     nghttp2_session_callbacks* callbacks_;
     174             :   };
     175             : 
     176             :   /**
     177             :    * Wrapper for static nghttp2 session options.
     178             :    */
     179             :   class Http2Options {
     180             :   public:
     181             :     Http2Options(const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
     182             :                  uint32_t max_headers_kb);
     183             :     ~Http2Options();
     184             : 
     185         279 :     const nghttp2_option* options() { return options_; }
     186        2581 :     const http2::adapter::OgHttp2Adapter::Options& ogOptions() { return og_options_; }
     187             : 
     188             :   protected:
     189             :     nghttp2_option* options_;
     190             :     http2::adapter::OgHttp2Adapter::Options og_options_;
     191             :   };
     192             : 
     193             :   class ClientHttp2Options : public Http2Options {
     194             :   public:
     195             :     ClientHttp2Options(const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
     196             :                        uint32_t max_headers_kb);
     197             :   };
     198             : 
     199             :   /**
     200             :    * Base class for client and server side streams.
     201             :    */
     202             :   struct StreamImpl : public virtual StreamEncoder,
     203             :                       public LinkedObject<StreamImpl>,
     204             :                       public Event::DeferredDeletable,
     205             :                       public Http::MultiplexedStreamImplBase,
     206             :                       public ScopeTrackedObject {
     207             : 
     208             :     StreamImpl(ConnectionImpl& parent, uint32_t buffer_limit);
     209             : 
     210             :     // Http::MultiplexedStreamImplBase
     211             :     void destroy() override;
     212             :     void onPendingFlushTimer() override;
     213             :     CodecEventCallbacks*
     214         424 :     registerCodecEventCallbacks(CodecEventCallbacks* codec_callbacks) override {
     215         424 :       extend_stream_lifetime_flag_ = true;
     216         424 :       return MultiplexedStreamImplBase::registerCodecEventCallbacks(codec_callbacks);
     217         424 :     }
     218             : 
     219         459 :     StreamImpl* base() { return this; }
     220             :     void resetStreamWorker(StreamResetReason reason);
     221             :     static void buildHeaders(std::vector<nghttp2_nv>& final_headers, const HeaderMap& headers);
     222             :     static std::vector<http2::adapter::Header> buildHeaders(const HeaderMap& headers);
     223             :     void saveHeader(HeaderString&& name, HeaderString&& value);
     224             :     void encodeHeadersBase(const HeaderMap& headers, bool end_stream);
     225             :     virtual void submitHeaders(const HeaderMap& headers, bool end_stream) PURE;
     226             :     void encodeTrailersBase(const HeaderMap& headers);
     227             :     void submitTrailers(const HeaderMap& trailers);
     228             :     // Returns true if the stream should defer the local reset stream until after the next call to
     229             :     // sendPendingFrames so pending outbound frames have one final chance to be flushed. If we
     230             :     // submit a reset, nghttp2 will cancel outbound frames that have not yet been sent.
     231             :     virtual bool useDeferredReset() const PURE;
     232             :     virtual StreamDecoder& decoder() PURE;
     233             :     virtual HeaderMap& headers() PURE;
     234             :     virtual void allocTrailers() PURE;
     235             :     virtual HeaderMapPtr cloneTrailers(const HeaderMap& trailers) PURE;
     236             : 
     237             :     // Http::StreamEncoder
     238             :     void encodeData(Buffer::Instance& data, bool end_stream) override;
     239        4947 :     Stream& getStream() override { return *this; }
     240             :     void encodeMetadata(const MetadataMapVector& metadata_map_vector) override;
     241           0 :     Http1StreamEncoderOptionsOptRef http1StreamEncoderOptions() override { return absl::nullopt; }
     242             : 
     243             :     // Http::Stream
     244        2102 :     void addCallbacks(StreamCallbacks& callbacks) override { addCallbacksHelper(callbacks); }
     245         394 :     void removeCallbacks(StreamCallbacks& callbacks) override { removeCallbacksHelper(callbacks); }
     246             :     void resetStream(StreamResetReason reason) override;
     247             :     void readDisable(bool disable) override;
     248         212 :     uint32_t bufferLimit() const override { return pending_recv_data_->highWatermark(); }
     249         148 :     const Network::ConnectionInfoProvider& connectionInfoProvider() override {
     250         148 :       return parent_.connection_.connectionInfoProvider();
     251         148 :     }
     252         138 :     absl::string_view responseDetails() override { return details_; }
     253         212 :     Buffer::BufferMemoryAccountSharedPtr account() const override { return buffer_memory_account_; }
     254             :     void setAccount(Buffer::BufferMemoryAccountSharedPtr account) override;
     255             : 
     256             :     // ScopeTrackedObject
     257             :     void dumpState(std::ostream& os, int indent_level) const override;
     258             : 
     259             :     // This code assumes that details is a static string, so that we
     260             :     // can avoid copying it.
     261         206 :     void setDetails(absl::string_view details) {
     262             :       // TODO(asraa): In some cases nghttp2's error handling may cause processing of multiple
     263             :       // invalid frames for a single stream. If a temporal stream error is returned from a callback,
     264             :       // remaining frames in the buffer will still be partially processed. For example, remaining
     265             :       // frames will still parse through nghttp2's push promise error handling and in
     266             :       // onBeforeFrame(Send/Received) callbacks, which may return invalid frame errors and attempt
     267             :       // to set details again. In these cases, we simply do not overwrite details. When internal
     268             :       // error latching is implemented in the codec for exception removal, we should prevent calling
     269             :       // setDetails in an error state.
     270         206 :       if (details_.empty()) {
     271         203 :         details_ = details;
     272         203 :       }
     273         206 :     }
     274             : 
     275        1974 :     void setWriteBufferWatermarks(uint32_t high_watermark) {
     276        1974 :       pending_recv_data_->setWatermarks(high_watermark);
     277        1974 :       pending_send_data_->setWatermarks(high_watermark);
     278        1974 :     }
     279             : 
     280             :     // If the receive buffer encounters watermark callbacks, enable/disable reads on this stream.
     281             :     void pendingRecvBufferHighWatermark();
     282             :     void pendingRecvBufferLowWatermark();
     283             : 
     284             :     // If the send buffer encounters watermark callbacks, propagate this information to the streams.
     285             :     // The router and connection manager will propagate them on as appropriate.
     286             :     void pendingSendBufferHighWatermark();
     287             :     void pendingSendBufferLowWatermark();
     288             : 
     289             :     // Does any necessary WebSocket/Upgrade conversion, then passes the headers
     290             :     // to the decoder_.
     291             :     virtual void decodeHeaders() PURE;
     292             :     virtual void decodeTrailers() PURE;
     293             :     bool maybeDeferDecodeTrailers();
     294             :     // Consumes any decoded data, buffering if backed up.
     295             :     void decodeData();
     296             : 
     297             :     // Get MetadataEncoder for this stream.
     298             :     NewMetadataEncoder& getMetadataEncoder();
     299             :     // Get MetadataDecoder for this stream.
     300             :     MetadataDecoder& getMetadataDecoder();
     301             :     // Callback function for MetadataDecoder.
     302             :     void onMetadataDecoded(MetadataMapPtr&& metadata_map_ptr);
     303             : 
     304       61182 :     bool buffersOverrun() const { return read_disable_count_ > 0; }
     305       38910 :     bool shouldAllowPeerAdditionalStreamWindow() const {
     306       38911 :       return !buffersOverrun() && !pending_recv_data_->highWatermarkTriggered();
     307       38910 :     }
     308             : 
     309             :     void encodeDataHelper(Buffer::Instance& data, bool end_stream,
     310             :                           bool skip_encoding_empty_trailers);
     311             :     // Called from either process_buffered_data_callback_.
     312             :     void processBufferedData();
     313             : 
     314             :     // Called when the frame with END_STREAM is sent for this stream.
     315         467 :     void onEndStreamEncoded() {
     316         467 :       if (codec_callbacks_) {
     317         153 :         codec_callbacks_->onCodecEncodeComplete();
     318         153 :       }
     319         467 :     }
     320             : 
     321         360 :     const StreamInfo::BytesMeterSharedPtr& bytesMeter() override { return bytes_meter_; }
     322             :     ConnectionImpl& parent_;
     323             :     int32_t stream_id_{-1};
     324             :     uint32_t unconsumed_bytes_{0};
     325             :     uint32_t read_disable_count_{0};
     326             :     StreamInfo::BytesMeterSharedPtr bytes_meter_{std::make_shared<StreamInfo::BytesMeter>()};
     327             : 
     328             :     Buffer::BufferMemoryAccountSharedPtr buffer_memory_account_;
     329             :     // Note that in current implementation the watermark callbacks of the pending_recv_data_ are
     330             :     // never called. The watermark value is set to the size of the stream window. As a result this
     331             :     // watermark can never overflow because the peer can never send more bytes than the stream
     332             :     // window without triggering protocol error. This buffer is drained after each DATA frame was
     333             :     // dispatched through the filter chain unless
     334             :     // envoy.reloadable_features.defer_processing_backedup_streams is enabled,
     335             :     // in which case this buffer may accumulate data.
     336             :     // See source/docs/flow_control.md for more information.
     337             :     Buffer::InstancePtr pending_recv_data_;
     338             :     Buffer::InstancePtr pending_send_data_;
     339             :     HeaderMapPtr pending_trailers_to_encode_;
     340             :     std::unique_ptr<MetadataDecoder> metadata_decoder_;
     341             :     std::unique_ptr<NewMetadataEncoder> metadata_encoder_;
     342             :     absl::optional<StreamResetReason> deferred_reset_;
     343             :     // Holds the reset reason for this stream. Useful if we have buffered data
     344             :     // to determine whether we should continue processing that data.
     345             :     absl::optional<StreamResetReason> reset_reason_;
     346             :     HeaderString cookies_;
     347             :     bool local_end_stream_sent_ : 1;
     348             :     bool remote_end_stream_ : 1;
     349             :     bool remote_rst_ : 1;
     350             :     bool data_deferred_ : 1;
     351             :     bool received_noninformational_headers_ : 1;
     352             :     bool pending_receive_buffer_high_watermark_called_ : 1;
     353             :     bool pending_send_buffer_high_watermark_called_ : 1;
     354             :     bool reset_due_to_messaging_error_ : 1;
     355             :     bool defer_processing_backedup_streams_ : 1;
     356             :     // Latch whether this stream is operating with this flag.
     357             :     bool extend_stream_lifetime_flag_ : 1;
     358             :     absl::string_view details_;
     359             : 
     360             :     /**
     361             :      * Tracks buffering that may occur for a stream if it is backed up.
     362             :      */
     363             :     struct BufferedStreamManager {
     364             :       bool body_buffered_{false};
     365             :       bool trailers_buffered_{false};
     366             : 
     367             :       // We received a call to onStreamClose for the stream, but deferred it
     368             :       // as the stream had pending data to process and the stream was not reset.
     369             :       bool buffered_on_stream_close_{false};
     370             : 
     371             :       // Segment size for processing body data. Defaults to the value of high
     372             :       // watermark of the *pending_recv_data_* buffer.
     373             :       // If 0, we will process all buffered data.
     374             :       uint32_t defer_processing_segment_size_{0};
     375             : 
     376       22169 :       bool decodeAsChunks() const { return defer_processing_segment_size_ > 0; }
     377         352 :       bool hasBufferedBodyOrTrailers() const { return body_buffered_ || trailers_buffered_; }
     378             :     };
     379             : 
     380             :     BufferedStreamManager stream_manager_;
     381             :     Event::SchedulableCallbackPtr process_buffered_data_callback_;
     382             : 
     383             :   protected:
     384             :     // Http::MultiplexedStreamImplBase
     385         267 :     bool hasPendingData() override {
     386         267 :       return pending_send_data_->length() > 0 || pending_trailers_to_encode_ != nullptr;
     387         267 :     }
     388          16 :     bool continueProcessingBufferedData() const {
     389             :       // We should stop processing buffered data if either
     390             :       // 1) Buffers become overrun
     391             :       // 2) The stream ends up getting reset
     392             :       // Both of these can end up changing as a result of processing buffered data.
     393          16 :       return !buffersOverrun() && !reset_reason_.has_value();
     394          16 :     }
     395             : 
     396             :     // Avoid inversion in the case where we saw trailers, acquiring the
     397             :     // remote_end_stream_ being set to true, but the trailers ended up being
     398             :     // buffered.
     399             :     // All buffered body must be consumed before we send end stream.
     400       22659 :     bool sendEndStream() const {
     401       22659 :       return remote_end_stream_ && !stream_manager_.trailers_buffered_ &&
     402       22659 :              !stream_manager_.body_buffered_;
     403       22659 :     }
     404             : 
     405             :     // Schedules a callback either in the current or next iteration to process
     406             :     // buffered data.
     407             :     void scheduleProcessingOfBufferedData(bool schedule_next_iteration);
     408             : 
     409             :     // Marks data consumed by the stream, granting the peer additional stream
     410             :     // window.
     411             :     void grantPeerAdditionalStreamWindow();
     412             :   };
     413             : 
     414             :   // Encapsulates the logic for sending DATA frames on a given stream.
     415             :   class StreamDataFrameSource : public http2::adapter::DataFrameSource {
     416             :   public:
     417         513 :     explicit StreamDataFrameSource(StreamImpl& stream) : stream_(stream) {}
     418             : 
     419             :     // Returns a pair of the next payload length, and whether that payload is the end of the data
     420             :     // for this stream.
     421             :     std::pair<int64_t, bool> SelectPayloadLength(size_t max_length) override;
     422             :     // Queues the frame header and a DATA frame payload of the specified length for writing.
     423             :     bool Send(absl::string_view frame_header, size_t payload_length) override;
     424             :     // Whether the codec should send the END_STREAM flag on the final DATA frame.
     425       10977 :     bool send_fin() const override { return send_fin_; }
     426             : 
     427             :   private:
     428             :     StreamImpl& stream_;
     429             :     bool send_fin_ = false;
     430             :   };
     431             : 
     432             :   using StreamImplPtr = std::unique_ptr<StreamImpl>;
     433             : 
     434             :   /**
     435             :    * Client side stream (request).
     436             :    */
     437             :   struct ClientStreamImpl : public StreamImpl, public RequestEncoder {
     438             :     ClientStreamImpl(ConnectionImpl& parent, uint32_t buffer_limit,
     439             :                      ResponseDecoder& response_decoder)
     440             :         : StreamImpl(parent, buffer_limit), response_decoder_(response_decoder),
     441             :           headers_or_trailers_(
     442         475 :               ResponseHeaderMapImpl::create(parent_.max_headers_kb_, parent_.max_headers_count_)) {}
     443             : 
     444             :     // Http::MultiplexedStreamImplBase
     445             :     // Client streams do not need a flush timer because we currently assume that any failure
     446             :     // to flush would be covered by a request/stream/etc. timeout.
     447           0 :     void setFlushTimeout(std::chrono::milliseconds /*timeout*/) override {}
     448           0 :     CodecEventCallbacks* registerCodecEventCallbacks(CodecEventCallbacks*) override {
     449           0 :       ENVOY_BUG(false, "CodecEventCallbacks for HTTP2 client stream unimplemented.");
     450           0 :       return nullptr;
     451           0 :     }
     452             :     // StreamImpl
     453             :     void submitHeaders(const HeaderMap& headers, bool end_stream) override;
     454             :     // Do not use deferred reset on upstream connections.
     455         121 :     bool useDeferredReset() const override { return false; }
     456         439 :     StreamDecoder& decoder() override { return response_decoder_; }
     457             :     void decodeHeaders() override;
     458             :     void decodeTrailers() override;
     459       43818 :     HeaderMap& headers() override {
     460       43818 :       if (absl::holds_alternative<ResponseHeaderMapPtr>(headers_or_trailers_)) {
     461       43788 :         return *absl::get<ResponseHeaderMapPtr>(headers_or_trailers_);
     462       43788 :       } else {
     463          30 :         return *absl::get<ResponseTrailerMapPtr>(headers_or_trailers_);
     464          30 :       }
     465       43818 :     }
     466          14 :     void allocTrailers() override {
     467             :       // If we are waiting for informational headers, make a new response header map, otherwise
     468             :       // we are about to receive trailers. The codec makes sure this is the only valid sequence.
     469          14 :       if (received_noninformational_headers_) {
     470          10 :         headers_or_trailers_.emplace<ResponseTrailerMapPtr>(
     471          10 :             ResponseTrailerMapImpl::create(parent_.max_headers_kb_, parent_.max_headers_count_));
     472          10 :       } else {
     473           4 :         headers_or_trailers_.emplace<ResponseHeaderMapPtr>(
     474           4 :             ResponseHeaderMapImpl::create(parent_.max_headers_kb_, parent_.max_headers_count_));
     475           4 :       }
     476          14 :     }
     477           0 :     HeaderMapPtr cloneTrailers(const HeaderMap& trailers) override {
     478           0 :       return createHeaderMap<RequestTrailerMapImpl>(trailers);
     479           0 :     }
     480             : 
     481             :     // RequestEncoder
     482             :     Status encodeHeaders(const RequestHeaderMap& headers, bool end_stream) override;
     483          14 :     void encodeTrailers(const RequestTrailerMap& trailers) override {
     484          14 :       encodeTrailersBase(trailers);
     485          14 :     }
     486           0 :     void enableTcpTunneling() override {}
     487             : 
     488             :     // ScopeTrackedObject
     489             :     void dumpState(std::ostream& os, int indent_level) const override;
     490             : 
     491             :     ResponseDecoder& response_decoder_;
     492             :     absl::variant<ResponseHeaderMapPtr, ResponseTrailerMapPtr> headers_or_trailers_;
     493             :     std::string upgrade_type_;
     494             :   };
     495             : 
     496             :   using ClientStreamImplPtr = std::unique_ptr<ClientStreamImpl>;
     497             : 
     498             :   /**
     499             :    * Server side stream (response).
     500             :    */
     501             :   struct ServerStreamImpl : public StreamImpl, public ResponseEncoder {
     502             :     ServerStreamImpl(ConnectionImpl& parent, uint32_t buffer_limit)
     503             :         : StreamImpl(parent, buffer_limit),
     504             :           headers_or_trailers_(
     505        1499 :               RequestHeaderMapImpl::create(parent_.max_headers_kb_, parent_.max_headers_count_)) {}
     506             : 
     507             :     // StreamImpl
     508             :     void destroy() override;
     509             :     void submitHeaders(const HeaderMap& headers, bool end_stream) override;
     510             :     // Enable deferred reset on downstream connections so outbound HTTP internal error replies are
     511             :     // written out before force resetting the stream, assuming there is enough H2 connection flow
     512             :     // control window is available.
     513          15 :     bool useDeferredReset() const override { return true; }
     514       21738 :     StreamDecoder& decoder() override { return *request_decoder_; }
     515             :     void decodeHeaders() override;
     516             :     void decodeTrailers() override;
     517       12235 :     HeaderMap& headers() override {
     518       12235 :       if (absl::holds_alternative<RequestHeaderMapSharedPtr>(headers_or_trailers_)) {
     519       12139 :         return *absl::get<RequestHeaderMapSharedPtr>(headers_or_trailers_);
     520       12139 :       } else {
     521          96 :         return *absl::get<RequestTrailerMapPtr>(headers_or_trailers_);
     522          96 :       }
     523       12235 :     }
     524          16 :     void allocTrailers() override {
     525          16 :       headers_or_trailers_.emplace<RequestTrailerMapPtr>(
     526          16 :           RequestTrailerMapImpl::create(parent_.max_headers_kb_, parent_.max_headers_count_));
     527          16 :     }
     528           0 :     HeaderMapPtr cloneTrailers(const HeaderMap& trailers) override {
     529           0 :       return createHeaderMap<ResponseTrailerMapImpl>(trailers);
     530           0 :     }
     531             :     void resetStream(StreamResetReason reason) override;
     532             : 
     533             :     // ResponseEncoder
     534             :     void encode1xxHeaders(const ResponseHeaderMap& headers) override;
     535             :     void encodeHeaders(const ResponseHeaderMap& headers, bool end_stream) override;
     536          44 :     void encodeTrailers(const ResponseTrailerMap& trailers) override {
     537          44 :       encodeTrailersBase(trailers);
     538          44 :     }
     539        1499 :     void setRequestDecoder(Http::RequestDecoder& decoder) override { request_decoder_ = &decoder; }
     540             :     void setDeferredLoggingHeadersAndTrailers(Http::RequestHeaderMapConstSharedPtr,
     541             :                                               Http::ResponseHeaderMapConstSharedPtr,
     542             :                                               Http::ResponseTrailerMapConstSharedPtr,
     543           0 :                                               StreamInfo::StreamInfo&) override {}
     544             : 
     545             :     // ScopeTrackedObject
     546             :     void dumpState(std::ostream& os, int indent_level) const override;
     547             : 
     548             :     absl::variant<RequestHeaderMapSharedPtr, RequestTrailerMapPtr> headers_or_trailers_;
     549             : 
     550           0 :     bool streamErrorOnInvalidHttpMessage() const override {
     551           0 :       return parent_.stream_error_on_invalid_http_messaging_;
     552           0 :     }
     553             : 
     554             :   private:
     555             :     RequestDecoder* request_decoder_{};
     556             :   };
     557             : 
     558             :   using ServerStreamImplPtr = std::unique_ptr<ServerStreamImpl>;
     559             : 
     560        3221 :   ConnectionImpl* base() { return this; }
     561             :   // NOTE: Always use non debug nullptr checks against the return value of this function. There are
     562             :   // edge cases (such as for METADATA frames) where nghttp2 will issue a callback for a stream_id
     563             :   // that is not associated with an existing stream.
     564             :   const StreamImpl* getStream(int32_t stream_id) const;
     565             :   StreamImpl* getStream(int32_t stream_id);
     566             :   // Same as getStream, but without the ASSERT.
     567             :   const StreamImpl* getStreamUnchecked(int32_t stream_id) const;
     568             :   StreamImpl* getStreamUnchecked(int32_t stream_id);
     569             :   int saveHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value);
     570             : 
     571             :   /**
     572             :    * Copies any frames pending internally by nghttp2 into outbound buffer.
     573             :    * The `sendPendingFrames()` can be called in 3 different contexts:
     574             :    * 1. dispatching_ == true, aka the dispatching context. The `sendPendingFrames()` is no-op and
     575             :    *    always returns success to avoid reentering nghttp2 library.
     576             :    * 2. Server codec only. dispatching_ == false.
     577             :    *    The `sendPendingFrames()` returns the status of the protocol constraint checks. Outbound
     578             :    *    frame accounting is performed.
     579             :    * 3. dispatching_ == false. The `sendPendingFrames()` always returns success. No outbound
     580             :    *    frame accounting.
     581             :    *
     582             :    * TODO(yanavlasov): harmonize behavior for cases 2, 3.
     583             :    */
     584             :   Status sendPendingFrames();
     585             : 
     586             :   /**
     587             :    * Call the sendPendingFrames() method and schedule disconnect callback when
     588             :    * sendPendingFrames() returns an error.
     589             :    * Return true if the disconnect callback has been scheduled.
     590             :    */
     591             :   bool sendPendingFramesAndHandleError();
     592             :   void sendSettings(const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
     593             :                     bool disable_push);
     594             :   void sendSettingsHelper(const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
     595             :                           bool disable_push);
     596             :   void sendSettingsHelperOld(const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
     597             :                              bool disable_push);
     598             :   // Callback triggered when the peer's SETTINGS frame is received.
     599         903 :   virtual void onSettings(const nghttp2_settings& settings) {
     600         903 :     ReceivedSettingsImpl received_settings(settings);
     601         903 :     callbacks().onSettings(received_settings);
     602         903 :   }
     603             : 
     604             :   /**
     605             :    * Check if header name contains underscore character.
     606             :    * Underscore character is allowed in header names by the RFC-7230 and this check is implemented
     607             :    * as a security measure due to systems that treat '_' and '-' as interchangeable.
     608             :    * The ServerConnectionImpl may drop header or reject request based on the
     609             :    * `common_http_protocol_options.headers_with_underscores_action` configuration option in the
     610             :    * HttpConnectionManager.
     611             :    */
     612       20847 :   virtual absl::optional<int> checkHeaderNameForUnderscores(absl::string_view /* header_name */) {
     613       20847 :     return absl::nullopt;
     614       20847 :   }
     615             : 
     616             :   /**
     617             :    * Save `status` into codec_callback_status_.
     618             :    * Return codec callback return code corresponding to `status`.
     619             :    */
     620             :   int setAndCheckCodecCallbackStatus(Status&& status);
     621             : 
     622             :   /**
     623             :    * Callback for terminating connection when protocol constrain has been violated
     624             :    * outside of the dispatch context.
     625             :    */
     626             :   void scheduleProtocolConstraintViolationCallback();
     627             :   void onProtocolConstraintViolation();
     628             : 
     629             :   // Whether to use the new HTTP/2 library.
     630             :   bool use_oghttp2_library_;
     631             :   static Http2Callbacks http2_callbacks_;
     632             : 
     633             :   // If deferred processing, the streams will be in LRU order based on when the
     634             :   // stream encoded to the http2 connection. The LRU property is used when
     635             :   // raising low watermark on the http2 connection to prioritize how streams get
     636             :   // notified, prefering those that haven't recently written.
     637             :   std::list<StreamImplPtr> active_streams_;
     638             : 
     639             :   // Tracks the stream id of the current stream we're processing.
     640             :   // This should only be set while we're in the context of dispatching to nghttp2.
     641             :   absl::optional<int32_t> current_stream_id_;
     642             :   std::unique_ptr<http2::adapter::Http2VisitorInterface> visitor_;
     643             :   std::unique_ptr<http2::adapter::Http2Adapter> adapter_;
     644             : 
     645             :   CodecStats& stats_;
     646             :   Network::Connection& connection_;
     647             :   const uint32_t max_headers_kb_;
     648             :   const uint32_t max_headers_count_;
     649             :   uint32_t per_stream_buffer_limit_;
     650             :   bool allow_metadata_;
     651             :   const bool stream_error_on_invalid_http_messaging_;
     652             : 
     653             :   // Status for any errors encountered by the nghttp2 callbacks.
     654             :   // nghttp2 library uses single return code to indicate callback failure and
     655             :   // `codec_callback_status_` is used to save right error information returned by a callback. The
     656             :   // `codec_callback_status_` is valid iff nghttp call returned NGHTTP2_ERR_CALLBACK_FAILURE.
     657             :   Status codec_callback_status_;
     658             : 
     659             :   // Set if the type of frame that is about to be sent is PING or SETTINGS with the ACK flag set, or
     660             :   // RST_STREAM.
     661             :   bool is_outbound_flood_monitored_control_frame_ = 0;
     662             :   ProtocolConstraints protocol_constraints_;
     663             : 
     664             :   // For the flood mitigation to work the onSend callback must be called once for each outbound
     665             :   // frame. This is what the nghttp2 library is doing, however this is not documented. The
     666             :   // Http2FloodMitigationTest.* tests in test/integration/http2_integration_test.cc will break if
     667             :   // this changes in the future. Also it is important that onSend does not do partial writes, as the
     668             :   // nghttp2 library will keep calling this callback to write the rest of the frame.
     669             :   ssize_t onSend(const uint8_t* data, size_t length);
     670             : 
     671             :   // Called when a stream encodes to the http2 connection which enables us to
     672             :   // keep the active_streams list in LRU if deferred processing.
     673        2198 :   void updateActiveStreamsOnEncode(StreamImpl& stream) {
     674        2198 :     if (stream.defer_processing_backedup_streams_) {
     675        2198 :       LinkedList::moveIntoList(stream.removeFromList(active_streams_), active_streams_);
     676        2198 :     }
     677        2198 :   }
     678             : 
     679             :   // dumpState helper method.
     680             :   virtual void dumpStreams(std::ostream& os, int indent_level) const;
     681             : 
     682             :   // Send a keepalive ping, and set the idle timer for ping timeout.
     683             :   void sendKeepalive();
     684             : 
     685           0 :   const MonotonicTime& lastReceivedDataTime() { return last_received_data_time_; }
     686             : 
     687             : private:
     688             :   friend class Http2CodecImplTestFixture;
     689             : 
     690             :   virtual ConnectionCallbacks& callbacks() PURE;
     691             :   virtual Status onBeginHeaders(const nghttp2_frame* frame) PURE;
     692             :   int onData(int32_t stream_id, const uint8_t* data, size_t len);
     693             :   Status onBeforeFrameReceived(int32_t stream_id, size_t length, uint8_t type, uint8_t flags);
     694             :   Status onFrameReceived(const nghttp2_frame* frame);
     695             :   int onBeforeFrameSend(int32_t stream_id, size_t length, uint8_t type, uint8_t flags);
     696             :   int onFrameSend(int32_t stream_id, size_t length, uint8_t type, uint8_t flags,
     697             :                   uint32_t error_code);
     698             :   int onError(absl::string_view error);
     699             :   virtual int onHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value) PURE;
     700             :   int onInvalidFrame(int32_t stream_id, int error_code);
     701             :   // Pass through invoking with the actual stream.
     702             :   Status onStreamClose(int32_t stream_id, uint32_t error_code);
     703             :   // Should be invoked directly in buffered onStreamClose scenarios
     704             :   // where nghttp2 might have already forgotten about the stream.
     705             :   Status onStreamClose(StreamImpl* stream, uint32_t error_code);
     706             :   int onMetadataReceived(int32_t stream_id, const uint8_t* data, size_t len);
     707             :   int onMetadataFrameComplete(int32_t stream_id, bool end_metadata);
     708             : 
     709             :   // Adds buffer fragment for a new outbound frame to the supplied Buffer::OwnedImpl.
     710             :   void addOutboundFrameFragment(Buffer::OwnedImpl& output, const uint8_t* data, size_t length);
     711             :   virtual Status trackInboundFrames(int32_t stream_id, size_t length, uint8_t type, uint8_t flags,
     712             :                                     uint32_t padding_length) PURE;
     713             :   void onKeepaliveResponse();
     714             :   void onKeepaliveResponseTimeout();
     715             :   bool slowContainsStreamId(int32_t stream_id) const;
     716             :   virtual StreamResetReason getMessagingErrorResetReason() const PURE;
     717             : 
     718             :   // Tracks the current slice we're processing in the dispatch loop.
     719             :   const Buffer::RawSlice* current_slice_ = nullptr;
     720             :   // Streams that are pending deferred reset. Using an ordered map provides determinism in the rare
     721             :   // case where there are multiple streams waiting for deferred reset. The stream id is also used to
     722             :   // remove streams from the map when they are closed in order to avoid calls to resetStreamWorker
     723             :   // after the stream has been removed from the active list.
     724             :   std::map<int32_t, StreamImpl*> pending_deferred_reset_streams_;
     725             :   bool dispatching_ : 1;
     726             :   bool raised_goaway_ : 1;
     727             :   Event::SchedulableCallbackPtr protocol_constraint_violation_callback_;
     728             :   Random::RandomGenerator& random_;
     729             :   MonotonicTime last_received_data_time_{};
     730             :   Event::TimerPtr keepalive_send_timer_;
     731             :   Event::TimerPtr keepalive_timeout_timer_;
     732             :   std::chrono::milliseconds keepalive_interval_;
     733             :   std::chrono::milliseconds keepalive_timeout_;
     734             :   uint32_t keepalive_interval_jitter_percent_;
     735             : };
     736             : 
     737             : /**
     738             :  * HTTP/2 client connection codec.
     739             :  */
     740             : class ClientConnectionImpl : public ClientConnection, public ConnectionImpl {
     741             : public:
     742             :   using SessionFactory = Http2SessionFactory;
     743             :   ClientConnectionImpl(Network::Connection& connection, ConnectionCallbacks& callbacks,
     744             :                        CodecStats& stats, Random::RandomGenerator& random_generator,
     745             :                        const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
     746             :                        const uint32_t max_response_headers_kb,
     747             :                        const uint32_t max_response_headers_count,
     748             :                        SessionFactory& http2_session_factory);
     749             : 
     750             :   // Http::ClientConnection
     751             :   RequestEncoder& newStream(ResponseDecoder& response_decoder) override;
     752             : 
     753             : private:
     754             :   // ConnectionImpl
     755         155 :   ConnectionCallbacks& callbacks() override { return callbacks_; }
     756             :   Status onBeginHeaders(const nghttp2_frame* frame) override;
     757             :   int onHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value) override;
     758             :   Status trackInboundFrames(int32_t stream_id, size_t length, uint8_t type, uint8_t flags,
     759             :                             uint32_t) override;
     760             :   void dumpStreams(std::ostream& os, int indent_level) const override;
     761             :   StreamResetReason getMessagingErrorResetReason() const override;
     762             :   Http::ConnectionCallbacks& callbacks_;
     763             :   std::chrono::milliseconds idle_session_requires_ping_interval_;
     764             : };
     765             : 
     766             : /**
     767             :  * HTTP/2 server connection codec.
     768             :  */
     769             : class ServerConnectionImpl : public ServerConnection, public ConnectionImpl {
     770             : public:
     771             :   ServerConnectionImpl(Network::Connection& connection, ServerConnectionCallbacks& callbacks,
     772             :                        CodecStats& stats, Random::RandomGenerator& random_generator,
     773             :                        const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
     774             :                        const uint32_t max_request_headers_kb,
     775             :                        const uint32_t max_request_headers_count,
     776             :                        envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
     777             :                            headers_with_underscores_action,
     778             :                        Server::OverloadManager& overload_manager);
     779             : 
     780             : private:
     781             :   // ConnectionImpl
     782         763 :   ConnectionCallbacks& callbacks() override { return callbacks_; }
     783             :   Status onBeginHeaders(const nghttp2_frame* frame) override;
     784             :   int onHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value) override;
     785             :   Status trackInboundFrames(int32_t stream_id, size_t length, uint8_t type, uint8_t flags,
     786             :                             uint32_t padding_length) override;
     787             :   absl::optional<int> checkHeaderNameForUnderscores(absl::string_view header_name) override;
     788           0 :   StreamResetReason getMessagingErrorResetReason() const override {
     789           0 :     return StreamResetReason::LocalReset;
     790           0 :   }
     791             : 
     792             :   // Http::Connection
     793             :   // The reason for overriding the dispatch method is to do flood mitigation only when
     794             :   // processing data from downstream client. Doing flood mitigation when processing upstream
     795             :   // responses makes clean-up tricky, which needs to be improved (see comments for the
     796             :   // ClientConnectionImpl::checkProtocolConstraintsStatus method). The dispatch method on the
     797             :   // ServerConnectionImpl objects is called only when processing data from the downstream client in
     798             :   // the ConnectionManagerImpl::onData method.
     799             :   Http::Status dispatch(Buffer::Instance& data) override;
     800             : 
     801             :   ServerConnectionCallbacks& callbacks_;
     802             : 
     803             :   // The action to take when a request header name contains underscore characters.
     804             :   envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
     805             :       headers_with_underscores_action_;
     806             :   Server::LoadShedPoint* should_send_go_away_on_dispatch_{nullptr};
     807             :   bool sent_go_away_on_dispatch_{false};
     808             : };
     809             : 
     810             : } // namespace Http2
     811             : } // namespace Http
     812             : } // namespace Envoy

Generated by: LCOV version 1.15