blob: 17c9e4d69e849b1166c931392bd9c38f6b01bc18 [file] [log] [blame]
Adrien Béraudefe27372023-05-27 18:56:29 -04001/*
Adrien Béraudcb753622023-07-17 22:32:49 -04002 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
Adrien Béraudefe27372023-05-27 18:56:29 -04003 *
Adrien Béraudcb753622023-07-17 22:32:49 -04004 * This program is free software: you can redistribute it and/or modify
Adrien Béraudefe27372023-05-27 18:56:29 -04005 * it under the terms of the GNU General Public License as published by
Adrien Béraudcb753622023-07-17 22:32:49 -04006 * the Free Software Foundation, either version 3 of the License, or
Adrien Béraudefe27372023-05-27 18:56:29 -04007 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Adrien Béraudcb753622023-07-17 22:32:49 -040011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Adrien Béraudefe27372023-05-27 18:56:29 -040012 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
Adrien Béraudefe27372023-05-27 18:56:29 -040017
18#include "connectionmanager.h"
19#include "multiplexed_socket.h"
20#include "test_runner.h"
Morteza Namvar82960b32023-07-04 17:08:22 -040021#include "certstore.h"
Adrien Béraudefe27372023-05-27 18:56:29 -040022
Adrien Béraud027af2a2023-08-27 12:08:50 -040023#include <opendht/log.h>
24#include <asio/executor_work_guard.hpp>
25#include <asio/io_context.hpp>
Adrien Béraud75754b22023-10-17 09:16:06 -040026#include <fmt/compile.h>
Adrien Béraud027af2a2023-08-27 12:08:50 -040027
28#include <cppunit/TestAssert.h>
29#include <cppunit/TestFixture.h>
30#include <cppunit/extensions/HelperMacros.h>
31
32#include <condition_variable>
33#include <iostream>
34#include <filesystem>
35
Adrien Béraudefe27372023-05-27 18:56:29 -040036using namespace std::literals::chrono_literals;
37
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040038namespace dhtnet {
Adrien Béraudefe27372023-05-27 18:56:29 -040039namespace test {
40
Amnab8c33bb2023-08-03 14:40:01 -040041struct ConnectionHandler
42{
Morteza Namvar82960b32023-07-04 17:08:22 -040043 dht::crypto::Identity id;
44 std::shared_ptr<Logger> logger;
45 std::shared_ptr<tls::CertificateStore> certStore;
46 std::shared_ptr<dht::DhtRunner> dht;
47 std::shared_ptr<ConnectionManager> connectionManager;
48 std::shared_ptr<asio::io_context> ioContext;
Amnab8c33bb2023-08-03 14:40:01 -040049 std::shared_ptr<std::thread> ioContextRunner;
Morteza Namvar82960b32023-07-04 17:08:22 -040050};
51
Adrien Béraudefe27372023-05-27 18:56:29 -040052class ConnectionManagerTest : public CppUnit::TestFixture
53{
54public:
Adrien Béraud4796de12023-09-25 14:46:47 -040055 ConnectionManagerTest() {
56 pj_log_set_level(0);
57 pj_log_set_log_func([](int level, const char* data, int /*len*/) {});
58 // logger->debug("Using PJSIP version {} for {}", pj_get_version(), PJ_OS_NAME);
59 // logger->debug("Using GnuTLS version {}", gnutls_check_version(nullptr));
60 // logger->debug("Using OpenDHT version {}", dht::version());
François-Simon Fauteux-Chapleau91761312024-03-25 14:29:25 -040061 testDir_ = std::filesystem::current_path() / "tmp_tests_connectionManager";
Adrien Béraud4796de12023-09-25 14:46:47 -040062 }
Amnab8c33bb2023-08-03 14:40:01 -040063 ~ConnectionManagerTest() {}
Adrien Béraudefe27372023-05-27 18:56:29 -040064 static std::string name() { return "ConnectionManager"; }
65 void setUp();
66 void tearDown();
67
Adrien Béraud4796de12023-09-25 14:46:47 -040068 std::shared_ptr<dht::DhtRunner> bootstrap_node;
69 dht::crypto::Identity org1Id, org2Id;
70 dht::crypto::Identity aliceId, bobId;
71 dht::crypto::Identity aliceDevice1Id, bobDevice1Id;
72
Morteza Namvar82960b32023-07-04 17:08:22 -040073 std::unique_ptr<ConnectionHandler> alice;
74 std::unique_ptr<ConnectionHandler> bob;
Adrien Béraudefe27372023-05-27 18:56:29 -040075
Morteza Namvar82960b32023-07-04 17:08:22 -040076 std::mutex mtx;
77 std::shared_ptr<asio::io_context> ioContext;
Amnab8c33bb2023-08-03 14:40:01 -040078 std::shared_ptr<std::thread> ioContextRunner;
Adrien Béraud4796de12023-09-25 14:46:47 -040079 std::shared_ptr<Logger> logger = dht::log::getStdLogger();
Amna81221ad2023-09-14 17:33:26 -040080 std::shared_ptr<IceTransportFactory> factory;
Morteza Namvar82960b32023-07-04 17:08:22 -040081
Amnab8c33bb2023-08-03 14:40:01 -040082private:
Adrien Béraud4796de12023-09-25 14:46:47 -040083 std::unique_ptr<ConnectionHandler> setupHandler(const dht::crypto::Identity& id, const std::string& bootstrap = "bootstrap.jami.net");
François-Simon Fauteux-Chapleau91761312024-03-25 14:29:25 -040084 std::filesystem::path testDir_;
Morteza Namvar82960b32023-07-04 17:08:22 -040085
Amnab8c33bb2023-08-03 14:40:01 -040086 void testConnectDevice();
87 void testAcceptConnection();
Adrien Béraud8d787732023-10-16 12:58:17 -040088 void testManyChannels();
Amnab8c33bb2023-08-03 14:40:01 -040089 void testMultipleChannels();
90 void testMultipleChannelsOneDeclined();
91 void testMultipleChannelsSameName();
92 void testDeclineConnection();
93 void testSendReceiveData();
94 void testAcceptsICERequest();
95 void testDeclineICERequest();
96 void testChannelRcvShutdown();
97 void testChannelSenderShutdown();
98 void testCloseConnectionWith();
99 void testShutdownCallbacks();
100 void testFloodSocket();
101 void testDestroyWhileSending();
102 void testIsConnecting();
Sébastien Blind0c92c72023-12-07 15:27:51 -0500103 void testIsConnected();
Amnab8c33bb2023-08-03 14:40:01 -0400104 void testCanSendBeacon();
105 void testCannotSendBeacon();
106 void testConnectivityChangeTriggerBeacon();
107 void testOnNoBeaconTriggersShutdown();
108 void testShutdownWhileNegotiating();
109 void testGetChannelList();
Sébastien Blind0c92c72023-12-07 15:27:51 -0500110
Adrien Béraudefe27372023-05-27 18:56:29 -0400111 CPPUNIT_TEST_SUITE(ConnectionManagerTest);
Amnab8c33bb2023-08-03 14:40:01 -0400112 CPPUNIT_TEST(testDeclineICERequest);
113 CPPUNIT_TEST(testConnectDevice);
114 CPPUNIT_TEST(testIsConnecting);
Sébastien Blind0c92c72023-12-07 15:27:51 -0500115 CPPUNIT_TEST(testIsConnected);
Amnab8c33bb2023-08-03 14:40:01 -0400116 CPPUNIT_TEST(testAcceptConnection);
117 CPPUNIT_TEST(testDeclineConnection);
Sébastien Blin55be5da2024-02-12 11:29:54 -0500118 // [[disabled-sporadic failures]]CPPUNIT_TEST(testManyChannels);
Amnab8c33bb2023-08-03 14:40:01 -0400119 CPPUNIT_TEST(testMultipleChannels);
120 CPPUNIT_TEST(testMultipleChannelsOneDeclined);
121 CPPUNIT_TEST(testMultipleChannelsSameName);
122 CPPUNIT_TEST(testSendReceiveData);
123 CPPUNIT_TEST(testAcceptsICERequest);
124 CPPUNIT_TEST(testChannelRcvShutdown);
125 CPPUNIT_TEST(testChannelSenderShutdown);
126 CPPUNIT_TEST(testCloseConnectionWith);
127 CPPUNIT_TEST(testShutdownCallbacks);
128 CPPUNIT_TEST(testFloodSocket);
129 CPPUNIT_TEST(testDestroyWhileSending);
130 CPPUNIT_TEST(testCanSendBeacon);
131 CPPUNIT_TEST(testCannotSendBeacon);
132 CPPUNIT_TEST(testConnectivityChangeTriggerBeacon);
133 CPPUNIT_TEST(testOnNoBeaconTriggersShutdown);
134 CPPUNIT_TEST(testShutdownWhileNegotiating);
135 CPPUNIT_TEST(testGetChannelList);
Adrien Béraudefe27372023-05-27 18:56:29 -0400136 CPPUNIT_TEST_SUITE_END();
137};
138
139CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ConnectionManagerTest, ConnectionManagerTest::name());
140
Morteza Namvar82960b32023-07-04 17:08:22 -0400141std::unique_ptr<ConnectionHandler>
Adrien Béraud4796de12023-09-25 14:46:47 -0400142ConnectionManagerTest::setupHandler(const dht::crypto::Identity& id, const std::string& bootstrap)
Amnab8c33bb2023-08-03 14:40:01 -0400143{
Morteza Namvar82960b32023-07-04 17:08:22 -0400144 auto h = std::make_unique<ConnectionHandler>();
Adrien Béraud4796de12023-09-25 14:46:47 -0400145 h->id = id;
146 h->logger = {};//logger;
François-Simon Fauteux-Chapleau91761312024-03-25 14:29:25 -0400147 h->certStore = std::make_shared<tls::CertificateStore>(testDir_ / id.second->getName(), nullptr/*h->logger*/);
Amnab8c33bb2023-08-03 14:40:01 -0400148 h->ioContext = ioContext;
Amnab8c33bb2023-08-03 14:40:01 -0400149 h->ioContextRunner = ioContextRunner;
Morteza Namvar82960b32023-07-04 17:08:22 -0400150
151 dht::DhtRunner::Config dhtConfig;
152 dhtConfig.dht_config.id = h->id;
153 dhtConfig.threaded = true;
154
155 dht::DhtRunner::Context dhtContext;
Amnab8c33bb2023-08-03 14:40:01 -0400156 dhtContext.certificateStore = [c = h->certStore](const dht::InfoHash& pk_id) {
Morteza Namvar82960b32023-07-04 17:08:22 -0400157 std::vector<std::shared_ptr<dht::crypto::Certificate>> ret;
158 if (auto cert = c->getCertificate(pk_id.toString()))
159 ret.emplace_back(std::move(cert));
160 return ret;
161 };
Amnab8c33bb2023-08-03 14:40:01 -0400162 // dhtContext.logger = h->logger;
Morteza Namvar82960b32023-07-04 17:08:22 -0400163
164 h->dht = std::make_shared<dht::DhtRunner>();
165 h->dht->run(dhtConfig, std::move(dhtContext));
Adrien Béraud4796de12023-09-25 14:46:47 -0400166 h->dht->bootstrap(bootstrap);
Morteza Namvar82960b32023-07-04 17:08:22 -0400167
168 auto config = std::make_shared<ConnectionManager::Config>();
169 config->dht = h->dht;
170 config->id = h->id;
171 config->ioContext = h->ioContext;
Amna81221ad2023-09-14 17:33:26 -0400172 config->factory = factory;
Adrien Béraud4796de12023-09-25 14:46:47 -0400173 // config->logger = logger;
Amna81221ad2023-09-14 17:33:26 -0400174 config->certStore = h->certStore;
François-Simon Fauteux-Chapleau91761312024-03-25 14:29:25 -0400175 config->cachePath = testDir_ / id.second->getName() / "temp";
Morteza Namvar82960b32023-07-04 17:08:22 -0400176
177 h->connectionManager = std::make_shared<ConnectionManager>(config);
Amnab8c33bb2023-08-03 14:40:01 -0400178 h->connectionManager->onICERequest([](const DeviceId&) { return true; });
Adrien Béraud4796de12023-09-25 14:46:47 -0400179 h->connectionManager->onDhtConnected(h->id.first->getPublicKey());
180
Morteza Namvar82960b32023-07-04 17:08:22 -0400181 return h;
182}
183
Adrien Béraudefe27372023-05-27 18:56:29 -0400184void
185ConnectionManagerTest::setUp()
186{
Adrien Béraud4796de12023-09-25 14:46:47 -0400187 if (not org1Id.first) {
188 org1Id = dht::crypto::generateIdentity("org1");
189 org2Id = dht::crypto::generateIdentity("org2");
190 aliceId = dht::crypto::generateIdentity("alice", org1Id, 2048, true);
191 bobId = dht::crypto::generateIdentity("bob", org2Id, 2048, true);
192 aliceDevice1Id = dht::crypto::generateIdentity("aliceDevice1", aliceId);
193 bobDevice1Id = dht::crypto::generateIdentity("bobDevice1", bobId);
194 }
Adrien Béraudc631a832023-07-26 22:19:00 -0400195
Morteza Namvar82960b32023-07-04 17:08:22 -0400196 ioContext = std::make_shared<asio::io_context>();
Amnab8c33bb2023-08-03 14:40:01 -0400197 ioContextRunner = std::make_shared<std::thread>([context = ioContext]() {
Morteza Namvar82960b32023-07-04 17:08:22 -0400198 try {
199 auto work = asio::make_work_guard(*context);
200 context->run();
201 } catch (const std::exception& ex) {
Adrien Béraud4796de12023-09-25 14:46:47 -0400202 fmt::print("Exception in ioContextRunner: {}\n", ex.what());
Morteza Namvar82960b32023-07-04 17:08:22 -0400203 }
204 });
Adrien Béraud4796de12023-09-25 14:46:47 -0400205 bootstrap_node = std::make_shared<dht::DhtRunner>();
206 bootstrap_node->run(36432);
Sébastien Blind0c92c72023-12-07 15:27:51 -0500207
Adrien Béraud4796de12023-09-25 14:46:47 -0400208 factory = std::make_unique<IceTransportFactory>(/*logger*/);
209 alice = setupHandler(aliceDevice1Id, "127.0.0.1:36432");
210 bob = setupHandler(bobDevice1Id, "127.0.0.1:36432");
Adrien Béraudefe27372023-05-27 18:56:29 -0400211}
212
213void
214ConnectionManagerTest::tearDown()
215{
Amnab8c33bb2023-08-03 14:40:01 -0400216 // wait_for_removal_of({aliceId, bobId});
217 // Stop the io_context and join the ioContextRunner thread
Morteza Namvar82960b32023-07-04 17:08:22 -0400218 ioContext->stop();
Amnab8c33bb2023-08-03 14:40:01 -0400219
220 if (ioContextRunner && ioContextRunner->joinable()) {
221 ioContextRunner->join();
222 }
Adrien Béraud4796de12023-09-25 14:46:47 -0400223
224 bootstrap_node.reset();
225 alice.reset();
226 bob.reset();
227 factory.reset();
François-Simon Fauteux-Chapleau91761312024-03-25 14:29:25 -0400228 std::filesystem::remove_all(testDir_);
Adrien Béraudefe27372023-05-27 18:56:29 -0400229}
Adrien Béraudefe27372023-05-27 18:56:29 -0400230void
Morteza Namvar82960b32023-07-04 17:08:22 -0400231ConnectionManagerTest::testConnectDevice()
Adrien Béraudefe27372023-05-27 18:56:29 -0400232{
Morteza Namvar82960b32023-07-04 17:08:22 -0400233 std::condition_variable bobConVar;
234 bool isBobRecvChanlReq = false;
Amnab8c33bb2023-08-03 14:40:01 -0400235 bob->connectionManager->onChannelRequest(
Adrien Béraud4796de12023-09-25 14:46:47 -0400236 [&](const std::shared_ptr<dht::crypto::Certificate>&,
Amnab8c33bb2023-08-03 14:40:01 -0400237 const std::string& name) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500238 std::lock_guard lock {mtx};
Sébastien Blin55be5da2024-02-12 11:29:54 -0500239 isBobRecvChanlReq = name == "dummyName";
Amnab8c33bb2023-08-03 14:40:01 -0400240 bobConVar.notify_one();
241 return true;
242 });
Adrien Béraudefe27372023-05-27 18:56:29 -0400243
Morteza Namvar82960b32023-07-04 17:08:22 -0400244 std::condition_variable alicConVar;
245 bool isAlicConnected = false;
Sébastien Blin55be5da2024-02-12 11:29:54 -0500246 alice->connectionManager->connectDevice(bob->id.second, "dummyName", [&](std::shared_ptr<ChannelSocket> socket, const DeviceId&) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500247 std::lock_guard lock {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400248 if (socket) {
249 isAlicConnected = true;
250 }
Morteza Namvar82960b32023-07-04 17:08:22 -0400251 alicConVar.notify_one();
Adrien Béraud4796de12023-09-25 14:46:47 -0400252 });
Adrien Béraudefe27372023-05-27 18:56:29 -0400253
Adrien Béraud024c46f2024-03-02 23:53:18 -0500254 std::unique_lock lock {mtx};
Adrien Béraud4796de12023-09-25 14:46:47 -0400255 CPPUNIT_ASSERT(bobConVar.wait_for(lock, 30s, [&] { return isBobRecvChanlReq; }));
256 CPPUNIT_ASSERT(alicConVar.wait_for(lock, 30s, [&] { return isAlicConnected; }));
Adrien Béraudefe27372023-05-27 18:56:29 -0400257}
258
Amnab8c33bb2023-08-03 14:40:01 -0400259void
260ConnectionManagerTest::testAcceptConnection()
261{
Adrien Béraud024c46f2024-03-02 23:53:18 -0500262 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400263 std::condition_variable cv;
264 bool successfullyConnected = false;
265 bool successfullyReceive = false;
266 bool receiverConnected = false;
Adrien Béraudefe27372023-05-27 18:56:29 -0400267
Amnab8c33bb2023-08-03 14:40:01 -0400268 bob->connectionManager->onChannelRequest(
269 [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
270 const std::string& name) {
271 successfullyReceive = name == "git://*";
272 return true;
273 });
Adrien Béraudefe27372023-05-27 18:56:29 -0400274
Amnab8c33bb2023-08-03 14:40:01 -0400275 bob->connectionManager->onConnectionReady(
276 [&receiverConnected](const DeviceId&,
277 const std::string& name,
278 std::shared_ptr<ChannelSocket> socket) {
279 receiverConnected = socket && (name == "git://*");
280 });
Adrien Béraudefe27372023-05-27 18:56:29 -0400281
Amnab8c33bb2023-08-03 14:40:01 -0400282 alice->connectionManager->connectDevice(bob->id.second,
283 "git://*",
284 [&](std::shared_ptr<ChannelSocket> socket,
285 const DeviceId&) {
286 if (socket) {
287 successfullyConnected = true;
288 }
289 cv.notify_one();
290 });
291 CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
292 return successfullyReceive && successfullyConnected && receiverConnected;
293 }));
294}
Adrien Béraudefe27372023-05-27 18:56:29 -0400295
Amnab8c33bb2023-08-03 14:40:01 -0400296void
297ConnectionManagerTest::testDeclineConnection()
298{
Amnab8c33bb2023-08-03 14:40:01 -0400299 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
300 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Adrien Béraudefe27372023-05-27 18:56:29 -0400301
Adrien Béraud024c46f2024-03-02 23:53:18 -0500302 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400303 std::condition_variable cv;
Adrien Béraud4796de12023-09-25 14:46:47 -0400304 bool connectCompleted = false;
Amnab8c33bb2023-08-03 14:40:01 -0400305 bool successfullyConnected = false;
306 bool successfullyReceive = false;
307 bool receiverConnected = false;
Adrien Béraudefe27372023-05-27 18:56:29 -0400308
Amnab8c33bb2023-08-03 14:40:01 -0400309 bob->connectionManager->onChannelRequest(
Adrien Béraud4796de12023-09-25 14:46:47 -0400310 [&](const std::shared_ptr<dht::crypto::Certificate>&,
Amnab8c33bb2023-08-03 14:40:01 -0400311 const std::string&) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500312 std::lock_guard lock {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400313 successfullyReceive = true;
Adrien Béraud4796de12023-09-25 14:46:47 -0400314 cv.notify_one();
Amnab8c33bb2023-08-03 14:40:01 -0400315 return false;
316 });
Adrien Béraudefe27372023-05-27 18:56:29 -0400317
Amnab8c33bb2023-08-03 14:40:01 -0400318 bob->connectionManager->onConnectionReady(
319 [&receiverConnected](const DeviceId&,
320 const std::string&,
321 std::shared_ptr<ChannelSocket> socket) {
322 if (socket)
323 receiverConnected = true;
324 });
Adrien Béraudefe27372023-05-27 18:56:29 -0400325
Amnab8c33bb2023-08-03 14:40:01 -0400326 alice->connectionManager->connectDevice(bob->id.second,
327 "git://*",
328 [&](std::shared_ptr<ChannelSocket> socket,
329 const DeviceId&) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500330 std::lock_guard lock {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400331 if (socket) {
332 successfullyConnected = true;
333 }
Adrien Béraud4796de12023-09-25 14:46:47 -0400334 connectCompleted = true;
Amnab8c33bb2023-08-03 14:40:01 -0400335 cv.notify_one();
336 });
Adrien Béraud4796de12023-09-25 14:46:47 -0400337 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyReceive; }));
338 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return connectCompleted; }));
Amnab8c33bb2023-08-03 14:40:01 -0400339 CPPUNIT_ASSERT(!successfullyConnected);
340 CPPUNIT_ASSERT(!receiverConnected);
341}
Adrien Béraudefe27372023-05-27 18:56:29 -0400342
Adrien Béraud8d787732023-10-16 12:58:17 -0400343
344void
345ConnectionManagerTest::testManyChannels()
346{
347 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
348 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
349
350 std::condition_variable cv;
351 size_t successfullyConnected = 0;
352 size_t accepted = 0;
353 size_t receiverConnected = 0;
354 size_t successfullyReceived = 0;
355 size_t shutdownCount = 0;
356
357 auto acceptAll = [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& name) {
358 if (name.empty()) return false;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500359 std::lock_guard lk {mtx};
Adrien Béraud8d787732023-10-16 12:58:17 -0400360 accepted++;
361 cv.notify_one();
362 return true;
363 };
364 bob->connectionManager->onChannelRequest(acceptAll);
365 alice->connectionManager->onChannelRequest(acceptAll);
366
367 auto onReady = [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
368 if (not socket or name.empty()) return;
369 if (socket->isInitiator())
370 return;
371 socket->setOnRecv([rxbuf = std::make_shared<std::vector<uint8_t>>(), w = std::weak_ptr(socket)](const uint8_t* data, size_t size) {
372 rxbuf->insert(rxbuf->end(), data, data + size);
373 if (rxbuf->size() == 32) {
374 if (auto socket = w.lock()) {
375 std::error_code ec;
376 socket->write(rxbuf->data(), rxbuf->size(), ec);
377 CPPUNIT_ASSERT(!ec);
378 socket->shutdown();
379 }
380 }
381 return size;
382 });
Adrien Béraud024c46f2024-03-02 23:53:18 -0500383 std::lock_guard lk {mtx};
Adrien Béraud8d787732023-10-16 12:58:17 -0400384 receiverConnected++;
385 cv.notify_one();
386 };
387 bob->connectionManager->onConnectionReady(onReady);
388 alice->connectionManager->onConnectionReady(onReady);
389
390 // max supported number of channels per side (64k - 2 reserved channels)
391 static constexpr size_t N = 1024 * 32 - 1;
392
393 auto onConnect = [&](std::shared_ptr<ChannelSocket> socket, const DeviceId&) {
394 CPPUNIT_ASSERT(socket);
395 if (socket) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500396 std::lock_guard lk {mtx};
Adrien Béraud8d787732023-10-16 12:58:17 -0400397 successfullyConnected++;
398 cv.notify_one();
399 }
400 auto data_sent = dht::PkId::get(socket->name());
401 socket->setOnRecv([&, data_sent, rxbuf = std::make_shared<std::vector<uint8_t>>()](const uint8_t* data, size_t size) {
402 rxbuf->insert(rxbuf->end(), data, data + size);
403 if (rxbuf->size() == 32) {
404 CPPUNIT_ASSERT(!std::memcmp(data_sent.data(), rxbuf->data(), data_sent.size()));
Adrien Béraud024c46f2024-03-02 23:53:18 -0500405 std::lock_guard lk {mtx};
Adrien Béraud8d787732023-10-16 12:58:17 -0400406 successfullyReceived++;
407 cv.notify_one();
408 }
409 return size;
410 });
411 socket->onShutdown([&]() {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500412 std::lock_guard lk {mtx};
Adrien Béraud8d787732023-10-16 12:58:17 -0400413 shutdownCount++;
414 cv.notify_one();
415 });
416 std::error_code ec;
417 socket->write(data_sent.data(), data_sent.size(), ec);
418 CPPUNIT_ASSERT(!ec);
419 };
420
421 for (size_t i = 0; i < N; ++i) {
422 alice->connectionManager->connectDevice(bob->id.second,
423 fmt::format("git://{}", i+1),
424 onConnect);
425
426 bob->connectionManager->connectDevice(alice->id.second,
427 fmt::format("sip://{}", i+1),
428 onConnect);
429
430 if (i % 128 == 0)
431 std::this_thread::sleep_for(5ms);
432 }
433
Adrien Béraud024c46f2024-03-02 23:53:18 -0500434 std::unique_lock lk {mtx};
Adrien Béraud8d787732023-10-16 12:58:17 -0400435 cv.wait_for(lk, 30s, [&] { return successfullyConnected == N * 2; });
436 CPPUNIT_ASSERT_EQUAL(N * 2, successfullyConnected);
437 cv.wait_for(lk, 30s, [&] { return accepted == N * 2; });
438 CPPUNIT_ASSERT_EQUAL(N * 2, accepted);
439 cv.wait_for(lk, 20s, [&] { return receiverConnected == N * 2; });
440 CPPUNIT_ASSERT_EQUAL(N * 2, receiverConnected);
441 cv.wait_for(lk, 60s, [&] { return successfullyReceived == N * 2; });
442 CPPUNIT_ASSERT_EQUAL(N * 2, successfullyReceived);
443 cv.wait_for(lk, 60s, [&] { return shutdownCount == N * 2; });
444 CPPUNIT_ASSERT_EQUAL(N * 2, shutdownCount);
445 lk.unlock();
446
447 // Wait a bit to let at least some channels shutdown
448 std::this_thread::sleep_for(10ms);
449
450 // Second time to make sure we can re-use the channels after shutdown
451 for (size_t i = 0; i < N; ++i) {
452 alice->connectionManager->connectDevice(bob->id.second,
453 fmt::format("git://{}", N+i+1),
454 onConnect);
455
456 bob->connectionManager->connectDevice(alice->id.second,
457 fmt::format("sip://{}", N+i+1),
458 onConnect);
459
460 if (i % 128 == 0)
461 std::this_thread::sleep_for(5ms);
462 }
463
464 lk.lock();
465 cv.wait_for(lk, 30s, [&] { return successfullyConnected == N * 4; });
466 CPPUNIT_ASSERT_EQUAL(N * 4, successfullyConnected);
467 cv.wait_for(lk, 30s, [&] { return accepted == N * 4; });
468 CPPUNIT_ASSERT_EQUAL(N * 4, accepted);
469 cv.wait_for(lk, 20s, [&] { return receiverConnected == N * 4; });
470 CPPUNIT_ASSERT_EQUAL(N * 4, receiverConnected);
471 cv.wait_for(lk, 60s, [&] { return successfullyReceived == N * 4; });
472 CPPUNIT_ASSERT_EQUAL(N * 4, successfullyReceived);
473 cv.wait_for(lk, 60s, [&] { return shutdownCount == N * 4; });
474 CPPUNIT_ASSERT_EQUAL(N * 4, shutdownCount);
475}
476
Amnab8c33bb2023-08-03 14:40:01 -0400477void
478ConnectionManagerTest::testMultipleChannels()
479{
Amnab8c33bb2023-08-03 14:40:01 -0400480 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
481 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Adrien Béraudefe27372023-05-27 18:56:29 -0400482
Amnab8c33bb2023-08-03 14:40:01 -0400483 std::condition_variable cv;
484 bool successfullyConnected = false;
485 bool successfullyConnected2 = false;
486 int receiverConnected = 0;
Adrien Béraudefe27372023-05-27 18:56:29 -0400487
Amnab8c33bb2023-08-03 14:40:01 -0400488 bob->connectionManager->onChannelRequest(
489 [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
Adrien Béraudefe27372023-05-27 18:56:29 -0400490
Amnab8c33bb2023-08-03 14:40:01 -0400491 bob->connectionManager->onConnectionReady(
Adrien Béraud4796de12023-09-25 14:46:47 -0400492 [&](const DeviceId&, const std::string& name,
Amnab8c33bb2023-08-03 14:40:01 -0400493 std::shared_ptr<ChannelSocket> socket) {
Adrien Béraud4796de12023-09-25 14:46:47 -0400494 if (not name.empty()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500495 std::lock_guard lk {mtx};
Adrien Béraud4796de12023-09-25 14:46:47 -0400496 if (socket)
497 receiverConnected += 1;
498 cv.notify_one();
499 }
Amnab8c33bb2023-08-03 14:40:01 -0400500 });
Adrien Béraudefe27372023-05-27 18:56:29 -0400501
Amnab8c33bb2023-08-03 14:40:01 -0400502 alice->connectionManager->connectDevice(bob->id.second,
503 "git://*",
504 [&](std::shared_ptr<ChannelSocket> socket,
505 const DeviceId&) {
506 if (socket) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500507 std::lock_guard lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400508 successfullyConnected = true;
Adrien Béraud4796de12023-09-25 14:46:47 -0400509 cv.notify_one();
Amnab8c33bb2023-08-03 14:40:01 -0400510 }
Amnab8c33bb2023-08-03 14:40:01 -0400511 });
Adrien Béraudefe27372023-05-27 18:56:29 -0400512
Amnab8c33bb2023-08-03 14:40:01 -0400513 alice->connectionManager->connectDevice(bob->id.second,
514 "sip://*",
515 [&](std::shared_ptr<ChannelSocket> socket,
516 const DeviceId&) {
517 if (socket) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500518 std::lock_guard lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400519 successfullyConnected2 = true;
Adrien Béraud4796de12023-09-25 14:46:47 -0400520 cv.notify_one();
Amnab8c33bb2023-08-03 14:40:01 -0400521 }
Amnab8c33bb2023-08-03 14:40:01 -0400522 });
Adrien Béraudefe27372023-05-27 18:56:29 -0400523
Adrien Béraud024c46f2024-03-02 23:53:18 -0500524 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400525 CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
526 return successfullyConnected && successfullyConnected2 && receiverConnected == 2;
527 }));
528 CPPUNIT_ASSERT(alice->connectionManager->activeSockets() == 1);
529}
Adrien Béraudefe27372023-05-27 18:56:29 -0400530
Amnab8c33bb2023-08-03 14:40:01 -0400531void
532ConnectionManagerTest::testMultipleChannelsOneDeclined()
533{
Amnab8c33bb2023-08-03 14:40:01 -0400534 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
535 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Adrien Béraudefe27372023-05-27 18:56:29 -0400536
Adrien Béraud024c46f2024-03-02 23:53:18 -0500537 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400538 std::condition_variable cv;
539 bool successfullyNotConnected = false;
540 bool successfullyConnected2 = false;
541 int receiverConnected = 0;
Adrien Béraudefe27372023-05-27 18:56:29 -0400542
Amnab8c33bb2023-08-03 14:40:01 -0400543 bob->connectionManager->onChannelRequest(
544 [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& name) {
545 if (name == "git://*")
546 return false;
547 return true;
548 });
Adrien Béraudefe27372023-05-27 18:56:29 -0400549
Amnab8c33bb2023-08-03 14:40:01 -0400550 bob->connectionManager->onConnectionReady(
551 [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
552 if (socket)
553 receiverConnected += 1;
554 cv.notify_one();
555 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400556
Amnab8c33bb2023-08-03 14:40:01 -0400557 alice->connectionManager->connectDevice(bob->id.second,
558 "git://*",
559 [&](std::shared_ptr<ChannelSocket> socket,
560 const DeviceId&) {
561 if (!socket)
562 successfullyNotConnected = true;
563 cv.notify_one();
564 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400565
Amnab8c33bb2023-08-03 14:40:01 -0400566 alice->connectionManager->connectDevice(bob->id.second,
567 "sip://*",
568 [&](std::shared_ptr<ChannelSocket> socket,
569 const DeviceId&) {
570 if (socket)
571 successfullyConnected2 = true;
572 cv.notify_one();
573 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400574
Amnab8c33bb2023-08-03 14:40:01 -0400575 CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
576 return successfullyNotConnected && successfullyConnected2 && receiverConnected == 1;
577 }));
578 CPPUNIT_ASSERT(alice->connectionManager->activeSockets() == 1);
579}
Morteza Namvar82960b32023-07-04 17:08:22 -0400580
Amnab8c33bb2023-08-03 14:40:01 -0400581void
582ConnectionManagerTest::testMultipleChannelsSameName()
583{
Amnab8c33bb2023-08-03 14:40:01 -0400584 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
585 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -0400586
Adrien Béraud024c46f2024-03-02 23:53:18 -0500587 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400588 std::condition_variable cv;
589 bool successfullyConnected = false;
590 bool successfullyConnected2 = false;
591 int receiverConnected = 0;
Morteza Namvar82960b32023-07-04 17:08:22 -0400592
Amnab8c33bb2023-08-03 14:40:01 -0400593 bob->connectionManager->onChannelRequest(
594 [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -0400595
Amnab8c33bb2023-08-03 14:40:01 -0400596 bob->connectionManager->onConnectionReady(
597 [&receiverConnected](const DeviceId&,
598 const std::string&,
599 std::shared_ptr<ChannelSocket> socket) {
600 if (socket)
601 receiverConnected += 1;
602 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400603
Amnab8c33bb2023-08-03 14:40:01 -0400604 alice->connectionManager->connectDevice(bob->id.second,
605 "git://*",
606 [&](std::shared_ptr<ChannelSocket> socket,
607 const DeviceId&) {
608 if (socket) {
609 successfullyConnected = true;
610 }
611 cv.notify_one();
612 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400613
Amnab8c33bb2023-08-03 14:40:01 -0400614 // We can open two sockets with the same name, it will be two different channel
615 alice->connectionManager->connectDevice(bob->id.second,
616 "git://*",
617 [&](std::shared_ptr<ChannelSocket> socket,
618 const DeviceId&) {
619 if (socket) {
620 successfullyConnected2 = true;
621 }
622 cv.notify_one();
623 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400624
Amnab8c33bb2023-08-03 14:40:01 -0400625 CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
626 return successfullyConnected && successfullyConnected2 && receiverConnected == 2;
627 }));
628}
Morteza Namvar82960b32023-07-04 17:08:22 -0400629
Amnab8c33bb2023-08-03 14:40:01 -0400630void
631ConnectionManagerTest::testSendReceiveData()
632{
Amnab8c33bb2023-08-03 14:40:01 -0400633 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
634 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -0400635
Adrien Béraud024c46f2024-03-02 23:53:18 -0500636 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400637 std::condition_variable cv;
638 std::atomic_int events(0);
639 bool successfullyConnected = false, successfullyConnected2 = false, successfullyReceive = false,
640 receiverConnected = false;
641 const uint8_t buf_other[] = {0x64, 0x65, 0x66, 0x67};
642 const uint8_t buf_test[] = {0x68, 0x69, 0x70, 0x71};
643 bool dataOk = false, dataOk2 = false;
Morteza Namvar82960b32023-07-04 17:08:22 -0400644
Amnab8c33bb2023-08-03 14:40:01 -0400645 bob->connectionManager->onChannelRequest(
646 [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
647 const std::string&) {
648 successfullyReceive = true;
649 return true;
650 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400651
Amnab8c33bb2023-08-03 14:40:01 -0400652 bob->connectionManager->onConnectionReady(
653 [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
654 if (socket && (name == "test" || name == "other")) {
655 receiverConnected = true;
656 std::error_code ec;
657 auto res = socket->waitForData(std::chrono::milliseconds(5000), ec);
658 if (res == 4) {
659 uint8_t buf[4];
660 socket->read(&buf[0], 4, ec);
661 if (name == "test")
662 dataOk = std::equal(std::begin(buf), std::end(buf), std::begin(buf_test));
663 else
664 dataOk2 = std::equal(std::begin(buf), std::end(buf), std::begin(buf_other));
665 events++;
666 cv.notify_one();
667 }
668 }
669 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400670
Amnab8c33bb2023-08-03 14:40:01 -0400671 alice->connectionManager->connectDevice(bob->id.second,
672 "test",
673 [&](std::shared_ptr<ChannelSocket> socket,
674 const DeviceId&) {
675 if (socket) {
676 successfullyConnected = true;
677 std::error_code ec;
678 socket->write(&buf_test[0], 4, ec);
679 }
680 events++;
681 cv.notify_one();
682 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400683
Amnab8c33bb2023-08-03 14:40:01 -0400684 alice->connectionManager->connectDevice(bob->id.second,
685 "other",
686 [&](std::shared_ptr<ChannelSocket> socket,
687 const DeviceId&) {
688 if (socket) {
689 successfullyConnected2 = true;
690 std::error_code ec;
691 socket->write(&buf_other[0], 4, ec);
692 }
693 events++;
694 cv.notify_one();
695 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400696
Amnab8c33bb2023-08-03 14:40:01 -0400697 CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
698 return events == 4 && successfullyReceive && successfullyConnected && successfullyConnected2
699 && dataOk && dataOk2;
700 }));
701}
Morteza Namvar82960b32023-07-04 17:08:22 -0400702
Amnab8c33bb2023-08-03 14:40:01 -0400703void
704ConnectionManagerTest::testAcceptsICERequest()
705{
Amnab8c33bb2023-08-03 14:40:01 -0400706 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -0400707
Adrien Béraud024c46f2024-03-02 23:53:18 -0500708 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400709 std::condition_variable cv;
710 bool successfullyConnected = false;
711 bool successfullyReceive = false;
712 bool receiverConnected = false;
Morteza Namvar82960b32023-07-04 17:08:22 -0400713
Amnab8c33bb2023-08-03 14:40:01 -0400714 bob->connectionManager->onChannelRequest(
715 [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
716 bob->connectionManager->onICERequest([&](const DeviceId&) {
717 successfullyReceive = true;
718 return true;
719 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400720
Amnab8c33bb2023-08-03 14:40:01 -0400721 bob->connectionManager->onConnectionReady(
722 [&receiverConnected](const DeviceId&,
723 const std::string& name,
724 std::shared_ptr<ChannelSocket> socket) {
725 receiverConnected = socket && (name == "git://*");
726 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400727
Amnab8c33bb2023-08-03 14:40:01 -0400728 alice->connectionManager->connectDevice(bob->id.second,
729 "git://*",
730 [&](std::shared_ptr<ChannelSocket> socket,
731 const DeviceId&) {
732 if (socket) {
733 successfullyConnected = true;
734 }
735 cv.notify_one();
736 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400737
Amnab8c33bb2023-08-03 14:40:01 -0400738 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] {
739 return successfullyReceive && successfullyConnected && receiverConnected;
740 }));
741}
Morteza Namvar82960b32023-07-04 17:08:22 -0400742
Amnab8c33bb2023-08-03 14:40:01 -0400743void
744ConnectionManagerTest::testDeclineICERequest()
745{
Amnab8c33bb2023-08-03 14:40:01 -0400746 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -0400747
Amnab8c33bb2023-08-03 14:40:01 -0400748 std::condition_variable cv;
Adrien Béraud4796de12023-09-25 14:46:47 -0400749 bool connectCompleted = false;
Amnab8c33bb2023-08-03 14:40:01 -0400750 bool successfullyConnected = false;
751 bool successfullyReceive = false;
752 bool receiverConnected = false;
Morteza Namvar82960b32023-07-04 17:08:22 -0400753
Amnab8c33bb2023-08-03 14:40:01 -0400754 bob->connectionManager->onChannelRequest(
755 [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
756 bob->connectionManager->onICERequest([&](const DeviceId&) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500757 std::lock_guard lock {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400758 successfullyReceive = true;
Adrien Béraud4796de12023-09-25 14:46:47 -0400759 cv.notify_one();
Amnab8c33bb2023-08-03 14:40:01 -0400760 return false;
761 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400762
Amnab8c33bb2023-08-03 14:40:01 -0400763 bob->connectionManager->onConnectionReady(
764 [&receiverConnected](const DeviceId&,
765 const std::string& name,
766 std::shared_ptr<ChannelSocket> socket) {
767 receiverConnected = socket && (name == "git://*");
768 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400769
Amnab8c33bb2023-08-03 14:40:01 -0400770 alice->connectionManager->connectDevice(bob->id.second,
771 "git://*",
772 [&](std::shared_ptr<ChannelSocket> socket,
773 const DeviceId&) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500774 std::lock_guard lock {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400775 if (socket) {
776 successfullyConnected = true;
777 }
Adrien Béraud4796de12023-09-25 14:46:47 -0400778 connectCompleted = true;
Amnab8c33bb2023-08-03 14:40:01 -0400779 cv.notify_one();
780 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400781
Adrien Béraud024c46f2024-03-02 23:53:18 -0500782 std::unique_lock lk {mtx};
Adrien Béraud4796de12023-09-25 14:46:47 -0400783 CPPUNIT_ASSERT(cv.wait_for(lk, 35s, [&] { return successfullyReceive; }));
784 CPPUNIT_ASSERT(cv.wait_for(lk, 35s, [&] { return connectCompleted; }));
Amnab8c33bb2023-08-03 14:40:01 -0400785 CPPUNIT_ASSERT(!receiverConnected);
786 CPPUNIT_ASSERT(!successfullyConnected);
787}
Morteza Namvar82960b32023-07-04 17:08:22 -0400788
Amnab8c33bb2023-08-03 14:40:01 -0400789void
790ConnectionManagerTest::testChannelRcvShutdown()
791{
Amnab8c33bb2023-08-03 14:40:01 -0400792 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
793 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -0400794
Adrien Béraud024c46f2024-03-02 23:53:18 -0500795 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400796 std::condition_variable cv;
797 bool successfullyConnected = false;
798 bool shutdownReceived = false;
Morteza Namvar82960b32023-07-04 17:08:22 -0400799
Amnab8c33bb2023-08-03 14:40:01 -0400800 std::shared_ptr<ChannelSocket> bobSock;
Morteza Namvar82960b32023-07-04 17:08:22 -0400801
Amnab8c33bb2023-08-03 14:40:01 -0400802 bob->connectionManager->onChannelRequest(
803 [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -0400804
Amnab8c33bb2023-08-03 14:40:01 -0400805 bob->connectionManager->onConnectionReady(
806 [&](const DeviceId& did, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
807 if (socket && name == "git://*" && did != bob->id.second->getLongId()) {
808 bobSock = socket;
809 cv.notify_one();
810 }
811 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400812
Amnab8c33bb2023-08-03 14:40:01 -0400813 alice->connectionManager->connectDevice(bob->id.second,
814 "git://*",
815 [&](std::shared_ptr<ChannelSocket> socket,
816 const DeviceId&) {
817 if (socket) {
818 socket->onShutdown([&] {
819 shutdownReceived = true;
820 cv.notify_one();
821 });
822 successfullyConnected = true;
823 cv.notify_one();
824 }
825 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400826
Amnab8c33bb2023-08-03 14:40:01 -0400827 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return bobSock && successfullyConnected; }));
Morteza Namvar82960b32023-07-04 17:08:22 -0400828
Amnab8c33bb2023-08-03 14:40:01 -0400829 bobSock->shutdown();
Morteza Namvar82960b32023-07-04 17:08:22 -0400830
Amnab8c33bb2023-08-03 14:40:01 -0400831 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return shutdownReceived; }));
832}
Morteza Namvar82960b32023-07-04 17:08:22 -0400833
Amnab8c33bb2023-08-03 14:40:01 -0400834void
835ConnectionManagerTest::testChannelSenderShutdown()
836{
Amnab8c33bb2023-08-03 14:40:01 -0400837 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
838 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -0400839
Amnab8c33bb2023-08-03 14:40:01 -0400840 std::condition_variable rcv, scv;
841 bool successfullyConnected = false;
842 bool successfullyReceive = false;
843 bool receiverConnected = false;
844 bool shutdownReceived = false;
Morteza Namvar82960b32023-07-04 17:08:22 -0400845
Amnab8c33bb2023-08-03 14:40:01 -0400846 bob->connectionManager->onChannelRequest(
Adrien Béraud4796de12023-09-25 14:46:47 -0400847 [&](const std::shared_ptr<dht::crypto::Certificate>&,
Amnab8c33bb2023-08-03 14:40:01 -0400848 const std::string& name) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500849 std::lock_guard lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400850 successfullyReceive = name == "git://*";
Adrien Béraud4796de12023-09-25 14:46:47 -0400851 rcv.notify_one();
Amnab8c33bb2023-08-03 14:40:01 -0400852 return true;
853 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400854
Amnab8c33bb2023-08-03 14:40:01 -0400855 bob->connectionManager->onConnectionReady(
856 [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
857 if (socket) {
858 socket->onShutdown([&] {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500859 std::lock_guard lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400860 shutdownReceived = true;
861 scv.notify_one();
862 });
863 }
Adrien Béraud4796de12023-09-25 14:46:47 -0400864 if (not name.empty()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500865 std::lock_guard lk {mtx};
Adrien Béraud4796de12023-09-25 14:46:47 -0400866 receiverConnected = socket && (name == "git://*");
867 rcv.notify_one();
868 }
Amnab8c33bb2023-08-03 14:40:01 -0400869 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400870
Amnab8c33bb2023-08-03 14:40:01 -0400871 alice->connectionManager->connectDevice(bob->id.second,
872 "git://*",
873 [&](std::shared_ptr<ChannelSocket> socket,
874 const DeviceId&) {
875 if (socket) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500876 std::lock_guard lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400877 successfullyConnected = true;
878 rcv.notify_one();
879 socket->shutdown();
880 }
881 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400882
Adrien Béraud024c46f2024-03-02 23:53:18 -0500883 std::unique_lock lk {mtx};
François-Simon Fauteux-Chapleau7e86e072024-04-22 13:41:02 -0400884 CPPUNIT_ASSERT(rcv.wait_for(lk, 30s, [&] { return successfullyConnected && successfullyReceive && receiverConnected; }));
885 CPPUNIT_ASSERT(scv.wait_for(lk, 30s, [&] { return shutdownReceived; }));
Amnab8c33bb2023-08-03 14:40:01 -0400886}
Morteza Namvar82960b32023-07-04 17:08:22 -0400887
Amnab8c33bb2023-08-03 14:40:01 -0400888void
889ConnectionManagerTest::testCloseConnectionWith()
890{
Amnab8c33bb2023-08-03 14:40:01 -0400891 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
892 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -0400893
Amnab8c33bb2023-08-03 14:40:01 -0400894 auto bobUri = bob->id.second->issuer->getId().toString();
Amnab8c33bb2023-08-03 14:40:01 -0400895 std::condition_variable rcv, scv;
Adrien Béraud4796de12023-09-25 14:46:47 -0400896 unsigned events(0);
Amnab8c33bb2023-08-03 14:40:01 -0400897 bool successfullyConnected = false;
898 bool successfullyReceive = false;
899 bool receiverConnected = false;
Morteza Namvar82960b32023-07-04 17:08:22 -0400900
Amnab8c33bb2023-08-03 14:40:01 -0400901 bob->connectionManager->onChannelRequest(
Adrien Béraud4796de12023-09-25 14:46:47 -0400902 [&](const std::shared_ptr<dht::crypto::Certificate>&,
Amnab8c33bb2023-08-03 14:40:01 -0400903 const std::string& name) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500904 std::lock_guard lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400905 successfullyReceive = name == "git://*";
906 return true;
907 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400908
Amnab8c33bb2023-08-03 14:40:01 -0400909 bob->connectionManager->onConnectionReady([&](const DeviceId&,
910 const std::string& name,
911 std::shared_ptr<dhtnet::ChannelSocket> socket) {
912 if (socket) {
913 socket->onShutdown([&] {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500914 std::lock_guard lk {mtx};
Adrien Béraud4796de12023-09-25 14:46:47 -0400915 events++;
Amnab8c33bb2023-08-03 14:40:01 -0400916 scv.notify_one();
917 });
918 }
Adrien Béraud4796de12023-09-25 14:46:47 -0400919 if (not name.empty()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500920 std::lock_guard lk {mtx};
Adrien Béraud4796de12023-09-25 14:46:47 -0400921 receiverConnected = socket && (name == "git://*");
922 rcv.notify_one();
923 }
Amnab8c33bb2023-08-03 14:40:01 -0400924 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400925
Adrien Béraud4796de12023-09-25 14:46:47 -0400926 alice->connectionManager->connectDevice(bob->id.second,
Amnab8c33bb2023-08-03 14:40:01 -0400927 "git://*",
928 [&](std::shared_ptr<dhtnet::ChannelSocket> socket,
Adrien Béraud4796de12023-09-25 14:46:47 -0400929 const DeviceId&) {
Amnab8c33bb2023-08-03 14:40:01 -0400930 if (socket) {
931 socket->onShutdown([&] {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500932 std::lock_guard lk {mtx};
Adrien Béraud4796de12023-09-25 14:46:47 -0400933 events++;
Amnab8c33bb2023-08-03 14:40:01 -0400934 scv.notify_one();
935 });
Adrien Béraud024c46f2024-03-02 23:53:18 -0500936 std::lock_guard lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400937 successfullyConnected = true;
938 rcv.notify_one();
939 }
940 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400941
Adrien Béraud4796de12023-09-25 14:46:47 -0400942 {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500943 std::unique_lock lk {mtx};
Adrien Béraud4796de12023-09-25 14:46:47 -0400944 rcv.wait_for(lk, 30s, [&] {
945 return successfullyReceive && successfullyConnected && receiverConnected;
946 });
947 }
948 std::this_thread::sleep_for(1s);
Amnab8c33bb2023-08-03 14:40:01 -0400949 // This should trigger onShutdown
950 alice->connectionManager->closeConnectionsWith(bobUri);
Adrien Béraud024c46f2024-03-02 23:53:18 -0500951 std::unique_lock lk {mtx};
Adrien Béraud4796de12023-09-25 14:46:47 -0400952 CPPUNIT_ASSERT(scv.wait_for(lk, 10s, [&] { return events == 2; }));
Amnab8c33bb2023-08-03 14:40:01 -0400953}
Morteza Namvar82960b32023-07-04 17:08:22 -0400954
Amnab8c33bb2023-08-03 14:40:01 -0400955// explain algorithm
956void
957ConnectionManagerTest::testShutdownCallbacks()
958{
Amnab8c33bb2023-08-03 14:40:01 -0400959 auto aliceUri = alice->id.second->issuer->getId().toString();
Morteza Namvar82960b32023-07-04 17:08:22 -0400960
Amnab8c33bb2023-08-03 14:40:01 -0400961 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
962 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -0400963
Amnab8c33bb2023-08-03 14:40:01 -0400964 std::condition_variable rcv, chan2cv;
965 bool successfullyConnected = false;
966 bool successfullyReceive = false;
967 bool receiverConnected = false;
Morteza Namvar82960b32023-07-04 17:08:22 -0400968
Amnab8c33bb2023-08-03 14:40:01 -0400969 bob->connectionManager->onChannelRequest(
Adrien Béraud4796de12023-09-25 14:46:47 -0400970 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& name) {
Amnab8c33bb2023-08-03 14:40:01 -0400971 if (name == "1") {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500972 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400973 successfullyReceive = true;
Adrien Béraud4796de12023-09-25 14:46:47 -0400974 rcv.notify_one();
Amnab8c33bb2023-08-03 14:40:01 -0400975 } else {
976 chan2cv.notify_one();
977 // Do not return directly. Let the connection be closed
978 std::this_thread::sleep_for(10s);
979 }
980 return true;
981 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400982
Amnab8c33bb2023-08-03 14:40:01 -0400983 bob->connectionManager->onConnectionReady([&](const DeviceId&,
984 const std::string& name,
985 std::shared_ptr<dhtnet::ChannelSocket> socket) {
Adrien Béraud4796de12023-09-25 14:46:47 -0400986 if (name == "1") {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500987 std::unique_lock lk {mtx};
Adrien Béraud4796de12023-09-25 14:46:47 -0400988 receiverConnected = (bool)socket;
989 rcv.notify_one();
990 }
Amnab8c33bb2023-08-03 14:40:01 -0400991 });
Morteza Namvar82960b32023-07-04 17:08:22 -0400992
Adrien Béraud4796de12023-09-25 14:46:47 -0400993 alice->connectionManager->connectDevice(bob->id.second,
Amnab8c33bb2023-08-03 14:40:01 -0400994 "1",
995 [&](std::shared_ptr<dhtnet::ChannelSocket> socket,
Adrien Béraud4796de12023-09-25 14:46:47 -0400996 const DeviceId&) {
Amnab8c33bb2023-08-03 14:40:01 -0400997 if (socket) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500998 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -0400999 successfullyConnected = true;
1000 rcv.notify_one();
1001 }
1002 });
Sébastien Blind0c92c72023-12-07 15:27:51 -05001003
Adrien Béraud024c46f2024-03-02 23:53:18 -05001004 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001005 // Connect first channel. This will initiate a mx sock
1006 CPPUNIT_ASSERT(rcv.wait_for(lk, 30s, [&] {
Amnab8c33bb2023-08-03 14:40:01 -04001007 return successfullyReceive && successfullyConnected && receiverConnected;
1008 }));
Morteza Namvar82960b32023-07-04 17:08:22 -04001009
Amnab8c33bb2023-08-03 14:40:01 -04001010 // Connect another channel, but close the connection
1011 bool channel2NotConnected = false;
Adrien Béraud4796de12023-09-25 14:46:47 -04001012 alice->connectionManager->connectDevice(bob->id.second,
Amnab8c33bb2023-08-03 14:40:01 -04001013 "2",
1014 [&](std::shared_ptr<dhtnet::ChannelSocket> socket,
Adrien Béraud4796de12023-09-25 14:46:47 -04001015 const DeviceId&) {
Amnab8c33bb2023-08-03 14:40:01 -04001016 channel2NotConnected = !socket;
1017 rcv.notify_one();
1018 });
1019 chan2cv.wait_for(lk, 30s);
Morteza Namvar82960b32023-07-04 17:08:22 -04001020
Amnab8c33bb2023-08-03 14:40:01 -04001021 // This should trigger onShutdown for second callback
1022 bob->connectionManager->closeConnectionsWith(aliceUri);
1023 CPPUNIT_ASSERT(rcv.wait_for(lk, 30s, [&] { return channel2NotConnected; }));
1024}
Morteza Namvar82960b32023-07-04 17:08:22 -04001025
Amnab8c33bb2023-08-03 14:40:01 -04001026void
1027ConnectionManagerTest::testFloodSocket()
1028{
Amnab8c33bb2023-08-03 14:40:01 -04001029 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
1030 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -04001031
Amnab8c33bb2023-08-03 14:40:01 -04001032 std::condition_variable cv;
1033 bool successfullyConnected = false;
1034 bool successfullyReceive = false;
1035 bool receiverConnected = false;
1036 std::shared_ptr<dhtnet::ChannelSocket> rcvSock1, rcvSock2, rcvSock3, sendSock, sendSock2,
1037 sendSock3;
1038 bob->connectionManager->onChannelRequest(
1039 [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
1040 const std::string& name) {
1041 successfullyReceive = name == "1";
1042 return true;
1043 });
1044 bob->connectionManager->onConnectionReady([&](const DeviceId&,
1045 const std::string& name,
1046 std::shared_ptr<dhtnet::ChannelSocket> socket) {
1047 receiverConnected = socket != nullptr;
1048 if (name == "1")
1049 rcvSock1 = socket;
1050 else if (name == "2")
1051 rcvSock2 = socket;
1052 else if (name == "3")
1053 rcvSock3 = socket;
1054 });
1055 alice->connectionManager->connectDevice(bob->id.second,
1056 "1",
1057 [&](std::shared_ptr<dhtnet::ChannelSocket> socket,
1058 const DeviceId&) {
1059 if (socket) {
1060 sendSock = socket;
1061 successfullyConnected = true;
1062 }
1063 cv.notify_one();
1064 });
Adrien Béraud024c46f2024-03-02 23:53:18 -05001065 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001066 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] {
1067 return successfullyReceive && successfullyConnected && receiverConnected;
1068 }));
1069 CPPUNIT_ASSERT(receiverConnected);
1070 successfullyConnected = false;
1071 receiverConnected = false;
1072 alice->connectionManager->connectDevice(bob->id.second,
1073 "2",
1074 [&](std::shared_ptr<dhtnet::ChannelSocket> socket,
1075 const DeviceId&) {
1076 if (socket) {
1077 sendSock2 = socket;
1078 successfullyConnected = true;
1079 }
1080 cv.notify_one();
1081 });
1082 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyConnected && receiverConnected; }));
1083 successfullyConnected = false;
1084 receiverConnected = false;
1085 alice->connectionManager->connectDevice(bob->id.second,
1086 "3",
1087 [&](std::shared_ptr<dhtnet::ChannelSocket> socket,
1088 const DeviceId&) {
1089 if (socket) {
1090 sendSock3 = socket;
1091 successfullyConnected = true;
1092 }
1093 cv.notify_one();
1094 });
1095 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyConnected && receiverConnected; }));
Adrien Béraud4796de12023-09-25 14:46:47 -04001096 constexpr size_t C = 8000;
Amnab8c33bb2023-08-03 14:40:01 -04001097 std::string alphabet, shouldRcv, rcv1, rcv2, rcv3;
Adrien Béraud4796de12023-09-25 14:46:47 -04001098 std::mutex mtx1, mtx2, mtx3;
Amnab8c33bb2023-08-03 14:40:01 -04001099 for (int i = 0; i < 100; ++i)
1100 alphabet += "QWERTYUIOPASDFGHJKLZXCVBNM";
Adrien Béraud4796de12023-09-25 14:46:47 -04001101 auto totSize = C * alphabet.size();
1102 shouldRcv.reserve(totSize);
1103 rcv1.reserve(totSize);
1104 rcv2.reserve(totSize);
1105 rcv3.reserve(totSize);
Amnab8c33bb2023-08-03 14:40:01 -04001106 rcvSock1->setOnRecv([&](const uint8_t* buf, size_t len) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001107 std::lock_guard lk {mtx1};
Adrien Béraud4796de12023-09-25 14:46:47 -04001108 rcv1 += std::string_view((const char*)buf, len);
1109 if (rcv1.size() == totSize)
1110 cv.notify_one();
Amnab8c33bb2023-08-03 14:40:01 -04001111 return len;
1112 });
1113 rcvSock2->setOnRecv([&](const uint8_t* buf, size_t len) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001114 std::lock_guard lk {mtx2};
Adrien Béraud4796de12023-09-25 14:46:47 -04001115 rcv2 += std::string_view((const char*)buf, len);
1116 if (rcv2.size() == totSize)
1117 cv.notify_one();
Amnab8c33bb2023-08-03 14:40:01 -04001118 return len;
1119 });
1120 rcvSock3->setOnRecv([&](const uint8_t* buf, size_t len) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001121 std::lock_guard lk {mtx3};
Adrien Béraud4796de12023-09-25 14:46:47 -04001122 rcv3 += std::string_view((const char*)buf, len);
1123 if (rcv3.size() == totSize)
1124 cv.notify_one();
Amnab8c33bb2023-08-03 14:40:01 -04001125 return len;
1126 });
1127 for (uint64_t i = 0; i < alphabet.size(); ++i) {
Adrien Béraud4796de12023-09-25 14:46:47 -04001128 auto send = std::string(C, alphabet[i]);
Amnab8c33bb2023-08-03 14:40:01 -04001129 shouldRcv += send;
1130 std::error_code ec;
1131 sendSock->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
1132 sendSock2->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
1133 sendSock3->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
1134 CPPUNIT_ASSERT(!ec);
1135 }
Adrien Béraud4796de12023-09-25 14:46:47 -04001136 {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001137 std::unique_lock lk {mtx1};
Adrien Béraud4796de12023-09-25 14:46:47 -04001138 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return shouldRcv == rcv1; }));
1139 }
1140 {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001141 std::unique_lock lk {mtx2};
Adrien Béraud4796de12023-09-25 14:46:47 -04001142 CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return shouldRcv == rcv2; }));
1143 }
1144 {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001145 std::unique_lock lk {mtx3};
Adrien Béraud4796de12023-09-25 14:46:47 -04001146 CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return shouldRcv == rcv3; }));
1147 }
Amnab8c33bb2023-08-03 14:40:01 -04001148}
Morteza Namvar82960b32023-07-04 17:08:22 -04001149
Amnab8c33bb2023-08-03 14:40:01 -04001150void
1151ConnectionManagerTest::testDestroyWhileSending()
1152{
1153 // Same as test before, but destroy the accounts while sending.
1154 // This test if a segfault occurs
Amnab8c33bb2023-08-03 14:40:01 -04001155 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
1156 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Adrien Béraud024c46f2024-03-02 23:53:18 -05001157 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001158 std::condition_variable cv;
1159 bool successfullyConnected = false;
1160 bool successfullyReceive = false;
1161 bool receiverConnected = false;
1162 std::shared_ptr<ChannelSocket> rcvSock1, rcvSock2, rcvSock3, sendSock, sendSock2, sendSock3;
1163 bob->connectionManager->onChannelRequest(
1164 [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
1165 const std::string& name) {
1166 successfullyReceive = name == "1";
1167 return true;
1168 });
1169 bob->connectionManager->onConnectionReady(
1170 [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
1171 receiverConnected = socket != nullptr;
1172 if (name == "1")
1173 rcvSock1 = socket;
1174 else if (name == "2")
1175 rcvSock2 = socket;
1176 else if (name == "3")
1177 rcvSock3 = socket;
1178 });
1179 alice->connectionManager->connectDevice(bob->id.second,
1180 "1",
1181 [&](std::shared_ptr<ChannelSocket> socket,
1182 const DeviceId&) {
1183 if (socket) {
1184 sendSock = socket;
1185 successfullyConnected = true;
1186 }
1187 cv.notify_one();
1188 });
1189 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] {
1190 return successfullyReceive && successfullyConnected && receiverConnected;
1191 }));
1192 successfullyConnected = false;
1193 receiverConnected = false;
1194 alice->connectionManager->connectDevice(bob->id.second,
1195 "2",
1196 [&](std::shared_ptr<ChannelSocket> socket,
1197 const DeviceId&) {
1198 if (socket) {
1199 sendSock2 = socket;
1200 successfullyConnected = true;
1201 }
1202 cv.notify_one();
1203 });
1204 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyConnected && receiverConnected; }));
1205 successfullyConnected = false;
1206 receiverConnected = false;
1207 alice->connectionManager->connectDevice(bob->id.second,
1208 "3",
1209 [&](std::shared_ptr<ChannelSocket> socket,
1210 const DeviceId&) {
1211 if (socket) {
1212 sendSock3 = socket;
1213 successfullyConnected = true;
1214 }
1215 cv.notify_one();
1216 });
1217 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyConnected && receiverConnected; }));
Amnab8c33bb2023-08-03 14:40:01 -04001218 std::string alphabet;
1219 for (int i = 0; i < 100; ++i)
1220 alphabet += "QWERTYUIOPASDFGHJKLZXCVBNM";
1221 rcvSock1->setOnRecv([&](const uint8_t*, size_t len) { return len; });
1222 rcvSock2->setOnRecv([&](const uint8_t*, size_t len) { return len; });
1223 rcvSock3->setOnRecv([&](const uint8_t*, size_t len) { return len; });
1224 for (uint64_t i = 0; i < alphabet.size(); ++i) {
1225 auto send = std::string(8000, alphabet[i]);
1226 std::error_code ec;
1227 sendSock->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
1228 sendSock2->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
1229 sendSock3->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
1230 CPPUNIT_ASSERT(!ec);
1231 }
Morteza Namvar82960b32023-07-04 17:08:22 -04001232
Amnab8c33bb2023-08-03 14:40:01 -04001233 // No need to wait, immediately destroy, no segfault must occurs
1234}
Morteza Namvar82960b32023-07-04 17:08:22 -04001235
Amnab8c33bb2023-08-03 14:40:01 -04001236void
1237ConnectionManagerTest::testIsConnecting()
1238{
Amnab8c33bb2023-08-03 14:40:01 -04001239 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
1240 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -04001241
Adrien Béraud024c46f2024-03-02 23:53:18 -05001242 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001243 std::condition_variable cv;
1244 bool successfullyConnected = false, successfullyReceive = false;
Morteza Namvar82960b32023-07-04 17:08:22 -04001245
Amnab8c33bb2023-08-03 14:40:01 -04001246 bob->connectionManager->onChannelRequest(
1247 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) {
1248 successfullyReceive = true;
1249 cv.notify_one();
1250 std::this_thread::sleep_for(2s);
1251 return true;
1252 });
Morteza Namvar82960b32023-07-04 17:08:22 -04001253
Amnab8c33bb2023-08-03 14:40:01 -04001254 CPPUNIT_ASSERT(!alice->connectionManager->isConnecting(bob->id.second->getLongId(), "sip"));
Morteza Namvar82960b32023-07-04 17:08:22 -04001255
Amnab8c33bb2023-08-03 14:40:01 -04001256 alice->connectionManager->connectDevice(bob->id.second,
1257 "sip",
1258 [&](std::shared_ptr<ChannelSocket> socket,
1259 const DeviceId&) {
1260 if (socket) {
1261 successfullyConnected = true;
1262 }
1263 cv.notify_one();
1264 });
1265 // connectDevice is full async, so isConnecting will be true after a few ms.
1266 CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return successfullyReceive; }));
1267 CPPUNIT_ASSERT(alice->connectionManager->isConnecting(bob->id.second->getLongId(), "sip"));
1268 CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return successfullyConnected; }));
1269 std::this_thread::sleep_for(
1270 std::chrono::milliseconds(100)); // Just to wait for the callback to finish
1271 CPPUNIT_ASSERT(!alice->connectionManager->isConnecting(bob->id.second->getLongId(), "sip"));
1272}
Morteza Namvar82960b32023-07-04 17:08:22 -04001273
Amnab8c33bb2023-08-03 14:40:01 -04001274void
Sébastien Blind0c92c72023-12-07 15:27:51 -05001275ConnectionManagerTest::testIsConnected()
1276{
1277 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
1278 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
1279
Adrien Béraud024c46f2024-03-02 23:53:18 -05001280 std::unique_lock lk {mtx};
Sébastien Blind0c92c72023-12-07 15:27:51 -05001281 std::condition_variable cv;
1282 bool successfullyConnected = false, successfullyReceive = false;
1283
1284 bob->connectionManager->onChannelRequest(
1285 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) {
1286 return true;
1287 });
1288
1289 alice->connectionManager->connectDevice(bob->id.second,
1290 "sip",
1291 [&](std::shared_ptr<ChannelSocket> socket,
1292 const DeviceId&) {
1293 if (socket) {
1294 successfullyConnected = true;
1295 }
1296 cv.notify_one();
1297 });
1298 CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return successfullyConnected; }));
1299 std::this_thread::sleep_for(
1300 std::chrono::milliseconds(100)); // Just to wait for the callback to finish
1301 CPPUNIT_ASSERT(alice->connectionManager->isConnected(bob->id.second->getLongId()));
1302}
1303
1304void
Amnab8c33bb2023-08-03 14:40:01 -04001305ConnectionManagerTest::testCanSendBeacon()
1306{
Amnab8c33bb2023-08-03 14:40:01 -04001307 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
1308 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -04001309
Adrien Béraud024c46f2024-03-02 23:53:18 -05001310 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001311 std::condition_variable cv;
1312 bool successfullyConnected = false;
Morteza Namvar82960b32023-07-04 17:08:22 -04001313
Amnab8c33bb2023-08-03 14:40:01 -04001314 std::shared_ptr<MultiplexedSocket> aliceSocket, bobSocket;
1315 bob->connectionManager->onChannelRequest(
1316 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
1317 bob->connectionManager->onConnectionReady(
1318 [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
1319 if (socket && socket->name() == "sip")
1320 bobSocket = socket->underlyingSocket();
1321 cv.notify_one();
1322 });
Morteza Namvar82960b32023-07-04 17:08:22 -04001323
Amnab8c33bb2023-08-03 14:40:01 -04001324 alice->connectionManager->connectDevice(bob->id.second,
1325 "sip",
1326 [&](std::shared_ptr<ChannelSocket> socket,
1327 const DeviceId&) {
1328 if (socket) {
1329 aliceSocket = socket->underlyingSocket();
1330 successfullyConnected = true;
1331 }
1332 cv.notify_one();
1333 });
1334 // connectDevice is full async, so isConnecting will be true after a few ms.
1335 CPPUNIT_ASSERT(
1336 cv.wait_for(lk, 30s, [&] { return aliceSocket && bobSocket && successfullyConnected; }));
1337 CPPUNIT_ASSERT(aliceSocket->canSendBeacon());
Morteza Namvar82960b32023-07-04 17:08:22 -04001338
Amnab8c33bb2023-08-03 14:40:01 -04001339 // Because onConnectionReady is true before version is sent, we can wait a bit
1340 // before canSendBeacon is true.
1341 auto start = std::chrono::steady_clock::now();
1342 auto aliceCanSendBeacon = false;
1343 auto bobCanSendBeacon = false;
1344 do {
1345 aliceCanSendBeacon = aliceSocket->canSendBeacon();
1346 bobCanSendBeacon = bobSocket->canSendBeacon();
1347 if (!bobCanSendBeacon || !aliceCanSendBeacon)
1348 std::this_thread::sleep_for(1s);
1349 } while ((not bobCanSendBeacon or not aliceCanSendBeacon)
1350 and std::chrono::steady_clock::now() - start < 5s);
Morteza Namvar82960b32023-07-04 17:08:22 -04001351
Amnab8c33bb2023-08-03 14:40:01 -04001352 CPPUNIT_ASSERT(bobCanSendBeacon && aliceCanSendBeacon);
1353}
Morteza Namvar82960b32023-07-04 17:08:22 -04001354
Amnab8c33bb2023-08-03 14:40:01 -04001355void
1356ConnectionManagerTest::testCannotSendBeacon()
1357{
Amnab8c33bb2023-08-03 14:40:01 -04001358 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
1359 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -04001360
Adrien Béraud024c46f2024-03-02 23:53:18 -05001361 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001362 std::condition_variable cv;
1363 bool successfullyConnected = false;
Morteza Namvar82960b32023-07-04 17:08:22 -04001364
Amnab8c33bb2023-08-03 14:40:01 -04001365 std::shared_ptr<MultiplexedSocket> aliceSocket, bobSocket;
1366 bob->connectionManager->onChannelRequest(
1367 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
1368 bob->connectionManager->onConnectionReady(
1369 [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
1370 if (socket && socket->name() == "sip")
1371 bobSocket = socket->underlyingSocket();
1372 cv.notify_one();
1373 });
Morteza Namvar82960b32023-07-04 17:08:22 -04001374
Amnab8c33bb2023-08-03 14:40:01 -04001375 alice->connectionManager->connectDevice(bob->id.second,
1376 "sip",
1377 [&](std::shared_ptr<ChannelSocket> socket,
1378 const DeviceId&) {
1379 if (socket) {
1380 aliceSocket = socket->underlyingSocket();
1381 successfullyConnected = true;
1382 }
1383 cv.notify_one();
1384 });
1385 // connectDevice is full async, so isConnecting will be true after a few ms.
1386 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceSocket && bobSocket; }));
Morteza Namvar82960b32023-07-04 17:08:22 -04001387
Amnab8c33bb2023-08-03 14:40:01 -04001388 int version = 1412;
1389 bobSocket->setOnVersionCb([&](auto v) {
1390 version = v;
1391 cv.notify_one();
1392 });
1393 aliceSocket->setVersion(0);
1394 aliceSocket->sendVersion();
1395 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return version == 0; }));
1396 CPPUNIT_ASSERT(!bobSocket->canSendBeacon());
1397}
Morteza Namvar82960b32023-07-04 17:08:22 -04001398
Amnab8c33bb2023-08-03 14:40:01 -04001399void
1400ConnectionManagerTest::testConnectivityChangeTriggerBeacon()
1401{
Amnab8c33bb2023-08-03 14:40:01 -04001402 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
1403 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -04001404
Adrien Béraud024c46f2024-03-02 23:53:18 -05001405 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001406 std::condition_variable cv;
1407 bool successfullyConnected = false;
Morteza Namvar82960b32023-07-04 17:08:22 -04001408
Amnab8c33bb2023-08-03 14:40:01 -04001409 std::shared_ptr<MultiplexedSocket> aliceSocket, bobSocket;
1410 bob->connectionManager->onChannelRequest(
1411 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
1412 bob->connectionManager->onConnectionReady(
1413 [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
1414 if (socket && socket->name() == "sip")
1415 bobSocket = socket->underlyingSocket();
1416 cv.notify_one();
1417 });
Morteza Namvar82960b32023-07-04 17:08:22 -04001418
Amnab8c33bb2023-08-03 14:40:01 -04001419 alice->connectionManager->connectDevice(bob->id.second,
1420 "sip",
1421 [&](std::shared_ptr<ChannelSocket> socket,
1422 const DeviceId&) {
1423 if (socket) {
1424 aliceSocket = socket->underlyingSocket();
1425 successfullyConnected = true;
1426 }
1427 cv.notify_one();
1428 });
1429 // connectDevice is full async, so isConnecting will be true after a few ms.
1430 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceSocket && bobSocket; }));
Morteza Namvar82960b32023-07-04 17:08:22 -04001431
Amnab8c33bb2023-08-03 14:40:01 -04001432 bool hasRequest = false;
1433 bobSocket->setOnBeaconCb([&](auto p) {
1434 if (p)
1435 hasRequest = true;
1436 cv.notify_one();
1437 });
1438 alice->connectionManager->connectivityChanged();
1439 CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return hasRequest; }));
1440}
Morteza Namvar82960b32023-07-04 17:08:22 -04001441
Amnab8c33bb2023-08-03 14:40:01 -04001442void
1443ConnectionManagerTest::testOnNoBeaconTriggersShutdown()
1444{
Amnab8c33bb2023-08-03 14:40:01 -04001445 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
1446 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -04001447
Adrien Béraud024c46f2024-03-02 23:53:18 -05001448 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001449 std::condition_variable cv;
1450 bool successfullyConnected = false;
Morteza Namvar82960b32023-07-04 17:08:22 -04001451
Amnab8c33bb2023-08-03 14:40:01 -04001452 std::shared_ptr<MultiplexedSocket> aliceSocket, bobSocket;
1453 bob->connectionManager->onChannelRequest(
1454 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
1455 bob->connectionManager->onConnectionReady(
1456 [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
1457 if (socket && socket->name() == "sip")
1458 bobSocket = socket->underlyingSocket();
1459 cv.notify_one();
1460 });
Morteza Namvar82960b32023-07-04 17:08:22 -04001461
Amnab8c33bb2023-08-03 14:40:01 -04001462 alice->connectionManager->connectDevice(bob->id.second,
1463 "sip",
1464 [&](std::shared_ptr<ChannelSocket> socket,
1465 const DeviceId&) {
1466 if (socket) {
1467 aliceSocket = socket->underlyingSocket();
1468 successfullyConnected = true;
1469 }
1470 cv.notify_one();
1471 });
1472 // connectDevice is full async, so isConnecting will be true after a few ms.
1473 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceSocket && bobSocket; }));
Morteza Namvar82960b32023-07-04 17:08:22 -04001474
Amnab8c33bb2023-08-03 14:40:01 -04001475 bool isClosed = false;
1476 aliceSocket->onShutdown([&] {
1477 isClosed = true;
1478 cv.notify_one();
1479 });
1480 bobSocket->answerToBeacon(false);
1481 alice->connectionManager->connectivityChanged();
1482 CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return isClosed; }));
1483}
Morteza Namvar82960b32023-07-04 17:08:22 -04001484
Amnab8c33bb2023-08-03 14:40:01 -04001485void
1486ConnectionManagerTest::testShutdownWhileNegotiating()
1487{
Amnab8c33bb2023-08-03 14:40:01 -04001488 alice->connectionManager->onICERequest([](const DeviceId&) { return true; });
Morteza Namvar82960b32023-07-04 17:08:22 -04001489
Adrien Béraud024c46f2024-03-02 23:53:18 -05001490 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001491 std::condition_variable cv;
1492 bool successfullyReceive = false;
1493 bool notConnected = false;
Morteza Namvar82960b32023-07-04 17:08:22 -04001494
Amnab8c33bb2023-08-03 14:40:01 -04001495 bob->connectionManager->onICERequest([&](const DeviceId&) {
1496 successfullyReceive = true;
1497 cv.notify_one();
1498 return true;
1499 });
Morteza Namvar82960b32023-07-04 17:08:22 -04001500
Amnab8c33bb2023-08-03 14:40:01 -04001501 alice->connectionManager->connectDevice(bob->id.second,
1502 "git://*",
1503 [&](std::shared_ptr<ChannelSocket> socket,
1504 const DeviceId&) {
1505 notConnected = !socket;
1506 cv.notify_one();
1507 });
Morteza Namvar82960b32023-07-04 17:08:22 -04001508
Amnab8c33bb2023-08-03 14:40:01 -04001509 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyReceive; }));
1510 // Manager::instance().setAccountActive(alice->id.second, false, true);
Morteza Namvar82960b32023-07-04 17:08:22 -04001511
Amnab8c33bb2023-08-03 14:40:01 -04001512 // Just move destruction on another thread.
1513 // dht::threadpool::io().run([conMgr =std::move(alice->connectionManager)] {});
1514 alice->connectionManager.reset();
Morteza Namvar82960b32023-07-04 17:08:22 -04001515
Amnab8c33bb2023-08-03 14:40:01 -04001516 CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return notConnected; }));
1517}
Adrien Béraud75754b22023-10-17 09:16:06 -04001518
Amnab8c33bb2023-08-03 14:40:01 -04001519void
1520ConnectionManagerTest::testGetChannelList()
1521{
Amnab8c33bb2023-08-03 14:40:01 -04001522 bob->connectionManager->onICERequest([](const DeviceId&) { return true; });
Amnab8c33bb2023-08-03 14:40:01 -04001523 std::condition_variable cv;
Adrien Béraud024c46f2024-03-02 23:53:18 -05001524 std::unique_lock lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001525 bool successfullyConnected = false;
1526 int receiverConnected = 0;
1527 bob->connectionManager->onChannelRequest(
1528 [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
1529 bob->connectionManager->onConnectionReady(
Adrien Béraud75754b22023-10-17 09:16:06 -04001530 [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001531 std::lock_guard lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001532 if (socket)
1533 receiverConnected += 1;
Amnab8c33bb2023-08-03 14:40:01 -04001534 cv.notify_one();
1535 });
1536 std::string channelId;
1537 alice->connectionManager->connectDevice(bob->id.second,
1538 "git://*",
1539 [&](std::shared_ptr<ChannelSocket> socket,
1540 const DeviceId&) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001541 std::lock_guard lk {mtx};
Amnab8c33bb2023-08-03 14:40:01 -04001542 if (socket) {
Adrien Béraud75754b22023-10-17 09:16:06 -04001543 channelId = fmt::format(FMT_COMPILE("{:x}"), socket->channel());
Amnab8c33bb2023-08-03 14:40:01 -04001544 successfullyConnected = true;
1545 }
Amnab8c33bb2023-08-03 14:40:01 -04001546 cv.notify_one();
1547 });
1548 CPPUNIT_ASSERT(
1549 cv.wait_for(lk, 60s, [&] { return successfullyConnected && receiverConnected == 1; }));
1550 std::vector<std::map<std::string, std::string>> expectedList = {
Adrien Béraud75754b22023-10-17 09:16:06 -04001551 {{"id", channelId}, {"name", "git://*"}}};
Amnab8c33bb2023-08-03 14:40:01 -04001552 auto connectionList = alice->connectionManager->getConnectionList();
1553 CPPUNIT_ASSERT(!connectionList.empty());
1554 const auto& connectionInfo = connectionList[0];
1555 auto it = connectionInfo.find("id");
1556 CPPUNIT_ASSERT(it != connectionInfo.end());
Adrien Béraud75754b22023-10-17 09:16:06 -04001557 auto actualList = alice->connectionManager->getChannelList(it->second);
Amnab8c33bb2023-08-03 14:40:01 -04001558 CPPUNIT_ASSERT(expectedList.size() == actualList.size());
Amnab8c33bb2023-08-03 14:40:01 -04001559 for (const auto& expectedMap : expectedList) {
Adrien Béraud75754b22023-10-17 09:16:06 -04001560 CPPUNIT_ASSERT(std::find(actualList.begin(), actualList.end(), expectedMap)
1561 != actualList.end());
Amnab8c33bb2023-08-03 14:40:01 -04001562 }
1563}
Adrien Béraudefe27372023-05-27 18:56:29 -04001564
1565} // namespace test
Sébastien Blin464bdff2023-07-19 08:02:53 -04001566} // namespace dhtnet
Adrien Béraudefe27372023-05-27 18:56:29 -04001567
Adrien Béraud1ae60aa2023-07-07 09:55:09 -04001568JAMI_TEST_RUNNER(dhtnet::test::ConnectionManagerTest::name())