123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "net/quic/dedicated_web_transport_http3_client.h"
- #include "base/containers/contains.h"
- #include "base/containers/cxx20_erase.h"
- #include "base/memory/raw_ptr.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/strings/abseil_string_conversions.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "net/base/address_list.h"
- #include "net/base/port_util.h"
- #include "net/base/url_util.h"
- #include "net/http/http_network_session.h"
- #include "net/log/net_log_values.h"
- #include "net/proxy_resolution/configured_proxy_resolution_service.h"
- #include "net/proxy_resolution/proxy_resolution_request.h"
- #include "net/quic/address_utils.h"
- #include "net/quic/crypto/proof_verifier_chromium.h"
- #include "net/quic/quic_chromium_alarm_factory.h"
- #include "net/spdy/spdy_http_utils.h"
- #include "net/third_party/quiche/src/quiche/quic/core/http/web_transport_http3.h"
- #include "net/third_party/quiche/src/quiche/quic/core/quic_connection.h"
- #include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h"
- #include "net/url_request/url_request_context.h"
- #include "url/scheme_host_port.h"
- namespace net {
- namespace {
- // From
- // https://wicg.github.io/web-transport/#dom-quictransportconfiguration-server_certificate_fingerprints
- constexpr int kCustomCertificateMaxValidityDays = 14;
- // The time the client would wait for the server to acknowledge the session
- // being closed.
- constexpr base::TimeDelta kMaxCloseTimeout = base::Seconds(2);
- std::set<std::string> HostsFromOrigins(std::set<HostPortPair> origins) {
- std::set<std::string> hosts;
- for (const auto& origin : origins) {
- hosts.insert(origin.host());
- }
- return hosts;
- }
- // A version of WebTransportFingerprintProofVerifier that enforces
- // Chromium-specific policies.
- class ChromiumWebTransportFingerprintProofVerifier
- : public quic::WebTransportFingerprintProofVerifier {
- public:
- using WebTransportFingerprintProofVerifier::
- WebTransportFingerprintProofVerifier;
- protected:
- bool IsKeyTypeAllowedByPolicy(
- const quic::CertificateView& certificate) override {
- if (certificate.public_key_type() == quic::PublicKeyType::kRsa) {
- return false;
- }
- return WebTransportFingerprintProofVerifier::IsKeyTypeAllowedByPolicy(
- certificate);
- }
- };
- std::unique_ptr<quic::ProofVerifier> CreateProofVerifier(
- const NetworkIsolationKey& isolation_key,
- URLRequestContext* context,
- const WebTransportParameters& parameters) {
- if (parameters.server_certificate_fingerprints.empty()) {
- return std::make_unique<ProofVerifierChromium>(
- context->cert_verifier(), context->ct_policy_enforcer(),
- context->transport_security_state(), context->sct_auditing_delegate(),
- HostsFromOrigins(
- context->quic_context()->params()->origins_to_force_quic_on),
- isolation_key);
- }
- auto verifier =
- std::make_unique<ChromiumWebTransportFingerprintProofVerifier>(
- context->quic_context()->clock(), kCustomCertificateMaxValidityDays);
- for (const quic::CertificateFingerprint& fingerprint :
- parameters.server_certificate_fingerprints) {
- bool success = verifier->AddFingerprint(fingerprint);
- if (!success) {
- DLOG(WARNING) << "Failed to add a certificate fingerprint: "
- << fingerprint.fingerprint;
- }
- }
- return verifier;
- }
- void RecordNetLogQuicSessionClientStateChanged(
- NetLogWithSource& net_log,
- WebTransportState last_state,
- WebTransportState next_state,
- const absl::optional<WebTransportError>& error) {
- net_log.AddEvent(
- NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_STATE_CHANGED, [&] {
- base::Value::Dict dict;
- dict.Set("last_state", WebTransportStateString(last_state));
- dict.Set("next_state", WebTransportStateString(next_state));
- if (error.has_value()) {
- base::Value::Dict error_dict;
- error_dict.Set("net_error", error->net_error);
- error_dict.Set("quic_error", error->quic_error);
- error_dict.Set("details", error->details);
- dict.Set("error", std::move(error_dict));
- }
- return base::Value(std::move(dict));
- });
- }
- // The stream associated with an extended CONNECT request for the WebTransport
- // session.
- class ConnectStream : public quic::QuicSpdyClientStream {
- public:
- ConnectStream(quic::QuicStreamId id,
- quic::QuicSpdyClientSession* session,
- quic::StreamType type,
- DedicatedWebTransportHttp3Client* client)
- : quic::QuicSpdyClientStream(id, session, type), client_(client) {}
- void OnInitialHeadersComplete(
- bool fin,
- size_t frame_len,
- const quic::QuicHeaderList& header_list) override {
- quic::QuicSpdyClientStream::OnInitialHeadersComplete(fin, frame_len,
- header_list);
- client_->OnHeadersComplete();
- }
- void OnClose() override {
- quic::QuicSpdyClientStream::OnClose();
- if (fin_received() && fin_sent()) {
- // Clean close.
- return;
- }
- if (stream_error() == quic::QUIC_STREAM_CONNECTION_ERROR) {
- // If stream is closed due to the connection error, OnConnectionClosed()
- // will populate the correct error details.
- return;
- }
- client_->OnConnectStreamAborted();
- }
- void OnWriteSideInDataRecvdState() override {
- quic::QuicSpdyClientStream::OnWriteSideInDataRecvdState();
- client_->OnConnectStreamWriteSideInDataRecvdState();
- }
- private:
- raw_ptr<DedicatedWebTransportHttp3Client> client_;
- };
- class DedicatedWebTransportHttp3ClientSession
- : public quic::QuicSpdyClientSession {
- public:
- DedicatedWebTransportHttp3ClientSession(
- const quic::QuicConfig& config,
- const quic::ParsedQuicVersionVector& supported_versions,
- quic::QuicConnection* connection,
- const quic::QuicServerId& server_id,
- quic::QuicCryptoClientConfig* crypto_config,
- quic::QuicClientPushPromiseIndex* push_promise_index,
- DedicatedWebTransportHttp3Client* client)
- : quic::QuicSpdyClientSession(config,
- supported_versions,
- connection,
- server_id,
- crypto_config,
- push_promise_index),
- client_(client) {}
- bool OnSettingsFrame(const quic::SettingsFrame& frame) override {
- if (!quic::QuicSpdyClientSession::OnSettingsFrame(frame)) {
- return false;
- }
- client_->OnSettingsReceived();
- return true;
- }
- bool ShouldNegotiateWebTransport() override { return true; }
- quic::HttpDatagramSupport LocalHttpDatagramSupport() override {
- return quic::HttpDatagramSupport::kDraft04;
- }
- void OnConnectionClosed(const quic::QuicConnectionCloseFrame& frame,
- quic::ConnectionCloseSource source) override {
- quic::QuicSpdyClientSession::OnConnectionClosed(frame, source);
- client_->OnConnectionClosed(frame.quic_error_code, frame.error_details,
- source);
- }
- ConnectStream* CreateConnectStream() {
- if (!ShouldCreateOutgoingBidirectionalStream()) {
- return nullptr;
- }
- std::unique_ptr<ConnectStream> stream =
- std::make_unique<ConnectStream>(GetNextOutgoingBidirectionalStreamId(),
- this, quic::BIDIRECTIONAL, client_);
- ConnectStream* stream_ptr = stream.get();
- ActivateStream(std::move(stream));
- return stream_ptr;
- }
- void OnDatagramProcessed(
- absl::optional<quic::MessageStatus> status) override {
- client_->OnDatagramProcessed(
- status.has_value() ? absl::optional<quic::MessageStatus>(*status)
- : absl::optional<quic::MessageStatus>());
- }
- private:
- raw_ptr<DedicatedWebTransportHttp3Client> client_;
- };
- class WebTransportVisitorProxy : public quic::WebTransportVisitor {
- public:
- explicit WebTransportVisitorProxy(quic::WebTransportVisitor* visitor)
- : visitor_(visitor) {}
- void OnSessionReady(const spdy::Http2HeaderBlock& block) override {
- visitor_->OnSessionReady(block);
- }
- void OnSessionClosed(quic::WebTransportSessionError error_code,
- const std::string& error_message) override {
- visitor_->OnSessionClosed(error_code, error_message);
- }
- void OnIncomingBidirectionalStreamAvailable() override {
- visitor_->OnIncomingBidirectionalStreamAvailable();
- }
- void OnIncomingUnidirectionalStreamAvailable() override {
- visitor_->OnIncomingUnidirectionalStreamAvailable();
- }
- void OnDatagramReceived(absl::string_view datagram) override {
- visitor_->OnDatagramReceived(datagram);
- }
- void OnCanCreateNewOutgoingBidirectionalStream() override {
- visitor_->OnCanCreateNewOutgoingBidirectionalStream();
- }
- void OnCanCreateNewOutgoingUnidirectionalStream() override {
- visitor_->OnCanCreateNewOutgoingUnidirectionalStream();
- }
- private:
- raw_ptr<quic::WebTransportVisitor> visitor_;
- };
- bool IsTerminalState(WebTransportState state) {
- return state == WebTransportState::CLOSED ||
- state == WebTransportState::FAILED;
- }
- } // namespace
- DedicatedWebTransportHttp3Client::DedicatedWebTransportHttp3Client(
- const GURL& url,
- const url::Origin& origin,
- WebTransportClientVisitor* visitor,
- const NetworkIsolationKey& isolation_key,
- URLRequestContext* context,
- const WebTransportParameters& parameters)
- : url_(url),
- origin_(origin),
- isolation_key_(isolation_key),
- context_(context),
- visitor_(visitor),
- quic_context_(context->quic_context()),
- net_log_(NetLogWithSource::Make(context->net_log(),
- NetLogSourceType::WEB_TRANSPORT_CLIENT)),
- task_runner_(base::ThreadTaskRunnerHandle::Get().get()),
- alarm_factory_(
- std::make_unique<QuicChromiumAlarmFactory>(task_runner_,
- quic_context_->clock())),
- // TODO(vasilvv): proof verifier should have proper error reporting
- // (currently, all certificate verification errors result in "TLS
- // handshake error" even when more detailed message is available). This
- // requires implementing ProofHandler::OnProofVerifyDetailsAvailable.
- crypto_config_(CreateProofVerifier(isolation_key_, context, parameters),
- /* session_cache */ nullptr) {
- net_log_.BeginEvent(
- NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_ALIVE, [&] {
- base::Value::Dict dict;
- dict.Set("url", url.possibly_invalid_spec());
- dict.Set("network_isolation_key", isolation_key.ToDebugString());
- return base::Value(std::move(dict));
- });
- }
- DedicatedWebTransportHttp3Client::~DedicatedWebTransportHttp3Client() {
- net_log_.EndEventWithNetErrorCode(
- NetLogEventType::QUIC_SESSION_WEBTRANSPORT_CLIENT_ALIVE,
- error_ ? error_->net_error : OK);
- }
- void DedicatedWebTransportHttp3Client::Connect() {
- if (state_ != WebTransportState::NEW ||
- next_connect_state_ != CONNECT_STATE_NONE) {
- NOTREACHED();
- return;
- }
- TransitionToState(WebTransportState::CONNECTING);
- next_connect_state_ = CONNECT_STATE_INIT;
- DoLoop(OK);
- }
- void DedicatedWebTransportHttp3Client::Close(
- const absl::optional<WebTransportCloseInfo>& close_info) {
- base::TimeDelta probe_timeout = base::Microseconds(
- connection_->sent_packet_manager().GetPtoDelay().ToMicroseconds());
- // Wait for at least three PTOs similar to what's used in
- // https://www.rfc-editor.org/rfc/rfc9000.html#name-immediate-close
- base::TimeDelta close_timeout = std::min(3 * probe_timeout, kMaxCloseTimeout);
- close_timeout_timer_.Start(
- FROM_HERE, close_timeout,
- base::BindOnce(&DedicatedWebTransportHttp3Client::OnCloseTimeout,
- weak_factory_.GetWeakPtr()));
- if (close_info.has_value()) {
- session()->CloseSession(close_info->code, close_info->reason);
- } else {
- session()->CloseSession(0, "");
- }
- }
- quic::WebTransportSession* DedicatedWebTransportHttp3Client::session() {
- if (web_transport_session_ == nullptr)
- return nullptr;
- return web_transport_session_;
- }
- void DedicatedWebTransportHttp3Client::DoLoop(int rv) {
- do {
- ConnectState connect_state = next_connect_state_;
- next_connect_state_ = CONNECT_STATE_NONE;
- switch (connect_state) {
- case CONNECT_STATE_INIT:
- DCHECK_EQ(rv, OK);
- rv = DoInit();
- break;
- case CONNECT_STATE_CHECK_PROXY:
- DCHECK_EQ(rv, OK);
- rv = DoCheckProxy();
- break;
- case CONNECT_STATE_CHECK_PROXY_COMPLETE:
- rv = DoCheckProxyComplete(rv);
- break;
- case CONNECT_STATE_RESOLVE_HOST:
- DCHECK_EQ(rv, OK);
- rv = DoResolveHost();
- break;
- case CONNECT_STATE_RESOLVE_HOST_COMPLETE:
- rv = DoResolveHostComplete(rv);
- break;
- case CONNECT_STATE_CONNECT:
- DCHECK_EQ(rv, OK);
- rv = DoConnect();
- break;
- case CONNECT_STATE_CONNECT_COMPLETE:
- DCHECK_EQ(rv, OK);
- rv = DoConnectComplete();
- break;
- case CONNECT_STATE_SEND_REQUEST:
- DCHECK_EQ(rv, OK);
- rv = DoSendRequest();
- break;
- case CONNECT_STATE_CONFIRM_CONNECTION:
- DCHECK_EQ(rv, OK);
- rv = DoConfirmConnection();
- break;
- default:
- NOTREACHED() << "Invalid state reached: " << connect_state;
- rv = ERR_FAILED;
- break;
- }
- } while (rv == OK && next_connect_state_ != CONNECT_STATE_NONE);
- if (rv == OK || rv == ERR_IO_PENDING)
- return;
- SetErrorIfNecessary(rv);
- TransitionToState(WebTransportState::FAILED);
- }
- int DedicatedWebTransportHttp3Client::DoInit() {
- if (!url_.is_valid())
- return ERR_INVALID_URL;
- if (url_.scheme_piece() != url::kHttpsScheme)
- return ERR_DISALLOWED_URL_SCHEME;
- if (!IsPortAllowedForScheme(url_.EffectiveIntPort(), url_.scheme_piece()))
- return ERR_UNSAFE_PORT;
- // TODO(vasilvv): check if QUIC is disabled by policy.
- // Ensure that RFC 9000 is always supported.
- supported_versions_ = quic::ParsedQuicVersionVector{
- quic::ParsedQuicVersion::RFCv1(),
- };
- // Add other supported versions if available.
- for (quic::ParsedQuicVersion& version :
- quic_context_->params()->supported_versions) {
- if (!version.UsesHttp3())
- continue;
- if (base::Contains(supported_versions_, version))
- continue; // Skip as we've already added it above.
- supported_versions_.push_back(version);
- }
- if (supported_versions_.empty()) {
- DLOG(ERROR) << "Attempted using WebTransport with no compatible QUIC "
- "versions available";
- return ERR_NOT_IMPLEMENTED;
- }
- next_connect_state_ = CONNECT_STATE_CHECK_PROXY;
- return OK;
- }
- int DedicatedWebTransportHttp3Client::DoCheckProxy() {
- next_connect_state_ = CONNECT_STATE_CHECK_PROXY_COMPLETE;
- return context_->proxy_resolution_service()->ResolveProxy(
- url_, /* method */ "CONNECT", isolation_key_, &proxy_info_,
- base::BindOnce(&DedicatedWebTransportHttp3Client::DoLoop,
- base::Unretained(this)),
- &proxy_resolution_request_, net_log_);
- }
- int DedicatedWebTransportHttp3Client::DoCheckProxyComplete(int rv) {
- if (rv != OK)
- return rv;
- // If a proxy is configured, we fail the connection.
- if (!proxy_info_.is_direct())
- return ERR_TUNNEL_CONNECTION_FAILED;
- next_connect_state_ = CONNECT_STATE_RESOLVE_HOST;
- return OK;
- }
- int DedicatedWebTransportHttp3Client::DoResolveHost() {
- next_connect_state_ = CONNECT_STATE_RESOLVE_HOST_COMPLETE;
- HostResolver::ResolveHostParameters parameters;
- resolve_host_request_ = context_->host_resolver()->CreateRequest(
- url::SchemeHostPort(url_), isolation_key_, net_log_, absl::nullopt);
- return resolve_host_request_->Start(base::BindOnce(
- &DedicatedWebTransportHttp3Client::DoLoop, base::Unretained(this)));
- }
- int DedicatedWebTransportHttp3Client::DoResolveHostComplete(int rv) {
- if (rv != OK)
- return rv;
- DCHECK(resolve_host_request_->GetAddressResults());
- next_connect_state_ = CONNECT_STATE_CONNECT;
- return OK;
- }
- int DedicatedWebTransportHttp3Client::DoConnect() {
- int rv = OK;
- // TODO(vasilvv): consider unifying parts of this code with QuicSocketFactory
- // (which currently has a lot of code specific to QuicChromiumClientSession).
- socket_ = context_->GetNetworkSessionContext()
- ->client_socket_factory->CreateDatagramClientSocket(
- DatagramSocket::DEFAULT_BIND, net_log_.net_log(),
- net_log_.source());
- if (quic_context_->params()->enable_socket_recv_optimization)
- socket_->EnableRecvOptimization();
- socket_->UseNonBlockingIO();
- IPEndPoint server_address =
- *resolve_host_request_->GetAddressResults()->begin();
- rv = socket_->Connect(server_address);
- if (rv != OK)
- return rv;
- rv = socket_->SetReceiveBufferSize(kQuicSocketReceiveBufferSize);
- if (rv != OK)
- return rv;
- rv = socket_->SetDoNotFragment();
- if (rv == ERR_NOT_IMPLEMENTED)
- rv = OK;
- if (rv != OK)
- return rv;
- rv = socket_->SetSendBufferSize(quic::kMaxOutgoingPacketSize * 20);
- if (rv != OK)
- return rv;
- CreateConnection();
- next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
- return ERR_IO_PENDING;
- }
- void DedicatedWebTransportHttp3Client::CreateConnection() {
- // Delete the objects in the same order they would be normally deleted by the
- // destructor.
- packet_reader_ = nullptr;
- session_ = nullptr;
- IPEndPoint server_address =
- *resolve_host_request_->GetAddressResults()->begin();
- quic::QuicConnectionId connection_id =
- quic::QuicUtils::CreateRandomConnectionId(
- quic_context_->random_generator());
- auto connection = std::make_unique<quic::QuicConnection>(
- connection_id, quic::QuicSocketAddress(),
- ToQuicSocketAddress(server_address), quic_context_->helper(),
- alarm_factory_.get(),
- new QuicChromiumPacketWriter(socket_.get(), task_runner_),
- /* owns_writer */ true, quic::Perspective::IS_CLIENT,
- supported_versions_);
- connection_ = connection.get();
- connection->SetMaxPacketLength(quic_context_->params()->max_packet_length);
- session_ = std::make_unique<DedicatedWebTransportHttp3ClientSession>(
- InitializeQuicConfig(*quic_context_->params()), supported_versions_,
- connection.release(),
- quic::QuicServerId(url_.host(), url_.EffectiveIntPort()), &crypto_config_,
- &push_promise_index_, this);
- if (!original_supported_versions_.empty()) {
- session_->set_client_original_supported_versions(
- original_supported_versions_);
- }
- packet_reader_ = std::make_unique<QuicChromiumPacketReader>(
- socket_.get(), quic_context_->clock(), this, kQuicYieldAfterPacketsRead,
- quic::QuicTime::Delta::FromMilliseconds(
- kQuicYieldAfterDurationMilliseconds),
- net_log_);
- event_logger_ = std::make_unique<QuicEventLogger>(session_.get(), net_log_);
- connection_->set_debug_visitor(event_logger_.get());
- connection_->set_creator_debug_delegate(event_logger_.get());
- session_->Initialize();
- packet_reader_->StartReading();
- DCHECK(session_->WillNegotiateWebTransport());
- session_->CryptoConnect();
- }
- int DedicatedWebTransportHttp3Client::DoConnectComplete() {
- if (!connection_->connected()) {
- return ERR_QUIC_PROTOCOL_ERROR;
- }
- // Fail the connection if the received SETTINGS do not support WebTransport.
- if (!session_->SupportsWebTransport()) {
- return ERR_METHOD_NOT_SUPPORTED;
- }
- safe_to_report_error_details_ = true;
- next_connect_state_ = CONNECT_STATE_SEND_REQUEST;
- return OK;
- }
- void DedicatedWebTransportHttp3Client::OnSettingsReceived() {
- DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
- // Wait until the SETTINGS parser is finished, and then send the request.
- task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&DedicatedWebTransportHttp3Client::DoLoop,
- weak_factory_.GetWeakPtr(), OK));
- }
- void DedicatedWebTransportHttp3Client::OnHeadersComplete() {
- DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONFIRM_CONNECTION);
- DoLoop(OK);
- }
- void DedicatedWebTransportHttp3Client::
- OnConnectStreamWriteSideInDataRecvdState() {
- task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&DedicatedWebTransportHttp3Client::TransitionToState,
- weak_factory_.GetWeakPtr(), WebTransportState::CLOSED));
- }
- void DedicatedWebTransportHttp3Client::OnConnectStreamAborted() {
- SetErrorIfNecessary(session_ready_ ? ERR_FAILED : ERR_METHOD_NOT_SUPPORTED);
- TransitionToState(WebTransportState::FAILED);
- }
- void DedicatedWebTransportHttp3Client::OnCloseTimeout() {
- SetErrorIfNecessary(ERR_TIMED_OUT);
- TransitionToState(WebTransportState::FAILED);
- }
- int DedicatedWebTransportHttp3Client::DoSendRequest() {
- quic::QuicConnection::ScopedPacketFlusher scope(connection_);
- DedicatedWebTransportHttp3ClientSession* session =
- static_cast<DedicatedWebTransportHttp3ClientSession*>(session_.get());
- ConnectStream* stream = session->CreateConnectStream();
- if (stream == nullptr) {
- return ERR_QUIC_PROTOCOL_ERROR;
- }
- connect_stream_ = stream;
- spdy::Http2HeaderBlock headers;
- DCHECK_EQ(url_.scheme(), url::kHttpsScheme);
- headers[":scheme"] = url_.scheme();
- headers[":method"] = "CONNECT";
- headers[":authority"] = GetHostAndOptionalPort(url_);
- headers[":path"] = url_.PathForRequest();
- headers[":protocol"] = "webtransport";
- headers["sec-webtransport-http3-draft02"] = "1";
- headers["origin"] = origin_.Serialize();
- stream->WriteHeaders(std::move(headers), /*fin=*/false, nullptr);
- web_transport_session_ = stream->web_transport();
- if (web_transport_session_ == nullptr) {
- return ERR_METHOD_NOT_SUPPORTED;
- }
- stream->web_transport()->SetVisitor(
- std::make_unique<WebTransportVisitorProxy>(this));
- next_connect_state_ = CONNECT_STATE_CONFIRM_CONNECTION;
- return ERR_IO_PENDING;
- }
- int DedicatedWebTransportHttp3Client::DoConfirmConnection() {
- if (!session_ready_) {
- return ERR_METHOD_NOT_SUPPORTED;
- }
- TransitionToState(WebTransportState::CONNECTED);
- return OK;
- }
- void DedicatedWebTransportHttp3Client::TransitionToState(
- WebTransportState next_state) {
- // Ignore all state transition requests if we have reached the terminal
- // state.
- if (IsTerminalState(state_)) {
- DCHECK(IsTerminalState(next_state))
- << "from: " << state_ << ", to: " << next_state;
- return;
- }
- DCHECK_NE(state_, next_state);
- const WebTransportState last_state = state_;
- state_ = next_state;
- RecordNetLogQuicSessionClientStateChanged(net_log_, last_state, next_state,
- error_);
- switch (next_state) {
- case WebTransportState::CONNECTING:
- DCHECK_EQ(last_state, WebTransportState::NEW);
- break;
- case WebTransportState::CONNECTED:
- DCHECK_EQ(last_state, WebTransportState::CONNECTING);
- visitor_->OnConnected(http_response_info_->headers);
- break;
- case WebTransportState::CLOSED:
- DCHECK_EQ(last_state, WebTransportState::CONNECTED);
- connection_->CloseConnection(quic::QUIC_NO_ERROR,
- "WebTransport client terminated",
- quic::ConnectionCloseBehavior::SILENT_CLOSE);
- visitor_->OnClosed(close_info_);
- break;
- case WebTransportState::FAILED:
- DCHECK(error_.has_value());
- if (last_state == WebTransportState::CONNECTING) {
- visitor_->OnConnectionFailed(*error_);
- break;
- }
- DCHECK_EQ(last_state, WebTransportState::CONNECTED);
- // Ensure the connection is properly closed before deleting it.
- connection_->CloseConnection(
- quic::QUIC_INTERNAL_ERROR,
- "WebTransportState::ERROR reached but the connection still open",
- quic::ConnectionCloseBehavior::SILENT_CLOSE);
- visitor_->OnError(*error_);
- break;
- default:
- NOTREACHED() << "Invalid state reached: " << next_state;
- break;
- }
- }
- void DedicatedWebTransportHttp3Client::SetErrorIfNecessary(int error) {
- SetErrorIfNecessary(error, quic::QUIC_NO_ERROR, ErrorToString(error));
- }
- void DedicatedWebTransportHttp3Client::SetErrorIfNecessary(
- int error,
- quic::QuicErrorCode quic_error,
- base::StringPiece details) {
- if (!error_) {
- error_ = WebTransportError(error, quic_error, details,
- safe_to_report_error_details_);
- }
- }
- void DedicatedWebTransportHttp3Client::OnSessionReady(
- const spdy::Http2HeaderBlock& spdy_headers) {
- session_ready_ = true;
- http_response_info_ = std::make_unique<HttpResponseInfo>();
- const int rv =
- SpdyHeadersToHttpResponse(spdy_headers, http_response_info_.get());
- if (rv != OK) {
- SetErrorIfNecessary(ERR_QUIC_PROTOCOL_ERROR);
- TransitionToState(WebTransportState::FAILED);
- return;
- }
- // TODO(vasilvv): add support for this header in downstream tests and remove
- // this.
- http_response_info_->headers->RemoveHeader("sec-webtransport-http3-draft");
- DCHECK(http_response_info_->headers);
- }
- void DedicatedWebTransportHttp3Client::OnSessionClosed(
- quic::WebTransportSessionError error_code,
- const std::string& error_message) {
- close_info_ = WebTransportCloseInfo(error_code, error_message);
- task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&DedicatedWebTransportHttp3Client::TransitionToState,
- weak_factory_.GetWeakPtr(), WebTransportState::CLOSED));
- }
- void DedicatedWebTransportHttp3Client::
- OnIncomingBidirectionalStreamAvailable() {
- visitor_->OnIncomingBidirectionalStreamAvailable();
- }
- void DedicatedWebTransportHttp3Client::
- OnIncomingUnidirectionalStreamAvailable() {
- visitor_->OnIncomingUnidirectionalStreamAvailable();
- }
- void DedicatedWebTransportHttp3Client::OnDatagramReceived(
- absl::string_view datagram) {
- visitor_->OnDatagramReceived(base::StringViewToStringPiece(datagram));
- }
- void DedicatedWebTransportHttp3Client::
- OnCanCreateNewOutgoingBidirectionalStream() {
- visitor_->OnCanCreateNewOutgoingBidirectionalStream();
- }
- void DedicatedWebTransportHttp3Client::
- OnCanCreateNewOutgoingUnidirectionalStream() {
- visitor_->OnCanCreateNewOutgoingUnidirectionalStream();
- }
- bool DedicatedWebTransportHttp3Client::OnReadError(
- int result,
- const DatagramClientSocket* socket) {
- SetErrorIfNecessary(result);
- connection_->CloseConnection(quic::QUIC_PACKET_READ_ERROR,
- ErrorToString(result),
- quic::ConnectionCloseBehavior::SILENT_CLOSE);
- return false;
- }
- bool DedicatedWebTransportHttp3Client::OnPacket(
- const quic::QuicReceivedPacket& packet,
- const quic::QuicSocketAddress& local_address,
- const quic::QuicSocketAddress& peer_address) {
- session_->ProcessUdpPacket(local_address, peer_address, packet);
- return connection_->connected();
- }
- int DedicatedWebTransportHttp3Client::HandleWriteError(
- int error_code,
- scoped_refptr<QuicChromiumPacketWriter::ReusableIOBuffer> /*last_packet*/) {
- return error_code;
- }
- void DedicatedWebTransportHttp3Client::OnWriteError(int error_code) {
- SetErrorIfNecessary(error_code);
- connection_->OnWriteError(error_code);
- }
- void DedicatedWebTransportHttp3Client::OnWriteUnblocked() {
- connection_->OnCanWrite();
- }
- void DedicatedWebTransportHttp3Client::OnConnectionClosed(
- quic::QuicErrorCode error,
- const std::string& error_details,
- quic::ConnectionCloseSource source) {
- // If the session is already in a terminal state due to reasons other than
- // connection close, we should ignore it; otherwise we risk re-entering the
- // connection teardown process.
- if (IsTerminalState(state_)) {
- return;
- }
- if (!retried_with_new_version_ &&
- session_->error() == quic::QUIC_INVALID_VERSION) {
- retried_with_new_version_ = true;
- DCHECK(original_supported_versions_.empty());
- original_supported_versions_ = supported_versions_;
- base::EraseIf(
- supported_versions_, [this](const quic::ParsedQuicVersion& version) {
- return !base::Contains(
- session_->connection()->server_supported_versions(), version);
- });
- if (!supported_versions_.empty()) {
- // Since this is a callback from QuicConnection, we can't replace the
- // connection object in this method; do it from the top of the event loop
- // instead.
- task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&DedicatedWebTransportHttp3Client::CreateConnection,
- weak_factory_.GetWeakPtr()));
- return;
- }
- // If there are no supported versions, treat this as a regular error.
- }
- if (error == quic::QUIC_NO_ERROR) {
- TransitionToState(WebTransportState::CLOSED);
- return;
- }
- SetErrorIfNecessary(ERR_QUIC_PROTOCOL_ERROR, error, error_details);
- if (state_ == WebTransportState::CONNECTING) {
- DoLoop(OK);
- return;
- }
- TransitionToState(WebTransportState::FAILED);
- }
- void DedicatedWebTransportHttp3Client::OnDatagramProcessed(
- absl::optional<quic::MessageStatus> status) {
- visitor_->OnDatagramProcessed(status);
- }
- } // namespace net
|