| // Copyright 2023 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 "quiche/quic/moqt/tools/moqt_client.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/status/status.h" |
| #include "absl/strings/string_view.h" |
| #include "quiche/quic/core/crypto/proof_verifier.h" |
| #include "quiche/quic/core/http/quic_spdy_client_stream.h" |
| #include "quiche/quic/core/http/web_transport_http3.h" |
| #include "quiche/quic/core/io/quic_event_loop.h" |
| #include "quiche/quic/core/quic_server_id.h" |
| #include "quiche/quic/core/quic_types.h" |
| #include "quiche/quic/moqt/moqt_messages.h" |
| #include "quiche/quic/moqt/moqt_session.h" |
| #include "quiche/quic/platform/api/quic_socket_address.h" |
| #include "quiche/quic/tools/quic_default_client.h" |
| #include "quiche/quic/tools/quic_event_loop_tools.h" |
| #include "quiche/quic/tools/quic_name_lookup.h" |
| #include "quiche/common/http/http_header_block.h" |
| #include "quiche/common/platform/api/quiche_logging.h" |
| |
| namespace moqt { |
| |
| MoqtClient::MoqtClient(quic::QuicSocketAddress peer_address, |
| const quic::QuicServerId& server_id, |
| std::unique_ptr<quic::ProofVerifier> proof_verifier, |
| quic::QuicEventLoop* event_loop) |
| : spdy_client_(peer_address, server_id, GetMoqtSupportedQuicVersions(), |
| event_loop, std::move(proof_verifier)) { |
| spdy_client_.set_enable_web_transport(true); |
| } |
| |
| void MoqtClient::Connect(std::string path, MoqtSessionCallbacks callbacks) { |
| absl::Status status = ConnectInner(std::move(path), callbacks); |
| if (!status.ok()) { |
| std::move(callbacks.session_terminated_callback)(status.message()); |
| } |
| } |
| |
| absl::Status MoqtClient::ConnectInner(std::string path, |
| MoqtSessionCallbacks& callbacks) { |
| if (!spdy_client_.Initialize()) { |
| return absl::InternalError("Initialization failed"); |
| } |
| if (!spdy_client_.Connect()) { |
| return absl::UnavailableError("Failed to establish a QUIC connection"); |
| } |
| bool settings_received = quic::ProcessEventsUntil( |
| spdy_client_.default_network_helper()->event_loop(), |
| [&] { return spdy_client_.client_session()->settings_received(); }); |
| if (!settings_received) { |
| return absl::UnavailableError( |
| "Timed out while waiting for server SETTINGS"); |
| } |
| if (!spdy_client_.client_session()->SupportsWebTransport()) { |
| QUICHE_DLOG(INFO) << "session: SupportsWebTransport = " |
| << spdy_client_.client_session()->SupportsWebTransport() |
| << ", SupportsH3Datagram = " |
| << spdy_client_.client_session()->SupportsH3Datagram() |
| << ", OneRttKeysAvailable = " |
| << spdy_client_.client_session()->OneRttKeysAvailable(); |
| return absl::FailedPreconditionError( |
| "Server does not support WebTransport"); |
| } |
| auto* stream = static_cast<quic::QuicSpdyClientStream*>( |
| spdy_client_.client_session()->CreateOutgoingBidirectionalStream()); |
| if (!stream) { |
| return absl::InternalError("Could not open a CONNECT stream"); |
| } |
| spdy_client_.set_store_response(true); |
| |
| quiche::HttpHeaderBlock headers; |
| headers[":scheme"] = "https"; |
| headers[":authority"] = spdy_client_.server_id().host(); |
| headers[":path"] = path; |
| headers[":method"] = "CONNECT"; |
| headers[":protocol"] = "webtransport"; |
| stream->SendRequest(std::move(headers), "", false); |
| |
| quic::WebTransportHttp3* web_transport = stream->web_transport(); |
| if (web_transport == nullptr) { |
| return absl::InternalError("Failed to initialize WebTransport session"); |
| } |
| |
| MoqtSessionParameters parameters(quic::Perspective::IS_CLIENT); |
| |
| // Ensure that we never have a dangling pointer to the session. |
| MoqtSessionDeletedCallback deleted_callback = |
| std::move(callbacks.session_deleted_callback); |
| callbacks.session_deleted_callback = |
| [this, old = std::move(deleted_callback)]() mutable { |
| session_ = nullptr; |
| std::move(old)(); |
| }; |
| |
| auto session = std::make_unique<MoqtSession>(web_transport, parameters, |
| std::move(callbacks)); |
| session_ = session.get(); |
| web_transport->SetVisitor(std::move(session)); |
| return absl::OkStatus(); |
| } |
| |
| } // namespace moqt |