LCOV - code coverage report
Current view: top level - source/common/http/http3 - conn_pool.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 0 36 0.0 %
Date: 2024-01-05 06:35:25 Functions: 0 11 0.0 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <cstdint>
       4             : #include <memory>
       5             : 
       6             : #include "envoy/common/optref.h"
       7             : #include "envoy/http/persistent_quic_info.h"
       8             : #include "envoy/upstream/upstream.h"
       9             : 
      10             : #include "source/common/http/codec_client.h"
      11             : #include "source/common/http/conn_pool_base.h"
      12             : 
      13             : #ifdef ENVOY_ENABLE_QUIC
      14             : #include "source/common/quic/client_connection_factory_impl.h"
      15             : #include "source/common/quic/envoy_quic_utils.h"
      16             : #include "source/common/quic/quic_transport_socket_factory.h"
      17             : #include "quiche/quic/core/deterministic_connection_id_generator.h"
      18             : #else
      19             : #error "http3 conn pool should not be built with QUIC disabled"
      20             : #endif
      21             : 
      22             : namespace Envoy {
      23             : namespace Http {
      24             : namespace Http3 {
      25             : 
      26             : class ActiveClient : public MultiplexedActiveClientBase {
      27             : public:
      28             :   ActiveClient(Envoy::Http::HttpConnPoolImplBase& parent,
      29             :                Upstream::Host::CreateConnectionData& data);
      30             : 
      31           0 :   ~ActiveClient() override {
      32           0 :     if (async_connect_callback_ != nullptr && async_connect_callback_->enabled()) {
      33           0 :       async_connect_callback_->cancel();
      34           0 :     }
      35           0 :   }
      36             :   // Http::ConnectionCallbacks
      37             :   void onMaxStreamsChanged(uint32_t num_streams) override;
      38             : 
      39           0 :   RequestEncoder& newStreamEncoder(ResponseDecoder& response_decoder) override {
      40           0 :     ASSERT(quiche_capacity_ != 0);
      41           0 :     has_created_stream_ = true;
      42             :     // Each time a quic stream is allocated the quic capacity needs to get
      43             :     // decremented. See comments by quiche_capacity_.
      44           0 :     updateCapacity(quiche_capacity_ - 1);
      45           0 :     return MultiplexedActiveClientBase::newStreamEncoder(response_decoder);
      46           0 :   }
      47             : 
      48           0 :   uint32_t effectiveConcurrentStreamLimit() const override {
      49           0 :     return std::min<int64_t>(MultiplexedActiveClientBase::effectiveConcurrentStreamLimit(),
      50           0 :                              quiche_capacity_);
      51           0 :   }
      52             : 
      53             :   // Overload the default capacity calculations to return the quic capacity
      54             :   // (modified by any stream limits in Envoy config)
      55           0 :   int64_t currentUnusedCapacity() const override {
      56           0 :     return std::min<int64_t>(quiche_capacity_, effectiveConcurrentStreamLimit());
      57           0 :   }
      58             : 
      59             :   // Overridden to return true as long as the client is doing handshake even when it is ready for
      60             :   // early data streams.
      61           0 :   bool hasHandshakeCompleted() const override { return has_handshake_completed_; }
      62             : 
      63             :   // Overridden to include ReadyForEarlyData state.
      64           0 :   bool readyForStream() const override {
      65           0 :     return state() == State::Ready || state() == State::ReadyForEarlyData;
      66           0 :   }
      67             : 
      68           0 :   void updateCapacity(uint64_t new_quiche_capacity) {
      69             :     // Each time we update the capacity make sure to reflect the update in the
      70             :     // connection pool.
      71             :     //
      72             :     // Due to interplay between the max number of concurrent streams Envoy will
      73             :     // allow and the max number of streams per connection this is not as simple
      74             :     // as just updating based on the delta between quiche_capacity_ and
      75             :     // new_quiche_capacity, so we use the delta between the actual calculated
      76             :     // capacity before and after the update.
      77           0 :     uint64_t old_capacity = currentUnusedCapacity();
      78           0 :     quiche_capacity_ = new_quiche_capacity;
      79           0 :     uint64_t new_capacity = currentUnusedCapacity();
      80             : 
      81           0 :     if (new_capacity < old_capacity) {
      82           0 :       parent_.decrConnectingAndConnectedStreamCapacity(old_capacity - new_capacity, *this);
      83           0 :     } else if (old_capacity < new_capacity) {
      84           0 :       parent_.incrConnectingAndConnectedStreamCapacity(new_capacity - old_capacity, *this);
      85           0 :     }
      86           0 :   }
      87             : 
      88           0 :   bool hasCreatedStream() const { return has_created_stream_; }
      89             : 
      90             : protected:
      91           0 :   bool supportsEarlyData() const override { return true; }
      92             : 
      93             : private:
      94             :   // Unlike HTTP/2 and HTTP/1, rather than having a cap on the number of active
      95             :   // streams, QUIC has a fixed number of streams available which is updated via
      96             :   // the MAX_STREAMS frame.
      97             :   //
      98             :   // As such each time we create a new stream for QUIC, the capacity goes down
      99             :   // by one, but unlike the other two codecs it is _not_ restored on stream
     100             :   // closure.
     101             :   //
     102             :   // We track the QUIC capacity here, and overload currentUnusedCapacity so the
     103             :   // connection pool can accurately keep track of when it is safe to create new
     104             :   // streams.
     105             :   //
     106             :   // Though HTTP/3 should arguably start out with 0 stream capacity until the
     107             :   // initial handshake is complete and MAX_STREAMS frame has been received,
     108             :   // assume optimistically it will get ~100 streams, so that the connection pool
     109             :   // won't fetch a connection for each incoming stream but will assume that the
     110             :   // first connection will likely be able to serve 100.
     111             :   // This number will be updated to the correct value before the connection is
     112             :   // deemed connected, at which point further connections will be established if
     113             :   // necessary.
     114             :   uint64_t quiche_capacity_ = 100;
     115             :   // Used to schedule a deferred connect() call. Because HTTP/3 codec client can
     116             :   // do 0-RTT during connect(), deferring it to avoid handling network events during CodecClient
     117             :   // construction.
     118             :   Event::SchedulableCallbackPtr async_connect_callback_;
     119             :   // True if newStream() is ever called.
     120             :   bool has_created_stream_{false};
     121             : };
     122             : 
     123             : // An interface to propagate H3 handshake result.
     124             : // TODO(danzh) add an API to propagate 0-RTT handshake failure.
     125             : class PoolConnectResultCallback {
     126             : public:
     127           0 :   virtual ~PoolConnectResultCallback() = default;
     128             : 
     129             :   // Called when the mandatory handshake is complete. This is when a HTTP/3 connection is regarded
     130             :   // as connected and is able to send requests.
     131             :   virtual void onHandshakeComplete() PURE;
     132             :   // Called upon connection close event from a client who hasn't finish handshake but already sent
     133             :   // early data.
     134             :   // TODO(danzh) actually call it from h3 pool.
     135             :   virtual void onZeroRttHandshakeFailed() PURE;
     136             : };
     137             : 
     138             : // Http3 subclass of FixedHttpConnPoolImpl which exists to store quic data.
     139             : class Http3ConnPoolImpl : public FixedHttpConnPoolImpl {
     140             : public:
     141             :   Http3ConnPoolImpl(Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority,
     142             :                     Event::Dispatcher& dispatcher,
     143             :                     const Network::ConnectionSocket::OptionsSharedPtr& options,
     144             :                     const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options,
     145             :                     Random::RandomGenerator& random_generator,
     146             :                     Upstream::ClusterConnectivityState& state, CreateClientFn client_fn,
     147             :                     CreateCodecFn codec_fn, std::vector<Http::Protocol> protocol,
     148             :                     OptRef<PoolConnectResultCallback> connect_callback,
     149             :                     Http::PersistentQuicInfo& quic_info);
     150             : 
     151             :   ~Http3ConnPoolImpl() override;
     152             :   ConnectionPool::Cancellable* newStream(Http::ResponseDecoder& response_decoder,
     153             :                                          ConnectionPool::Callbacks& callbacks,
     154             :                                          const Instance::StreamOptions& options) override;
     155             : 
     156             :   // For HTTP/3 the base connection pool does not track stream capacity, rather
     157             :   // the HTTP3 active client does.
     158           0 :   bool trackStreamCapacity() override { return false; }
     159             : 
     160             :   std::unique_ptr<Network::ClientConnection>
     161             :   createClientConnection(Quic::QuicStatNames& quic_stat_names,
     162             :                          OptRef<Http::HttpServerPropertiesCache> rtt_cache, Stats::Scope& scope);
     163             : 
     164             : protected:
     165             :   void onConnected(Envoy::ConnectionPool::ActiveClient&) override;
     166             :   void onConnectFailed(Envoy::ConnectionPool::ActiveClient&) override;
     167             : 
     168             : private:
     169             :   friend class Http3ConnPoolImplPeer;
     170             : 
     171             :   // Latches Quic helpers shared across the cluster
     172             :   Quic::PersistentQuicInfoImpl& quic_info_;
     173             :   // server-id can change over the lifetime of Envoy but will be consistent for a
     174             :   // given connection pool.
     175             :   quic::QuicServerId server_id_;
     176             :   // If not nullopt, called when the handshake state changes.
     177             :   OptRef<PoolConnectResultCallback> connect_callback_;
     178             : 
     179             :   quic::DeterministicConnectionIdGenerator connection_id_generator_{
     180             :       quic::kQuicDefaultConnectionIdLength};
     181             : };
     182             : 
     183             : std::unique_ptr<Http3ConnPoolImpl>
     184             : allocateConnPool(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator,
     185             :                  Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority,
     186             :                  const Network::ConnectionSocket::OptionsSharedPtr& options,
     187             :                  const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options,
     188             :                  Upstream::ClusterConnectivityState& state, Quic::QuicStatNames& quic_stat_names,
     189             :                  OptRef<Http::HttpServerPropertiesCache> rtt_cache, Stats::Scope& scope,
     190             :                  OptRef<PoolConnectResultCallback> connect_callback,
     191             :                  Http::PersistentQuicInfo& quic_info);
     192             : 
     193             : } // namespace Http3
     194             : } // namespace Http
     195             : } // namespace Envoy

Generated by: LCOV version 1.15