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 <memory>
6 #include <string>
7
8 #include "absl/strings/string_view.h"
9 #include "quiche/quic/core/crypto/quic_compressed_certs_cache.h"
10 #include "quiche/quic/core/crypto/quic_crypto_client_config.h"
11 #include "quiche/quic/core/crypto/quic_crypto_server_config.h"
12 #include "quiche/quic/core/crypto/quic_random.h"
13 #include "quiche/quic/core/quic_config.h"
14 #include "quiche/quic/core/quic_generic_session.h"
15 #include "quiche/quic/core/quic_types.h"
16 #include "quiche/quic/moqt/moqt_messages.h"
17 #include "quiche/quic/moqt/moqt_session.h"
18 #include "quiche/quic/test_tools/crypto_test_utils.h"
19 #include "quiche/quic/test_tools/simulator/simulator.h"
20 #include "quiche/quic/test_tools/simulator/test_harness.h"
21 #include "quiche/common/platform/api/quiche_test.h"
22
23 namespace moqt::test {
24 namespace {
25
26 using ::quic::simulator::Simulator;
27 using ::testing::_;
28 using ::testing::Assign;
29
30 struct MockSessionCallbacks {
31 testing::MockFunction<void()> session_established_callback;
32 testing::MockFunction<void(absl::string_view)> session_terminated_callback;
33 testing::MockFunction<void()> session_deleted_callback;
34
AsSessionCallbacksmoqt::test::__anon8df813690111::MockSessionCallbacks35 MoqtSessionCallbacks AsSessionCallbacks() {
36 return MoqtSessionCallbacks{session_established_callback.AsStdFunction(),
37 session_terminated_callback.AsStdFunction(),
38 session_deleted_callback.AsStdFunction()};
39 }
40 };
41
42 class ClientEndpoint : public quic::simulator::QuicEndpointWithConnection {
43 public:
ClientEndpoint(Simulator * simulator,const std::string & name,const std::string & peer_name,MoqtVersion version)44 ClientEndpoint(Simulator* simulator, const std::string& name,
45 const std::string& peer_name, MoqtVersion version)
46 : QuicEndpointWithConnection(simulator, name, peer_name,
47 quic::Perspective::IS_CLIENT,
48 quic::GetQuicVersionsForGenericSession()),
49 crypto_config_(
50 quic::test::crypto_test_utils::ProofVerifierForTesting()),
51 quic_session_(connection_.get(), false, nullptr, quic::QuicConfig(),
52 "test.example.com", 443, "moqt", &session_,
53 /*visitor_owned=*/false, nullptr, &crypto_config_),
54 session_(
55 &quic_session_,
56 MoqtSessionParameters{.version = version,
57 .perspective = quic::Perspective::IS_CLIENT,
58 .using_webtrans = false},
59 callbacks_.AsSessionCallbacks()) {
60 quic_session_.Initialize();
61 }
62
session()63 MoqtSession* session() { return &session_; }
quic_session()64 quic::QuicGenericClientSession* quic_session() { return &quic_session_; }
established_callback()65 testing::MockFunction<void()>& established_callback() {
66 return callbacks_.session_established_callback;
67 }
terminated_callback()68 testing::MockFunction<void(absl::string_view)>& terminated_callback() {
69 return callbacks_.session_terminated_callback;
70 }
71
72 private:
73 MockSessionCallbacks callbacks_;
74 quic::QuicCryptoClientConfig crypto_config_;
75 quic::QuicGenericClientSession quic_session_;
76 MoqtSession session_;
77 };
78
79 class ServerEndpoint : public quic::simulator::QuicEndpointWithConnection {
80 public:
ServerEndpoint(Simulator * simulator,const std::string & name,const std::string & peer_name,MoqtVersion version)81 ServerEndpoint(Simulator* simulator, const std::string& name,
82 const std::string& peer_name, MoqtVersion version)
83 : QuicEndpointWithConnection(simulator, name, peer_name,
84 quic::Perspective::IS_SERVER,
85 quic::GetQuicVersionsForGenericSession()),
86 compressed_certs_cache_(
87 quic::QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
88 crypto_config_(quic::QuicCryptoServerConfig::TESTING,
89 quic::QuicRandom::GetInstance(),
90 quic::test::crypto_test_utils::ProofSourceForTesting(),
91 quic::KeyExchangeSource::Default()),
92 quic_session_(connection_.get(), false, nullptr, quic::QuicConfig(),
93 "moqt", &session_,
94 /*visitor_owned=*/false, nullptr, &crypto_config_,
95 &compressed_certs_cache_),
96 session_(
97 &quic_session_,
98 MoqtSessionParameters{.version = version,
99 .perspective = quic::Perspective::IS_SERVER,
100 .using_webtrans = false},
101 callbacks_.AsSessionCallbacks()) {
102 quic_session_.Initialize();
103 }
104
session()105 MoqtSession* session() { return &session_; }
established_callback()106 testing::MockFunction<void()>& established_callback() {
107 return callbacks_.session_established_callback;
108 }
terminated_callback()109 testing::MockFunction<void(absl::string_view)>& terminated_callback() {
110 return callbacks_.session_terminated_callback;
111 }
112
113 private:
114 MockSessionCallbacks callbacks_;
115 quic::QuicCompressedCertsCache compressed_certs_cache_;
116 quic::QuicCryptoServerConfig crypto_config_;
117 quic::QuicGenericServerSession quic_session_;
118 MoqtSession session_;
119 };
120
121 class MoqtIntegrationTest : public quiche::test::QuicheTest {
122 public:
CreateDefaultEndpoints()123 void CreateDefaultEndpoints() {
124 client_ = std::make_unique<ClientEndpoint>(
125 &test_harness_.simulator(), "Client", "Server", MoqtVersion::kDraft01);
126 server_ = std::make_unique<ServerEndpoint>(
127 &test_harness_.simulator(), "Server", "Client", MoqtVersion::kDraft01);
128 test_harness_.set_client(client_.get());
129 test_harness_.set_server(server_.get());
130 }
131
WireUpEndpoints()132 void WireUpEndpoints() { test_harness_.WireUpEndpoints(); }
133
134 protected:
135 quic::simulator::TestHarness test_harness_;
136
137 std::unique_ptr<ClientEndpoint> client_;
138 std::unique_ptr<ServerEndpoint> server_;
139 };
140
TEST_F(MoqtIntegrationTest,Handshake)141 TEST_F(MoqtIntegrationTest, Handshake) {
142 CreateDefaultEndpoints();
143 WireUpEndpoints();
144
145 client_->quic_session()->CryptoConnect();
146 bool client_established = false;
147 bool server_established = false;
148 EXPECT_CALL(client_->established_callback(), Call())
149 .WillOnce(Assign(&client_established, true));
150 EXPECT_CALL(server_->established_callback(), Call())
151 .WillOnce(Assign(&server_established, true));
152 bool success = test_harness_.RunUntilWithDefaultTimeout(
153 [&]() { return client_established && server_established; });
154 EXPECT_TRUE(success);
155 }
156
TEST_F(MoqtIntegrationTest,VersionMismatch)157 TEST_F(MoqtIntegrationTest, VersionMismatch) {
158 client_ = std::make_unique<ClientEndpoint>(
159 &test_harness_.simulator(), "Client", "Server",
160 MoqtVersion::kUnrecognizedVersionForTests);
161 server_ = std::make_unique<ServerEndpoint>(
162 &test_harness_.simulator(), "Server", "Client", MoqtVersion::kDraft01);
163 test_harness_.set_client(client_.get());
164 test_harness_.set_server(server_.get());
165 WireUpEndpoints();
166
167 client_->quic_session()->CryptoConnect();
168 bool client_terminated = false;
169 bool server_terminated = false;
170 EXPECT_CALL(client_->established_callback(), Call()).Times(0);
171 EXPECT_CALL(server_->established_callback(), Call()).Times(0);
172 EXPECT_CALL(client_->terminated_callback(), Call(_))
173 .WillOnce(Assign(&client_terminated, true));
174 EXPECT_CALL(server_->terminated_callback(), Call(_))
175 .WillOnce(Assign(&server_terminated, true));
176 bool success = test_harness_.RunUntilWithDefaultTimeout(
177 [&]() { return client_terminated && server_terminated; });
178 EXPECT_TRUE(success);
179 }
180
181 } // namespace
182 } // namespace moqt::test
183