xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/moqt/tools/moqt_client.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2023 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 
5 #include "quiche/quic/moqt/tools/moqt_client.h"
6 
7 #include <memory>
8 #include <string>
9 #include <utility>
10 
11 #include "absl/status/status.h"
12 #include "absl/strings/string_view.h"
13 #include "quiche/quic/core/crypto/proof_verifier.h"
14 #include "quiche/quic/core/http/quic_spdy_client_stream.h"
15 #include "quiche/quic/core/http/web_transport_http3.h"
16 #include "quiche/quic/core/io/quic_event_loop.h"
17 #include "quiche/quic/core/quic_server_id.h"
18 #include "quiche/quic/core/quic_types.h"
19 #include "quiche/quic/moqt/moqt_messages.h"
20 #include "quiche/quic/moqt/moqt_session.h"
21 #include "quiche/quic/platform/api/quic_socket_address.h"
22 #include "quiche/quic/tools/quic_default_client.h"
23 #include "quiche/quic/tools/quic_event_loop_tools.h"
24 #include "quiche/quic/tools/quic_name_lookup.h"
25 #include "quiche/common/platform/api/quiche_logging.h"
26 #include "quiche/spdy/core/http2_header_block.h"
27 
28 namespace moqt {
29 
MoqtClient(quic::QuicSocketAddress peer_address,const quic::QuicServerId & server_id,std::unique_ptr<quic::ProofVerifier> proof_verifier,quic::QuicEventLoop * event_loop)30 MoqtClient::MoqtClient(quic::QuicSocketAddress peer_address,
31                        const quic::QuicServerId& server_id,
32                        std::unique_ptr<quic::ProofVerifier> proof_verifier,
33                        quic::QuicEventLoop* event_loop)
34     : spdy_client_(peer_address, server_id, GetMoqtSupportedQuicVersions(),
35                    event_loop, std::move(proof_verifier)) {
36   spdy_client_.set_enable_web_transport(true);
37 }
38 
Connect(std::string path,MoqtSessionCallbacks callbacks)39 void MoqtClient::Connect(std::string path, MoqtSessionCallbacks callbacks) {
40   absl::Status status = ConnectInner(std::move(path), callbacks);
41   if (!status.ok()) {
42     std::move(callbacks.session_terminated_callback)(status.message());
43   }
44 }
45 
ConnectInner(std::string path,MoqtSessionCallbacks & callbacks)46 absl::Status MoqtClient::ConnectInner(std::string path,
47                                       MoqtSessionCallbacks& callbacks) {
48   if (!spdy_client_.Initialize()) {
49     return absl::InternalError("Initialization failed");
50   }
51   if (!spdy_client_.Connect()) {
52     return absl::UnavailableError("Failed to establish a QUIC connection");
53   }
54   bool settings_received = quic::ProcessEventsUntil(
55       spdy_client_.default_network_helper()->event_loop(),
56       [&] { return spdy_client_.client_session()->settings_received(); });
57   if (!settings_received) {
58     return absl::UnavailableError(
59         "Timed out while waiting for server SETTINGS");
60   }
61   if (!spdy_client_.client_session()->SupportsWebTransport()) {
62     QUICHE_DLOG(INFO) << "session: SupportsWebTransport = "
63                       << spdy_client_.client_session()->SupportsWebTransport()
64                       << ", SupportsH3Datagram = "
65                       << spdy_client_.client_session()->SupportsH3Datagram()
66                       << ", OneRttKeysAvailable = "
67                       << spdy_client_.client_session()->OneRttKeysAvailable();
68     return absl::FailedPreconditionError(
69         "Server does not support WebTransport");
70   }
71   auto* stream = static_cast<quic::QuicSpdyClientStream*>(
72       spdy_client_.client_session()->CreateOutgoingBidirectionalStream());
73   if (!stream) {
74     return absl::InternalError("Could not open a CONNECT stream");
75   }
76   spdy_client_.set_store_response(true);
77 
78   spdy::Http2HeaderBlock headers;
79   headers[":scheme"] = "https";
80   headers[":authority"] = spdy_client_.server_id().host();
81   headers[":path"] = path;
82   headers[":method"] = "CONNECT";
83   headers[":protocol"] = "webtransport";
84   stream->SendRequest(std::move(headers), "", false);
85 
86   quic::WebTransportHttp3* web_transport = stream->web_transport();
87   if (web_transport == nullptr) {
88     return absl::InternalError("Failed to initialize WebTransport session");
89   }
90 
91   MoqtSessionParameters parameters;
92   parameters.version = MoqtVersion::kDraft03;
93   parameters.perspective = quic::Perspective::IS_CLIENT,
94   parameters.using_webtrans = true;
95   parameters.path = "";
96   parameters.deliver_partial_objects = false;
97 
98   // Ensure that we never have a dangling pointer to the session.
99   MoqtSessionDeletedCallback deleted_callback =
100       std::move(callbacks.session_deleted_callback);
101   callbacks.session_deleted_callback =
102       [this, old = std::move(deleted_callback)]() mutable {
103         session_ = nullptr;
104         std::move(old)();
105       };
106 
107   auto session = std::make_unique<MoqtSession>(web_transport, parameters,
108                                                std::move(callbacks));
109   session_ = session.get();
110   web_transport->SetVisitor(std::move(session));
111   return absl::OkStatus();
112 }
113 
114 }  // namespace moqt
115