• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/quic/dedicated_web_transport_http3_client.h"
6 
7 #include <string_view>
8 #include <vector>
9 
10 #include "base/containers/contains.h"
11 #include "base/feature_list.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/metrics/field_trial_params.h"
14 #include "base/metrics/histogram_functions.h"
15 #include "base/task/single_thread_task_runner.h"
16 #include "net/base/address_list.h"
17 #include "net/base/port_util.h"
18 #include "net/base/url_util.h"
19 #include "net/http/http_network_session.h"
20 #include "net/log/net_log_values.h"
21 #include "net/proxy_resolution/configured_proxy_resolution_service.h"
22 #include "net/proxy_resolution/proxy_resolution_request.h"
23 #include "net/quic/address_utils.h"
24 #include "net/quic/crypto/proof_verifier_chromium.h"
25 #include "net/quic/quic_chromium_alarm_factory.h"
26 #include "net/spdy/spdy_http_utils.h"
27 #include "net/third_party/quiche/src/quiche/quic/core/http/web_transport_http3.h"
28 #include "net/third_party/quiche/src/quiche/quic/core/quic_connection.h"
29 #include "net/third_party/quiche/src/quiche/quic/core/quic_types.h"
30 #include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h"
31 #include "net/url_request/url_request_context.h"
32 #include "url/scheme_host_port.h"
33 
34 namespace net {
35 
36 namespace {
37 
38 // From
39 // https://wicg.github.io/web-transport/#dom-quictransportconfiguration-server_certificate_fingerprints
40 constexpr int kCustomCertificateMaxValidityDays = 14;
41 
42 // The time the client would wait for the server to acknowledge the session
43 // being closed.
44 constexpr base::TimeDelta kMaxCloseTimeout = base::Seconds(2);
45 
46 // Enables custom congestion control for WebTransport over HTTP/3.
47 BASE_FEATURE(kWebTransportCongestionControl,
48              "WebTransportCongestionControl",
49              base::FEATURE_DISABLED_BY_DEFAULT);
50 constexpr base::FeatureParam<quic::CongestionControlType>::Option
51     kWebTransportCongestionControlAlgorithms[] = {
52         {quic::kCubicBytes, "CUBIC"},
53         {quic::kRenoBytes, "Reno"},
54         {quic::kBBR, "BBRv1"},
55         {quic::kBBRv2, "BBRv2"},
56 };
57 constexpr base::FeatureParam<quic::CongestionControlType>
58     kWebTransportCongestionControlAlgorithm{
59         &kWebTransportCongestionControl, /*name=*/"algorithm",
60         /*default_value=*/quic::kCubicBytes,
61         &kWebTransportCongestionControlAlgorithms};
62 
HostsFromOrigins(std::set<HostPortPair> origins)63 std::set<std::string> HostsFromOrigins(std::set<HostPortPair> origins) {
64   std::set<std::string> hosts;
65   for (const auto& origin : origins) {
66     hosts.insert(origin.host());
67   }
68   return hosts;
69 }
70 
71 // A version of WebTransportFingerprintProofVerifier that enforces
72 // Chromium-specific policies.
73 class ChromiumWebTransportFingerprintProofVerifier
74     : public quic::WebTransportFingerprintProofVerifier {
75  public:
76   using WebTransportFingerprintProofVerifier::
77       WebTransportFingerprintProofVerifier;
78 
79  protected:
IsKeyTypeAllowedByPolicy(const quic::CertificateView & certificate)80   bool IsKeyTypeAllowedByPolicy(
81       const quic::CertificateView& certificate) override {
82     if (certificate.public_key_type() == quic::PublicKeyType::kRsa) {
83       return false;
84     }
85     return WebTransportFingerprintProofVerifier::IsKeyTypeAllowedByPolicy(
86         certificate);
87   }
88 };
89 
CreateProofVerifier(const NetworkAnonymizationKey & anonymization_key,URLRequestContext * context,const WebTransportParameters & parameters)90 std::unique_ptr<quic::ProofVerifier> CreateProofVerifier(
91     const NetworkAnonymizationKey& anonymization_key,
92     URLRequestContext* context,
93     const WebTransportParameters& parameters) {
94   if (parameters.server_certificate_fingerprints.empty()) {
95     std::set<std::string> hostnames_to_allow_unknown_roots = HostsFromOrigins(
96         context->quic_context()->params()->origins_to_force_quic_on);
97     if (context->quic_context()->params()->webtransport_developer_mode) {
98       hostnames_to_allow_unknown_roots.insert("");
99     }
100     return std::make_unique<ProofVerifierChromium>(
101         context->cert_verifier(), context->transport_security_state(),
102         context->sct_auditing_delegate(),
103         std::move(hostnames_to_allow_unknown_roots), anonymization_key);
104   }
105 
106   auto verifier =
107       std::make_unique<ChromiumWebTransportFingerprintProofVerifier>(
108           context->quic_context()->clock(), kCustomCertificateMaxValidityDays);
109   for (const quic::CertificateFingerprint& fingerprint :
110        parameters.server_certificate_fingerprints) {
111     bool success = verifier->AddFingerprint(fingerprint);
112     if (!success) {
113       DLOG(WARNING) << "Failed to add a certificate fingerprint: "
114                     << fingerprint.fingerprint;
115     }
116   }
117   return verifier;
118 }
119 
RecordNetLogQuicSessionClientStateChanged(NetLogWithSource & net_log,WebTransportState last_state,WebTransportState next_state,const std::optional<WebTransportError> & error)120 void RecordNetLogQuicSessionClientStateChanged(
121     NetLogWithSource& net_log,
122     WebTransportState last_state,
123     WebTransportState next_state,
124     const std::optional<WebTransportError>& error) {
125   net_log.AddEvent(
126       NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_STATE_CHANGED, [&] {
127         auto dict = base::Value::Dict()
128                         .Set("last_state", WebTransportStateString(last_state))
129                         .Set("next_state", WebTransportStateString(next_state));
130         if (error.has_value()) {
131           dict.Set("error",
132                    base::Value::Dict()
133                        .Set("net_error", error->net_error)
134                        .Set("quic_error", static_cast<int>(error->quic_error))
135                        .Set("details", error->details));
136         }
137         return dict;
138       });
139 }
140 
141 // The stream associated with an extended CONNECT request for the WebTransport
142 // session.
143 class ConnectStream : public quic::QuicSpdyClientStream {
144  public:
ConnectStream(quic::QuicStreamId id,quic::QuicSpdyClientSession * session,quic::StreamType type,DedicatedWebTransportHttp3Client * client)145   ConnectStream(quic::QuicStreamId id,
146                 quic::QuicSpdyClientSession* session,
147                 quic::StreamType type,
148                 DedicatedWebTransportHttp3Client* client)
149       : quic::QuicSpdyClientStream(id, session, type), client_(client) {}
150 
~ConnectStream()151   ~ConnectStream() override { client_->OnConnectStreamDeleted(); }
152 
OnInitialHeadersComplete(bool fin,size_t frame_len,const quic::QuicHeaderList & header_list)153   void OnInitialHeadersComplete(
154       bool fin,
155       size_t frame_len,
156       const quic::QuicHeaderList& header_list) override {
157     quic::QuicSpdyClientStream::OnInitialHeadersComplete(fin, frame_len,
158                                                          header_list);
159     client_->OnHeadersComplete(response_headers());
160   }
161 
OnClose()162   void OnClose() override {
163     quic::QuicSpdyClientStream::OnClose();
164     if (fin_received() && fin_sent()) {
165       // Clean close.
166       return;
167     }
168     if (stream_error() == quic::QUIC_STREAM_CONNECTION_ERROR) {
169       // If stream is closed due to the connection error, OnConnectionClosed()
170       // will populate the correct error details.
171       return;
172     }
173     client_->OnConnectStreamAborted();
174   }
175 
OnWriteSideInDataRecvdState()176   void OnWriteSideInDataRecvdState() override {
177     quic::QuicSpdyClientStream::OnWriteSideInDataRecvdState();
178     client_->OnConnectStreamWriteSideInDataRecvdState();
179   }
180 
181  private:
182   raw_ptr<DedicatedWebTransportHttp3Client> client_;
183 };
184 
185 class DedicatedWebTransportHttp3ClientSession
186     : public quic::QuicSpdyClientSession {
187  public:
DedicatedWebTransportHttp3ClientSession(const quic::QuicConfig & config,const quic::ParsedQuicVersionVector & supported_versions,quic::QuicConnection * connection,const quic::QuicServerId & server_id,quic::QuicCryptoClientConfig * crypto_config,DedicatedWebTransportHttp3Client * client)188   DedicatedWebTransportHttp3ClientSession(
189       const quic::QuicConfig& config,
190       const quic::ParsedQuicVersionVector& supported_versions,
191       quic::QuicConnection* connection,
192       const quic::QuicServerId& server_id,
193       quic::QuicCryptoClientConfig* crypto_config,
194       DedicatedWebTransportHttp3Client* client)
195       : quic::QuicSpdyClientSession(config,
196                                     supported_versions,
197                                     connection,
198                                     server_id,
199                                     crypto_config),
200         client_(client) {}
201 
OnSettingsFrame(const quic::SettingsFrame & frame)202   bool OnSettingsFrame(const quic::SettingsFrame& frame) override {
203     if (!quic::QuicSpdyClientSession::OnSettingsFrame(frame)) {
204       return false;
205     }
206     client_->OnSettingsReceived();
207     return true;
208   }
209 
LocallySupportedWebTransportVersions() const210   quic::WebTransportHttp3VersionSet LocallySupportedWebTransportVersions()
211       const override {
212     quic::WebTransportHttp3VersionSet versions =
213         quic::WebTransportHttp3VersionSet(
214             {quic::WebTransportHttp3Version::kDraft02});
215     if (base::FeatureList::IsEnabled(features::kEnableWebTransportDraft07)) {
216       versions.Set(quic::WebTransportHttp3Version::kDraft07);
217     }
218     return versions;
219   }
220 
LocalHttpDatagramSupport()221   quic::HttpDatagramSupport LocalHttpDatagramSupport() override {
222     return quic::HttpDatagramSupport::kRfcAndDraft04;
223   }
224 
OnConnectionClosed(const quic::QuicConnectionCloseFrame & frame,quic::ConnectionCloseSource source)225   void OnConnectionClosed(const quic::QuicConnectionCloseFrame& frame,
226                           quic::ConnectionCloseSource source) override {
227     quic::QuicSpdyClientSession::OnConnectionClosed(frame, source);
228     client_->OnConnectionClosed(frame.quic_error_code, frame.error_details,
229                                 source);
230   }
231 
CreateConnectStream()232   ConnectStream* CreateConnectStream() {
233     if (!ShouldCreateOutgoingBidirectionalStream()) {
234       return nullptr;
235     }
236     std::unique_ptr<ConnectStream> stream =
237         std::make_unique<ConnectStream>(GetNextOutgoingBidirectionalStreamId(),
238                                         this, quic::BIDIRECTIONAL, client_);
239     ConnectStream* stream_ptr = stream.get();
240     ActivateStream(std::move(stream));
241     return stream_ptr;
242   }
243 
OnDatagramProcessed(std::optional<quic::MessageStatus> status)244   void OnDatagramProcessed(std::optional<quic::MessageStatus> status) override {
245     client_->OnDatagramProcessed(
246         status.has_value() ? std::optional<quic::MessageStatus>(*status)
247                            : std::optional<quic::MessageStatus>());
248   }
249 
250  private:
251   raw_ptr<DedicatedWebTransportHttp3Client> client_;
252 };
253 
254 class WebTransportVisitorProxy : public quic::WebTransportVisitor {
255  public:
WebTransportVisitorProxy(quic::WebTransportVisitor * visitor)256   explicit WebTransportVisitorProxy(quic::WebTransportVisitor* visitor)
257       : visitor_(visitor) {}
258 
OnSessionReady()259   void OnSessionReady() override { visitor_->OnSessionReady(); }
OnSessionClosed(quic::WebTransportSessionError error_code,const std::string & error_message)260   void OnSessionClosed(quic::WebTransportSessionError error_code,
261                        const std::string& error_message) override {
262     visitor_->OnSessionClosed(error_code, error_message);
263   }
OnIncomingBidirectionalStreamAvailable()264   void OnIncomingBidirectionalStreamAvailable() override {
265     visitor_->OnIncomingBidirectionalStreamAvailable();
266   }
OnIncomingUnidirectionalStreamAvailable()267   void OnIncomingUnidirectionalStreamAvailable() override {
268     visitor_->OnIncomingUnidirectionalStreamAvailable();
269   }
OnDatagramReceived(std::string_view datagram)270   void OnDatagramReceived(std::string_view datagram) override {
271     visitor_->OnDatagramReceived(datagram);
272   }
OnCanCreateNewOutgoingBidirectionalStream()273   void OnCanCreateNewOutgoingBidirectionalStream() override {
274     visitor_->OnCanCreateNewOutgoingBidirectionalStream();
275   }
OnCanCreateNewOutgoingUnidirectionalStream()276   void OnCanCreateNewOutgoingUnidirectionalStream() override {
277     visitor_->OnCanCreateNewOutgoingUnidirectionalStream();
278   }
279 
280  private:
281   raw_ptr<quic::WebTransportVisitor> visitor_;
282 };
283 
IsTerminalState(WebTransportState state)284 bool IsTerminalState(WebTransportState state) {
285   return state == WebTransportState::CLOSED ||
286          state == WebTransportState::FAILED;
287 }
288 
289 // These values are persisted to logs. Entries should not be renumbered and
290 // numeric values should never be reused.
291 enum class NegotiatedHttpDatagramVersion {
292   kNone = 0,
293   kDraft04 = 1,
294   kRfc = 2,
295   kMaxValue = kRfc,
296 };
297 
RecordNegotiatedHttpDatagramSupport(quic::HttpDatagramSupport support)298 void RecordNegotiatedHttpDatagramSupport(quic::HttpDatagramSupport support) {
299   NegotiatedHttpDatagramVersion negotiated;
300   switch (support) {
301     case quic::HttpDatagramSupport::kNone:
302       negotiated = NegotiatedHttpDatagramVersion::kNone;
303       break;
304     case quic::HttpDatagramSupport::kDraft04:
305       negotiated = NegotiatedHttpDatagramVersion::kDraft04;
306       break;
307     case quic::HttpDatagramSupport::kRfc:
308       negotiated = NegotiatedHttpDatagramVersion::kRfc;
309       break;
310     case quic::HttpDatagramSupport::kRfcAndDraft04:
311       NOTREACHED();
312   }
313   base::UmaHistogramEnumeration(
314       "Net.WebTransport.NegotiatedHttpDatagramVersion", negotiated);
315 }
316 
WebTransportHttp3VersionString(quic::WebTransportHttp3Version version)317 const char* WebTransportHttp3VersionString(
318     quic::WebTransportHttp3Version version) {
319   switch (version) {
320     case quic::WebTransportHttp3Version::kDraft02:
321       return "draft-02";
322     case quic::WebTransportHttp3Version::kDraft07:
323       return "draft-07";
324   }
325 }
326 
327 enum class NegotiatedWebTransportVersion {
328   kDraft02 = 0,
329   kDraft07 = 1,
330   kMaxValue = kDraft07,
331 };
332 
RecordNegotiatedWebTransportVersion(quic::WebTransportHttp3Version version)333 void RecordNegotiatedWebTransportVersion(
334     quic::WebTransportHttp3Version version) {
335   NegotiatedWebTransportVersion negotiated;
336   switch (version) {
337     case quic::WebTransportHttp3Version::kDraft02:
338       negotiated = NegotiatedWebTransportVersion::kDraft02;
339       break;
340     case quic::WebTransportHttp3Version::kDraft07:
341       negotiated = NegotiatedWebTransportVersion::kDraft07;
342       break;
343   }
344   base::UmaHistogramEnumeration(
345       "Net.WebTransport.NegotiatedWebTransportVersion", negotiated);
346 }
347 
AdjustSendAlgorithm(quic::QuicConnection & connection)348 void AdjustSendAlgorithm(quic::QuicConnection& connection) {
349   if (!base::FeatureList::IsEnabled(kWebTransportCongestionControl)) {
350     return;
351   }
352   connection.sent_packet_manager().SetSendAlgorithm(
353       kWebTransportCongestionControlAlgorithm.Get());
354 }
355 
356 }  // namespace
357 
DedicatedWebTransportHttp3Client(const GURL & url,const url::Origin & origin,WebTransportClientVisitor * visitor,const NetworkAnonymizationKey & anonymization_key,URLRequestContext * context,const WebTransportParameters & parameters)358 DedicatedWebTransportHttp3Client::DedicatedWebTransportHttp3Client(
359     const GURL& url,
360     const url::Origin& origin,
361     WebTransportClientVisitor* visitor,
362     const NetworkAnonymizationKey& anonymization_key,
363     URLRequestContext* context,
364     const WebTransportParameters& parameters)
365     : url_(url),
366       origin_(origin),
367       anonymization_key_(anonymization_key),
368       context_(context),
369       visitor_(visitor),
370       quic_context_(context->quic_context()),
371       net_log_(NetLogWithSource::Make(context->net_log(),
372                                       NetLogSourceType::WEB_TRANSPORT_CLIENT)),
373       task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault().get()),
374       alarm_factory_(
375           std::make_unique<QuicChromiumAlarmFactory>(task_runner_,
376                                                      quic_context_->clock())),
377       // TODO(vasilvv): proof verifier should have proper error reporting
378       // (currently, all certificate verification errors result in "TLS
379       // handshake error" even when more detailed message is available).  This
380       // requires implementing ProofHandler::OnProofVerifyDetailsAvailable.
381       crypto_config_(
382           CreateProofVerifier(anonymization_key_, context, parameters),
383           /* session_cache */ nullptr) {
384   ConfigureQuicCryptoClientConfig(crypto_config_);
385   net_log_.BeginEvent(
386       NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_ALIVE, [&] {
387         base::Value::Dict dict;
388         dict.Set("url", url.possibly_invalid_spec());
389         dict.Set("network_anonymization_key",
390                  anonymization_key.ToDebugString());
391         return dict;
392       });
393 }
394 
~DedicatedWebTransportHttp3Client()395 DedicatedWebTransportHttp3Client::~DedicatedWebTransportHttp3Client() {
396   net_log_.EndEventWithNetErrorCode(
397       NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_ALIVE,
398       error_ ? error_->net_error : OK);
399   // |session_| owns this, so we need to make sure we release it before
400   // it gets dangling.
401   connection_ = nullptr;
402 }
403 
Connect()404 void DedicatedWebTransportHttp3Client::Connect() {
405   if (state_ != WebTransportState::NEW ||
406       next_connect_state_ != CONNECT_STATE_NONE) {
407     NOTREACHED();
408   }
409 
410   TransitionToState(WebTransportState::CONNECTING);
411   next_connect_state_ = CONNECT_STATE_INIT;
412   DoLoop(OK);
413 }
414 
Close(const std::optional<WebTransportCloseInfo> & close_info)415 void DedicatedWebTransportHttp3Client::Close(
416     const std::optional<WebTransportCloseInfo>& close_info) {
417   CHECK(session());
418   base::TimeDelta probe_timeout = base::Microseconds(
419       connection_->sent_packet_manager().GetPtoDelay().ToMicroseconds());
420   // Wait for at least three PTOs similar to what's used in
421   // https://www.rfc-editor.org/rfc/rfc9000.html#name-immediate-close
422   base::TimeDelta close_timeout = std::min(3 * probe_timeout, kMaxCloseTimeout);
423   close_timeout_timer_.Start(
424       FROM_HERE, close_timeout,
425       base::BindOnce(&DedicatedWebTransportHttp3Client::OnCloseTimeout,
426                      weak_factory_.GetWeakPtr()));
427   if (close_info.has_value()) {
428     session()->CloseSession(close_info->code, close_info->reason);
429   } else {
430     session()->CloseSession(0, "");
431   }
432 }
433 
session()434 quic::WebTransportSession* DedicatedWebTransportHttp3Client::session() {
435   if (web_transport_session_ == nullptr)
436     return nullptr;
437   return web_transport_session_;
438 }
439 
DoLoop(int rv)440 void DedicatedWebTransportHttp3Client::DoLoop(int rv) {
441   do {
442     ConnectState connect_state = next_connect_state_;
443     next_connect_state_ = CONNECT_STATE_NONE;
444     switch (connect_state) {
445       case CONNECT_STATE_INIT:
446         DCHECK_EQ(rv, OK);
447         rv = DoInit();
448         break;
449       case CONNECT_STATE_CHECK_PROXY:
450         DCHECK_EQ(rv, OK);
451         rv = DoCheckProxy();
452         break;
453       case CONNECT_STATE_CHECK_PROXY_COMPLETE:
454         rv = DoCheckProxyComplete(rv);
455         break;
456       case CONNECT_STATE_RESOLVE_HOST:
457         DCHECK_EQ(rv, OK);
458         rv = DoResolveHost();
459         break;
460       case CONNECT_STATE_RESOLVE_HOST_COMPLETE:
461         rv = DoResolveHostComplete(rv);
462         break;
463       case CONNECT_STATE_CONNECT:
464         DCHECK_EQ(rv, OK);
465         rv = DoConnect();
466         break;
467       case CONNECT_STATE_CONNECT_CONFIGURE:
468         rv = DoConnectConfigure(rv);
469         break;
470       case CONNECT_STATE_CONNECT_COMPLETE:
471         rv = DoConnectComplete();
472         break;
473       case CONNECT_STATE_SEND_REQUEST:
474         DCHECK_EQ(rv, OK);
475         rv = DoSendRequest();
476         break;
477       case CONNECT_STATE_CONFIRM_CONNECTION:
478         DCHECK_EQ(rv, OK);
479         rv = DoConfirmConnection();
480         break;
481       default:
482         NOTREACHED() << "Invalid state reached: " << connect_state;
483     }
484   } while (rv == OK && next_connect_state_ != CONNECT_STATE_NONE);
485 
486   if (rv == OK || rv == ERR_IO_PENDING)
487     return;
488   SetErrorIfNecessary(rv);
489   TransitionToState(WebTransportState::FAILED);
490 }
491 
DoInit()492 int DedicatedWebTransportHttp3Client::DoInit() {
493   if (!url_.is_valid())
494     return ERR_INVALID_URL;
495   if (url_.scheme_piece() != url::kHttpsScheme)
496     return ERR_DISALLOWED_URL_SCHEME;
497 
498   if (!IsPortAllowedForScheme(url_.EffectiveIntPort(), url_.scheme_piece()))
499     return ERR_UNSAFE_PORT;
500 
501   // TODO(vasilvv): check if QUIC is disabled by policy.
502 
503   // Ensure that RFC 9000 is always supported.
504   supported_versions_ = quic::ParsedQuicVersionVector{
505       quic::ParsedQuicVersion::RFCv1(),
506   };
507   // Add other supported versions if available.
508   for (quic::ParsedQuicVersion& version :
509        quic_context_->params()->supported_versions) {
510     if (base::Contains(supported_versions_, version))
511       continue;  // Skip as we've already added it above.
512     supported_versions_.push_back(version);
513   }
514   if (supported_versions_.empty()) {
515     DLOG(ERROR) << "Attempted using WebTransport with no compatible QUIC "
516                    "versions available";
517     return ERR_NOT_IMPLEMENTED;
518   }
519 
520   next_connect_state_ = CONNECT_STATE_CHECK_PROXY;
521   return OK;
522 }
523 
DoCheckProxy()524 int DedicatedWebTransportHttp3Client::DoCheckProxy() {
525   next_connect_state_ = CONNECT_STATE_CHECK_PROXY_COMPLETE;
526   return context_->proxy_resolution_service()->ResolveProxy(
527       url_, /* method */ "CONNECT", anonymization_key_, &proxy_info_,
528       base::BindOnce(&DedicatedWebTransportHttp3Client::DoLoop,
529                      base::Unretained(this)),
530       &proxy_resolution_request_, net_log_);
531 }
532 
DoCheckProxyComplete(int rv)533 int DedicatedWebTransportHttp3Client::DoCheckProxyComplete(int rv) {
534   if (rv != OK)
535     return rv;
536 
537   // If a proxy is configured, we fail the connection.
538   if (!proxy_info_.is_direct())
539     return ERR_TUNNEL_CONNECTION_FAILED;
540 
541   next_connect_state_ = CONNECT_STATE_RESOLVE_HOST;
542   return OK;
543 }
544 
DoResolveHost()545 int DedicatedWebTransportHttp3Client::DoResolveHost() {
546   next_connect_state_ = CONNECT_STATE_RESOLVE_HOST_COMPLETE;
547   HostResolver::ResolveHostParameters parameters;
548   resolve_host_request_ = context_->host_resolver()->CreateRequest(
549       url::SchemeHostPort(url_), anonymization_key_, net_log_, std::nullopt);
550   return resolve_host_request_->Start(base::BindOnce(
551       &DedicatedWebTransportHttp3Client::DoLoop, base::Unretained(this)));
552 }
553 
DoResolveHostComplete(int rv)554 int DedicatedWebTransportHttp3Client::DoResolveHostComplete(int rv) {
555   if (rv != OK)
556     return rv;
557 
558   DCHECK(resolve_host_request_->GetAddressResults());
559   next_connect_state_ = CONNECT_STATE_CONNECT;
560   return OK;
561 }
562 
DoConnect()563 int DedicatedWebTransportHttp3Client::DoConnect() {
564   next_connect_state_ = CONNECT_STATE_CONNECT_CONFIGURE;
565 
566   // TODO(vasilvv): consider unifying parts of this code with QuicSocketFactory
567   // (which currently has a lot of code specific to QuicChromiumClientSession).
568   socket_ = context_->GetNetworkSessionContext()
569                 ->client_socket_factory->CreateDatagramClientSocket(
570                     DatagramSocket::DEFAULT_BIND, net_log_.net_log(),
571                     net_log_.source());
572   if (quic_context_->params()->enable_socket_recv_optimization)
573     socket_->EnableRecvOptimization();
574   socket_->UseNonBlockingIO();
575 
576   IPEndPoint server_address =
577       *resolve_host_request_->GetAddressResults()->begin();
578   return socket_->ConnectAsync(
579       server_address, base::BindOnce(&DedicatedWebTransportHttp3Client::DoLoop,
580                                      base::Unretained(this)));
581 }
582 
CreateConnection()583 void DedicatedWebTransportHttp3Client::CreateConnection() {
584   // Delete the objects in the same order they would be normally deleted by the
585   // destructor.
586   session_ = nullptr;
587   packet_reader_ = nullptr;
588 
589   IPEndPoint server_address =
590       *resolve_host_request_->GetAddressResults()->begin();
591   quic::QuicConnectionId connection_id =
592       quic::QuicUtils::CreateRandomConnectionId(
593           quic_context_->random_generator());
594   auto connection = std::make_unique<quic::QuicConnection>(
595       connection_id, quic::QuicSocketAddress(),
596       ToQuicSocketAddress(server_address), quic_context_->helper(),
597       alarm_factory_.get(),
598       new QuicChromiumPacketWriter(socket_.get(), task_runner_),
599       /* owns_writer */ true, quic::Perspective::IS_CLIENT, supported_versions_,
600       connection_id_generator_);
601   connection_ = connection.get();
602   connection->SetMaxPacketLength(quic_context_->params()->max_packet_length);
603 
604   session_ = std::make_unique<DedicatedWebTransportHttp3ClientSession>(
605       InitializeQuicConfig(*quic_context_->params()), supported_versions_,
606       connection.release(),
607       quic::QuicServerId(url_.host(), url_.EffectiveIntPort()), &crypto_config_,
608       this);
609   if (!original_supported_versions_.empty()) {
610     session_->set_client_original_supported_versions(
611         original_supported_versions_);
612   }
613 
614   packet_reader_ = std::make_unique<QuicChromiumPacketReader>(
615       std::move(socket_), quic_context_->clock(), this,
616       kQuicYieldAfterPacketsRead,
617       quic::QuicTime::Delta::FromMilliseconds(
618           kQuicYieldAfterDurationMilliseconds),
619       quic_context_->params()->report_ecn, net_log_);
620 
621   event_logger_ = std::make_unique<QuicEventLogger>(session_.get(), net_log_);
622   connection_->set_debug_visitor(event_logger_.get());
623   connection_->set_creator_debug_delegate(event_logger_.get());
624   AdjustSendAlgorithm(*connection_);
625 
626   session_->Initialize();
627   packet_reader_->StartReading();
628 
629   DCHECK(session_->WillNegotiateWebTransport());
630   session_->CryptoConnect();
631 }
632 
DoConnectComplete()633 int DedicatedWebTransportHttp3Client::DoConnectComplete() {
634   if (!connection_->connected()) {
635     return ERR_QUIC_PROTOCOL_ERROR;
636   }
637   // Fail the connection if the received SETTINGS do not support WebTransport.
638   if (!session_->SupportsWebTransport()) {
639     return ERR_METHOD_NOT_SUPPORTED;
640   }
641   safe_to_report_error_details_ = true;
642   next_connect_state_ = CONNECT_STATE_SEND_REQUEST;
643   return OK;
644 }
645 
DoConnectConfigure(int rv)646 int DedicatedWebTransportHttp3Client::DoConnectConfigure(int rv) {
647   if (rv != OK) {
648     return rv;
649   }
650 
651   rv = socket_->SetReceiveBufferSize(kQuicSocketReceiveBufferSize);
652   if (rv != OK) {
653     return rv;
654   }
655 
656   rv = socket_->SetDoNotFragment();
657   if (rv == ERR_NOT_IMPLEMENTED) {
658     rv = OK;
659   }
660   if (rv != OK) {
661     return rv;
662   }
663 
664   rv = socket_->SetSendBufferSize(quic::kMaxOutgoingPacketSize * 20);
665   if (rv != OK) {
666     return rv;
667   }
668 
669   next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
670   CreateConnection();
671   return ERR_IO_PENDING;
672 }
673 
OnSettingsReceived()674 void DedicatedWebTransportHttp3Client::OnSettingsReceived() {
675   DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
676   // Wait until the SETTINGS parser is finished, and then send the request.
677   task_runner_->PostTask(
678       FROM_HERE, base::BindOnce(&DedicatedWebTransportHttp3Client::DoLoop,
679                                 weak_factory_.GetWeakPtr(), OK));
680 }
681 
OnHeadersComplete(const quiche::HttpHeaderBlock & headers)682 void DedicatedWebTransportHttp3Client::OnHeadersComplete(
683     const quiche::HttpHeaderBlock& headers) {
684   http_response_info_ = std::make_unique<HttpResponseInfo>();
685   const int rv = SpdyHeadersToHttpResponse(headers, http_response_info_.get());
686   if (rv != OK) {
687     SetErrorIfNecessary(ERR_QUIC_PROTOCOL_ERROR);
688     TransitionToState(WebTransportState::FAILED);
689     return;
690   }
691   // TODO(vasilvv): add support for this header in downstream tests and remove
692   // this.
693   DCHECK(http_response_info_->headers);
694   http_response_info_->headers->RemoveHeader("sec-webtransport-http3-draft");
695 
696   DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONFIRM_CONNECTION);
697   DoLoop(OK);
698 }
699 
700 void DedicatedWebTransportHttp3Client::
OnConnectStreamWriteSideInDataRecvdState()701     OnConnectStreamWriteSideInDataRecvdState() {
702   task_runner_->PostTask(
703       FROM_HERE,
704       base::BindOnce(&DedicatedWebTransportHttp3Client::TransitionToState,
705                      weak_factory_.GetWeakPtr(), WebTransportState::CLOSED));
706 }
707 
OnConnectStreamAborted()708 void DedicatedWebTransportHttp3Client::OnConnectStreamAborted() {
709   SetErrorIfNecessary(session_ready_ ? ERR_FAILED : ERR_METHOD_NOT_SUPPORTED);
710   TransitionToState(WebTransportState::FAILED);
711 }
712 
OnConnectStreamDeleted()713 void DedicatedWebTransportHttp3Client::OnConnectStreamDeleted() {
714   // `web_transport_session_` is owned by ConnectStream. Clear so that it
715   // doesn't get dangling.
716   web_transport_session_ = nullptr;
717 }
718 
OnCloseTimeout()719 void DedicatedWebTransportHttp3Client::OnCloseTimeout() {
720   SetErrorIfNecessary(ERR_TIMED_OUT);
721   TransitionToState(WebTransportState::FAILED);
722 }
723 
DoSendRequest()724 int DedicatedWebTransportHttp3Client::DoSendRequest() {
725   quic::QuicConnection::ScopedPacketFlusher scope(connection_);
726 
727   DedicatedWebTransportHttp3ClientSession* session =
728       static_cast<DedicatedWebTransportHttp3ClientSession*>(session_.get());
729   ConnectStream* stream = session->CreateConnectStream();
730   if (stream == nullptr) {
731     return ERR_QUIC_PROTOCOL_ERROR;
732   }
733 
734   quiche::HttpHeaderBlock headers;
735   DCHECK_EQ(url_.scheme(), url::kHttpsScheme);
736   headers[":scheme"] = url_.scheme();
737   headers[":method"] = "CONNECT";
738   headers[":authority"] = GetHostAndOptionalPort(url_);
739   headers[":path"] = url_.PathForRequest();
740   headers[":protocol"] = "webtransport";
741   headers["sec-webtransport-http3-draft02"] = "1";
742   headers["origin"] = origin_.Serialize();
743   stream->WriteHeaders(std::move(headers), /*fin=*/false, nullptr);
744 
745   web_transport_session_ = stream->web_transport();
746   if (web_transport_session_ == nullptr) {
747     return ERR_METHOD_NOT_SUPPORTED;
748   }
749   stream->web_transport()->SetVisitor(
750       std::make_unique<WebTransportVisitorProxy>(this));
751 
752   next_connect_state_ = CONNECT_STATE_CONFIRM_CONNECTION;
753   return ERR_IO_PENDING;
754 }
755 
DoConfirmConnection()756 int DedicatedWebTransportHttp3Client::DoConfirmConnection() {
757   if (!session_ready_) {
758     return ERR_METHOD_NOT_SUPPORTED;
759   }
760 
761   TransitionToState(WebTransportState::CONNECTED);
762   return OK;
763 }
764 
TransitionToState(WebTransportState next_state)765 void DedicatedWebTransportHttp3Client::TransitionToState(
766     WebTransportState next_state) {
767   // Ignore all state transition requests if we have reached the terminal
768   // state.
769   if (IsTerminalState(state_)) {
770     DCHECK(IsTerminalState(next_state))
771         << "from: " << state_ << ", to: " << next_state;
772     return;
773   }
774 
775   DCHECK_NE(state_, next_state);
776   const WebTransportState last_state = state_;
777   state_ = next_state;
778   RecordNetLogQuicSessionClientStateChanged(net_log_, last_state, next_state,
779                                             error_);
780   switch (next_state) {
781     case WebTransportState::CONNECTING:
782       DCHECK_EQ(last_state, WebTransportState::NEW);
783       break;
784 
785     case WebTransportState::CONNECTED:
786       DCHECK_EQ(last_state, WebTransportState::CONNECTING);
787       visitor_->OnConnected(http_response_info_->headers);
788       break;
789 
790     case WebTransportState::CLOSED:
791       DCHECK_EQ(last_state, WebTransportState::CONNECTED);
792       connection_->CloseConnection(quic::QUIC_NO_ERROR,
793                                    "WebTransport client terminated",
794                                    quic::ConnectionCloseBehavior::SILENT_CLOSE);
795       visitor_->OnClosed(close_info_);
796       break;
797 
798     case WebTransportState::FAILED:
799       DCHECK(error_.has_value());
800       if (last_state == WebTransportState::CONNECTING) {
801         visitor_->OnConnectionFailed(*error_);
802         break;
803       }
804       DCHECK_EQ(last_state, WebTransportState::CONNECTED);
805       // Ensure the connection is properly closed before deleting it.
806       connection_->CloseConnection(
807           quic::QUIC_INTERNAL_ERROR,
808           "WebTransportState::ERROR reached but the connection still open",
809           quic::ConnectionCloseBehavior::SILENT_CLOSE);
810       visitor_->OnError(*error_);
811       break;
812 
813     default:
814       NOTREACHED() << "Invalid state reached: " << next_state;
815   }
816 }
817 
SetErrorIfNecessary(int error)818 void DedicatedWebTransportHttp3Client::SetErrorIfNecessary(int error) {
819   SetErrorIfNecessary(error, quic::QUIC_NO_ERROR, ErrorToString(error));
820 }
821 
SetErrorIfNecessary(int error,quic::QuicErrorCode quic_error,std::string_view details)822 void DedicatedWebTransportHttp3Client::SetErrorIfNecessary(
823     int error,
824     quic::QuicErrorCode quic_error,
825     std::string_view details) {
826   if (!error_) {
827     error_ = WebTransportError(error, quic_error, details,
828                                safe_to_report_error_details_);
829   }
830 }
831 
OnSessionReady()832 void DedicatedWebTransportHttp3Client::OnSessionReady() {
833   CHECK(session_->SupportsWebTransport());
834 
835   session_ready_ = true;
836 
837   RecordNegotiatedWebTransportVersion(
838       *session_->SupportedWebTransportVersion());
839   RecordNegotiatedHttpDatagramSupport(session_->http_datagram_support());
840   net_log_.AddEvent(NetLogEventType::QUIC_SESSION_WEBTRANSPORT_SESSION_READY,
841                     [&] {
842                       base::Value::Dict dict;
843                       dict.Set("http_datagram_version",
844                                quic::HttpDatagramSupportToString(
845                                    session_->http_datagram_support()));
846                       dict.Set("webtransport_http3_version",
847                                WebTransportHttp3VersionString(
848                                    *session_->SupportedWebTransportVersion()));
849                       return dict;
850                     });
851 }
852 
OnSessionClosed(quic::WebTransportSessionError error_code,const std::string & error_message)853 void DedicatedWebTransportHttp3Client::OnSessionClosed(
854     quic::WebTransportSessionError error_code,
855     const std::string& error_message) {
856   close_info_ = WebTransportCloseInfo(error_code, error_message);
857   task_runner_->PostTask(
858       FROM_HERE,
859       base::BindOnce(&DedicatedWebTransportHttp3Client::TransitionToState,
860                      weak_factory_.GetWeakPtr(), WebTransportState::CLOSED));
861 }
862 
863 void DedicatedWebTransportHttp3Client::
OnIncomingBidirectionalStreamAvailable()864     OnIncomingBidirectionalStreamAvailable() {
865   visitor_->OnIncomingBidirectionalStreamAvailable();
866 }
867 
868 void DedicatedWebTransportHttp3Client::
OnIncomingUnidirectionalStreamAvailable()869     OnIncomingUnidirectionalStreamAvailable() {
870   visitor_->OnIncomingUnidirectionalStreamAvailable();
871 }
872 
OnDatagramReceived(std::string_view datagram)873 void DedicatedWebTransportHttp3Client::OnDatagramReceived(
874     std::string_view datagram) {
875   visitor_->OnDatagramReceived(datagram);
876 }
877 
878 void DedicatedWebTransportHttp3Client::
OnCanCreateNewOutgoingBidirectionalStream()879     OnCanCreateNewOutgoingBidirectionalStream() {
880   visitor_->OnCanCreateNewOutgoingBidirectionalStream();
881 }
882 
883 void DedicatedWebTransportHttp3Client::
OnCanCreateNewOutgoingUnidirectionalStream()884     OnCanCreateNewOutgoingUnidirectionalStream() {
885   visitor_->OnCanCreateNewOutgoingUnidirectionalStream();
886 }
887 
OnReadError(int result,const DatagramClientSocket * socket)888 bool DedicatedWebTransportHttp3Client::OnReadError(
889     int result,
890     const DatagramClientSocket* socket) {
891   SetErrorIfNecessary(result);
892   connection_->CloseConnection(quic::QUIC_PACKET_READ_ERROR,
893                                ErrorToString(result),
894                                quic::ConnectionCloseBehavior::SILENT_CLOSE);
895   return false;
896 }
897 
OnPacket(const quic::QuicReceivedPacket & packet,const quic::QuicSocketAddress & local_address,const quic::QuicSocketAddress & peer_address)898 bool DedicatedWebTransportHttp3Client::OnPacket(
899     const quic::QuicReceivedPacket& packet,
900     const quic::QuicSocketAddress& local_address,
901     const quic::QuicSocketAddress& peer_address) {
902   session_->ProcessUdpPacket(local_address, peer_address, packet);
903   return connection_->connected();
904 }
905 
HandleWriteError(int error_code,scoped_refptr<QuicChromiumPacketWriter::ReusableIOBuffer>)906 int DedicatedWebTransportHttp3Client::HandleWriteError(
907     int error_code,
908     scoped_refptr<QuicChromiumPacketWriter::ReusableIOBuffer> /*last_packet*/) {
909   return error_code;
910 }
911 
OnWriteError(int error_code)912 void DedicatedWebTransportHttp3Client::OnWriteError(int error_code) {
913   SetErrorIfNecessary(error_code);
914   connection_->OnWriteError(error_code);
915 }
916 
OnWriteUnblocked()917 void DedicatedWebTransportHttp3Client::OnWriteUnblocked() {
918   connection_->OnCanWrite();
919 }
920 
OnConnectionClosed(quic::QuicErrorCode error,const std::string & error_details,quic::ConnectionCloseSource source)921 void DedicatedWebTransportHttp3Client::OnConnectionClosed(
922     quic::QuicErrorCode error,
923     const std::string& error_details,
924     quic::ConnectionCloseSource source) {
925   // If the session is already in a terminal state due to reasons other than
926   // connection close, we should ignore it; otherwise we risk re-entering the
927   // connection teardown process.
928   if (IsTerminalState(state_)) {
929     return;
930   }
931 
932   if (!retried_with_new_version_ &&
933       session_->error() == quic::QUIC_INVALID_VERSION) {
934     retried_with_new_version_ = true;
935     DCHECK(original_supported_versions_.empty());
936     original_supported_versions_ = supported_versions_;
937     std::erase_if(
938         supported_versions_, [this](const quic::ParsedQuicVersion& version) {
939           return !base::Contains(
940               session_->connection()->server_supported_versions(), version);
941         });
942     if (!supported_versions_.empty()) {
943       // Since this is a callback from QuicConnection, we can't replace the
944       // connection object in this method; do it from the top of the event loop
945       // instead.
946       task_runner_->PostTask(
947           FROM_HERE,
948           base::BindOnce(&DedicatedWebTransportHttp3Client::CreateConnection,
949                          weak_factory_.GetWeakPtr()));
950       return;
951     }
952     // If there are no supported versions, treat this as a regular error.
953   }
954 
955   if (error == quic::QUIC_NO_ERROR) {
956     TransitionToState(WebTransportState::CLOSED);
957     return;
958   }
959 
960   SetErrorIfNecessary(ERR_QUIC_PROTOCOL_ERROR, error, error_details);
961 
962   if (state_ == WebTransportState::CONNECTING) {
963     DoLoop(OK);
964     return;
965   }
966 
967   TransitionToState(WebTransportState::FAILED);
968 }
969 
OnDatagramProcessed(std::optional<quic::MessageStatus> status)970 void DedicatedWebTransportHttp3Client::OnDatagramProcessed(
971     std::optional<quic::MessageStatus> status) {
972   visitor_->OnDatagramProcessed(status);
973 }
974 
975 }  // namespace net
976