1
#pragma once
2

            
3
#include "envoy/server/overload/overload_manager.h"
4

            
5
#include "source/common/http/conn_pool_base.h"
6
#include "source/common/http/http3/conn_pool.h"
7
#include "source/common/http/http_server_properties_cache_impl.h"
8
#include "source/common/quic/quic_stat_names.h"
9

            
10
#include "absl/container/flat_hash_map.h"
11
#include "absl/container/inlined_vector.h"
12

            
13
namespace Envoy {
14
namespace Http {
15

            
16
// An HTTP connection pool which handles HTTP/3 failing over to HTTP/2
17
//
18
// The ConnectivityGrid wraps an inner HTTP/3 and HTTP/2 pool.
19
//
20
// each newStream attempt to the grid creates a wrapper callback which attempts
21
// to hide from the caller that there may be serial or parallel newStream calls
22
// to the HTTP/3 and HTTP/2 pools.
23
//
24
// Any cancel call on the wrapper callbacks cancels either or both stream
25
// attempts to the wrapped pools. the wrapper callbacks will only pass up
26
// failure if both HTTP/3 and HTTP/2 attempts fail.
27
//
28
// The grid also handles HTTP/3 "happy eyeballs" which is a best-effort attempt
29
// to try using one IPv4 address and one IPv6 address if both families exist in
30
// the host's address list.
31
class ConnectivityGrid : public ConnectionPool::Instance,
32
                         public Http3::PoolConnectResultCallback,
33
                         protected Logger::Loggable<Logger::Id::pool> {
34
public:
35
  struct ConnectivityOptions {
36
    explicit ConnectivityOptions(const std::vector<Http::Protocol>& protocols)
37
88
        : protocols_(protocols) {}
38
    std::vector<Http::Protocol> protocols_;
39
  };
40

            
41
  enum class StreamCreationResult {
42
    ImmediateResult,
43
    StreamCreationPending,
44
  };
45

            
46
  // This is a class which wraps a caller's connection pool callbacks to
47
  // auto-retry pools in the case of connection failure.
48
  //
49
  // It also relays cancellation calls between the original caller and the
50
  // current connection attempts.
51
  class WrapperCallbacks : public ConnectionPool::Cancellable,
52
                           public LinkedObject<WrapperCallbacks>,
53
                           public Event::DeferredDeletable {
54
  public:
55
    WrapperCallbacks(ConnectivityGrid& grid, Http::ResponseDecoder& decoder,
56
                     ConnectionPool::Callbacks& callbacks, const Instance::StreamOptions& options);
57

            
58
80
    bool hasNotifiedCaller() { return inner_callbacks_ == nullptr; }
59

            
60
    // Event::DeferredDeletable
61
    // The wrapper is being deleted - cancel all alarms.
62
98
    void deleteIsPending() override { next_attempt_timer_.reset(); }
63

            
64
    // This holds state for a single connection attempt to a specific pool.
65
    class ConnectionAttemptCallbacks : public ConnectionPool::Callbacks,
66
                                       public LinkedObject<ConnectionAttemptCallbacks>,
67
                                       public Event::DeferredDeletable {
68
    public:
69
      ConnectionAttemptCallbacks(WrapperCallbacks& parent, ConnectionPool::Instance& pool);
70
      ~ConnectionAttemptCallbacks() override;
71
122
      void deleteIsPending() override {}
72

            
73
      StreamCreationResult newStream();
74

            
75
      // ConnectionPool::Callbacks
76
      void onPoolFailure(ConnectionPool::PoolFailureReason reason,
77
                         absl::string_view transport_failure_reason,
78
                         Upstream::HostDescriptionConstSharedPtr host) override;
79
      void onPoolReady(RequestEncoder& encoder, Upstream::HostDescriptionConstSharedPtr host,
80
                       StreamInfo::StreamInfo& info,
81
                       absl::optional<Http::Protocol> protocol) override;
82

            
83
389
      ConnectionPool::Instance& pool() { return pool_; }
84

            
85
      void cancel(Envoy::ConnectionPool::CancelPolicy cancel_policy);
86

            
87
    private:
88
      // A pointer back up to the parent.
89
      WrapperCallbacks& parent_;
90
      ConnectionPool::Instance& pool_;
91
      // The handle to cancel this connection attempt.
92
      // This is owned by the pool which created it.
93
      Cancellable* cancellable_{nullptr};
94
    };
95
    using ConnectionAttemptCallbacksPtr = std::unique_ptr<ConnectionAttemptCallbacks>;
96

            
97
    // ConnectionPool::Cancellable
98
    void cancel(Envoy::ConnectionPool::CancelPolicy cancel_policy) override;
99

            
100
    // Attempt to create a new stream for pool.
101
    StreamCreationResult newStream(ConnectionPool::Instance& pool);
102

            
103
    // Called on pool failure or timeout to kick off another connection attempt.
104
    // Returns the StreamCreationResult if there is a failover pool and a
105
    // connection has been attempted, an empty optional otherwise.
106
    absl::optional<StreamCreationResult> tryAnotherConnection();
107

            
108
    // This timer is registered when an initial HTTP/3 attempt is started.
109
    // The timeout for TCP failover and HTTP/3 happy eyeballs are the same, so
110
    // when this timer fires it's possible that two additional connections will
111
    // be kicked off.
112
    void onNextAttemptTimer();
113

            
114
    // Called by a ConnectionAttempt when the underlying pool fails.
115
    void onConnectionAttemptFailed(ConnectionAttemptCallbacks* attempt,
116
                                   ConnectionPool::PoolFailureReason reason,
117
                                   absl::string_view transport_failure_reason,
118
                                   Upstream::HostDescriptionConstSharedPtr host);
119

            
120
    // Called by a ConnectionAttempt when the underlying pool is ready.
121
    void onConnectionAttemptReady(ConnectionAttemptCallbacks* attempt, RequestEncoder& encoder,
122
                                  Upstream::HostDescriptionConstSharedPtr host,
123
                                  StreamInfo::StreamInfo& info,
124
                                  absl::optional<Http::Protocol> protocol);
125

            
126
    // Called by onConnectionAttemptFailed and on grid deletion destruction to let wrapper
127
    // callback subscribers know the connect attempt failed.
128
    void signalFailureAndDeleteSelf(ConnectionPool::PoolFailureReason reason,
129
                                    absl::string_view transport_failure_reason,
130
                                    Upstream::HostDescriptionConstSharedPtr host);
131

            
132
    // Called if the initial HTTP/3 connection fails.
133
    // Returns true if an HTTP/3 happy eyeballs attempt can be kicked off
134
    // (runtime guard is on, IPv6 and IPv6 addresses are present, happy eyeballs
135
    // has not been tried yet for this wrapper, grid is not in shutdown).
136
    bool shouldAttemptSecondHttp3Connection();
137
    // This kicks off an HTTP/3 happy eyeballs attempt, connecting to the second
138
    // address in the host's address list.
139
    ConnectivityGrid::StreamCreationResult attemptSecondHttp3Connection();
140

            
141
  private:
142
    // Removes this from the owning list, deleting it.
143
    void deleteThis();
144

            
145
    // Marks HTTP/3 broken if the HTTP/3 attempt failed but a TCP attempt succeeded.
146
    // While HTTP/3 is broken the grid will not attempt to make new HTTP/3 connections.
147
    void maybeMarkHttp3Broken();
148

            
149
    // Cancels any pending attempts and deletes them.
150
    void cancelAllPendingAttempts(Envoy::ConnectionPool::CancelPolicy cancel_policy);
151

            
152
    // Tracks all the connection attempts which currently in flight.
153
    std::list<ConnectionAttemptCallbacksPtr> connection_attempts_;
154

            
155
    // The owning grid.
156
    ConnectivityGrid& grid_;
157
    // The decoder for the original newStream, needed to create streams on subsequent pools.
158
    Http::ResponseDecoder& decoder_;
159
    Http::ResponseDecoderHandlePtr decoder_handle_;
160

            
161
    // The callbacks from the original caller, which must get onPoolFailure or
162
    // onPoolReady unless there is call to cancel(). Will be nullptr if the caller
163
    // has been notified while attempts are still pending.
164
    ConnectionPool::Callbacks* inner_callbacks_;
165
    // The timer which tracks when new connections should be attempted.
166
    Event::TimerPtr next_attempt_timer_;
167
    // Checks if http2 has been attempted.
168
    bool has_attempted_http2_ = false;
169
    // Checks if "happy eyeballs" has been done for HTTP/3. This largely makes
170
    // sure that if we kick off a secondary attempt due to timeout we don't kick
171
    // off another one if the original HTTP/3 connection explicitly fails.
172
    bool has_tried_http3_alternate_address_ = false;
173
    // True if the HTTP/3 attempt failed.
174
    bool http3_attempt_failed_{};
175
    // True if the TCP attempt succeeded.
176
    bool tcp_attempt_succeeded_{};
177
    // Latch the passed-in stream options.
178
    const Instance::StreamOptions stream_options_{};
179
    absl::optional<ConnectionPool::PoolFailureReason> prev_pool_failure_reason_;
180
    std::string prev_pool_transport_failure_reason_;
181
    bool delete_started_ = false;
182
  };
183
  using WrapperCallbacksPtr = std::unique_ptr<WrapperCallbacks>;
184

            
185
  ConnectivityGrid(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator,
186
                   Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority,
187
                   const Network::ConnectionSocket::OptionsSharedPtr& options,
188
                   const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options,
189
                   Upstream::ClusterConnectivityState& state, TimeSource& time_source,
190
                   HttpServerPropertiesCacheSharedPtr alternate_protocols,
191
                   ConnectivityOptions connectivity_options, Quic::QuicStatNames& quic_stat_names,
192
                   Stats::Scope& scope, Http::PersistentQuicInfo& quic_info,
193
                   OptRef<Quic::EnvoyQuicNetworkObserverRegistry> network_observer_registry,
194
                   Server::OverloadManager& overload_manager);
195
  ~ConnectivityGrid() override;
196

            
197
  // Event::DeferredDeletable
198
  void deleteIsPending() override;
199

            
200
  // Http::ConnPool::Instance
201
  bool hasActiveConnections() const override;
202
  ConnectionPool::Cancellable* newStream(Http::ResponseDecoder& response_decoder,
203
                                         ConnectionPool::Callbacks& callbacks,
204
                                         const Instance::StreamOptions& options) override;
205
  void addIdleCallback(IdleCb cb) override;
206
  bool isIdle() const override;
207
  void drainConnections(Envoy::ConnectionPool::DrainBehavior drain_behavior) override;
208
  Upstream::HostDescriptionConstSharedPtr host() const override;
209
  bool maybePreconnect(float preconnect_ratio) override;
210
1
  absl::string_view protocolDescription() const override { return "connection grid"; }
211

            
212
  // Returns true if pool is the grid's HTTP/3 connection pool.
213
  bool isPoolHttp3(const ConnectionPool::Instance& pool);
214

            
215
  // Returns true if HTTP/3 is currently broken. While HTTP/3 is broken the grid will not
216
  // attempt to make new HTTP/3 connections.
217
  bool isHttp3Broken() const;
218

            
219
  // Marks HTTP/3 broken for a period of time subject to exponential backoff. While HTTP/3
220
  // is broken the grid will not attempt to make new HTTP/3 connections.
221
  void markHttp3Broken();
222

            
223
  // Marks that HTTP/3 is working, which resets the exponential backoff counter in the
224
  // event that HTTP/3 is marked broken again.
225
  void markHttp3Confirmed();
226

            
227
  // Http3::PoolConnectResultCallback
228
  void onHandshakeComplete() override;
229
  void onZeroRttHandshakeFailed() override;
230

            
231
protected:
232
  // Set the required idle callback on the pool.
233
  void setupPool(ConnectionPool::Instance& pool);
234

            
235
private:
236
  friend class ConnectivityGridForTest;
237

            
238
  // Return origin of the remote host. If the host doesn't have an IP address,
239
  // the port of the origin will be 0.
240
  HttpServerPropertiesCache::Http3StatusTracker& getHttp3StatusTracker() const;
241

            
242
  // Called by each pool as it idles. The grid is responsible for calling
243
  // idle_callbacks_ once all pools have idled.
244
  void onIdleReceived();
245

            
246
  // Returns true if HTTP/3 should be attempted because there is an alternate protocol
247
  // that specifies HTTP/3 and HTTP/3 is not broken.
248
  bool shouldAttemptHttp3();
249

            
250
  // Returns the specified pool, which will be created if necessary
251
  ConnectionPool::Instance* getOrCreateHttp3Pool();
252
  ConnectionPool::Instance* getOrCreateHttp2Pool();
253
  ConnectionPool::Instance* getOrCreateHttp3AlternativePool();
254

            
255
  // True if this pool is the "happy eyeballs" attempt, and should use the
256
  // secondary address family.
257
  virtual ConnectionPool::InstancePtr createHttp3Pool(bool attempt_alternate_address);
258
  virtual ConnectionPool::InstancePtr createHttp2Pool();
259

            
260
  // This batch of member variables are latched objects required for pool creation.
261
  Event::Dispatcher& dispatcher_;
262
  Random::RandomGenerator& random_generator_;
263
  Upstream::HostConstSharedPtr host_;
264
  const Network::ConnectionSocket::OptionsSharedPtr options_;
265
  const Network::TransportSocketOptionsConstSharedPtr transport_socket_options_;
266
  Upstream::ClusterConnectivityState& state_;
267
  std::chrono::milliseconds next_attempt_duration_;
268
  TimeSource& time_source_;
269
  HttpServerPropertiesCacheSharedPtr alternate_protocols_;
270

            
271
  // Tracks the callbacks to be called on drain completion.
272
  std::list<Instance::IdleCb> idle_callbacks_;
273

            
274
  // The connection pools to use to create new streams
275
  ConnectionPool::InstancePtr http3_pool_;
276
  // This is the pool used for the HTTP/3 "happy eyeballs" attempt. If it is
277
  // created it will have the opposite address family from http3_pool_ above.
278
  ConnectionPool::InstancePtr http3_alternate_pool_;
279
  ConnectionPool::InstancePtr http2_pool_;
280
  // A convenience vector to allow taking actions on all pools.
281
  absl::InlinedVector<ConnectionPool::Instance*, 2> pools_;
282

            
283
  // Wrapped callbacks are stashed in the wrapped_callbacks_ for ownership.
284
  std::list<WrapperCallbacksPtr> wrapped_callbacks_;
285

            
286
  Quic::QuicStatNames& quic_stat_names_;
287

            
288
  Stats::Scope& scope_;
289

            
290
  // The origin for this pool.
291
  // Note the host name here is based off of the host name used for SNI, which
292
  // may be from the cluster config, or the request headers for auto-sni.
293
  HttpServerPropertiesCache::Origin origin_;
294

            
295
  Http::PersistentQuicInfo& quic_info_;
296
  Upstream::ResourcePriority priority_;
297
  Server::OverloadManager& overload_manager_;
298

            
299
  // True iff this pool is draining. No new streams or connections should be created
300
  // in this state.
301
  bool draining_{false};
302

            
303
  // True iff under the stack of the destructor, to avoid calling drain
304
  // callbacks on deletion.
305
  bool destroying_{};
306

            
307
  // True iff this pool is being deferred deleted.
308
  bool deferred_deleting_{};
309

            
310
  OptRef<Quic::EnvoyQuicNetworkObserverRegistry> network_observer_registry_;
311
};
312

            
313
} // namespace Http
314
} // namespace Envoy