1
#include "source/common/http/http3/conn_pool.h"
2

            
3
#include <cstdint>
4
#include <memory>
5

            
6
#include "envoy/event/dispatcher.h"
7
#include "envoy/server/overload/overload_manager.h"
8

            
9
#include "source/common/config/utility.h"
10
#include "source/common/http/utility.h"
11
#include "source/common/network/address_impl.h"
12
#include "source/common/network/utility.h"
13
#include "source/common/runtime/runtime_features.h"
14
#include "source/common/upstream/upstream_impl.h"
15

            
16
namespace Envoy {
17
namespace Http {
18
namespace Http3 {
19
namespace {
20

            
21
// Assuming here that happy eyeballs sorting results in the first address being
22
// a different address family than the second, make a best effort attempt to
23
// return a secondary address family.
24
Network::Address::InstanceConstSharedPtr
25
getHostAddress(std::shared_ptr<const Upstream::HostDescription> host,
26
943
               bool secondary_address_family) {
27
943
  if (!secondary_address_family) {
28
939
    return host->address();
29
939
  }
30
4
  Upstream::HostDescription::SharedConstAddressVector list_or_null = host->addressListOrNull();
31
4
  if (!list_or_null || list_or_null->size() < 2 || !(*list_or_null)[0]->ip() ||
32
4
      !(*list_or_null)[1]->ip()) {
33
    // This function is only called if the parallel address checks in conn_pool_grid.cc
34
    // already passed so it should not return here, but if there was a DNS
35
    // re-resolve between checking the address list in the grid and checking
36
    // here, we'll fail over here rather than fast-fail the connection.
37
1
    return host->address();
38
1
  }
39
3
  return (*list_or_null)[1];
40
4
}
41

            
42
1886
uint32_t getMaxStreams(const Upstream::ClusterInfo& cluster) {
43
1886
  return PROTOBUF_GET_WRAPPED_OR_DEFAULT(
44
1886
      cluster.httpProtocolOptions().http3Options().quic_protocol_options(), max_concurrent_streams,
45
1886
      100);
46
1886
}
47

            
48
const Envoy::Ssl::ClientContextConfig&
49
216
getConfig(Network::UpstreamTransportSocketFactory& transport_socket_factory) {
50
216
  ASSERT(transport_socket_factory.clientContextConfig().has_value());
51
216
  return transport_socket_factory.clientContextConfig().value();
52
216
}
53

            
54
std::string sni(const Network::TransportSocketOptionsConstSharedPtr& options,
55
949
                Upstream::HostConstSharedPtr host) {
56
949
  return options && options->serverNameOverride().has_value()
57
949
             ? options->serverNameOverride().value()
58
949
             : getConfig(host->transportSocketFactory()).serverNameIndication();
59
949
}
60

            
61
} // namespace
62

            
63
ActiveClient::ActiveClient(Envoy::Http::HttpConnPoolImplBase& parent,
64
                           Upstream::Host::CreateConnectionData& data)
65
943
    : MultiplexedActiveClientBase(
66
943
          parent, getMaxStreams(parent.host()->cluster()), getMaxStreams(parent.host()->cluster()),
67
943
          parent.host()->cluster().trafficStats()->upstream_cx_http3_total_, data),
68
943
      async_connect_callback_(parent_.dispatcher().createSchedulableCallback([this]() {
69
942
        if (state() != Envoy::ConnectionPool::ActiveClient::State::Connecting) {
70
          return;
71
        }
72
942
        codec_client_->connect();
73
942
        if (readyForStream()) {
74
          // This client can send early data, so check if there are any pending streams can be sent
75
          // as early data.
76
49
          parent_.onUpstreamReadyForEarlyData(*this);
77
49
        }
78
943
      })) {
79
943
  ASSERT(codec_client_);
80
943
  if (!codec_client_->connectCalled()) {
81
    // Hasn't called connect() yet, schedule one for the next event loop.
82
943
    async_connect_callback_->scheduleCallbackNextIteration();
83
943
  }
84
943
}
85

            
86
1846
void ActiveClient::onMaxStreamsChanged(uint32_t num_streams) {
87
1846
  updateCapacity(num_streams);
88
1846
  if (state() == ActiveClient::State::Busy && currentUnusedCapacity() != 0) {
89
32
    ENVOY_BUG(hasHandshakeCompleted(), "Received MAX_STREAM frame before handshake completed.");
90
32
    parent_.transitionActiveClientState(*this, ActiveClient::State::Ready);
91
    // If there's waiting streams, make sure the pool will now serve them.
92
32
    parent_.onUpstreamReady();
93
1814
  } else if (currentUnusedCapacity() == 0 && state() == ActiveClient::State::ReadyForEarlyData) {
94
    // With HTTP/3 this can only happen during a rejected 0-RTT handshake.
95
    parent_.transitionActiveClientState(*this, ActiveClient::State::Busy);
96
  }
97
1846
}
98

            
99
ConnectionPool::Cancellable* Http3ConnPoolImpl::newStream(Http::ResponseDecoder& response_decoder,
100
                                                          ConnectionPool::Callbacks& callbacks,
101
1493
                                                          const Instance::StreamOptions& options) {
102
1493
  ENVOY_BUG(options.can_use_http3_,
103
1493
            "Trying to send request over h3 while alternate protocols is disabled.");
104
1493
  return FixedHttpConnPoolImpl::newStream(response_decoder, callbacks, options);
105
1493
}
106

            
107
Http3ConnPoolImpl::Http3ConnPoolImpl(
108
    Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority,
109
    Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options,
110
    const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options,
111
    Random::RandomGenerator& random_generator, Upstream::ClusterConnectivityState& state,
112
    CreateClientFn client_fn, CreateCodecFn codec_fn, std::vector<Http::Protocol> protocol,
113
    OptRef<PoolConnectResultCallback> connect_callback, Http::PersistentQuicInfo& quic_info,
114
    OptRef<Quic::EnvoyQuicNetworkObserverRegistry> network_observer_registry,
115
    Server::OverloadManager& overload_manager, bool attempt_happy_eyeballs)
116
949
    : FixedHttpConnPoolImpl(host, priority, dispatcher, options, transport_socket_options,
117
949
                            random_generator, state, client_fn, codec_fn, protocol,
118
949
                            overload_manager, {}, nullptr),
119
949
      quic_info_(dynamic_cast<Quic::PersistentQuicInfoImpl&>(quic_info)),
120
949
      server_id_(sni(transport_socket_options, host),
121
949
                 static_cast<uint16_t>(host_->address()->ip()->port())),
122
949
      connect_callback_(connect_callback), attempt_happy_eyeballs_(attempt_happy_eyeballs),
123
949
      network_observer_registry_(network_observer_registry) {}
124

            
125
908
void Http3ConnPoolImpl::onConnected(Envoy::ConnectionPool::ActiveClient&) {
126
908
  if (connect_callback_ != absl::nullopt) {
127
26
    connect_callback_->onHandshakeComplete();
128
26
  }
129
908
}
130

            
131
35
void Http3ConnPoolImpl::onConnectFailed(Envoy::ConnectionPool::ActiveClient& client) {
132
35
  ASSERT(client.numActiveStreams() == 0);
133
35
  if (static_cast<ActiveClient&>(client).hasCreatedStream() && connect_callback_ != absl::nullopt) {
134
2
    connect_callback_->onZeroRttHandshakeFailed();
135
2
  }
136
35
}
137

            
138
// Make sure all connections are torn down before quic_info_ is deleted.
139
949
Http3ConnPoolImpl::~Http3ConnPoolImpl() { destructAllConnections(); }
140

            
141
std::unique_ptr<Network::ClientConnection>
142
Http3ConnPoolImpl::createClientConnection(Quic::QuicStatNames& quic_stat_names,
143
                                          OptRef<Http::HttpServerPropertiesCache> rtt_cache,
144
944
                                          Stats::Scope& scope) {
145
944
  std::shared_ptr<quic::QuicCryptoClientConfig> crypto_config =
146
944
      host_->transportSocketFactory().getCryptoConfig();
147
944
  if (crypto_config == nullptr) {
148
1
    return nullptr; // no secrets available yet.
149
1
  }
150

            
151
943
  Network::Address::InstanceConstSharedPtr address =
152
943
      getHostAddress(host(), attempt_happy_eyeballs_);
153
943
  const auto& transport_options = transportSocketOptions();
154
943
  auto upstream_local_address_selector = host()->cluster().getUpstreamLocalAddressSelector();
155
943
  auto upstream_local_address = upstream_local_address_selector->getUpstreamLocalAddress(
156
943
      address, socketOptions(), makeOptRefFromPtr(transport_options.get()));
157

            
158
943
  return Quic::createQuicNetworkConnection(
159
943
      quic_info_, std::move(crypto_config), server_id_, dispatcher(), address,
160
943
      upstream_local_address.address_, quic_stat_names, rtt_cache, scope,
161
943
      upstream_local_address.socket_options_, transport_options, connection_id_generator_,
162
943
      host_->transportSocketFactory(), network_observer_registry_.ptr());
163
944
}
164

            
165
std::unique_ptr<Http3ConnPoolImpl>
166
allocateConnPool(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator,
167
                 Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority,
168
                 const Network::ConnectionSocket::OptionsSharedPtr& options,
169
                 const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options,
170
                 Upstream::ClusterConnectivityState& state, Quic::QuicStatNames& quic_stat_names,
171
                 OptRef<Http::HttpServerPropertiesCache> rtt_cache, Stats::Scope& scope,
172
                 OptRef<PoolConnectResultCallback> connect_callback,
173
                 Http::PersistentQuicInfo& quic_info,
174
                 OptRef<Quic::EnvoyQuicNetworkObserverRegistry> network_observer_registry,
175
949
                 Server::OverloadManager& overload_manager, bool attempt_happy_eyeballs) {
176
949
  return std::make_unique<Http3ConnPoolImpl>(
177
949
      host, priority, dispatcher, options, transport_socket_options, random_generator, state,
178
949
      [&quic_stat_names, rtt_cache,
179
964
       &scope](HttpConnPoolImplBase* pool) -> ::Envoy::ConnectionPool::ActiveClientPtr {
180
947
        ENVOY_LOG_TO_LOGGER(Envoy::Logger::Registry::getLog(Envoy::Logger::Id::pool), debug,
181
947
                            "Creating Http/3 client");
182
        // If there's no ssl context, the secrets are not loaded. Fast-fail by returning null.
183
947
        auto factory = &pool->host()->transportSocketFactory();
184
947
        if (factory->sslCtx() == nullptr) {
185
3
          ENVOY_LOG_EVERY_POW_2_TO_LOGGER(Envoy::Logger::Registry::getLog(Envoy::Logger::Id::pool),
186
3
                                          warn,
187
3
                                          "Failed to create Http/3 client. Transport socket "
188
3
                                          "factory is not configured correctly.");
189
3
          return nullptr;
190
3
        }
191
944
        Http3ConnPoolImpl* h3_pool = reinterpret_cast<Http3ConnPoolImpl*>(pool);
192
944
        Upstream::Host::CreateConnectionData data{};
193
944
        data.host_description_ = pool->host();
194
944
        data.connection_ = h3_pool->createClientConnection(quic_stat_names, rtt_cache, scope);
195
944
        if (data.connection_ == nullptr) {
196
1
          ENVOY_LOG_EVERY_POW_2_TO_LOGGER(
197
1
              Envoy::Logger::Registry::getLog(Envoy::Logger::Id::pool), warn,
198
1
              "Failed to create Http/3 client. Failed to create quic network connection.");
199
1
          return nullptr;
200
1
        }
201
943
        auto client = std::make_unique<ActiveClient>(*pool, data);
202
943
        return client;
203
944
      },
204
964
      [](Upstream::Host::CreateConnectionData& data, HttpConnPoolImplBase* pool) {
205
        // Because HTTP/3 codec client connect() can close connection inline and can raise 0-RTT
206
        // event inline, and both cases need to have network callbacks and http callbacks wired up
207
        // to propagate the event, so do not call connect() during codec client construction.
208
943
        bool auto_connect = false;
209
943
        CodecClientPtr codec = std::make_unique<CodecClientProd>(
210
943
            CodecType::HTTP3, std::move(data.connection_), data.host_description_,
211
943
            pool->dispatcher(), pool->randomGenerator(), pool->transportSocketOptions(),
212
943
            auto_connect);
213
943
        return codec;
214
943
      },
215
949
      std::vector<Protocol>{Protocol::Http3}, connect_callback, quic_info,
216
949
      network_observer_registry, overload_manager, attempt_happy_eyeballs);
217
949
}
218

            
219
} // namespace Http3
220
} // namespace Http
221
} // namespace Envoy