xref: /aosp_15_r20/external/cronet/net/quic/dedicated_web_transport_http3_client.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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       return;
313   }
314   base::UmaHistogramEnumeration(
315       "Net.WebTransport.NegotiatedHttpDatagramVersion", negotiated);
316 }
317 
WebTransportHttp3VersionString(quic::WebTransportHttp3Version version)318 const char* WebTransportHttp3VersionString(
319     quic::WebTransportHttp3Version version) {
320   switch (version) {
321     case quic::WebTransportHttp3Version::kDraft02:
322       return "draft-02";
323     case quic::WebTransportHttp3Version::kDraft07:
324       return "draft-07";
325   }
326 }
327 
328 enum class NegotiatedWebTransportVersion {
329   kDraft02 = 0,
330   kDraft07 = 1,
331   kMaxValue = kDraft07,
332 };
333 
RecordNegotiatedWebTransportVersion(quic::WebTransportHttp3Version version)334 void RecordNegotiatedWebTransportVersion(
335     quic::WebTransportHttp3Version version) {
336   NegotiatedWebTransportVersion negotiated;
337   switch (version) {
338     case quic::WebTransportHttp3Version::kDraft02:
339       negotiated = NegotiatedWebTransportVersion::kDraft02;
340       break;
341     case quic::WebTransportHttp3Version::kDraft07:
342       negotiated = NegotiatedWebTransportVersion::kDraft07;
343       break;
344   }
345   base::UmaHistogramEnumeration(
346       "Net.WebTransport.NegotiatedWebTransportVersion", negotiated);
347 }
348 
AdjustSendAlgorithm(quic::QuicConnection & connection)349 void AdjustSendAlgorithm(quic::QuicConnection& connection) {
350   if (!base::FeatureList::IsEnabled(kWebTransportCongestionControl)) {
351     return;
352   }
353   connection.sent_packet_manager().SetSendAlgorithm(
354       kWebTransportCongestionControlAlgorithm.Get());
355 }
356 
357 }  // namespace
358 
DedicatedWebTransportHttp3Client(const GURL & url,const url::Origin & origin,WebTransportClientVisitor * visitor,const NetworkAnonymizationKey & anonymization_key,URLRequestContext * context,const WebTransportParameters & parameters)359 DedicatedWebTransportHttp3Client::DedicatedWebTransportHttp3Client(
360     const GURL& url,
361     const url::Origin& origin,
362     WebTransportClientVisitor* visitor,
363     const NetworkAnonymizationKey& anonymization_key,
364     URLRequestContext* context,
365     const WebTransportParameters& parameters)
366     : url_(url),
367       origin_(origin),
368       anonymization_key_(anonymization_key),
369       context_(context),
370       visitor_(visitor),
371       quic_context_(context->quic_context()),
372       net_log_(NetLogWithSource::Make(context->net_log(),
373                                       NetLogSourceType::WEB_TRANSPORT_CLIENT)),
374       task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault().get()),
375       alarm_factory_(
376           std::make_unique<QuicChromiumAlarmFactory>(task_runner_,
377                                                      quic_context_->clock())),
378       // TODO(vasilvv): proof verifier should have proper error reporting
379       // (currently, all certificate verification errors result in "TLS
380       // handshake error" even when more detailed message is available).  This
381       // requires implementing ProofHandler::OnProofVerifyDetailsAvailable.
382       crypto_config_(
383           CreateProofVerifier(anonymization_key_, context, parameters),
384           /* session_cache */ nullptr) {
385   ConfigureQuicCryptoClientConfig(crypto_config_);
386   net_log_.BeginEvent(
387       NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_ALIVE, [&] {
388         base::Value::Dict dict;
389         dict.Set("url", url.possibly_invalid_spec());
390         dict.Set("network_anonymization_key",
391                  anonymization_key.ToDebugString());
392         return dict;
393       });
394 }
395 
~DedicatedWebTransportHttp3Client()396 DedicatedWebTransportHttp3Client::~DedicatedWebTransportHttp3Client() {
397   net_log_.EndEventWithNetErrorCode(
398       NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_ALIVE,
399       error_ ? error_->net_error : OK);
400   // |session_| owns this, so we need to make sure we release it before
401   // it gets dangling.
402   connection_ = nullptr;
403 }
404 
Connect()405 void DedicatedWebTransportHttp3Client::Connect() {
406   if (state_ != WebTransportState::NEW ||
407       next_connect_state_ != CONNECT_STATE_NONE) {
408     NOTREACHED();
409     return;
410   }
411 
412   TransitionToState(WebTransportState::CONNECTING);
413   next_connect_state_ = CONNECT_STATE_INIT;
414   DoLoop(OK);
415 }
416 
Close(const std::optional<WebTransportCloseInfo> & close_info)417 void DedicatedWebTransportHttp3Client::Close(
418     const std::optional<WebTransportCloseInfo>& close_info) {
419   CHECK(session());
420   base::TimeDelta probe_timeout = base::Microseconds(
421       connection_->sent_packet_manager().GetPtoDelay().ToMicroseconds());
422   // Wait for at least three PTOs similar to what's used in
423   // https://www.rfc-editor.org/rfc/rfc9000.html#name-immediate-close
424   base::TimeDelta close_timeout = std::min(3 * probe_timeout, kMaxCloseTimeout);
425   close_timeout_timer_.Start(
426       FROM_HERE, close_timeout,
427       base::BindOnce(&DedicatedWebTransportHttp3Client::OnCloseTimeout,
428                      weak_factory_.GetWeakPtr()));
429   if (close_info.has_value()) {
430     session()->CloseSession(close_info->code, close_info->reason);
431   } else {
432     session()->CloseSession(0, "");
433   }
434 }
435 
session()436 quic::WebTransportSession* DedicatedWebTransportHttp3Client::session() {
437   if (web_transport_session_ == nullptr)
438     return nullptr;
439   return web_transport_session_;
440 }
441 
DoLoop(int rv)442 void DedicatedWebTransportHttp3Client::DoLoop(int rv) {
443   do {
444     ConnectState connect_state = next_connect_state_;
445     next_connect_state_ = CONNECT_STATE_NONE;
446     switch (connect_state) {
447       case CONNECT_STATE_INIT:
448         DCHECK_EQ(rv, OK);
449         rv = DoInit();
450         break;
451       case CONNECT_STATE_CHECK_PROXY:
452         DCHECK_EQ(rv, OK);
453         rv = DoCheckProxy();
454         break;
455       case CONNECT_STATE_CHECK_PROXY_COMPLETE:
456         rv = DoCheckProxyComplete(rv);
457         break;
458       case CONNECT_STATE_RESOLVE_HOST:
459         DCHECK_EQ(rv, OK);
460         rv = DoResolveHost();
461         break;
462       case CONNECT_STATE_RESOLVE_HOST_COMPLETE:
463         rv = DoResolveHostComplete(rv);
464         break;
465       case CONNECT_STATE_CONNECT:
466         DCHECK_EQ(rv, OK);
467         rv = DoConnect();
468         break;
469       case CONNECT_STATE_CONNECT_CONFIGURE:
470         rv = DoConnectConfigure(rv);
471         break;
472       case CONNECT_STATE_CONNECT_COMPLETE:
473         rv = DoConnectComplete();
474         break;
475       case CONNECT_STATE_SEND_REQUEST:
476         DCHECK_EQ(rv, OK);
477         rv = DoSendRequest();
478         break;
479       case CONNECT_STATE_CONFIRM_CONNECTION:
480         DCHECK_EQ(rv, OK);
481         rv = DoConfirmConnection();
482         break;
483       default:
484         NOTREACHED() << "Invalid state reached: " << connect_state;
485         rv = ERR_FAILED;
486         break;
487     }
488   } while (rv == OK && next_connect_state_ != CONNECT_STATE_NONE);
489 
490   if (rv == OK || rv == ERR_IO_PENDING)
491     return;
492   SetErrorIfNecessary(rv);
493   TransitionToState(WebTransportState::FAILED);
494 }
495 
DoInit()496 int DedicatedWebTransportHttp3Client::DoInit() {
497   if (!url_.is_valid())
498     return ERR_INVALID_URL;
499   if (url_.scheme_piece() != url::kHttpsScheme)
500     return ERR_DISALLOWED_URL_SCHEME;
501 
502   if (!IsPortAllowedForScheme(url_.EffectiveIntPort(), url_.scheme_piece()))
503     return ERR_UNSAFE_PORT;
504 
505   // TODO(vasilvv): check if QUIC is disabled by policy.
506 
507   // Ensure that RFC 9000 is always supported.
508   supported_versions_ = quic::ParsedQuicVersionVector{
509       quic::ParsedQuicVersion::RFCv1(),
510   };
511   // Add other supported versions if available.
512   for (quic::ParsedQuicVersion& version :
513        quic_context_->params()->supported_versions) {
514     if (base::Contains(supported_versions_, version))
515       continue;  // Skip as we've already added it above.
516     supported_versions_.push_back(version);
517   }
518   if (supported_versions_.empty()) {
519     DLOG(ERROR) << "Attempted using WebTransport with no compatible QUIC "
520                    "versions available";
521     return ERR_NOT_IMPLEMENTED;
522   }
523 
524   next_connect_state_ = CONNECT_STATE_CHECK_PROXY;
525   return OK;
526 }
527 
DoCheckProxy()528 int DedicatedWebTransportHttp3Client::DoCheckProxy() {
529   next_connect_state_ = CONNECT_STATE_CHECK_PROXY_COMPLETE;
530   return context_->proxy_resolution_service()->ResolveProxy(
531       url_, /* method */ "CONNECT", anonymization_key_, &proxy_info_,
532       base::BindOnce(&DedicatedWebTransportHttp3Client::DoLoop,
533                      base::Unretained(this)),
534       &proxy_resolution_request_, net_log_);
535 }
536 
DoCheckProxyComplete(int rv)537 int DedicatedWebTransportHttp3Client::DoCheckProxyComplete(int rv) {
538   if (rv != OK)
539     return rv;
540 
541   // If a proxy is configured, we fail the connection.
542   if (!proxy_info_.is_direct())
543     return ERR_TUNNEL_CONNECTION_FAILED;
544 
545   next_connect_state_ = CONNECT_STATE_RESOLVE_HOST;
546   return OK;
547 }
548 
DoResolveHost()549 int DedicatedWebTransportHttp3Client::DoResolveHost() {
550   next_connect_state_ = CONNECT_STATE_RESOLVE_HOST_COMPLETE;
551   HostResolver::ResolveHostParameters parameters;
552   resolve_host_request_ = context_->host_resolver()->CreateRequest(
553       url::SchemeHostPort(url_), anonymization_key_, net_log_, std::nullopt);
554   return resolve_host_request_->Start(base::BindOnce(
555       &DedicatedWebTransportHttp3Client::DoLoop, base::Unretained(this)));
556 }
557 
DoResolveHostComplete(int rv)558 int DedicatedWebTransportHttp3Client::DoResolveHostComplete(int rv) {
559   if (rv != OK)
560     return rv;
561 
562   DCHECK(resolve_host_request_->GetAddressResults());
563   next_connect_state_ = CONNECT_STATE_CONNECT;
564   return OK;
565 }
566 
DoConnect()567 int DedicatedWebTransportHttp3Client::DoConnect() {
568   next_connect_state_ = CONNECT_STATE_CONNECT_CONFIGURE;
569 
570   // TODO(vasilvv): consider unifying parts of this code with QuicSocketFactory
571   // (which currently has a lot of code specific to QuicChromiumClientSession).
572   socket_ = context_->GetNetworkSessionContext()
573                 ->client_socket_factory->CreateDatagramClientSocket(
574                     DatagramSocket::DEFAULT_BIND, net_log_.net_log(),
575                     net_log_.source());
576   if (quic_context_->params()->enable_socket_recv_optimization)
577     socket_->EnableRecvOptimization();
578   socket_->UseNonBlockingIO();
579 
580   IPEndPoint server_address =
581       *resolve_host_request_->GetAddressResults()->begin();
582   return socket_->ConnectAsync(
583       server_address, base::BindOnce(&DedicatedWebTransportHttp3Client::DoLoop,
584                                      base::Unretained(this)));
585 }
586 
CreateConnection()587 void DedicatedWebTransportHttp3Client::CreateConnection() {
588   // Delete the objects in the same order they would be normally deleted by the
589   // destructor.
590   packet_reader_ = nullptr;
591   session_ = nullptr;
592 
593   IPEndPoint server_address =
594       *resolve_host_request_->GetAddressResults()->begin();
595   quic::QuicConnectionId connection_id =
596       quic::QuicUtils::CreateRandomConnectionId(
597           quic_context_->random_generator());
598   auto connection = std::make_unique<quic::QuicConnection>(
599       connection_id, quic::QuicSocketAddress(),
600       ToQuicSocketAddress(server_address), quic_context_->helper(),
601       alarm_factory_.get(),
602       new QuicChromiumPacketWriter(socket_.get(), task_runner_),
603       /* owns_writer */ true, quic::Perspective::IS_CLIENT, supported_versions_,
604       connection_id_generator_);
605   connection_ = connection.get();
606   connection->SetMaxPacketLength(quic_context_->params()->max_packet_length);
607 
608   session_ = std::make_unique<DedicatedWebTransportHttp3ClientSession>(
609       InitializeQuicConfig(*quic_context_->params()), supported_versions_,
610       connection.release(),
611       quic::QuicServerId(url_.host(), url_.EffectiveIntPort()), &crypto_config_,
612       this);
613   if (!original_supported_versions_.empty()) {
614     session_->set_client_original_supported_versions(
615         original_supported_versions_);
616   }
617 
618   packet_reader_ = std::make_unique<QuicChromiumPacketReader>(
619       std::move(socket_), quic_context_->clock(), this,
620       kQuicYieldAfterPacketsRead,
621       quic::QuicTime::Delta::FromMilliseconds(
622           kQuicYieldAfterDurationMilliseconds),
623       net_log_);
624 
625   event_logger_ = std::make_unique<QuicEventLogger>(session_.get(), net_log_);
626   connection_->set_debug_visitor(event_logger_.get());
627   connection_->set_creator_debug_delegate(event_logger_.get());
628   AdjustSendAlgorithm(*connection_);
629 
630   session_->Initialize();
631   packet_reader_->StartReading();
632 
633   DCHECK(session_->WillNegotiateWebTransport());
634   session_->CryptoConnect();
635 }
636 
DoConnectComplete()637 int DedicatedWebTransportHttp3Client::DoConnectComplete() {
638   if (!connection_->connected()) {
639     return ERR_QUIC_PROTOCOL_ERROR;
640   }
641   // Fail the connection if the received SETTINGS do not support WebTransport.
642   if (!session_->SupportsWebTransport()) {
643     return ERR_METHOD_NOT_SUPPORTED;
644   }
645   safe_to_report_error_details_ = true;
646   next_connect_state_ = CONNECT_STATE_SEND_REQUEST;
647   return OK;
648 }
649 
DoConnectConfigure(int rv)650 int DedicatedWebTransportHttp3Client::DoConnectConfigure(int rv) {
651   if (rv != OK) {
652     return rv;
653   }
654 
655   rv = socket_->SetReceiveBufferSize(kQuicSocketReceiveBufferSize);
656   if (rv != OK) {
657     return rv;
658   }
659 
660   rv = socket_->SetDoNotFragment();
661   if (rv == ERR_NOT_IMPLEMENTED) {
662     rv = OK;
663   }
664   if (rv != OK) {
665     return rv;
666   }
667 
668   rv = socket_->SetSendBufferSize(quic::kMaxOutgoingPacketSize * 20);
669   if (rv != OK) {
670     return rv;
671   }
672 
673   next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
674   CreateConnection();
675   return ERR_IO_PENDING;
676 }
677 
OnSettingsReceived()678 void DedicatedWebTransportHttp3Client::OnSettingsReceived() {
679   DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
680   // Wait until the SETTINGS parser is finished, and then send the request.
681   task_runner_->PostTask(
682       FROM_HERE, base::BindOnce(&DedicatedWebTransportHttp3Client::DoLoop,
683                                 weak_factory_.GetWeakPtr(), OK));
684 }
685 
OnHeadersComplete(const spdy::Http2HeaderBlock & headers)686 void DedicatedWebTransportHttp3Client::OnHeadersComplete(
687     const spdy::Http2HeaderBlock& headers) {
688   http_response_info_ = std::make_unique<HttpResponseInfo>();
689   const int rv = SpdyHeadersToHttpResponse(headers, http_response_info_.get());
690   if (rv != OK) {
691     SetErrorIfNecessary(ERR_QUIC_PROTOCOL_ERROR);
692     TransitionToState(WebTransportState::FAILED);
693     return;
694   }
695   // TODO(vasilvv): add support for this header in downstream tests and remove
696   // this.
697   DCHECK(http_response_info_->headers);
698   http_response_info_->headers->RemoveHeader("sec-webtransport-http3-draft");
699 
700   DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONFIRM_CONNECTION);
701   DoLoop(OK);
702 }
703 
704 void DedicatedWebTransportHttp3Client::
OnConnectStreamWriteSideInDataRecvdState()705     OnConnectStreamWriteSideInDataRecvdState() {
706   task_runner_->PostTask(
707       FROM_HERE,
708       base::BindOnce(&DedicatedWebTransportHttp3Client::TransitionToState,
709                      weak_factory_.GetWeakPtr(), WebTransportState::CLOSED));
710 }
711 
OnConnectStreamAborted()712 void DedicatedWebTransportHttp3Client::OnConnectStreamAborted() {
713   SetErrorIfNecessary(session_ready_ ? ERR_FAILED : ERR_METHOD_NOT_SUPPORTED);
714   TransitionToState(WebTransportState::FAILED);
715 }
716 
OnConnectStreamDeleted()717 void DedicatedWebTransportHttp3Client::OnConnectStreamDeleted() {
718   // `web_transport_session_` is owned by ConnectStream. Clear so that it
719   // doesn't get dangling.
720   web_transport_session_ = nullptr;
721 }
722 
OnCloseTimeout()723 void DedicatedWebTransportHttp3Client::OnCloseTimeout() {
724   SetErrorIfNecessary(ERR_TIMED_OUT);
725   TransitionToState(WebTransportState::FAILED);
726 }
727 
DoSendRequest()728 int DedicatedWebTransportHttp3Client::DoSendRequest() {
729   quic::QuicConnection::ScopedPacketFlusher scope(connection_);
730 
731   DedicatedWebTransportHttp3ClientSession* session =
732       static_cast<DedicatedWebTransportHttp3ClientSession*>(session_.get());
733   ConnectStream* stream = session->CreateConnectStream();
734   if (stream == nullptr) {
735     return ERR_QUIC_PROTOCOL_ERROR;
736   }
737 
738   spdy::Http2HeaderBlock headers;
739   DCHECK_EQ(url_.scheme(), url::kHttpsScheme);
740   headers[":scheme"] = url_.scheme();
741   headers[":method"] = "CONNECT";
742   headers[":authority"] = GetHostAndOptionalPort(url_);
743   headers[":path"] = url_.PathForRequest();
744   headers[":protocol"] = "webtransport";
745   headers["sec-webtransport-http3-draft02"] = "1";
746   headers["origin"] = origin_.Serialize();
747   stream->WriteHeaders(std::move(headers), /*fin=*/false, nullptr);
748 
749   web_transport_session_ = stream->web_transport();
750   if (web_transport_session_ == nullptr) {
751     return ERR_METHOD_NOT_SUPPORTED;
752   }
753   stream->web_transport()->SetVisitor(
754       std::make_unique<WebTransportVisitorProxy>(this));
755 
756   next_connect_state_ = CONNECT_STATE_CONFIRM_CONNECTION;
757   return ERR_IO_PENDING;
758 }
759 
DoConfirmConnection()760 int DedicatedWebTransportHttp3Client::DoConfirmConnection() {
761   if (!session_ready_) {
762     return ERR_METHOD_NOT_SUPPORTED;
763   }
764 
765   TransitionToState(WebTransportState::CONNECTED);
766   return OK;
767 }
768 
TransitionToState(WebTransportState next_state)769 void DedicatedWebTransportHttp3Client::TransitionToState(
770     WebTransportState next_state) {
771   // Ignore all state transition requests if we have reached the terminal
772   // state.
773   if (IsTerminalState(state_)) {
774     DCHECK(IsTerminalState(next_state))
775         << "from: " << state_ << ", to: " << next_state;
776     return;
777   }
778 
779   DCHECK_NE(state_, next_state);
780   const WebTransportState last_state = state_;
781   state_ = next_state;
782   RecordNetLogQuicSessionClientStateChanged(net_log_, last_state, next_state,
783                                             error_);
784   switch (next_state) {
785     case WebTransportState::CONNECTING:
786       DCHECK_EQ(last_state, WebTransportState::NEW);
787       break;
788 
789     case WebTransportState::CONNECTED:
790       DCHECK_EQ(last_state, WebTransportState::CONNECTING);
791       visitor_->OnConnected(http_response_info_->headers);
792       break;
793 
794     case WebTransportState::CLOSED:
795       DCHECK_EQ(last_state, WebTransportState::CONNECTED);
796       connection_->CloseConnection(quic::QUIC_NO_ERROR,
797                                    "WebTransport client terminated",
798                                    quic::ConnectionCloseBehavior::SILENT_CLOSE);
799       visitor_->OnClosed(close_info_);
800       break;
801 
802     case WebTransportState::FAILED:
803       DCHECK(error_.has_value());
804       if (last_state == WebTransportState::CONNECTING) {
805         visitor_->OnConnectionFailed(*error_);
806         break;
807       }
808       DCHECK_EQ(last_state, WebTransportState::CONNECTED);
809       // Ensure the connection is properly closed before deleting it.
810       connection_->CloseConnection(
811           quic::QUIC_INTERNAL_ERROR,
812           "WebTransportState::ERROR reached but the connection still open",
813           quic::ConnectionCloseBehavior::SILENT_CLOSE);
814       visitor_->OnError(*error_);
815       break;
816 
817     default:
818       NOTREACHED() << "Invalid state reached: " << next_state;
819       break;
820   }
821 }
822 
SetErrorIfNecessary(int error)823 void DedicatedWebTransportHttp3Client::SetErrorIfNecessary(int error) {
824   SetErrorIfNecessary(error, quic::QUIC_NO_ERROR, ErrorToString(error));
825 }
826 
SetErrorIfNecessary(int error,quic::QuicErrorCode quic_error,std::string_view details)827 void DedicatedWebTransportHttp3Client::SetErrorIfNecessary(
828     int error,
829     quic::QuicErrorCode quic_error,
830     std::string_view details) {
831   if (!error_) {
832     error_ = WebTransportError(error, quic_error, details,
833                                safe_to_report_error_details_);
834   }
835 }
836 
OnSessionReady()837 void DedicatedWebTransportHttp3Client::OnSessionReady() {
838   CHECK(session_->SupportsWebTransport());
839 
840   session_ready_ = true;
841 
842   RecordNegotiatedWebTransportVersion(
843       *session_->SupportedWebTransportVersion());
844   RecordNegotiatedHttpDatagramSupport(session_->http_datagram_support());
845   net_log_.AddEvent(NetLogEventType::QUIC_SESSION_WEBTRANSPORT_SESSION_READY,
846                     [&] {
847                       base::Value::Dict dict;
848                       dict.Set("http_datagram_version",
849                                quic::HttpDatagramSupportToString(
850                                    session_->http_datagram_support()));
851                       dict.Set("webtransport_http3_version",
852                                WebTransportHttp3VersionString(
853                                    *session_->SupportedWebTransportVersion()));
854                       return dict;
855                     });
856 }
857 
OnSessionClosed(quic::WebTransportSessionError error_code,const std::string & error_message)858 void DedicatedWebTransportHttp3Client::OnSessionClosed(
859     quic::WebTransportSessionError error_code,
860     const std::string& error_message) {
861   close_info_ = WebTransportCloseInfo(error_code, error_message);
862   task_runner_->PostTask(
863       FROM_HERE,
864       base::BindOnce(&DedicatedWebTransportHttp3Client::TransitionToState,
865                      weak_factory_.GetWeakPtr(), WebTransportState::CLOSED));
866 }
867 
868 void DedicatedWebTransportHttp3Client::
OnIncomingBidirectionalStreamAvailable()869     OnIncomingBidirectionalStreamAvailable() {
870   visitor_->OnIncomingBidirectionalStreamAvailable();
871 }
872 
873 void DedicatedWebTransportHttp3Client::
OnIncomingUnidirectionalStreamAvailable()874     OnIncomingUnidirectionalStreamAvailable() {
875   visitor_->OnIncomingUnidirectionalStreamAvailable();
876 }
877 
OnDatagramReceived(std::string_view datagram)878 void DedicatedWebTransportHttp3Client::OnDatagramReceived(
879     std::string_view datagram) {
880   visitor_->OnDatagramReceived(datagram);
881 }
882 
883 void DedicatedWebTransportHttp3Client::
OnCanCreateNewOutgoingBidirectionalStream()884     OnCanCreateNewOutgoingBidirectionalStream() {
885   visitor_->OnCanCreateNewOutgoingBidirectionalStream();
886 }
887 
888 void DedicatedWebTransportHttp3Client::
OnCanCreateNewOutgoingUnidirectionalStream()889     OnCanCreateNewOutgoingUnidirectionalStream() {
890   visitor_->OnCanCreateNewOutgoingUnidirectionalStream();
891 }
892 
OnReadError(int result,const DatagramClientSocket * socket)893 bool DedicatedWebTransportHttp3Client::OnReadError(
894     int result,
895     const DatagramClientSocket* socket) {
896   SetErrorIfNecessary(result);
897   connection_->CloseConnection(quic::QUIC_PACKET_READ_ERROR,
898                                ErrorToString(result),
899                                quic::ConnectionCloseBehavior::SILENT_CLOSE);
900   return false;
901 }
902 
OnPacket(const quic::QuicReceivedPacket & packet,const quic::QuicSocketAddress & local_address,const quic::QuicSocketAddress & peer_address)903 bool DedicatedWebTransportHttp3Client::OnPacket(
904     const quic::QuicReceivedPacket& packet,
905     const quic::QuicSocketAddress& local_address,
906     const quic::QuicSocketAddress& peer_address) {
907   session_->ProcessUdpPacket(local_address, peer_address, packet);
908   return connection_->connected();
909 }
910 
HandleWriteError(int error_code,scoped_refptr<QuicChromiumPacketWriter::ReusableIOBuffer>)911 int DedicatedWebTransportHttp3Client::HandleWriteError(
912     int error_code,
913     scoped_refptr<QuicChromiumPacketWriter::ReusableIOBuffer> /*last_packet*/) {
914   return error_code;
915 }
916 
OnWriteError(int error_code)917 void DedicatedWebTransportHttp3Client::OnWriteError(int error_code) {
918   SetErrorIfNecessary(error_code);
919   connection_->OnWriteError(error_code);
920 }
921 
OnWriteUnblocked()922 void DedicatedWebTransportHttp3Client::OnWriteUnblocked() {
923   connection_->OnCanWrite();
924 }
925 
OnConnectionClosed(quic::QuicErrorCode error,const std::string & error_details,quic::ConnectionCloseSource source)926 void DedicatedWebTransportHttp3Client::OnConnectionClosed(
927     quic::QuicErrorCode error,
928     const std::string& error_details,
929     quic::ConnectionCloseSource source) {
930   // If the session is already in a terminal state due to reasons other than
931   // connection close, we should ignore it; otherwise we risk re-entering the
932   // connection teardown process.
933   if (IsTerminalState(state_)) {
934     return;
935   }
936 
937   if (!retried_with_new_version_ &&
938       session_->error() == quic::QUIC_INVALID_VERSION) {
939     retried_with_new_version_ = true;
940     DCHECK(original_supported_versions_.empty());
941     original_supported_versions_ = supported_versions_;
942     std::erase_if(
943         supported_versions_, [this](const quic::ParsedQuicVersion& version) {
944           return !base::Contains(
945               session_->connection()->server_supported_versions(), version);
946         });
947     if (!supported_versions_.empty()) {
948       // Since this is a callback from QuicConnection, we can't replace the
949       // connection object in this method; do it from the top of the event loop
950       // instead.
951       task_runner_->PostTask(
952           FROM_HERE,
953           base::BindOnce(&DedicatedWebTransportHttp3Client::CreateConnection,
954                          weak_factory_.GetWeakPtr()));
955       return;
956     }
957     // If there are no supported versions, treat this as a regular error.
958   }
959 
960   if (error == quic::QUIC_NO_ERROR) {
961     TransitionToState(WebTransportState::CLOSED);
962     return;
963   }
964 
965   SetErrorIfNecessary(ERR_QUIC_PROTOCOL_ERROR, error, error_details);
966 
967   if (state_ == WebTransportState::CONNECTING) {
968     DoLoop(OK);
969     return;
970   }
971 
972   TransitionToState(WebTransportState::FAILED);
973 }
974 
OnDatagramProcessed(std::optional<quic::MessageStatus> status)975 void DedicatedWebTransportHttp3Client::OnDatagramProcessed(
976     std::optional<quic::MessageStatus> status) {
977   visitor_->OnDatagramProcessed(status);
978 }
979 
980 }  // namespace net
981