1
#include "source/common/quic/envoy_quic_client_connection.h"
2

            
3
#include <memory>
4

            
5
#include "envoy/config/core/v3/base.pb.h"
6

            
7
#include "source/common/network/socket_option_factory.h"
8
#include "source/common/network/udp_packet_writer_handler_impl.h"
9
#include "source/common/quic/envoy_quic_network_observer_registry_factory.h"
10
#include "source/common/runtime/runtime_features.h"
11

            
12
namespace Envoy {
13
namespace Quic {
14

            
15
// Used to defer deleting connection socket to avoid deleting IoHandle in a read loop.
16
class DeferredDeletableSocket : public Event::DeferredDeletable {
17
public:
18
  explicit DeferredDeletableSocket(std::unique_ptr<Network::ConnectionSocket> socket)
19
6
      : socket_(std::move(socket)) {}
20

            
21
6
  void deleteIsPending() override { socket_->close(); }
22

            
23
private:
24
  std::unique_ptr<Network::ConnectionSocket> socket_;
25
};
26

            
27
class EnvoyQuicClientPathValidationContext : public quic::QuicClientPathValidationContext {
28
public:
29
  EnvoyQuicClientPathValidationContext(quic::QuicSocketAddress self_address,
30
                                       quic::QuicSocketAddress peer_address,
31
                                       quic::QuicNetworkHandle network,
32
                                       std::unique_ptr<EnvoyQuicPacketWriter>&& writer,
33
                                       Network::ConnectionSocketPtr&& socket,
34
                                       Event::Dispatcher& dispatcher)
35
12
      : quic::QuicClientPathValidationContext(self_address, peer_address, network),
36
12
        writer_(std::make_unique<quic::QuicForceBlockablePacketWriter>()),
37
12
        socket_(std::move(socket)), dispatcher_(dispatcher) {
38
    // Owns the writer.
39
12
    writer_->set_writer(writer.release());
40
12
  }
41

            
42
12
  ~EnvoyQuicClientPathValidationContext() override {
43
12
    if (socket_ != nullptr) {
44
      // The socket wasn't used by the connection, the path validation must have failed. Now
45
      // deferred delete it to avoid deleting IoHandle in a read loop.
46
3
      dispatcher_.deferredDelete(std::make_unique<DeferredDeletableSocket>(std::move(socket_)));
47
3
    }
48
12
  }
49

            
50
9
  bool ShouldConnectionOwnWriter() const override { return true; }
51
42
  quic::QuicForceBlockablePacketWriter* ForceBlockableWriterToUse() override {
52
42
    return writer_.get();
53
42
  }
54

            
55
23
  Network::ConnectionSocket& probingSocket() { return *socket_; }
56

            
57
9
  quic::QuicForceBlockablePacketWriter* releaseWriter() { return writer_.release(); }
58
9
  std::unique_ptr<Network::ConnectionSocket> releaseSocket() { return std::move(socket_); }
59

            
60
private:
61
  std::unique_ptr<quic::QuicForceBlockablePacketWriter> writer_;
62
  Network::ConnectionSocketPtr socket_;
63
  Event::Dispatcher& dispatcher_;
64
};
65

            
66
void EnvoyQuicClientConnection::EnvoyQuicClinetPathContextFactory::CreatePathValidationContext(
67
    quic::QuicNetworkHandle network, quic::QuicSocketAddress peer_address,
68
12
    std::unique_ptr<quic::QuicPathContextFactory::CreationResultDelegate> result_delegate) {
69
12
  Network::Address::InstanceConstSharedPtr new_local_address;
70
12
  if (network == quic::kInvalidNetworkHandle) {
71
    // If there isn't a meaningful network handle to bind to, bind to the
72
    // local address of the current socket.
73
12
    Network::Address::InstanceConstSharedPtr current_local_address =
74
12
        connection_.connectionSocket()->connectionInfoProvider().localAddress();
75
12
    if (current_local_address->ip()->version() == Network::Address::IpVersion::v4) {
76
12
      new_local_address = std::make_shared<Network::Address::Ipv4Instance>(
77
12
          current_local_address->ip()->addressAsString(),
78
12
          &current_local_address->socketInterface());
79
12
    } else {
80
      new_local_address = std::make_shared<Network::Address::Ipv6Instance>(
81
          current_local_address->ip()->addressAsString(),
82
          &current_local_address->socketInterface());
83
    }
84
12
  }
85
12
  Network::Address::InstanceConstSharedPtr remote_address =
86
12
      (connection_.peer_address() == peer_address)
87
12
          ? connection_.connectionSocket()->connectionInfoProvider().remoteAddress()
88
12
          : quicAddressToEnvoyAddressInstance(peer_address);
89
  // new_local_address will be re-assigned if it is nullptr.
90
12
  QuicClientPacketWriterFactory::CreationResult result =
91
12
      writer_factory_.createSocketAndQuicPacketWriter(remote_address, network, new_local_address,
92
12
                                                      connection_.connectionSocket()->options());
93
12
  connection_.setUpConnectionSocket(*result.socket_, connection_.delegate_);
94
12
  result_delegate->OnCreationSucceeded(std::make_unique<EnvoyQuicClientPathValidationContext>(
95
12
      envoyIpAddressToQuicSocketAddress(new_local_address->ip()), peer_address, network,
96
12
      std::move(result.writer_), std::move(result.socket_), connection_.dispatcher_));
97
12
}
98

            
99
quic::QuicNetworkHandle EnvoyQuicClientConnection::EnvoyQuicMigrationHelper::FindAlternateNetwork(
100
    quic::QuicNetworkHandle network) {
101
  return registry_.has_value() ? registry_.value().get().getAlternativeNetwork(network)
102
                               : quic::kInvalidNetworkHandle;
103
}
104

            
105
2872
quic::QuicNetworkHandle EnvoyQuicClientConnection::EnvoyQuicMigrationHelper::GetDefaultNetwork() {
106
2872
  return registry_.has_value() ? registry_.value().get().getDefaultNetwork()
107
2872
                               : quic::kInvalidNetworkHandle;
108
2872
}
109

            
110
2872
quic::QuicNetworkHandle EnvoyQuicClientConnection::EnvoyQuicMigrationHelper::GetCurrentNetwork() {
111
2872
  return initial_network_;
112
2872
}
113

            
114
void EnvoyQuicClientConnection::EnvoyQuicMigrationHelper::OnMigrationToPathDone(
115
9
    std::unique_ptr<quic::QuicClientPathValidationContext> context, bool success) {
116
9
  if (success) {
117
9
    ENVOY_CONN_LOG(trace, "Successfully migrate to use path {} to {}", connection_,
118
9
                   context->self_address().ToString(), context->peer_address().ToString());
119
9
    auto* envoy_context = static_cast<EnvoyQuicClientPathValidationContext*>(context.get());
120
    // Connection already owns the writer.
121
9
    envoy_context->releaseWriter();
122
9
    ++connection_.num_socket_switches_;
123
9
    connection_.setConnectionSocket(envoy_context->releaseSocket());
124
    // Previous writer may have been force blocked and write events on it may have been dropped.
125
    // Synthesize a write event in case this case to unblock the connection.
126
9
    connection_.connectionSocket()->ioHandle().activateFileEvents(Event::FileReadyType::Write);
127
    // Send something to notify the peer of the address change immediately.
128
9
    connection_.SendPing();
129
9
  } else {
130
    ENVOY_CONN_LOG(trace, "Failed to migrate to use path {} to {}", connection_,
131
                   context->self_address().ToString(), context->peer_address().ToString());
132
  }
133
9
}
134

            
135
std::unique_ptr<quic::QuicPathContextFactory>
136
2872
EnvoyQuicClientConnection::EnvoyQuicMigrationHelper::CreateQuicPathContextFactory() {
137
2872
  return std::make_unique<EnvoyQuicClinetPathContextFactory>(writer_factory_, connection_);
138
2872
}
139

            
140
EnvoyQuicClientConnection::EnvoyQuicClientConnection(
141
    const quic::QuicConnectionId& server_connection_id, quic::QuicConnectionHelperInterface& helper,
142
    quic::QuicAlarmFactory& alarm_factory, quic::QuicPacketWriter* writer, bool owns_writer,
143
    const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher,
144
    Network::ConnectionSocketPtr&& connection_socket,
145
    quic::ConnectionIdGeneratorInterface& generator)
146
3096
    : quic::QuicConnection(server_connection_id, quic::QuicSocketAddress(),
147
3096
                           envoyIpAddressToQuicSocketAddress(
148
3096
                               connection_socket->connectionInfoProvider().remoteAddress()->ip()),
149
3096
                           &helper, &alarm_factory, writer, owns_writer,
150
3096
                           quic::Perspective::IS_CLIENT, supported_versions, generator),
151
3096
      QuicNetworkConnection(std::move(connection_socket)), dispatcher_(dispatcher),
152
3096
      disallow_mmsg_(Runtime::runtimeFeatureEnabled(
153
3096
          "envoy.reloadable_features.disallow_quic_client_udp_mmsg")) {}
154

            
155
void EnvoyQuicClientConnection::processPacket(
156
    Network::Address::InstanceConstSharedPtr local_address,
157
    Network::Address::InstanceConstSharedPtr peer_address, Buffer::InstancePtr buffer,
158
463002
    MonotonicTime receive_time, uint8_t tos, Buffer::OwnedImpl /*saved_cmsg*/) {
159
463002
  quic::QuicTime timestamp =
160
463002
      quic::QuicTime::Zero() +
161
463002
      quic::QuicTime::Delta::FromMicroseconds(
162
463002
          std::chrono::duration_cast<std::chrono::microseconds>(receive_time.time_since_epoch())
163
463002
              .count());
164
463002
  ASSERT(peer_address != nullptr && buffer != nullptr);
165
463002
  ASSERT(buffer->getRawSlices().size() == 1);
166
463002
  if (local_address == nullptr) {
167
    // Quic doesn't know how to handle packets without destination address. Drop them here.
168
40
    if (buffer->length() > 0) {
169
40
      ++num_packets_with_unknown_dst_address_;
170
40
      std::string error_message = fmt::format(
171
40
          "Unable to get destination address. Address family {}. Have{} pending path validation. "
172
40
          "self_address is{} initialized.",
173
40
          (peer_address->ip()->version() == Network::Address::IpVersion::v4 ? "v4" : "v6"),
174
40
          (HasPendingPathValidation() ? "" : " no"),
175
40
          (self_address().IsInitialized() ? "" : " not"));
176
40
      ENVOY_CONN_LOG(error, "{}", *this, error_message);
177
40
      if (num_packets_with_unknown_dst_address_ > 10) {
178
        // If too many packets are without destination addresses, close the connection.
179
        CloseConnection(quic::QUIC_PACKET_READ_ERROR, error_message,
180
                        quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
181
      }
182
40
    }
183
40
    return;
184
40
  }
185
462962
  Buffer::RawSlice slice = buffer->frontSlice();
186
462962
  quic::QuicReceivedPacket packet(reinterpret_cast<char*>(slice.mem_), slice.len_, timestamp,
187
462962
                                  /*owns_buffer=*/false, /*ttl=*/0, /*ttl_valid=*/false,
188
462962
                                  /*packet_headers=*/nullptr, /*headers_length=*/0,
189
462962
                                  /*owns_header_buffer*/ false,
190
462962
                                  getQuicEcnCodepointFromTosByte(tos));
191
462962
  ProcessUdpPacket(envoyIpAddressToQuicSocketAddress(local_address->ip()),
192
462962
                   envoyIpAddressToQuicSocketAddress(peer_address->ip()), packet);
193
462962
}
194

            
195
68
uint64_t EnvoyQuicClientConnection::maxDatagramSize() const {
196
  // TODO(danzh) make this variable configurable to support jumbo frames.
197
68
  return Network::DEFAULT_UDP_MAX_DATAGRAM_SIZE;
198
68
}
199

            
200
void EnvoyQuicClientConnection::setUpConnectionSocket(Network::ConnectionSocket& connection_socket,
201
3025
                                                      OptRef<PacketsToReadDelegate> delegate) {
202
3025
  delegate_ = delegate;
203
3025
  if (connection_socket.isOpen()) {
204
3020
    connection_socket.ioHandle().initializeFileEvent(
205
3020
        dispatcher_,
206
88683
        [this, &connection_socket](uint32_t events) {
207
88641
          onFileEvent(events, connection_socket);
208
88641
          return absl::OkStatus();
209
88641
        },
210
3020
        Event::PlatformDefaultTriggerType,
211
3020
        Event::FileReadyType::Read | Event::FileReadyType::Write);
212

            
213
3020
    if (!Network::Socket::applyOptions(connection_socket.options(), connection_socket,
214
3020
                                       envoy::config::core::v3::SocketOption::STATE_LISTENING)) {
215
1
      ENVOY_CONN_LOG(error, "Fail to apply listening options", *this);
216
1
      connection_socket.close();
217
1
    }
218
3020
  }
219
3025
  if (!connection_socket.isOpen() && connectionSocket().get() == &connection_socket) {
220
    // Only close the connection if the connection socket is the current one. If it is a probing
221
    // socket that isn't the current socket, do not close the connection upon failure, as the
222
    // current socket is still usable.
223
6
    CloseConnection(quic::QUIC_CONNECTION_CANCELLED, "Fail to set up connection socket.",
224
6
                    quic::ConnectionCloseBehavior::SILENT_CLOSE);
225
6
  }
226
3025
}
227

            
228
void EnvoyQuicClientConnection::switchConnectionSocket(
229
6
    Network::ConnectionSocketPtr&& connection_socket) {
230
6
  auto writer = std::make_unique<EnvoyQuicPacketWriter>(
231
6
      std::make_unique<Network::UdpDefaultWriter>(connection_socket->ioHandle()));
232
6
  quic::QuicSocketAddress self_address = envoyIpAddressToQuicSocketAddress(
233
6
      connection_socket->connectionInfoProvider().localAddress()->ip());
234
6
  quic::QuicSocketAddress peer_address = envoyIpAddressToQuicSocketAddress(
235
6
      connection_socket->connectionInfoProvider().remoteAddress()->ip());
236

            
237
  // The old socket is not closed in this call, because it could still receive useful packets.
238
6
  num_socket_switches_++;
239
6
  setConnectionSocket(std::move(connection_socket));
240
6
  setUpConnectionSocket(*connectionSocket(), delegate_);
241
6
  MigratePath(self_address, peer_address, writer.release(), true);
242
6
}
243

            
244
28
void EnvoyQuicClientConnection::OnPathDegradingDetected() {
245
  // This will trigger connection migration or port migration in QUICHE if
246
  // migration_helper_ is initialized. Otherwise do it in this class.
247
28
  QuicConnection::OnPathDegradingDetected();
248
28
  if (migration_helper_ == nullptr) {
249
10
    maybeMigratePort();
250
10
  }
251
28
}
252

            
253
10
void EnvoyQuicClientConnection::maybeMigratePort() {
254
10
  if (!IsHandshakeConfirmed() || HasPendingPathValidation() || !migrate_port_on_path_degrading_ ||
255
10
      num_socket_switches_ >= kMaxNumSocketSwitches) {
256
2
    return;
257
2
  }
258

            
259
8
  probeWithNewPort(peer_address(), quic::PathValidationReason::kPortMigration);
260
8
}
261

            
262
void EnvoyQuicClientConnection::probeWithNewPort(const quic::QuicSocketAddress& peer_addr,
263
12
                                                 quic::PathValidationReason reason) {
264
12
  const Network::Address::InstanceConstSharedPtr& current_local_address =
265
12
      connectionSocket()->connectionInfoProvider().localAddress();
266
  // Creates an IP address with unset port. The port will be set when the new socket is created.
267
12
  Network::Address::InstanceConstSharedPtr new_local_address;
268
12
  if (current_local_address->ip()->version() == Network::Address::IpVersion::v4) {
269
12
    new_local_address = std::make_shared<Network::Address::Ipv4Instance>(
270
12
        current_local_address->ip()->addressAsString(), &current_local_address->socketInterface());
271
12
  } else {
272
    new_local_address = std::make_shared<Network::Address::Ipv6Instance>(
273
        current_local_address->ip()->addressAsString(), &current_local_address->socketInterface());
274
  }
275

            
276
  // The probing socket will have the same host but a different port.
277
12
  ASSERT(migration_helper_ == nullptr && writer_factory_.has_value());
278
12
  QuicClientPacketWriterFactory::CreationResult creation_result =
279
12
      writer_factory_->createSocketAndQuicPacketWriter(
280
12
          (peer_addr == peer_address()
281
12
               ? connectionSocket()->connectionInfoProvider().remoteAddress()
282
12
               : quicAddressToEnvoyAddressInstance(peer_addr)),
283
12
          quic::kInvalidNetworkHandle, new_local_address, connectionSocket()->options());
284
12
  setUpConnectionSocket(*creation_result.socket_, delegate_);
285
12
  auto writer = std::move(creation_result.writer_);
286
12
  quic::QuicSocketAddress self_address = envoyIpAddressToQuicSocketAddress(
287
12
      creation_result.socket_->connectionInfoProvider().localAddress()->ip());
288

            
289
12
  auto context = std::make_unique<EnvoyQuicPathValidationContext>(
290
12
      self_address, peer_addr, std::move(writer), std::move(creation_result.socket_));
291
12
  ValidatePath(std::move(context), std::make_unique<EnvoyPathValidationResultDelegate>(*this),
292
12
               reason);
293
12
}
294

            
295
void EnvoyQuicClientConnection::onPathValidationSuccess(
296
9
    std::unique_ptr<quic::QuicPathValidationContext> context) {
297
9
  auto envoy_context =
298
9
      static_cast<EnvoyQuicClientConnection::EnvoyQuicPathValidationContext*>(context.get());
299

            
300
9
  auto probing_socket = envoy_context->releaseSocket();
301
9
  if (envoy_context->peer_address() != peer_address()) {
302
4
    OnServerPreferredAddressValidated(*envoy_context, true);
303
4
    envoy_context->releaseWriter();
304
8
  } else {
305
5
    MigratePath(envoy_context->self_address(), envoy_context->peer_address(),
306
5
                envoy_context->releaseWriter(), true);
307
5
  }
308

            
309
9
  if (self_address() == envoy_context->self_address() &&
310
9
      peer_address() == envoy_context->peer_address()) {
311
    // probing_socket will be set as the new default socket. But old sockets are still able to
312
    // receive packets.
313
9
    num_socket_switches_++;
314
9
    setConnectionSocket(std::move(probing_socket));
315
9
    return;
316
9
  }
317
  // MigratePath should always succeed since the migration happens after path
318
  // validation.
319
  ENVOY_CONN_LOG(error, "connection fails to migrate path after validation", *this);
320
}
321

            
322
void EnvoyQuicClientConnection::onPathValidationFailure(
323
3
    std::unique_ptr<quic::QuicPathValidationContext> context) {
324
  // Note that the probing socket and probing writer will be deleted once context goes out of
325
  // scope.
326
3
  OnPathValidationFailureAtClient(/*is_multi_port=*/false, *context);
327
3
  std::unique_ptr<Network::ConnectionSocket> probing_socket =
328
3
      static_cast<EnvoyQuicPathValidationContext*>(context.get())->releaseSocket();
329
  // Extend the socket life time till the end of the current event loop.
330
3
  dispatcher_.deferredDelete(std::make_unique<DeferredDeletableSocket>(std::move(probing_socket)));
331
3
}
332

            
333
void EnvoyQuicClientConnection::onFileEvent(uint32_t events,
334
88645
                                            Network::ConnectionSocket& connection_socket) {
335
88645
  ENVOY_CONN_LOG(trace, "socket event: {}", *this, events);
336
88645
  ASSERT(events & (Event::FileReadyType::Read | Event::FileReadyType::Write));
337

            
338
88645
  if (events & Event::FileReadyType::Write) {
339
88645
    writer()->SetWritable();
340
    // The writer might still be force blocked for migration in progress, in
341
    // which case no write should be attempted.
342
88645
    WriteIfNotBlocked();
343
88645
  }
344

            
345
  // Check if the event is on the probing socket before read.
346
88645
  const bool is_probing_socket =
347
88645
      HasPendingPathValidation() &&
348
88645
      (&connection_socket ==
349
47
       (writer_factory_
350
47
            ? &static_cast<EnvoyQuicPathValidationContext*>(GetPathValidationContext())
351
24
                   ->probingSocket()
352
47
            : &static_cast<EnvoyQuicClientPathValidationContext*>(GetPathValidationContext())
353
23
                   ->probingSocket()));
354

            
355
  // It's possible for a write event callback to close the connection, in such case ignore read
356
  // event processing.
357
  // TODO(mattklein123): Right now QUIC client is hard coded to use GRO because it is probably the
358
  // right default for QUIC. Determine whether this should be configurable or not.
359
88645
  if (connected() && (events & Event::FileReadyType::Read)) {
360
46604
    Api::IoErrorPtr err = Network::Utility::readPacketsFromSocket(
361
46604
        connection_socket.ioHandle(), *connection_socket.connectionInfoProvider().localAddress(),
362
46604
        *this, dispatcher_.timeSource(), /*allow_gro=*/true, !disallow_mmsg_, packets_dropped_);
363
46604
    if (err == nullptr) {
364
      // If this READ event is on the probing socket and any packet read failed the path validation
365
      // (i.e. via STATELESS_RESET), the probing socket should have been closed and the default
366
      // socket remained unchanged. In this case any remaining unread packets are no longer
367
      // interesting. Only re-register READ event to continue reading the remaining packets in the
368
      // next loop if this is not the case.
369
4851
      if (!(is_probing_socket && !HasPendingPathValidation() &&
370
4851
            connectionSocket().get() != &connection_socket)) {
371
4851
        connection_socket.ioHandle().activateFileEvents(Event::FileReadyType::Read);
372
4851
      }
373
41753
    } else if (err->getErrorCode() != Api::IoError::IoErrorCode::Again) {
374
18
      ENVOY_CONN_LOG(error, "recvmsg result {}: {}", *this, static_cast<int>(err->getErrorCode()),
375
18
                     err->getErrorDetails());
376
18
    }
377
46604
  }
378
88645
}
379

            
380
277
void EnvoyQuicClientConnection::setNumPtosForPortMigration(uint32_t num_ptos_for_path_degrading) {
381
277
  if (num_ptos_for_path_degrading < 1) {
382
157
    return;
383
157
  }
384
120
  migrate_port_on_path_degrading_ = true;
385
120
  sent_packet_manager().set_num_ptos_for_path_degrading(num_ptos_for_path_degrading);
386
120
}
387

            
388
EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::EnvoyQuicPathValidationContext(
389
    const quic::QuicSocketAddress& self_address, const quic::QuicSocketAddress& peer_address,
390
    std::unique_ptr<EnvoyQuicPacketWriter> writer,
391
    std::unique_ptr<Network::ConnectionSocket> probing_socket)
392
12
    : QuicPathValidationContext(self_address, peer_address), writer_(std::move(writer)),
393
12
      socket_(std::move(probing_socket)) {}
394

            
395
12
EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::~EnvoyQuicPathValidationContext() =
396
    default;
397

            
398
28
quic::QuicPacketWriter* EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::WriterToUse() {
399
28
  return writer_.get();
400
28
}
401

            
402
9
EnvoyQuicPacketWriter* EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::releaseWriter() {
403
9
  return writer_.release();
404
9
}
405

            
406
std::unique_ptr<Network::ConnectionSocket>
407
12
EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::releaseSocket() {
408
12
  return std::move(socket_);
409
12
}
410

            
411
Network::ConnectionSocket&
412
24
EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::probingSocket() {
413
24
  return *socket_;
414
24
}
415

            
416
EnvoyQuicClientConnection::EnvoyPathValidationResultDelegate::EnvoyPathValidationResultDelegate(
417
    EnvoyQuicClientConnection& connection)
418
12
    : connection_(connection) {}
419

            
420
void EnvoyQuicClientConnection::EnvoyPathValidationResultDelegate::OnPathValidationSuccess(
421
9
    std::unique_ptr<quic::QuicPathValidationContext> context, quic::QuicTime /*start_time*/) {
422
9
  connection_.onPathValidationSuccess(std::move(context));
423
9
}
424

            
425
void EnvoyQuicClientConnection::EnvoyPathValidationResultDelegate::OnPathValidationFailure(
426
3
    std::unique_ptr<quic::QuicPathValidationContext> context) {
427
3
  connection_.onPathValidationFailure(std::move(context));
428
3
}
429

            
430
549943
void EnvoyQuicClientConnection::OnCanWrite() {
431
549943
  quic::QuicConnection::OnCanWrite();
432
549943
  onWriteEventDone();
433
549943
}
434

            
435
EnvoyQuicClientConnection::EnvoyQuicMigrationHelper&
436
EnvoyQuicClientConnection::getOrCreateMigrationHelper(
437
    QuicClientPacketWriterFactory& writer_factory, quic::QuicNetworkHandle initial_network,
438
2872
    OptRef<EnvoyQuicNetworkObserverRegistry> registry) {
439
2872
  if (migration_helper_ == nullptr) {
440
2872
    migration_helper_ = std::make_unique<EnvoyQuicMigrationHelper>(*this, registry, writer_factory,
441
2872
                                                                   initial_network);
442
2872
  }
443
2872
  return *migration_helper_;
444
2872
}
445

            
446
void EnvoyQuicClientConnection::probeAndMigrateToServerPreferredAddress(
447
4
    const quic::QuicSocketAddress& server_preferred_address) {
448
4
  ASSERT(migration_helper_ == nullptr);
449
4
  probeWithNewPort(server_preferred_address,
450
4
                   quic::PathValidationReason::kServerPreferredAddressMigration);
451
4
}
452

            
453
} // namespace Quic
454
} // namespace Envoy