dedicated_web_transport_http3_client.cc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/quic/dedicated_web_transport_http3_client.h"
  5. #include "base/containers/contains.h"
  6. #include "base/containers/cxx20_erase.h"
  7. #include "base/memory/raw_ptr.h"
  8. #include "base/metrics/histogram_functions.h"
  9. #include "base/strings/abseil_string_conversions.h"
  10. #include "base/threading/thread_task_runner_handle.h"
  11. #include "net/base/address_list.h"
  12. #include "net/base/port_util.h"
  13. #include "net/base/url_util.h"
  14. #include "net/http/http_network_session.h"
  15. #include "net/log/net_log_values.h"
  16. #include "net/proxy_resolution/configured_proxy_resolution_service.h"
  17. #include "net/proxy_resolution/proxy_resolution_request.h"
  18. #include "net/quic/address_utils.h"
  19. #include "net/quic/crypto/proof_verifier_chromium.h"
  20. #include "net/quic/quic_chromium_alarm_factory.h"
  21. #include "net/spdy/spdy_http_utils.h"
  22. #include "net/third_party/quiche/src/quiche/quic/core/http/web_transport_http3.h"
  23. #include "net/third_party/quiche/src/quiche/quic/core/quic_connection.h"
  24. #include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h"
  25. #include "net/url_request/url_request_context.h"
  26. #include "url/scheme_host_port.h"
  27. namespace net {
  28. namespace {
  29. // From
  30. // https://wicg.github.io/web-transport/#dom-quictransportconfiguration-server_certificate_fingerprints
  31. constexpr int kCustomCertificateMaxValidityDays = 14;
  32. // The time the client would wait for the server to acknowledge the session
  33. // being closed.
  34. constexpr base::TimeDelta kMaxCloseTimeout = base::Seconds(2);
  35. std::set<std::string> HostsFromOrigins(std::set<HostPortPair> origins) {
  36. std::set<std::string> hosts;
  37. for (const auto& origin : origins) {
  38. hosts.insert(origin.host());
  39. }
  40. return hosts;
  41. }
  42. // A version of WebTransportFingerprintProofVerifier that enforces
  43. // Chromium-specific policies.
  44. class ChromiumWebTransportFingerprintProofVerifier
  45. : public quic::WebTransportFingerprintProofVerifier {
  46. public:
  47. using WebTransportFingerprintProofVerifier::
  48. WebTransportFingerprintProofVerifier;
  49. protected:
  50. bool IsKeyTypeAllowedByPolicy(
  51. const quic::CertificateView& certificate) override {
  52. if (certificate.public_key_type() == quic::PublicKeyType::kRsa) {
  53. return false;
  54. }
  55. return WebTransportFingerprintProofVerifier::IsKeyTypeAllowedByPolicy(
  56. certificate);
  57. }
  58. };
  59. std::unique_ptr<quic::ProofVerifier> CreateProofVerifier(
  60. const NetworkIsolationKey& isolation_key,
  61. URLRequestContext* context,
  62. const WebTransportParameters& parameters) {
  63. if (parameters.server_certificate_fingerprints.empty()) {
  64. return std::make_unique<ProofVerifierChromium>(
  65. context->cert_verifier(), context->ct_policy_enforcer(),
  66. context->transport_security_state(), context->sct_auditing_delegate(),
  67. HostsFromOrigins(
  68. context->quic_context()->params()->origins_to_force_quic_on),
  69. isolation_key);
  70. }
  71. auto verifier =
  72. std::make_unique<ChromiumWebTransportFingerprintProofVerifier>(
  73. context->quic_context()->clock(), kCustomCertificateMaxValidityDays);
  74. for (const quic::CertificateFingerprint& fingerprint :
  75. parameters.server_certificate_fingerprints) {
  76. bool success = verifier->AddFingerprint(fingerprint);
  77. if (!success) {
  78. DLOG(WARNING) << "Failed to add a certificate fingerprint: "
  79. << fingerprint.fingerprint;
  80. }
  81. }
  82. return verifier;
  83. }
  84. void RecordNetLogQuicSessionClientStateChanged(
  85. NetLogWithSource& net_log,
  86. WebTransportState last_state,
  87. WebTransportState next_state,
  88. const absl::optional<WebTransportError>& error) {
  89. net_log.AddEvent(
  90. NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_STATE_CHANGED, [&] {
  91. base::Value::Dict dict;
  92. dict.Set("last_state", WebTransportStateString(last_state));
  93. dict.Set("next_state", WebTransportStateString(next_state));
  94. if (error.has_value()) {
  95. base::Value::Dict error_dict;
  96. error_dict.Set("net_error", error->net_error);
  97. error_dict.Set("quic_error", error->quic_error);
  98. error_dict.Set("details", error->details);
  99. dict.Set("error", std::move(error_dict));
  100. }
  101. return base::Value(std::move(dict));
  102. });
  103. }
  104. // The stream associated with an extended CONNECT request for the WebTransport
  105. // session.
  106. class ConnectStream : public quic::QuicSpdyClientStream {
  107. public:
  108. ConnectStream(quic::QuicStreamId id,
  109. quic::QuicSpdyClientSession* session,
  110. quic::StreamType type,
  111. DedicatedWebTransportHttp3Client* client)
  112. : quic::QuicSpdyClientStream(id, session, type), client_(client) {}
  113. void OnInitialHeadersComplete(
  114. bool fin,
  115. size_t frame_len,
  116. const quic::QuicHeaderList& header_list) override {
  117. quic::QuicSpdyClientStream::OnInitialHeadersComplete(fin, frame_len,
  118. header_list);
  119. client_->OnHeadersComplete();
  120. }
  121. void OnClose() override {
  122. quic::QuicSpdyClientStream::OnClose();
  123. if (fin_received() && fin_sent()) {
  124. // Clean close.
  125. return;
  126. }
  127. if (stream_error() == quic::QUIC_STREAM_CONNECTION_ERROR) {
  128. // If stream is closed due to the connection error, OnConnectionClosed()
  129. // will populate the correct error details.
  130. return;
  131. }
  132. client_->OnConnectStreamAborted();
  133. }
  134. void OnWriteSideInDataRecvdState() override {
  135. quic::QuicSpdyClientStream::OnWriteSideInDataRecvdState();
  136. client_->OnConnectStreamWriteSideInDataRecvdState();
  137. }
  138. private:
  139. raw_ptr<DedicatedWebTransportHttp3Client> client_;
  140. };
  141. class DedicatedWebTransportHttp3ClientSession
  142. : public quic::QuicSpdyClientSession {
  143. public:
  144. DedicatedWebTransportHttp3ClientSession(
  145. const quic::QuicConfig& config,
  146. const quic::ParsedQuicVersionVector& supported_versions,
  147. quic::QuicConnection* connection,
  148. const quic::QuicServerId& server_id,
  149. quic::QuicCryptoClientConfig* crypto_config,
  150. quic::QuicClientPushPromiseIndex* push_promise_index,
  151. DedicatedWebTransportHttp3Client* client)
  152. : quic::QuicSpdyClientSession(config,
  153. supported_versions,
  154. connection,
  155. server_id,
  156. crypto_config,
  157. push_promise_index),
  158. client_(client) {}
  159. bool OnSettingsFrame(const quic::SettingsFrame& frame) override {
  160. if (!quic::QuicSpdyClientSession::OnSettingsFrame(frame)) {
  161. return false;
  162. }
  163. client_->OnSettingsReceived();
  164. return true;
  165. }
  166. bool ShouldNegotiateWebTransport() override { return true; }
  167. quic::HttpDatagramSupport LocalHttpDatagramSupport() override {
  168. return quic::HttpDatagramSupport::kDraft04;
  169. }
  170. void OnConnectionClosed(const quic::QuicConnectionCloseFrame& frame,
  171. quic::ConnectionCloseSource source) override {
  172. quic::QuicSpdyClientSession::OnConnectionClosed(frame, source);
  173. client_->OnConnectionClosed(frame.quic_error_code, frame.error_details,
  174. source);
  175. }
  176. ConnectStream* CreateConnectStream() {
  177. if (!ShouldCreateOutgoingBidirectionalStream()) {
  178. return nullptr;
  179. }
  180. std::unique_ptr<ConnectStream> stream =
  181. std::make_unique<ConnectStream>(GetNextOutgoingBidirectionalStreamId(),
  182. this, quic::BIDIRECTIONAL, client_);
  183. ConnectStream* stream_ptr = stream.get();
  184. ActivateStream(std::move(stream));
  185. return stream_ptr;
  186. }
  187. void OnDatagramProcessed(
  188. absl::optional<quic::MessageStatus> status) override {
  189. client_->OnDatagramProcessed(
  190. status.has_value() ? absl::optional<quic::MessageStatus>(*status)
  191. : absl::optional<quic::MessageStatus>());
  192. }
  193. private:
  194. raw_ptr<DedicatedWebTransportHttp3Client> client_;
  195. };
  196. class WebTransportVisitorProxy : public quic::WebTransportVisitor {
  197. public:
  198. explicit WebTransportVisitorProxy(quic::WebTransportVisitor* visitor)
  199. : visitor_(visitor) {}
  200. void OnSessionReady(const spdy::Http2HeaderBlock& block) override {
  201. visitor_->OnSessionReady(block);
  202. }
  203. void OnSessionClosed(quic::WebTransportSessionError error_code,
  204. const std::string& error_message) override {
  205. visitor_->OnSessionClosed(error_code, error_message);
  206. }
  207. void OnIncomingBidirectionalStreamAvailable() override {
  208. visitor_->OnIncomingBidirectionalStreamAvailable();
  209. }
  210. void OnIncomingUnidirectionalStreamAvailable() override {
  211. visitor_->OnIncomingUnidirectionalStreamAvailable();
  212. }
  213. void OnDatagramReceived(absl::string_view datagram) override {
  214. visitor_->OnDatagramReceived(datagram);
  215. }
  216. void OnCanCreateNewOutgoingBidirectionalStream() override {
  217. visitor_->OnCanCreateNewOutgoingBidirectionalStream();
  218. }
  219. void OnCanCreateNewOutgoingUnidirectionalStream() override {
  220. visitor_->OnCanCreateNewOutgoingUnidirectionalStream();
  221. }
  222. private:
  223. raw_ptr<quic::WebTransportVisitor> visitor_;
  224. };
  225. bool IsTerminalState(WebTransportState state) {
  226. return state == WebTransportState::CLOSED ||
  227. state == WebTransportState::FAILED;
  228. }
  229. } // namespace
  230. DedicatedWebTransportHttp3Client::DedicatedWebTransportHttp3Client(
  231. const GURL& url,
  232. const url::Origin& origin,
  233. WebTransportClientVisitor* visitor,
  234. const NetworkIsolationKey& isolation_key,
  235. URLRequestContext* context,
  236. const WebTransportParameters& parameters)
  237. : url_(url),
  238. origin_(origin),
  239. isolation_key_(isolation_key),
  240. context_(context),
  241. visitor_(visitor),
  242. quic_context_(context->quic_context()),
  243. net_log_(NetLogWithSource::Make(context->net_log(),
  244. NetLogSourceType::WEB_TRANSPORT_CLIENT)),
  245. task_runner_(base::ThreadTaskRunnerHandle::Get().get()),
  246. alarm_factory_(
  247. std::make_unique<QuicChromiumAlarmFactory>(task_runner_,
  248. quic_context_->clock())),
  249. // TODO(vasilvv): proof verifier should have proper error reporting
  250. // (currently, all certificate verification errors result in "TLS
  251. // handshake error" even when more detailed message is available). This
  252. // requires implementing ProofHandler::OnProofVerifyDetailsAvailable.
  253. crypto_config_(CreateProofVerifier(isolation_key_, context, parameters),
  254. /* session_cache */ nullptr) {
  255. net_log_.BeginEvent(
  256. NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_ALIVE, [&] {
  257. base::Value::Dict dict;
  258. dict.Set("url", url.possibly_invalid_spec());
  259. dict.Set("network_isolation_key", isolation_key.ToDebugString());
  260. return base::Value(std::move(dict));
  261. });
  262. }
  263. DedicatedWebTransportHttp3Client::~DedicatedWebTransportHttp3Client() {
  264. net_log_.EndEventWithNetErrorCode(
  265. NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_ALIVE,
  266. error_ ? error_->net_error : OK);
  267. }
  268. void DedicatedWebTransportHttp3Client::Connect() {
  269. if (state_ != WebTransportState::NEW ||
  270. next_connect_state_ != CONNECT_STATE_NONE) {
  271. NOTREACHED();
  272. return;
  273. }
  274. TransitionToState(WebTransportState::CONNECTING);
  275. next_connect_state_ = CONNECT_STATE_INIT;
  276. DoLoop(OK);
  277. }
  278. void DedicatedWebTransportHttp3Client::Close(
  279. const absl::optional<WebTransportCloseInfo>& close_info) {
  280. base::TimeDelta probe_timeout = base::Microseconds(
  281. connection_->sent_packet_manager().GetPtoDelay().ToMicroseconds());
  282. // Wait for at least three PTOs similar to what's used in
  283. // https://www.rfc-editor.org/rfc/rfc9000.html#name-immediate-close
  284. base::TimeDelta close_timeout = std::min(3 * probe_timeout, kMaxCloseTimeout);
  285. close_timeout_timer_.Start(
  286. FROM_HERE, close_timeout,
  287. base::BindOnce(&DedicatedWebTransportHttp3Client::OnCloseTimeout,
  288. weak_factory_.GetWeakPtr()));
  289. if (close_info.has_value()) {
  290. session()->CloseSession(close_info->code, close_info->reason);
  291. } else {
  292. session()->CloseSession(0, "");
  293. }
  294. }
  295. quic::WebTransportSession* DedicatedWebTransportHttp3Client::session() {
  296. if (web_transport_session_ == nullptr)
  297. return nullptr;
  298. return web_transport_session_;
  299. }
  300. void DedicatedWebTransportHttp3Client::DoLoop(int rv) {
  301. do {
  302. ConnectState connect_state = next_connect_state_;
  303. next_connect_state_ = CONNECT_STATE_NONE;
  304. switch (connect_state) {
  305. case CONNECT_STATE_INIT:
  306. DCHECK_EQ(rv, OK);
  307. rv = DoInit();
  308. break;
  309. case CONNECT_STATE_CHECK_PROXY:
  310. DCHECK_EQ(rv, OK);
  311. rv = DoCheckProxy();
  312. break;
  313. case CONNECT_STATE_CHECK_PROXY_COMPLETE:
  314. rv = DoCheckProxyComplete(rv);
  315. break;
  316. case CONNECT_STATE_RESOLVE_HOST:
  317. DCHECK_EQ(rv, OK);
  318. rv = DoResolveHost();
  319. break;
  320. case CONNECT_STATE_RESOLVE_HOST_COMPLETE:
  321. rv = DoResolveHostComplete(rv);
  322. break;
  323. case CONNECT_STATE_CONNECT:
  324. DCHECK_EQ(rv, OK);
  325. rv = DoConnect();
  326. break;
  327. case CONNECT_STATE_CONNECT_COMPLETE:
  328. DCHECK_EQ(rv, OK);
  329. rv = DoConnectComplete();
  330. break;
  331. case CONNECT_STATE_SEND_REQUEST:
  332. DCHECK_EQ(rv, OK);
  333. rv = DoSendRequest();
  334. break;
  335. case CONNECT_STATE_CONFIRM_CONNECTION:
  336. DCHECK_EQ(rv, OK);
  337. rv = DoConfirmConnection();
  338. break;
  339. default:
  340. NOTREACHED() << "Invalid state reached: " << connect_state;
  341. rv = ERR_FAILED;
  342. break;
  343. }
  344. } while (rv == OK && next_connect_state_ != CONNECT_STATE_NONE);
  345. if (rv == OK || rv == ERR_IO_PENDING)
  346. return;
  347. SetErrorIfNecessary(rv);
  348. TransitionToState(WebTransportState::FAILED);
  349. }
  350. int DedicatedWebTransportHttp3Client::DoInit() {
  351. if (!url_.is_valid())
  352. return ERR_INVALID_URL;
  353. if (url_.scheme_piece() != url::kHttpsScheme)
  354. return ERR_DISALLOWED_URL_SCHEME;
  355. if (!IsPortAllowedForScheme(url_.EffectiveIntPort(), url_.scheme_piece()))
  356. return ERR_UNSAFE_PORT;
  357. // TODO(vasilvv): check if QUIC is disabled by policy.
  358. // Ensure that RFC 9000 is always supported.
  359. supported_versions_ = quic::ParsedQuicVersionVector{
  360. quic::ParsedQuicVersion::RFCv1(),
  361. };
  362. // Add other supported versions if available.
  363. for (quic::ParsedQuicVersion& version :
  364. quic_context_->params()->supported_versions) {
  365. if (!version.UsesHttp3())
  366. continue;
  367. if (base::Contains(supported_versions_, version))
  368. continue; // Skip as we've already added it above.
  369. supported_versions_.push_back(version);
  370. }
  371. if (supported_versions_.empty()) {
  372. DLOG(ERROR) << "Attempted using WebTransport with no compatible QUIC "
  373. "versions available";
  374. return ERR_NOT_IMPLEMENTED;
  375. }
  376. next_connect_state_ = CONNECT_STATE_CHECK_PROXY;
  377. return OK;
  378. }
  379. int DedicatedWebTransportHttp3Client::DoCheckProxy() {
  380. next_connect_state_ = CONNECT_STATE_CHECK_PROXY_COMPLETE;
  381. return context_->proxy_resolution_service()->ResolveProxy(
  382. url_, /* method */ "CONNECT", isolation_key_, &proxy_info_,
  383. base::BindOnce(&DedicatedWebTransportHttp3Client::DoLoop,
  384. base::Unretained(this)),
  385. &proxy_resolution_request_, net_log_);
  386. }
  387. int DedicatedWebTransportHttp3Client::DoCheckProxyComplete(int rv) {
  388. if (rv != OK)
  389. return rv;
  390. // If a proxy is configured, we fail the connection.
  391. if (!proxy_info_.is_direct())
  392. return ERR_TUNNEL_CONNECTION_FAILED;
  393. next_connect_state_ = CONNECT_STATE_RESOLVE_HOST;
  394. return OK;
  395. }
  396. int DedicatedWebTransportHttp3Client::DoResolveHost() {
  397. next_connect_state_ = CONNECT_STATE_RESOLVE_HOST_COMPLETE;
  398. HostResolver::ResolveHostParameters parameters;
  399. resolve_host_request_ = context_->host_resolver()->CreateRequest(
  400. url::SchemeHostPort(url_), isolation_key_, net_log_, absl::nullopt);
  401. return resolve_host_request_->Start(base::BindOnce(
  402. &DedicatedWebTransportHttp3Client::DoLoop, base::Unretained(this)));
  403. }
  404. int DedicatedWebTransportHttp3Client::DoResolveHostComplete(int rv) {
  405. if (rv != OK)
  406. return rv;
  407. DCHECK(resolve_host_request_->GetAddressResults());
  408. next_connect_state_ = CONNECT_STATE_CONNECT;
  409. return OK;
  410. }
  411. int DedicatedWebTransportHttp3Client::DoConnect() {
  412. int rv = OK;
  413. // TODO(vasilvv): consider unifying parts of this code with QuicSocketFactory
  414. // (which currently has a lot of code specific to QuicChromiumClientSession).
  415. socket_ = context_->GetNetworkSessionContext()
  416. ->client_socket_factory->CreateDatagramClientSocket(
  417. DatagramSocket::DEFAULT_BIND, net_log_.net_log(),
  418. net_log_.source());
  419. if (quic_context_->params()->enable_socket_recv_optimization)
  420. socket_->EnableRecvOptimization();
  421. socket_->UseNonBlockingIO();
  422. IPEndPoint server_address =
  423. *resolve_host_request_->GetAddressResults()->begin();
  424. rv = socket_->Connect(server_address);
  425. if (rv != OK)
  426. return rv;
  427. rv = socket_->SetReceiveBufferSize(kQuicSocketReceiveBufferSize);
  428. if (rv != OK)
  429. return rv;
  430. rv = socket_->SetDoNotFragment();
  431. if (rv == ERR_NOT_IMPLEMENTED)
  432. rv = OK;
  433. if (rv != OK)
  434. return rv;
  435. rv = socket_->SetSendBufferSize(quic::kMaxOutgoingPacketSize * 20);
  436. if (rv != OK)
  437. return rv;
  438. CreateConnection();
  439. next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
  440. return ERR_IO_PENDING;
  441. }
  442. void DedicatedWebTransportHttp3Client::CreateConnection() {
  443. // Delete the objects in the same order they would be normally deleted by the
  444. // destructor.
  445. packet_reader_ = nullptr;
  446. session_ = nullptr;
  447. IPEndPoint server_address =
  448. *resolve_host_request_->GetAddressResults()->begin();
  449. quic::QuicConnectionId connection_id =
  450. quic::QuicUtils::CreateRandomConnectionId(
  451. quic_context_->random_generator());
  452. auto connection = std::make_unique<quic::QuicConnection>(
  453. connection_id, quic::QuicSocketAddress(),
  454. ToQuicSocketAddress(server_address), quic_context_->helper(),
  455. alarm_factory_.get(),
  456. new QuicChromiumPacketWriter(socket_.get(), task_runner_),
  457. /* owns_writer */ true, quic::Perspective::IS_CLIENT,
  458. supported_versions_);
  459. connection_ = connection.get();
  460. connection->SetMaxPacketLength(quic_context_->params()->max_packet_length);
  461. session_ = std::make_unique<DedicatedWebTransportHttp3ClientSession>(
  462. InitializeQuicConfig(*quic_context_->params()), supported_versions_,
  463. connection.release(),
  464. quic::QuicServerId(url_.host(), url_.EffectiveIntPort()), &crypto_config_,
  465. &push_promise_index_, this);
  466. if (!original_supported_versions_.empty()) {
  467. session_->set_client_original_supported_versions(
  468. original_supported_versions_);
  469. }
  470. packet_reader_ = std::make_unique<QuicChromiumPacketReader>(
  471. socket_.get(), quic_context_->clock(), this, kQuicYieldAfterPacketsRead,
  472. quic::QuicTime::Delta::FromMilliseconds(
  473. kQuicYieldAfterDurationMilliseconds),
  474. net_log_);
  475. event_logger_ = std::make_unique<QuicEventLogger>(session_.get(), net_log_);
  476. connection_->set_debug_visitor(event_logger_.get());
  477. connection_->set_creator_debug_delegate(event_logger_.get());
  478. session_->Initialize();
  479. packet_reader_->StartReading();
  480. DCHECK(session_->WillNegotiateWebTransport());
  481. session_->CryptoConnect();
  482. }
  483. int DedicatedWebTransportHttp3Client::DoConnectComplete() {
  484. if (!connection_->connected()) {
  485. return ERR_QUIC_PROTOCOL_ERROR;
  486. }
  487. // Fail the connection if the received SETTINGS do not support WebTransport.
  488. if (!session_->SupportsWebTransport()) {
  489. return ERR_METHOD_NOT_SUPPORTED;
  490. }
  491. safe_to_report_error_details_ = true;
  492. next_connect_state_ = CONNECT_STATE_SEND_REQUEST;
  493. return OK;
  494. }
  495. void DedicatedWebTransportHttp3Client::OnSettingsReceived() {
  496. DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
  497. // Wait until the SETTINGS parser is finished, and then send the request.
  498. task_runner_->PostTask(
  499. FROM_HERE, base::BindOnce(&DedicatedWebTransportHttp3Client::DoLoop,
  500. weak_factory_.GetWeakPtr(), OK));
  501. }
  502. void DedicatedWebTransportHttp3Client::OnHeadersComplete() {
  503. DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONFIRM_CONNECTION);
  504. DoLoop(OK);
  505. }
  506. void DedicatedWebTransportHttp3Client::
  507. OnConnectStreamWriteSideInDataRecvdState() {
  508. task_runner_->PostTask(
  509. FROM_HERE,
  510. base::BindOnce(&DedicatedWebTransportHttp3Client::TransitionToState,
  511. weak_factory_.GetWeakPtr(), WebTransportState::CLOSED));
  512. }
  513. void DedicatedWebTransportHttp3Client::OnConnectStreamAborted() {
  514. SetErrorIfNecessary(session_ready_ ? ERR_FAILED : ERR_METHOD_NOT_SUPPORTED);
  515. TransitionToState(WebTransportState::FAILED);
  516. }
  517. void DedicatedWebTransportHttp3Client::OnCloseTimeout() {
  518. SetErrorIfNecessary(ERR_TIMED_OUT);
  519. TransitionToState(WebTransportState::FAILED);
  520. }
  521. int DedicatedWebTransportHttp3Client::DoSendRequest() {
  522. quic::QuicConnection::ScopedPacketFlusher scope(connection_);
  523. DedicatedWebTransportHttp3ClientSession* session =
  524. static_cast<DedicatedWebTransportHttp3ClientSession*>(session_.get());
  525. ConnectStream* stream = session->CreateConnectStream();
  526. if (stream == nullptr) {
  527. return ERR_QUIC_PROTOCOL_ERROR;
  528. }
  529. connect_stream_ = stream;
  530. spdy::Http2HeaderBlock headers;
  531. DCHECK_EQ(url_.scheme(), url::kHttpsScheme);
  532. headers[":scheme"] = url_.scheme();
  533. headers[":method"] = "CONNECT";
  534. headers[":authority"] = GetHostAndOptionalPort(url_);
  535. headers[":path"] = url_.PathForRequest();
  536. headers[":protocol"] = "webtransport";
  537. headers["sec-webtransport-http3-draft02"] = "1";
  538. headers["origin"] = origin_.Serialize();
  539. stream->WriteHeaders(std::move(headers), /*fin=*/false, nullptr);
  540. web_transport_session_ = stream->web_transport();
  541. if (web_transport_session_ == nullptr) {
  542. return ERR_METHOD_NOT_SUPPORTED;
  543. }
  544. stream->web_transport()->SetVisitor(
  545. std::make_unique<WebTransportVisitorProxy>(this));
  546. next_connect_state_ = CONNECT_STATE_CONFIRM_CONNECTION;
  547. return ERR_IO_PENDING;
  548. }
  549. int DedicatedWebTransportHttp3Client::DoConfirmConnection() {
  550. if (!session_ready_) {
  551. return ERR_METHOD_NOT_SUPPORTED;
  552. }
  553. TransitionToState(WebTransportState::CONNECTED);
  554. return OK;
  555. }
  556. void DedicatedWebTransportHttp3Client::TransitionToState(
  557. WebTransportState next_state) {
  558. // Ignore all state transition requests if we have reached the terminal
  559. // state.
  560. if (IsTerminalState(state_)) {
  561. DCHECK(IsTerminalState(next_state))
  562. << "from: " << state_ << ", to: " << next_state;
  563. return;
  564. }
  565. DCHECK_NE(state_, next_state);
  566. const WebTransportState last_state = state_;
  567. state_ = next_state;
  568. RecordNetLogQuicSessionClientStateChanged(net_log_, last_state, next_state,
  569. error_);
  570. switch (next_state) {
  571. case WebTransportState::CONNECTING:
  572. DCHECK_EQ(last_state, WebTransportState::NEW);
  573. break;
  574. case WebTransportState::CONNECTED:
  575. DCHECK_EQ(last_state, WebTransportState::CONNECTING);
  576. visitor_->OnConnected(http_response_info_->headers);
  577. break;
  578. case WebTransportState::CLOSED:
  579. DCHECK_EQ(last_state, WebTransportState::CONNECTED);
  580. connection_->CloseConnection(quic::QUIC_NO_ERROR,
  581. "WebTransport client terminated",
  582. quic::ConnectionCloseBehavior::SILENT_CLOSE);
  583. visitor_->OnClosed(close_info_);
  584. break;
  585. case WebTransportState::FAILED:
  586. DCHECK(error_.has_value());
  587. if (last_state == WebTransportState::CONNECTING) {
  588. visitor_->OnConnectionFailed(*error_);
  589. break;
  590. }
  591. DCHECK_EQ(last_state, WebTransportState::CONNECTED);
  592. // Ensure the connection is properly closed before deleting it.
  593. connection_->CloseConnection(
  594. quic::QUIC_INTERNAL_ERROR,
  595. "WebTransportState::ERROR reached but the connection still open",
  596. quic::ConnectionCloseBehavior::SILENT_CLOSE);
  597. visitor_->OnError(*error_);
  598. break;
  599. default:
  600. NOTREACHED() << "Invalid state reached: " << next_state;
  601. break;
  602. }
  603. }
  604. void DedicatedWebTransportHttp3Client::SetErrorIfNecessary(int error) {
  605. SetErrorIfNecessary(error, quic::QUIC_NO_ERROR, ErrorToString(error));
  606. }
  607. void DedicatedWebTransportHttp3Client::SetErrorIfNecessary(
  608. int error,
  609. quic::QuicErrorCode quic_error,
  610. base::StringPiece details) {
  611. if (!error_) {
  612. error_ = WebTransportError(error, quic_error, details,
  613. safe_to_report_error_details_);
  614. }
  615. }
  616. void DedicatedWebTransportHttp3Client::OnSessionReady(
  617. const spdy::Http2HeaderBlock& spdy_headers) {
  618. session_ready_ = true;
  619. http_response_info_ = std::make_unique<HttpResponseInfo>();
  620. const int rv =
  621. SpdyHeadersToHttpResponse(spdy_headers, http_response_info_.get());
  622. if (rv != OK) {
  623. SetErrorIfNecessary(ERR_QUIC_PROTOCOL_ERROR);
  624. TransitionToState(WebTransportState::FAILED);
  625. return;
  626. }
  627. // TODO(vasilvv): add support for this header in downstream tests and remove
  628. // this.
  629. http_response_info_->headers->RemoveHeader("sec-webtransport-http3-draft");
  630. DCHECK(http_response_info_->headers);
  631. }
  632. void DedicatedWebTransportHttp3Client::OnSessionClosed(
  633. quic::WebTransportSessionError error_code,
  634. const std::string& error_message) {
  635. close_info_ = WebTransportCloseInfo(error_code, error_message);
  636. task_runner_->PostTask(
  637. FROM_HERE,
  638. base::BindOnce(&DedicatedWebTransportHttp3Client::TransitionToState,
  639. weak_factory_.GetWeakPtr(), WebTransportState::CLOSED));
  640. }
  641. void DedicatedWebTransportHttp3Client::
  642. OnIncomingBidirectionalStreamAvailable() {
  643. visitor_->OnIncomingBidirectionalStreamAvailable();
  644. }
  645. void DedicatedWebTransportHttp3Client::
  646. OnIncomingUnidirectionalStreamAvailable() {
  647. visitor_->OnIncomingUnidirectionalStreamAvailable();
  648. }
  649. void DedicatedWebTransportHttp3Client::OnDatagramReceived(
  650. absl::string_view datagram) {
  651. visitor_->OnDatagramReceived(base::StringViewToStringPiece(datagram));
  652. }
  653. void DedicatedWebTransportHttp3Client::
  654. OnCanCreateNewOutgoingBidirectionalStream() {
  655. visitor_->OnCanCreateNewOutgoingBidirectionalStream();
  656. }
  657. void DedicatedWebTransportHttp3Client::
  658. OnCanCreateNewOutgoingUnidirectionalStream() {
  659. visitor_->OnCanCreateNewOutgoingUnidirectionalStream();
  660. }
  661. bool DedicatedWebTransportHttp3Client::OnReadError(
  662. int result,
  663. const DatagramClientSocket* socket) {
  664. SetErrorIfNecessary(result);
  665. connection_->CloseConnection(quic::QUIC_PACKET_READ_ERROR,
  666. ErrorToString(result),
  667. quic::ConnectionCloseBehavior::SILENT_CLOSE);
  668. return false;
  669. }
  670. bool DedicatedWebTransportHttp3Client::OnPacket(
  671. const quic::QuicReceivedPacket& packet,
  672. const quic::QuicSocketAddress& local_address,
  673. const quic::QuicSocketAddress& peer_address) {
  674. session_->ProcessUdpPacket(local_address, peer_address, packet);
  675. return connection_->connected();
  676. }
  677. int DedicatedWebTransportHttp3Client::HandleWriteError(
  678. int error_code,
  679. scoped_refptr<QuicChromiumPacketWriter::ReusableIOBuffer> /*last_packet*/) {
  680. return error_code;
  681. }
  682. void DedicatedWebTransportHttp3Client::OnWriteError(int error_code) {
  683. SetErrorIfNecessary(error_code);
  684. connection_->OnWriteError(error_code);
  685. }
  686. void DedicatedWebTransportHttp3Client::OnWriteUnblocked() {
  687. connection_->OnCanWrite();
  688. }
  689. void DedicatedWebTransportHttp3Client::OnConnectionClosed(
  690. quic::QuicErrorCode error,
  691. const std::string& error_details,
  692. quic::ConnectionCloseSource source) {
  693. // If the session is already in a terminal state due to reasons other than
  694. // connection close, we should ignore it; otherwise we risk re-entering the
  695. // connection teardown process.
  696. if (IsTerminalState(state_)) {
  697. return;
  698. }
  699. if (!retried_with_new_version_ &&
  700. session_->error() == quic::QUIC_INVALID_VERSION) {
  701. retried_with_new_version_ = true;
  702. DCHECK(original_supported_versions_.empty());
  703. original_supported_versions_ = supported_versions_;
  704. base::EraseIf(
  705. supported_versions_, [this](const quic::ParsedQuicVersion& version) {
  706. return !base::Contains(
  707. session_->connection()->server_supported_versions(), version);
  708. });
  709. if (!supported_versions_.empty()) {
  710. // Since this is a callback from QuicConnection, we can't replace the
  711. // connection object in this method; do it from the top of the event loop
  712. // instead.
  713. task_runner_->PostTask(
  714. FROM_HERE,
  715. base::BindOnce(&DedicatedWebTransportHttp3Client::CreateConnection,
  716. weak_factory_.GetWeakPtr()));
  717. return;
  718. }
  719. // If there are no supported versions, treat this as a regular error.
  720. }
  721. if (error == quic::QUIC_NO_ERROR) {
  722. TransitionToState(WebTransportState::CLOSED);
  723. return;
  724. }
  725. SetErrorIfNecessary(ERR_QUIC_PROTOCOL_ERROR, error, error_details);
  726. if (state_ == WebTransportState::CONNECTING) {
  727. DoLoop(OK);
  728. return;
  729. }
  730. TransitionToState(WebTransportState::FAILED);
  731. }
  732. void DedicatedWebTransportHttp3Client::OnDatagramProcessed(
  733. absl::optional<quic::MessageStatus> status) {
  734. visitor_->OnDatagramProcessed(status);
  735. }
  736. } // namespace net