1
#include "source/common/http/conn_pool_grid.h"
2

            
3
#include <cstdint>
4

            
5
#include "source/common/http/http3_status_tracker_impl.h"
6
#include "source/common/http/mixed_conn_pool.h"
7
#include "source/common/runtime/runtime_features.h"
8

            
9
#include "quiche/quic/core/http/spdy_utils.h"
10
#include "quiche/quic/core/quic_versions.h"
11

            
12
namespace Envoy {
13
namespace Http {
14

            
15
11
bool hasBothAddressFamilies(Upstream::HostConstSharedPtr host) {
16
11
  Upstream::HostDescription::SharedConstAddressVector list_ = host->addressListOrNull();
17
11
  if (!list_ || list_->size() < 2 || !(*list_)[0]->ip() || !(*list_)[1]->ip()) {
18
    return false;
19
  }
20
11
  return (*list_)[0]->ip()->version() != (*list_)[1]->ip()->version();
21
11
}
22

            
23
namespace {
24
43
absl::string_view describePool(const ConnectionPool::Instance& pool) {
25
43
  return pool.protocolDescription();
26
43
}
27

            
28
static constexpr uint32_t kDefaultTimeoutMs = 300;
29

            
30
std::string getTargetHostname(const Network::TransportSocketOptionsConstSharedPtr& options,
31
90
                              Upstream::HostConstSharedPtr& host) {
32
90
  if (options && options->serverNameOverride().has_value()) {
33
88
    return options->serverNameOverride().value();
34
88
  }
35
2
  std::string default_sni =
36
2
      std::string(host->transportSocketFactory().defaultServerNameIndication());
37
2
  if (!default_sni.empty()) {
38
1
    return default_sni;
39
1
  }
40
  // If there's no configured SNI the hostname is probably an IP address. Return it here.
41
1
  return host->hostname();
42
2
}
43

            
44
} // namespace
45

            
46
ConnectivityGrid::WrapperCallbacks::WrapperCallbacks(ConnectivityGrid& grid,
47
                                                     Http::ResponseDecoder& decoder,
48
                                                     ConnectionPool::Callbacks& callbacks,
49
                                                     const Instance::StreamOptions& options)
50
98
    : grid_(grid), decoder_(decoder), inner_callbacks_(&callbacks),
51
      next_attempt_timer_(
52
98
          grid_.dispatcher_.createTimer([this]() -> void { onNextAttemptTimer(); })),
53
98
      stream_options_(options) {
54
98
  if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.use_response_decoder_handle")) {
55
98
    decoder_handle_ = decoder.createResponseDecoderHandle();
56
98
  }
57

            
58
98
  if (!stream_options_.can_use_http3_) {
59
    // If alternate protocols are explicitly disabled, there must have been a failed request over
60
    // HTTP/3 and the failure must be post-handshake. So disable HTTP/3 for this request.
61
4
    http3_attempt_failed_ = true;
62
4
  }
63
98
}
64

            
65
ConnectivityGrid::WrapperCallbacks::ConnectionAttemptCallbacks::ConnectionAttemptCallbacks(
66
    WrapperCallbacks& parent, ConnectionPool::Instance& pool)
67
141
    : parent_(parent), pool_(pool) {}
68

            
69
141
ConnectivityGrid::WrapperCallbacks::ConnectionAttemptCallbacks::~ConnectionAttemptCallbacks() {
70
141
  if (cancellable_ != nullptr) {
71
8
    cancel(Envoy::ConnectionPool::CancelPolicy::Default);
72
8
  }
73
141
}
74

            
75
ConnectivityGrid::StreamCreationResult
76
141
ConnectivityGrid::WrapperCallbacks::ConnectionAttemptCallbacks::newStream() {
77
141
  ASSERT(!parent_.grid_.isPoolHttp3(pool()) || parent_.stream_options_.can_use_http3_);
78
141
  Http::ResponseDecoder& decoder = parent_.decoder_;
79
141
  if (parent_.decoder_handle_ != nullptr) {
80
141
    if (OptRef<ResponseDecoder> opt_ref = parent_.decoder_handle_->get(); opt_ref.has_value()) {
81
141
      decoder = opt_ref.value().get();
82
141
    } else {
83
      const std::string error_msg = "parent_.decoder_ use after free detected.";
84
      IS_ENVOY_BUG(error_msg);
85
      RELEASE_ASSERT(!Runtime::runtimeFeatureEnabled(
86
                         "envoy.reloadable_features.abort_when_accessing_dead_decoder"),
87
                     error_msg);
88
      return StreamCreationResult::ImmediateResult;
89
    }
90
141
  }
91

            
92
141
  auto* cancellable = pool().newStream(decoder, *this, parent_.stream_options_);
93
141
  if (cancellable == nullptr) {
94
24
    return StreamCreationResult::ImmediateResult;
95
24
  }
96
117
  cancellable_ = cancellable;
97
117
  return StreamCreationResult::StreamCreationPending;
98
141
}
99

            
100
void ConnectivityGrid::WrapperCallbacks::ConnectionAttemptCallbacks::onPoolFailure(
101
    ConnectionPool::PoolFailureReason reason, absl::string_view transport_failure_reason,
102
34
    Upstream::HostDescriptionConstSharedPtr host) {
103
34
  cancellable_ = nullptr; // Attempt failed and can no longer be cancelled.
104
34
  parent_.onConnectionAttemptFailed(this, reason, transport_failure_reason, host);
105
34
}
106

            
107
ConnectivityGrid::StreamCreationResult
108
9
ConnectivityGrid::WrapperCallbacks::attemptSecondHttp3Connection() {
109
9
  has_tried_http3_alternate_address_ = true;
110
9
  auto attempt =
111
9
      std::make_unique<ConnectionAttemptCallbacks>(*this, *grid_.getOrCreateHttp3AlternativePool());
112
9
  LinkedList::moveIntoList(std::move(attempt), connection_attempts_);
113
  // Kick off a new stream attempt.
114
9
  return connection_attempts_.front()->newStream();
115
9
}
116

            
117
45
bool ConnectivityGrid::WrapperCallbacks::shouldAttemptSecondHttp3Connection() {
118
  // Don't connect if the grid is shutting down.
119
45
  if (grid_.destroying_) {
120
    return false;
121
  }
122
  // Make sure each wrapper callbacks only tries HTTP/3 alternate addresses
123
  // once.
124
45
  if (has_tried_http3_alternate_address_) {
125
7
    return false;
126
7
  }
127
  // Branch on reloadable flags.
128
38
  if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http3_happy_eyeballs")) {
129
27
    return false;
130
27
  }
131
  // QUIC "happy eyeballs" currently only handles one v4 and one v6 address. If
132
  // there's not multiple families don't bother.
133
11
  return hasBothAddressFamilies(grid_.host_);
134
38
}
135

            
136
void ConnectivityGrid::WrapperCallbacks::onConnectionAttemptFailed(
137
    ConnectionAttemptCallbacks* attempt, ConnectionPool::PoolFailureReason reason,
138
34
    absl::string_view transport_failure_reason, Upstream::HostDescriptionConstSharedPtr host) {
139
34
  ENVOY_LOG(trace, "{} pool failed to create connection to host '{}'.",
140
34
            describePool(attempt->pool()), host->hostname());
141
34
  grid_.dispatcher_.deferredDelete(attempt->removeFromList(connection_attempts_));
142

            
143
34
  if (grid_.isPoolHttp3(attempt->pool())) {
144
24
    if (shouldAttemptSecondHttp3Connection()) {
145
2
      attemptSecondHttp3Connection();
146
      // Return - the above attempt will handle everything else.
147
2
      return;
148
2
    }
149
    // HTTP/3 is only marked as failing if both the initial and any secondary
150
    // attempt have both failed (i.e. the only remaining attempts are TCP).
151
22
    if (connection_attempts_.empty() ||
152
22
        (connection_attempts_.size() == 1 &&
153
19
         &connection_attempts_.front()->pool() == grid_.http2_pool_.get())) {
154
19
      http3_attempt_failed_ = true;
155
19
    }
156
22
  }
157
32
  maybeMarkHttp3Broken();
158

            
159
  // If there is another connection attempt in flight then let that proceed.
160
32
  if (!connection_attempts_.empty()) {
161
14
    prev_pool_failure_reason_ = reason;
162
14
    prev_pool_transport_failure_reason_ = fmt::format(
163
14
        "{}: {}", grid_.isPoolHttp3(attempt->pool()) ? "QUIC" : "TCP", transport_failure_reason);
164
14
    return;
165
14
  }
166

            
167
  // If the next connection attempt does not immediately fail, let it proceed.
168
18
  if (tryAnotherConnection().has_value()) {
169
5
    return;
170
5
  }
171

            
172
  // If this point is reached, all pools have been tried. Pass the pool failure up to the
173
  // original caller, if the caller hasn't already been notified.
174
13
  signalFailureAndDeleteSelf(reason, transport_failure_reason, host);
175
13
}
176

            
177
void ConnectivityGrid::WrapperCallbacks::signalFailureAndDeleteSelf(
178
    ConnectionPool::PoolFailureReason reason, absl::string_view transport_failure_reason,
179
18
    Upstream::HostDescriptionConstSharedPtr host) {
180
18
  ConnectionPool::Callbacks* callbacks = inner_callbacks_;
181
18
  inner_callbacks_ = nullptr;
182
18
  deleteThis();
183
18
  if (callbacks != nullptr) {
184
11
    ENVOY_LOG(trace, "Passing pool failure up to caller.");
185
11
    std::string failure_str;
186
11
    if (prev_pool_failure_reason_.has_value()) {
187
      // The other pool (either TCP or QUIC depending on which failed first) also failed, log its
188
      // error details as well.
189
5
      failure_str = fmt::format(
190
5
          "{} (with earlier attempt failure reason {}, {})", transport_failure_reason,
191
5
          static_cast<int>(prev_pool_failure_reason_.value()), prev_pool_transport_failure_reason_);
192
5
      transport_failure_reason = failure_str;
193
5
    }
194
11
    callbacks->onPoolFailure(reason, transport_failure_reason, host);
195
11
  }
196
18
}
197

            
198
98
void ConnectivityGrid::WrapperCallbacks::deleteThis() {
199
98
  if (delete_started_) {
200
    // This instance has already been removed from the `wrapped_callbacks_` list and scheduled for
201
    // deferred deletion.
202
    return;
203
  }
204
98
  delete_started_ = true;
205
  // Set this to delete on the next dispatcher loop.
206
98
  grid_.dispatcher_.deferredDelete(removeFromList(grid_.wrapped_callbacks_));
207
98
}
208

            
209
ConnectivityGrid::StreamCreationResult
210
132
ConnectivityGrid::WrapperCallbacks::newStream(ConnectionPool::Instance& pool) {
211
132
  ENVOY_LOG(trace, "{} pool attempting to create a new stream to host '{}'.", describePool(pool),
212
132
            grid_.origin_.hostname_);
213
132
  auto attempt = std::make_unique<ConnectionAttemptCallbacks>(*this, pool);
214
132
  LinkedList::moveIntoList(std::move(attempt), connection_attempts_);
215
132
  if (!next_attempt_timer_->enabled()) {
216
114
    next_attempt_timer_->enableTimer(grid_.next_attempt_duration_);
217
114
  }
218
  // Note that in the case of immediate attempt/failure, newStream will delete this.
219
132
  return connection_attempts_.front()->newStream();
220
132
}
221

            
222
void ConnectivityGrid::WrapperCallbacks::onConnectionAttemptReady(
223
    ConnectionAttemptCallbacks* attempt, RequestEncoder& encoder,
224
    Upstream::HostDescriptionConstSharedPtr host, StreamInfo::StreamInfo& info,
225
88
    absl::optional<Http::Protocol> protocol) {
226
88
  ENVOY_LOG(trace, "{} pool successfully connected to host '{}'.", describePool(attempt->pool()),
227
88
            host->hostname());
228
88
  if (!grid_.isPoolHttp3(attempt->pool())) {
229
47
    tcp_attempt_succeeded_ = true;
230
47
    maybeMarkHttp3Broken();
231
47
  }
232

            
233
88
  grid_.dispatcher_.deferredDelete(attempt->removeFromList(connection_attempts_));
234
88
  ConnectionPool::Callbacks* callbacks = inner_callbacks_;
235
88
  inner_callbacks_ = nullptr;
236
  // If an HTTP/3 connection attempts is in progress, let it complete so that if it succeeds
237
  // it can be used for future requests. But if there is a TCP connection attempt in progress,
238
  // cancel it.
239
88
  if (grid_.isPoolHttp3(attempt->pool())) {
240
41
    cancelAllPendingAttempts(Envoy::ConnectionPool::CancelPolicy::Default);
241
41
  }
242
88
  if (connection_attempts_.empty()) {
243
76
    deleteThis();
244
76
  }
245
88
  if (callbacks != nullptr) {
246
83
    callbacks->onPoolReady(encoder, host, info, protocol);
247
83
  } else {
248
5
    encoder.getStream().resetStream(StreamResetReason::LocalReset);
249
5
  }
250
88
}
251

            
252
79
void ConnectivityGrid::WrapperCallbacks::maybeMarkHttp3Broken() {
253
79
  if (http3_attempt_failed_ && tcp_attempt_succeeded_) {
254
14
    ENVOY_LOG(trace, "Marking HTTP/3 broken for host '{}'.", grid_.origin_.hostname_);
255
14
    grid_.markHttp3Broken();
256
14
  }
257
79
}
258

            
259
void ConnectivityGrid::WrapperCallbacks::ConnectionAttemptCallbacks::onPoolReady(
260
    RequestEncoder& encoder, Upstream::HostDescriptionConstSharedPtr host,
261
88
    StreamInfo::StreamInfo& info, absl::optional<Http::Protocol> protocol) {
262
88
  cancellable_ = nullptr; // Attempt succeeded and can no longer be cancelled.
263
88
  parent_.onConnectionAttemptReady(this, encoder, host, info, protocol);
264
88
}
265

            
266
void ConnectivityGrid::WrapperCallbacks::ConnectionAttemptCallbacks::cancel(
267
18
    Envoy::ConnectionPool::CancelPolicy cancel_policy) {
268
18
  auto cancellable = cancellable_;
269
18
  cancellable_ = nullptr; // Prevent repeated cancellations.
270
18
  cancellable->cancel(cancel_policy);
271
18
}
272

            
273
4
void ConnectivityGrid::WrapperCallbacks::cancel(Envoy::ConnectionPool::CancelPolicy cancel_policy) {
274
  // If the newStream caller cancels the stream request, pass the cancellation on
275
  // to each connection attempt.
276
4
  cancelAllPendingAttempts(cancel_policy);
277
4
  deleteThis();
278
4
}
279

            
280
void ConnectivityGrid::WrapperCallbacks::cancelAllPendingAttempts(
281
45
    Envoy::ConnectionPool::CancelPolicy cancel_policy) {
282
47
  for (auto& attempt : connection_attempts_) {
283
10
    attempt->cancel(cancel_policy);
284
10
  }
285
45
  connection_attempts_.clear();
286
45
}
287

            
288
19
void ConnectivityGrid::WrapperCallbacks::onNextAttemptTimer() {
289
19
  if (grid_.destroying_) {
290
    return;
291
  }
292
19
  tryAnotherConnection();
293
19
  if (shouldAttemptSecondHttp3Connection()) {
294
5
    attemptSecondHttp3Connection();
295
5
  }
296
19
}
297
absl::optional<ConnectivityGrid::StreamCreationResult>
298
50
ConnectivityGrid::WrapperCallbacks::tryAnotherConnection() {
299
50
  if (grid_.destroying_) {
300
    return {};
301
  }
302
50
  if (has_attempted_http2_) {
303
    // If there are no other pools to try, return an empty optional.
304
16
    return {};
305
16
  }
306
  // Create a new connection attempt for the next pool. If we reach this point
307
  // return true regardless of if newStream resulted in an immediate result or
308
  // an async call, as either way the attempt will result in success/failure
309
  // callbacks.
310
34
  has_attempted_http2_ = true;
311
34
  return newStream(*grid_.getOrCreateHttp2Pool());
312
50
}
313

            
314
ConnectivityGrid::ConnectivityGrid(
315
    Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator,
316
    Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority,
317
    const Network::ConnectionSocket::OptionsSharedPtr& options,
318
    const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options,
319
    Upstream::ClusterConnectivityState& state, TimeSource& time_source,
320
    HttpServerPropertiesCacheSharedPtr alternate_protocols,
321
    ConnectivityOptions connectivity_options, Quic::QuicStatNames& quic_stat_names,
322
    Stats::Scope& scope, Http::PersistentQuicInfo& quic_info,
323
    OptRef<Quic::EnvoyQuicNetworkObserverRegistry> network_observer_registry,
324
    Server::OverloadManager& overload_manager)
325
90
    : dispatcher_(dispatcher), random_generator_(random_generator), host_(host), options_(options),
326
90
      transport_socket_options_(transport_socket_options), state_(state),
327
90
      next_attempt_duration_(std::chrono::milliseconds(kDefaultTimeoutMs)),
328
90
      time_source_(time_source), alternate_protocols_(alternate_protocols),
329
90
      quic_stat_names_(quic_stat_names), scope_(scope),
330
      // TODO(RyanTheOptimist): Figure out how scheme gets plumbed in here.
331
90
      origin_("https", getTargetHostname(transport_socket_options, host_),
332
90
              host_->address()->ip()->port()),
333
90
      quic_info_(quic_info), priority_(priority), overload_manager_(overload_manager),
334
90
      network_observer_registry_(network_observer_registry) {
335
  // ProdClusterManagerFactory::allocateConnPool verifies the protocols are HTTP/1, HTTP/2 and
336
  // HTTP/3.
337
90
  ASSERT(connectivity_options.protocols_.size() == 3);
338
90
  ASSERT(alternate_protocols);
339
90
  std::chrono::microseconds rtt = alternate_protocols_->getSrtt(
340
90
      origin_,
341
90
      Runtime::runtimeFeatureEnabled("envoy.reloadable_features.use_canonical_suffix_for_srtt"));
342
90
  if (rtt.count() != 0) {
343
7
    next_attempt_duration_ = std::chrono::duration_cast<std::chrono::milliseconds>(rtt * 1.5);
344
7
  }
345
90
}
346

            
347
90
ConnectivityGrid::~ConnectivityGrid() {
348
  // Ignore idle callbacks while the pools are destroyed below.
349
90
  destroying_ = true;
350
95
  while (!wrapped_callbacks_.empty()) {
351
    // Before tearing down the callbacks, make sure they pass up pool failure to
352
    // the caller. We do not call onPoolFailure because it does up-calls to the
353
    // (delete-in-process) grid.
354
5
    wrapped_callbacks_.front()->signalFailureAndDeleteSelf(
355
5
        ConnectionPool::PoolFailureReason::LocalConnectionFailure, "grid teardown", host_);
356
5
  }
357
90
  http2_pool_.reset();
358
90
  http3_pool_.reset();
359
90
  http3_alternate_pool_.reset();
360
90
}
361

            
362
38
void ConnectivityGrid::deleteIsPending() {
363
38
  deferred_deleting_ = true;
364

            
365
60
  for (const auto& pool : pools_) {
366
60
    pool->deleteIsPending();
367
60
  }
368
38
}
369

            
370
9
ConnectionPool::Instance* ConnectivityGrid::getOrCreateHttp3AlternativePool() {
371
9
  ASSERT(!deferred_deleting_);
372
9
  ASSERT(!draining_);
373
9
  if (http3_alternate_pool_ == nullptr) {
374
7
    http3_alternate_pool_ = createHttp3Pool(true);
375
7
    pools_.push_back(http3_alternate_pool_.get());
376
7
    setupPool(*http3_alternate_pool_.get());
377
7
  }
378
9
  return http3_alternate_pool_.get();
379
9
}
380

            
381
106
ConnectionPool::Instance* ConnectivityGrid::getOrCreateHttp3Pool() {
382
106
  ASSERT(!deferred_deleting_);
383
106
  ASSERT(!draining_);
384
106
  if (http3_pool_ == nullptr) {
385
86
    http3_pool_ = createHttp3Pool(false);
386
86
    pools_.push_back(http3_pool_.get());
387
86
    setupPool(*http3_pool_.get());
388
86
  }
389
106
  return http3_pool_.get();
390
106
}
391

            
392
71
ConnectionPool::Instance* ConnectivityGrid::getOrCreateHttp2Pool() {
393
71
  ASSERT(!deferred_deleting_);
394
71
  ASSERT(!draining_);
395
71
  if (http2_pool_ == nullptr) {
396
60
    http2_pool_ = createHttp2Pool();
397
60
    pools_.push_back(http2_pool_.get());
398
60
    setupPool(*http2_pool_.get());
399
60
  }
400

            
401
71
  return http2_pool_.get();
402
71
}
403

            
404
25
ConnectionPool::InstancePtr ConnectivityGrid::createHttp2Pool() {
405
25
  return std::make_unique<HttpConnPoolImplMixed>(dispatcher_, random_generator_, host_, priority_,
406
25
                                                 options_, transport_socket_options_, state_,
407
25
                                                 origin_, alternate_protocols_, overload_manager_);
408
25
}
409

            
410
45
ConnectionPool::InstancePtr ConnectivityGrid::createHttp3Pool(bool attempt_alternate_address) {
411
45
  return Http3::allocateConnPool(
412
45
      dispatcher_, random_generator_, host_, priority_, options_, transport_socket_options_, state_,
413
45
      quic_stat_names_, *alternate_protocols_, scope_,
414
45
      makeOptRefFromPtr<Http3::PoolConnectResultCallback>(this), quic_info_,
415
45
      network_observer_registry_, overload_manager_, attempt_alternate_address);
416
45
}
417

            
418
156
void ConnectivityGrid::setupPool(ConnectionPool::Instance& pool) {
419
156
  pool.addIdleCallback([this]() { onIdleReceived(); });
420
156
}
421

            
422
2
bool ConnectivityGrid::hasActiveConnections() const {
423
2
  for (const auto& pool : pools_) {
424
1
    if (pool->hasActiveConnections()) {
425
      return true;
426
    }
427
1
  }
428
2
  return false;
429
2
}
430
ConnectionPool::Cancellable* ConnectivityGrid::newStream(Http::ResponseDecoder& decoder,
431
                                                         ConnectionPool::Callbacks& callbacks,
432
98
                                                         const Instance::StreamOptions& options) {
433
98
  ASSERT(!deferred_deleting_);
434

            
435
  // New streams should not be created during draining.
436
98
  ASSERT(!draining_);
437

            
438
  // Always start with the HTTP/3 pool if it exists.
439
98
  ConnectionPool::Instance* pool = getOrCreateHttp3Pool();
440
98
  Instance::StreamOptions overriding_options = options;
441
98
  bool delay_tcp_attempt = true;
442
98
  bool delay_alternate_http3_attempt = true;
443
98
  if (shouldAttemptHttp3() && options.can_use_http3_) {
444
67
    if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.quic_no_tcp_delay")) {
445
9
      if (getHttp3StatusTracker().isHttp3Pending() ||
446
9
          getHttp3StatusTracker().hasHttp3FailedRecently()) {
447
6
        overriding_options.can_send_early_data_ = false;
448
6
        delay_tcp_attempt = false;
449
6
      }
450
58
    } else {
451
58
      if (getHttp3StatusTracker().hasHttp3FailedRecently()) {
452
8
        overriding_options.can_send_early_data_ = false;
453
8
        delay_tcp_attempt = false;
454
8
      }
455
58
    }
456
67
    if (http3_pool_ && http3_alternate_pool_ && !http3_pool_->hasActiveConnections() &&
457
67
        http3_alternate_pool_->hasActiveConnections()) {
458
      // If it looks like the main HTTP/3 pool is not functional and the
459
      // alternate works, don't wait 300ms before attempting to use the
460
      // alternate pool.
461
      // TODO(alyssawilk) look into skipping the original pool if this is the case.
462
2
      delay_alternate_http3_attempt = false;
463
2
    }
464
67
  } else {
465
31
    pool = getOrCreateHttp2Pool();
466
31
  }
467
98
  auto wrapped_callback =
468
98
      std::make_unique<WrapperCallbacks>(*this, decoder, callbacks, overriding_options);
469
98
  WrapperCallbacks* ret = wrapped_callback.get();
470
98
  LinkedList::moveIntoList(std::move(wrapped_callback), wrapped_callbacks_);
471
98
  if (ret->newStream(*pool) == StreamCreationResult::ImmediateResult) {
472
    // If newStream succeeds, return nullptr as the caller has received their
473
    // callback and does not need a cancellable handle. At this point the
474
    // WrappedCallbacks object is queued to be deleted.
475
17
    return nullptr;
476
17
  }
477
81
  if (!delay_alternate_http3_attempt && ret->shouldAttemptSecondHttp3Connection()) {
478
2
    if (ret->attemptSecondHttp3Connection() == StreamCreationResult::ImmediateResult) {
479
1
      return nullptr;
480
1
    }
481
2
  }
482
80
  if (!delay_tcp_attempt) {
483
    // Immediately start TCP attempt if HTTP/3 failed recently.
484
13
    ret->tryAnotherConnection();
485
13
  }
486

            
487
  // Return a handle if the caller hasn't yet been notified of success/failure.
488
  // There may still be connection attempts, if the wrapper is waiting on a slow H3 connection.
489
80
  return ret->hasNotifiedCaller() ? nullptr : ret;
490
81
}
491

            
492
44
void ConnectivityGrid::addIdleCallback(IdleCb cb) {
493
  // Add the callback to the list of callbacks to be called when all drains are
494
  // complete.
495
44
  idle_callbacks_.emplace_back(cb);
496
44
}
497

            
498
6
void ConnectivityGrid::drainConnections(Envoy::ConnectionPool::DrainBehavior drain_behavior) {
499
6
  if (drain_behavior == Envoy::ConnectionPool::DrainBehavior::DrainAndDelete) {
500
    // Note that no new pools should be created from this point on.
501
2
    draining_ = true;
502
2
  }
503
9
  for (auto& pool : pools_) {
504
9
    pool->drainConnections(drain_behavior);
505
9
  }
506
6
}
507

            
508
113
Upstream::HostDescriptionConstSharedPtr ConnectivityGrid::host() const { return host_; }
509

            
510
bool ConnectivityGrid::maybePreconnect(float) {
511
  return false; // Preconnect not yet supported for the grid.
512
}
513

            
514
224
bool ConnectivityGrid::isPoolHttp3(const ConnectionPool::Instance& pool) {
515
224
  return &pool == http3_pool_.get() || &pool == http3_alternate_pool_.get();
516
224
}
517

            
518
182
HttpServerPropertiesCache::Http3StatusTracker& ConnectivityGrid::getHttp3StatusTracker() const {
519
182
  ENVOY_BUG(host_->address()->type() == Network::Address::Type::Ip, "Address is not an IP address");
520
182
  return alternate_protocols_->getOrCreateHttp3StatusTracker(origin_);
521
182
}
522

            
523
111
bool ConnectivityGrid::isHttp3Broken() const {
524
111
  ENVOY_BUG(host_->address()->type() == Network::Address::Type::Ip, "Address is not an IP address");
525
111
  return alternate_protocols_->isHttp3Broken(origin_);
526
111
}
527

            
528
14
void ConnectivityGrid::markHttp3Broken() {
529
14
  host_->cluster().trafficStats()->upstream_http3_broken_.inc();
530
14
  ENVOY_BUG(host_->address()->type() == Network::Address::Type::Ip, "Address is not an IP address");
531
14
  alternate_protocols_->markHttp3Broken(origin_);
532
14
}
533

            
534
27
void ConnectivityGrid::markHttp3Confirmed() { getHttp3StatusTracker().markHttp3Confirmed(); }
535

            
536
50
bool ConnectivityGrid::isIdle() const {
537
50
  bool idle = true;
538
85
  for (const auto& pool : pools_) {
539
85
    idle &= pool->isIdle();
540
85
  }
541
50
  return idle;
542
50
}
543

            
544
58
void ConnectivityGrid::onIdleReceived() {
545
  // Don't do any work under the stack of ~ConnectivityGrid()
546
58
  if (destroying_) {
547
8
    return;
548
8
  }
549

            
550
50
  if (isIdle()) {
551
41
    for (auto& callback : idle_callbacks_) {
552
40
      callback();
553
40
    }
554
41
  }
555
50
}
556

            
557
98
bool ConnectivityGrid::shouldAttemptHttp3() {
558
98
  if (host_->address()->type() != Network::Address::Type::Ip) {
559
    IS_ENVOY_BUG("Address is not an IP address");
560
    return false;
561
  }
562
98
  uint32_t port = host_->address()->ip()->port();
563
98
  OptRef<const std::vector<HttpServerPropertiesCache::AlternateProtocol>> protocols =
564
98
      alternate_protocols_->findAlternatives(origin_);
565
98
  if (!protocols.has_value()) {
566
20
    ENVOY_LOG(trace, "No alternate protocols available for host '{}', skipping HTTP/3.",
567
20
              origin_.hostname_);
568
20
    return false;
569
20
  }
570
78
  if (isHttp3Broken()) {
571
4
    ENVOY_LOG(trace, "HTTP/3 is broken to host '{}', skipping.", host_->hostname());
572
4
    return false;
573
4
  }
574
74
  for (const HttpServerPropertiesCache::AlternateProtocol& protocol : protocols.ref()) {
575
    // TODO(RyanTheOptimist): Handle alternate protocols which change hostname or port.
576
74
    if (!protocol.hostname_.empty() || protocol.port_ != port) {
577
2
      ENVOY_LOG(trace,
578
2
                "Alternate protocol for host '{}' attempts to change host or port, skipping.",
579
2
                origin_.hostname_);
580
2
      continue;
581
2
    }
582

            
583
    // TODO(RyanTheOptimist): Cache this mapping, but handle the supported versions list
584
    // changing dynamically.
585
72
    spdy::SpdyAltSvcWireFormat::AlternativeService alt_svc(protocol.alpn_, protocol.hostname_,
586
72
                                                           protocol.port_, 0, {});
587
72
    quic::ParsedQuicVersion version = quic::SpdyUtils::ExtractQuicVersionFromAltSvcEntry(
588
72
        alt_svc, quic::CurrentSupportedVersions());
589
72
    if (version != quic::ParsedQuicVersion::Unsupported()) {
590
      // TODO(RyanTheOptimist): Pass this version down to the HTTP/3 pool.
591
71
      ENVOY_LOG(trace, "HTTP/3 advertised for host '{}'", origin_.hostname_);
592
71
      return true;
593
71
    }
594

            
595
1
    ENVOY_LOG(trace, "Alternate protocol for host '{}' has unsupported ALPN '{}', skipping.",
596
1
              origin_.hostname_, protocol.alpn_);
597
1
  }
598

            
599
3
  ENVOY_LOG(trace, "HTTP/3 is not available to host '{}', skipping.", origin_.hostname_);
600
3
  return false;
601
74
}
602

            
603
27
void ConnectivityGrid::onHandshakeComplete() {
604
27
  ENVOY_LOG(trace, "Marking HTTP/3 confirmed for host '{}'.", origin_.hostname_);
605
27
  markHttp3Confirmed();
606
27
}
607

            
608
8
void ConnectivityGrid::onZeroRttHandshakeFailed() {
609
8
  ENVOY_LOG(trace, "Marking HTTP/3 failed for host '{}'.", host_->hostname());
610
8
  getHttp3StatusTracker().markHttp3FailedRecently();
611
8
}
612

            
613
} // namespace Http
614
} // namespace Envoy