1 // Copyright (c) 2019 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 // Sets up a dispatcher and sends requests via the QboneClient.
6
7 #include "net/third_party/quiche/src/quic/qbone/qbone_client.h"
8
9 #include "absl/strings/string_view.h"
10 #include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
11 #include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h"
12 #include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
13 #include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
14 #include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
15 #include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
16 #include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h"
17 #include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h"
18 #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
19 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
20 #include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
21 #include "net/third_party/quiche/src/quic/qbone/qbone_constants.h"
22 #include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.h"
23 #include "net/third_party/quiche/src/quic/qbone/qbone_server_session.h"
24 #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
25 #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
26 #include "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h"
27 #include "net/third_party/quiche/src/quic/test_tools/server_thread.h"
28 #include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
29 #include "net/third_party/quiche/src/quic/tools/quic_server.h"
30
31 namespace quic {
32 namespace test {
33 namespace {
34
GetTestParams()35 ParsedQuicVersionVector GetTestParams() {
36 ParsedQuicVersionVector test_versions;
37
38 // TODO(b/113130636): Make QBONE work with TLS.
39 for (const auto& version : CurrentSupportedVersionsWithQuicCrypto()) {
40 // QBONE requires MESSAGE frames
41 if (!version.SupportsMessageFrames()) {
42 continue;
43 }
44 test_versions.push_back(version);
45 }
46
47 return test_versions;
48 }
49
TestPacketIn(const std::string & body)50 std::string TestPacketIn(const std::string& body) {
51 return PrependIPv6HeaderForTest(body, 5);
52 }
53
TestPacketOut(const std::string & body)54 std::string TestPacketOut(const std::string& body) {
55 return PrependIPv6HeaderForTest(body, 4);
56 }
57
58 class DataSavingQbonePacketWriter : public QbonePacketWriter {
59 public:
WritePacketToNetwork(const char * packet,size_t size)60 void WritePacketToNetwork(const char* packet, size_t size) override {
61 QuicWriterMutexLock lock(&mu_);
62 data_.push_back(std::string(packet, size));
63 }
64
data()65 std::vector<std::string> data() {
66 QuicWriterMutexLock lock(&mu_);
67 return data_;
68 }
69
70 private:
71 QuicMutex mu_;
72 std::vector<std::string> data_;
73 };
74
75 // A subclass of a QBONE session that will own the connection passed in.
76 class ConnectionOwningQboneServerSession : public QboneServerSession {
77 public:
ConnectionOwningQboneServerSession(const ParsedQuicVersionVector & supported_versions,QuicConnection * connection,Visitor * owner,const QuicConfig & config,const QuicCryptoServerConfig * quic_crypto_server_config,QuicCompressedCertsCache * compressed_certs_cache,QbonePacketWriter * writer)78 ConnectionOwningQboneServerSession(
79 const ParsedQuicVersionVector& supported_versions,
80 QuicConnection* connection,
81 Visitor* owner,
82 const QuicConfig& config,
83 const QuicCryptoServerConfig* quic_crypto_server_config,
84 QuicCompressedCertsCache* compressed_certs_cache,
85 QbonePacketWriter* writer)
86 : QboneServerSession(supported_versions,
87 connection,
88 owner,
89 config,
90 quic_crypto_server_config,
91 compressed_certs_cache,
92 writer,
93 TestLoopback6(),
94 TestLoopback6(),
95 64,
96 nullptr),
97 connection_(connection) {}
98
99 private:
100 // Note that we don't expect the QboneServerSession or any of its parent
101 // classes to do anything with the connection_ in their destructors.
102 std::unique_ptr<QuicConnection> connection_;
103 };
104
105 class QuicQboneDispatcher : public QuicDispatcher {
106 public:
QuicQboneDispatcher(const QuicConfig * config,const QuicCryptoServerConfig * crypto_config,QuicVersionManager * version_manager,std::unique_ptr<QuicConnectionHelperInterface> helper,std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper,std::unique_ptr<QuicAlarmFactory> alarm_factory,QbonePacketWriter * writer)107 QuicQboneDispatcher(
108 const QuicConfig* config,
109 const QuicCryptoServerConfig* crypto_config,
110 QuicVersionManager* version_manager,
111 std::unique_ptr<QuicConnectionHelperInterface> helper,
112 std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper,
113 std::unique_ptr<QuicAlarmFactory> alarm_factory,
114 QbonePacketWriter* writer)
115 : QuicDispatcher(config,
116 crypto_config,
117 version_manager,
118 std::move(helper),
119 std::move(session_helper),
120 std::move(alarm_factory),
121 kQuicDefaultConnectionIdLength),
122 writer_(writer) {}
123
CreateQuicSession(QuicConnectionId id,const QuicSocketAddress & self_address,const QuicSocketAddress & peer_address,absl::string_view alpn,const quic::ParsedQuicVersion & version)124 std::unique_ptr<QuicSession> CreateQuicSession(
125 QuicConnectionId id,
126 const QuicSocketAddress& self_address,
127 const QuicSocketAddress& peer_address,
128 absl::string_view alpn,
129 const quic::ParsedQuicVersion& version) override {
130 CHECK_EQ(alpn, "qbone");
131 QuicConnection* connection = new QuicConnection(
132 id, self_address, peer_address, helper(), alarm_factory(), writer(),
133 /* owns_writer= */ false, Perspective::IS_SERVER,
134 ParsedQuicVersionVector{version});
135 // The connection owning wrapper owns the connection created.
136 auto session = std::make_unique<ConnectionOwningQboneServerSession>(
137 GetSupportedVersions(), connection, this, config(), crypto_config(),
138 compressed_certs_cache(), writer_);
139 session->Initialize();
140 return session;
141 }
142
143 private:
144 QbonePacketWriter* writer_;
145 };
146
147 class QboneTestServer : public QuicServer {
148 public:
QboneTestServer(std::unique_ptr<ProofSource> proof_source)149 explicit QboneTestServer(std::unique_ptr<ProofSource> proof_source)
150 : QuicServer(std::move(proof_source), &response_cache_) {}
CreateQuicDispatcher()151 QuicDispatcher* CreateQuicDispatcher() override {
152 QuicEpollAlarmFactory alarm_factory(epoll_server());
153 return new QuicQboneDispatcher(
154 &config(), &crypto_config(), version_manager(),
155 std::unique_ptr<QuicEpollConnectionHelper>(
156 new QuicEpollConnectionHelper(epoll_server(),
157 QuicAllocator::BUFFER_POOL)),
158 std::unique_ptr<QuicCryptoServerStreamBase::Helper>(
159 new QboneCryptoServerStreamHelper()),
160 std::unique_ptr<QuicEpollAlarmFactory>(
161 new QuicEpollAlarmFactory(epoll_server())),
162 &writer_);
163 }
164
data()165 std::vector<std::string> data() { return writer_.data(); }
166
167 private:
168 quic::QuicMemoryCacheBackend response_cache_;
169 DataSavingQbonePacketWriter writer_;
170 };
171
172 class QboneTestClient : public QboneClient {
173 public:
QboneTestClient(QuicSocketAddress server_address,const QuicServerId & server_id,const ParsedQuicVersionVector & supported_versions,QuicEpollServer * epoll_server,std::unique_ptr<ProofVerifier> proof_verifier)174 QboneTestClient(QuicSocketAddress server_address,
175 const QuicServerId& server_id,
176 const ParsedQuicVersionVector& supported_versions,
177 QuicEpollServer* epoll_server,
178 std::unique_ptr<ProofVerifier> proof_verifier)
179 : QboneClient(server_address,
180 server_id,
181 supported_versions,
182 /*session_owner=*/nullptr,
183 QuicConfig(),
184 epoll_server,
185 std::move(proof_verifier),
186 &qbone_writer_,
187 nullptr) {}
188
~QboneTestClient()189 ~QboneTestClient() override {}
190
SendData(const std::string & data)191 void SendData(const std::string& data) {
192 qbone_session()->ProcessPacketFromNetwork(data);
193 }
194
WaitForWriteToFlush()195 void WaitForWriteToFlush() {
196 while (connected() && session()->HasDataToWrite()) {
197 WaitForEvents();
198 }
199 }
200
201 // Returns true when the data size is reached or false on timeouts.
WaitForDataSize(int n,QuicTime::Delta timeout)202 bool WaitForDataSize(int n, QuicTime::Delta timeout) {
203 const QuicClock* clock =
204 quic::test::QuicConnectionPeer::GetHelper(session()->connection())
205 ->GetClock();
206 const QuicTime deadline = clock->Now() + timeout;
207 while (data().size() < n) {
208 if (clock->Now() > deadline) {
209 return false;
210 }
211 WaitForEvents();
212 }
213 return true;
214 }
215
data()216 std::vector<std::string> data() { return qbone_writer_.data(); }
217
218 private:
219 DataSavingQbonePacketWriter qbone_writer_;
220 };
221
222 class QboneClientTest : public QuicTestWithParam<ParsedQuicVersion> {};
223
224 INSTANTIATE_TEST_SUITE_P(Tests,
225 QboneClientTest,
226 ::testing::ValuesIn(GetTestParams()),
227 ::testing::PrintToStringParamName());
228
TEST_P(QboneClientTest,SendDataFromClient)229 TEST_P(QboneClientTest, SendDataFromClient) {
230 auto server = new QboneTestServer(crypto_test_utils::ProofSourceForTesting());
231 QuicSocketAddress server_address(TestLoopback(),
232 QuicPickServerPortForTestsOrDie());
233 ServerThread server_thread(server, server_address);
234 server_thread.Initialize();
235 server_address =
236 QuicSocketAddress(server_address.host(), server_thread.GetPort());
237 server_thread.Start();
238
239 QuicEpollServer epoll_server;
240 QboneTestClient client(
241 server_address,
242 QuicServerId("test.example.com", server_address.port(), false),
243 ParsedQuicVersionVector{GetParam()}, &epoll_server,
244 crypto_test_utils::ProofVerifierForTesting());
245 ASSERT_TRUE(client.Initialize());
246 ASSERT_TRUE(client.Connect());
247 ASSERT_TRUE(client.WaitForOneRttKeysAvailable());
248 client.SendData(TestPacketIn("hello"));
249 client.SendData(TestPacketIn("world"));
250 client.WaitForWriteToFlush();
251
252 // Wait until the server has received at least two packets, timeout after 5s.
253 ASSERT_TRUE(
254 server_thread.WaitUntil([&] { return server->data().size() >= 2; },
255 QuicTime::Delta::FromSeconds(5)));
256
257 std::string long_data(
258 QboneConstants::kMaxQbonePacketBytes - sizeof(ip6_hdr) - 1, 'A');
259
260 // Pretend the server gets data.
261 server_thread.Schedule([&server, &long_data]() {
262 EXPECT_THAT(server->data()[0], testing::Eq(TestPacketOut("hello")));
263 EXPECT_THAT(server->data()[1], testing::Eq(TestPacketOut("world")));
264 auto server_session =
265 static_cast<QboneServerSession*>(QuicServerPeer::GetDispatcher(server)
266 ->session_map()
267 .begin()
268 ->second.get());
269 server_session->ProcessPacketFromNetwork(
270 TestPacketIn("Somethingsomething"));
271 server_session->ProcessPacketFromNetwork(TestPacketIn(long_data));
272 server_session->ProcessPacketFromNetwork(TestPacketIn(long_data));
273 });
274 ASSERT_TRUE(client.WaitForDataSize(3, QuicTime::Delta::FromSeconds(5)));
275 EXPECT_THAT(client.data()[0],
276 testing::Eq(TestPacketOut("Somethingsomething")));
277 EXPECT_THAT(client.data()[1], testing::Eq(TestPacketOut(long_data)));
278 EXPECT_THAT(client.data()[2], testing::Eq(TestPacketOut(long_data)));
279
280 client.Disconnect();
281 server_thread.Quit();
282 server_thread.Join();
283 }
284
285 } // namespace
286 } // namespace test
287 } // namespace quic
288