• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / github_pull_request_312964

19 Feb 2025 07:31PM UTC coverage: 90.814% (-0.3%) from 91.119%
github_pull_request_312964

Pull #8071

Evergreen

web-flow
Bump serialize-javascript and mocha

Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) to 6.0.2 and updates ancestor dependency [mocha](https://github.com/mochajs/mocha). These dependencies need to be updated together.


Updates `serialize-javascript` from 6.0.0 to 6.0.2
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.0...v6.0.2)

Updates `mocha` from 10.2.0 to 10.8.2
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v10.2.0...v10.8.2)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-type: indirect
- dependency-name: mocha
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #8071: Bump serialize-javascript and mocha

96552 of 179126 branches covered (53.9%)

212672 of 234185 relevant lines covered (90.81%)

3115802.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.51
/test/test_util_network_ssl.cpp
1
#include <thread>
2

3
#include <realm/sync/network/network_ssl.hpp>
4
#include <realm/util/future.hpp>
5

6
#include "test.hpp"
7
#include "util/semaphore.hpp"
8

9
#ifdef _WIN32
10
#include <wincrypt.h>
11
#endif
12

13
using namespace realm;
14
using namespace realm::sync;
15
using namespace realm::test_util;
16
using namespace realm::util;
17

18
// Test independence and thread-safety
19
// -----------------------------------
20
//
21
// All tests must be thread safe and independent of each other. This
22
// is required because it allows for both shuffling of the execution
23
// order and for parallelized testing.
24
//
25
// In particular, avoid using std::rand() since it is not guaranteed
26
// to be thread safe. Instead use the API offered in
27
// `test/util/random.hpp`.
28
//
29
// All files created in tests must use the TEST_PATH macro (or one of
30
// its friends) to obtain a suitable file system path. See
31
// `test/util/test_path.hpp`.
32
//
33
//
34
// Debugging and the ONLY() macro
35
// ------------------------------
36
//
37
// A simple way of disabling all tests except one called `Foo`, is to
38
// replace TEST(Foo) with ONLY(Foo) and then recompile and rerun the
39
// test suite. Note that you can also use filtering by setting the
40
// environment varible `UNITTEST_FILTER`. See `README.md` for more on
41
// this.
42
//
43
// Another way to debug a particular test, is to copy that test into
44
// `experiments/testcase.cpp` and then run `sh build.sh
45
// check-testcase` (or one of its friends) from the command line.
46

47
#if !REALM_MOBILE
48
namespace {
49

50
network::Endpoint bind_acceptor(network::Acceptor& acceptor)
51
{
20✔
52
    network::Endpoint ep; // Wildcard
20✔
53
    acceptor.open(ep.protocol());
20✔
54
    acceptor.bind(ep);
20✔
55
    ep = acceptor.local_endpoint(); // Get actual bound endpoint
20✔
56
    acceptor.listen();
20✔
57
    return ep;
20✔
58
}
20✔
59

60
void connect_sockets(network::Socket& server_socket, network::Socket& client_socket)
61
{
20✔
62
    network::Service& server_service = server_socket.get_service();
20✔
63
    network::Service& client_service = client_socket.get_service();
20✔
64
    network::Acceptor acceptor(server_service);
20✔
65
    network::Endpoint ep = bind_acceptor(acceptor);
20✔
66
    bool accept_occurred = false, connect_occurred = false;
20✔
67
    auto accept_handler = [&](std::error_code ec) {
20✔
68
        REALM_ASSERT(!ec);
20✔
69
        accept_occurred = true;
20✔
70
    };
20✔
71
    auto connect_handler = [&](std::error_code ec) {
20✔
72
        REALM_ASSERT(!ec);
20✔
73
        connect_occurred = true;
20✔
74
    };
20✔
75
    server_service.post([&](Status status) {
20✔
76
        if (!status.is_ok())
20✔
77
            return;
×
78
        acceptor.async_accept(server_socket, std::move(accept_handler));
20✔
79
    });
20✔
80
    client_service.post([&](Status status) {
20✔
81
        if (!status.is_ok())
20✔
82
            return;
×
83
        client_socket.async_connect(ep, std::move(connect_handler));
20✔
84
    });
20✔
85
    if (&server_service == &client_service) {
20✔
86
        server_service.run();
9✔
87
    }
9✔
88
    else {
11✔
89
        std::thread thread{[&] {
11✔
90
            server_service.run();
11✔
91
        }};
11✔
92
        client_service.run();
11✔
93
        thread.join();
11✔
94
    }
11✔
95
    REALM_ASSERT(accept_occurred);
20✔
96
    REALM_ASSERT(connect_occurred);
20✔
97
}
20✔
98

99
void configure_server_ssl_context_for_test(network::ssl::Context& ssl_context)
100
{
15✔
101
    ssl_context.use_certificate_chain_file(get_test_resource_path() + "test_util_network_ssl_ca.pem");
15✔
102
    ssl_context.use_private_key_file(get_test_resource_path() + "test_util_network_ssl_key.pem");
15✔
103
}
15✔
104

105
void connect_ssl_streams(network::ssl::Stream& server_stream, network::ssl::Stream& client_stream)
106
{
11✔
107
    network::Socket& server_socket = server_stream.lowest_layer();
11✔
108
    network::Socket& client_socket = client_stream.lowest_layer();
11✔
109
    connect_sockets(server_socket, client_socket);
11✔
110
    network::Service& server_service = server_socket.get_service();
11✔
111
    network::Service& client_service = client_socket.get_service();
11✔
112
    bool server_handshake_occurred = false, client_handshake_occurred = false;
11✔
113
    auto server_handshake_handler = [&](std::error_code ec) {
11✔
114
        REALM_ASSERT(!ec);
11✔
115
        server_handshake_occurred = true;
11✔
116
    };
11✔
117
    auto client_handshake_handler = [&](std::error_code ec) {
11✔
118
        REALM_ASSERT(!ec);
11✔
119
        client_handshake_occurred = true;
11✔
120
    };
11✔
121
    server_service.post([&](Status status) {
11✔
122
        if (!status.is_ok())
11✔
123
            return;
×
124
        server_stream.async_handshake(std::move(server_handshake_handler));
11✔
125
    });
11✔
126
    client_service.post([&](Status status) {
11✔
127
        if (!status.is_ok())
11✔
128
            return;
×
129
        client_stream.async_handshake(std::move(client_handshake_handler));
11✔
130
    });
11✔
131
    if (&server_service == &client_service) {
11✔
132
        server_service.run();
7✔
133
    }
7✔
134
    else {
4✔
135
        std::thread thread{[&] {
4✔
136
            server_service.run();
4✔
137
        }};
4✔
138
        client_service.run();
4✔
139
        thread.join();
4✔
140
    }
4✔
141
    REALM_ASSERT(server_handshake_occurred);
11✔
142
    REALM_ASSERT(client_handshake_occurred);
11✔
143
}
11✔
144

145

146
class PingPongDelayFixture {
147
public:
148
    PingPongDelayFixture(network::Service& service)
149
        : PingPongDelayFixture{service, service}
150
    {
×
151
    }
×
152

153
    PingPongDelayFixture(network::Service& server_service, network::Service& client_service)
154
        : m_server_socket{server_service}
155
        , m_client_socket{client_service}
156
    {
×
157
        connect_sockets(m_server_socket, m_client_socket);
×
158
    }
×
159

160
    // Must be called by thread associated with `server_service`
161
    void start_server()
162
    {
×
163
        initiate_server_read();
×
164
    }
×
165

166
    // Must be called by thread associated with `server_service`
167
    void stop_server()
168
    {
×
169
        m_server_socket.cancel();
×
170
    }
×
171

172
    // Must be called by thread associated with `client_service`
173
    void delay_client(UniqueFunction<void()> handler, int n = 512)
174
    {
×
175
        m_handler = std::move(handler);
×
176
        m_num = n;
×
177
        initiate_client_write();
×
178
    }
×
179

180
private:
181
    network::Socket m_server_socket, m_client_socket;
182
    char m_server_char = 0, m_client_char = 0;
183
    int m_num;
184
    UniqueFunction<void()> m_handler;
185

186
    void initiate_server_read()
187
    {
×
188
        auto handler = [this](std::error_code ec, size_t) {
×
189
            if (ec != error::operation_aborted)
×
190
                handle_server_read(ec);
×
191
        };
×
192
        m_server_socket.async_read(&m_server_char, 1, std::move(handler));
×
193
    }
×
194

195
    void handle_server_read(std::error_code ec)
196
    {
×
197
        if (ec)
×
198
            throw std::system_error(ec);
×
199
        initiate_server_write();
×
200
    }
×
201

202
    void initiate_server_write()
203
    {
×
204
        auto handler = [this](std::error_code ec, size_t) {
×
205
            if (ec != error::operation_aborted)
×
206
                handle_server_write(ec);
×
207
        };
×
208
        m_server_socket.async_write(&m_server_char, 1, std::move(handler));
×
209
    }
×
210

211
    void handle_server_write(std::error_code ec)
212
    {
×
213
        if (ec)
×
214
            throw std::system_error(ec);
×
215
        initiate_server_read();
×
216
    }
×
217

218
    void initiate_client_write()
219
    {
×
220
        if (m_num <= 0) {
×
221
            UniqueFunction<void()> handler = std::move(m_handler);
×
222
            m_handler = nullptr;
×
223
            handler();
×
224
            return;
×
225
        }
×
226
        --m_num;
×
227

×
228
        auto handler = [this](std::error_code ec, size_t) {
×
229
            if (ec != error::operation_aborted)
×
230
                handle_client_write(ec);
×
231
        };
×
232
        m_client_socket.async_write(&m_client_char, 1, std::move(handler));
×
233
    }
×
234

235
    void handle_client_write(std::error_code ec)
236
    {
×
237
        if (ec)
×
238
            throw std::system_error(ec);
×
239
        initiate_client_read();
×
240
    }
×
241

242
    void initiate_client_read()
243
    {
×
244
        auto handler = [this](std::error_code ec, size_t) {
×
245
            if (ec != error::operation_aborted)
×
246
                handle_client_read(ec);
×
247
        };
×
248
        m_client_socket.async_read(&m_client_char, 1, std::move(handler));
×
249
    }
×
250

251
    void handle_client_read(std::error_code ec)
252
    {
×
253
        if (ec)
×
254
            throw std::system_error(ec);
×
255
        initiate_client_write();
×
256
    }
×
257
};
258

259
} // unnamed namespace
260

261

262
TEST(Util_Network_SSL_Handshake)
263
{
1✔
264
    network::Service service_1, service_2;
1✔
265
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
266
    network::ssl::Context ssl_context_1;
1✔
267
    network::ssl::Context ssl_context_2;
1✔
268
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
269
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
270
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
271
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
272
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
273
    connect_sockets(socket_1, socket_2);
1✔
274

275
    auto connector = [&] {
1✔
276
        std::error_code ec;
1✔
277
        ssl_stream_2.handshake(ec);
1✔
278
        CHECK_EQUAL(std::error_code(), ec);
1✔
279
    };
1✔
280
    auto acceptor = [&] {
1✔
281
        std::error_code ec;
1✔
282
        ssl_stream_1.handshake(ec);
1✔
283
        CHECK_EQUAL(std::error_code(), ec);
1✔
284
    };
1✔
285

286
    std::thread thread_1(std::move(connector));
1✔
287
    std::thread thread_2(std::move(acceptor));
1✔
288
    thread_1.join();
1✔
289
    thread_2.join();
1✔
290
}
1✔
291

292

293
TEST(Util_Network_SSL_AsyncHandshake)
294
{
1✔
295
    network::Service service;
1✔
296
    network::Socket socket_1{service}, socket_2{service};
1✔
297
    network::ssl::Context ssl_context_1;
1✔
298
    network::ssl::Context ssl_context_2;
1✔
299
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
300
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
301
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
302
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
303
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
304
    connect_sockets(socket_1, socket_2);
1✔
305

306
    bool connect_completed = false;
1✔
307
    auto connect_handler = [&](std::error_code ec) {
1✔
308
        CHECK_EQUAL(std::error_code(), ec);
1✔
309
        connect_completed = true;
1✔
310
    };
1✔
311
    bool accept_completed = false;
1✔
312
    auto accept_handler = [&](std::error_code ec) {
1✔
313
        CHECK_EQUAL(std::error_code(), ec);
1✔
314
        accept_completed = true;
1✔
315
    };
1✔
316

317
    ssl_stream_1.async_handshake(std::move(accept_handler));
1✔
318
    ssl_stream_2.async_handshake(std::move(connect_handler));
1✔
319
    service.run();
1✔
320
    CHECK(connect_completed);
1✔
321
    CHECK(accept_completed);
1✔
322
}
1✔
323

324

325
TEST(Util_Network_SSL_ReadWriteShutdown)
326
{
1✔
327
    network::Service service_1, service_2;
1✔
328
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
329
    network::ssl::Context ssl_context_1;
1✔
330
    network::ssl::Context ssl_context_2;
1✔
331
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
332
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
333
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
334
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
335
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
336
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
337

338
    const char* message = "hello";
1✔
339
    char buffer[256];
1✔
340

341
    auto writer = [&] {
1✔
342
        std::size_t n = ssl_stream_1.write(message, std::strlen(message));
1✔
343
        CHECK_EQUAL(std::strlen(message), n);
1✔
344
        ssl_stream_1.shutdown();
1✔
345
    };
1✔
346
    auto reader = [&] {
1✔
347
        std::error_code ec;
1✔
348
        std::size_t n = ssl_stream_2.read(buffer, sizeof buffer, ec);
1✔
349
        if (CHECK_EQUAL(MiscExtErrors::end_of_input, ec)) {
1✔
350
            if (CHECK_EQUAL(std::strlen(message), n))
1✔
351
                CHECK(std::equal(buffer, buffer + n, message));
1✔
352
        }
1✔
353
    };
1✔
354

355
    std::thread thread_1(std::move(writer));
1✔
356
    std::thread thread_2(std::move(reader));
1✔
357
    thread_1.join();
1✔
358
    thread_2.join();
1✔
359
}
1✔
360

361

362
TEST(Util_Network_SSL_AsyncReadWriteShutdown)
363
{
1✔
364
    network::Service service;
1✔
365
    network::Socket socket_1{service}, socket_2{service};
1✔
366
    network::ssl::Context ssl_context_1;
1✔
367
    network::ssl::Context ssl_context_2;
1✔
368
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
369
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
370
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
371
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
372
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
373
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
374

375
    const char* message = "hello";
1✔
376
    char buffer[256];
1✔
377

378
    bool shutdown_completed = false;
1✔
379
    auto shutdown_handler = [&](std::error_code ec) {
1✔
380
        CHECK_EQUAL(std::error_code(), ec);
1✔
381
        shutdown_completed = true;
1✔
382
    };
1✔
383
    auto write_handler = [&](std::error_code ec, std::size_t n) {
1✔
384
        CHECK_EQUAL(std::error_code(), ec);
1✔
385
        CHECK_EQUAL(std::strlen(message), n);
1✔
386
        ssl_stream_1.async_shutdown(std::move(shutdown_handler));
1✔
387
    };
1✔
388
    bool read_completed = false;
1✔
389
    auto read_handler = [&](std::error_code ec, std::size_t n) {
1✔
390
        CHECK_EQUAL(MiscExtErrors::end_of_input, ec);
1✔
391
        if (CHECK_EQUAL(std::strlen(message), n))
1✔
392
            CHECK(std::equal(buffer, buffer + n, message));
1✔
393
        read_completed = true;
1✔
394
    };
1✔
395

396
    ssl_stream_1.async_write(message, std::strlen(message), std::move(write_handler));
1✔
397
    ssl_stream_2.async_read(buffer, sizeof buffer, std::move(read_handler));
1✔
398
    service.run();
1✔
399
    CHECK(shutdown_completed);
1✔
400
    CHECK(read_completed);
1✔
401
}
1✔
402

403

404
TEST(Util_Network_SSL_PrematureEndOfInputOnHandshakeRead)
405
{
1✔
406
    network::Service service_1, service_2;
1✔
407
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
408
    network::ssl::Context ssl_context_1;
1✔
409
    network::ssl::Context ssl_context_2;
1✔
410
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
411
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
412
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
413
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
414
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
415
    connect_sockets(socket_1, socket_2);
1✔
416

417
    socket_1.shutdown(network::Socket::shutdown_send);
1✔
418

419
    // Use a separate thread to consume data written by Stream::handshake(),
420
    // such that we can be sure not to block.
421
    auto consumer = [&] {
1✔
422
        constexpr std::size_t size = 4096;
1✔
423
        std::unique_ptr<char[]> buffer(new char[size]);
1✔
424
        std::error_code ec;
1✔
425
        do {
2✔
426
            socket_1.read_some(buffer.get(), size, ec);
2✔
427
        } while (!ec);
2✔
428
        REALM_ASSERT(ec == MiscExtErrors::end_of_input);
1✔
429
    };
1✔
430

431
    std::thread thread(std::move(consumer));
1✔
432

433
#if REALM_HAVE_OPENSSL
1✔
434
    CHECK_SYSTEM_ERROR(ssl_stream_2.handshake(), MiscExtErrors::premature_end_of_input);
1✔
435
#elif REALM_HAVE_SECURE_TRANSPORT
436
    // We replace the CHECK_SYSTEM_ERROR check for "premature end of input"
437
    // with a check for any error code, Mac OS occasionally reports another
438
    // system error. We can revisit the details of the error code later. The
439
    // detailed check is disabled for now to reduce the number of failed unit
440
    // test runs.
441
    CHECK_THROW(ssl_stream_2.handshake(), std::system_error);
442
#endif
443

444
    socket_2.close();
1✔
445
    thread.join();
1✔
446
}
1✔
447

448

449
#ifndef _WIN32 // FIXME: winsock doesn't have EPIPE, what's the equivalent?
450
TEST(Util_Network_SSL_BrokenPipeOnHandshakeWrite)
451
{
1✔
452
    network::Service service;
1✔
453
    network::Socket socket_1{service}, socket_2{service};
1✔
454
    network::ssl::Context ssl_context_1;
1✔
455
    network::ssl::Context ssl_context_2;
1✔
456
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
457
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
458
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
459
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
460
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
461
    connect_sockets(socket_1, socket_2);
1✔
462

463
    socket_1.close();
1✔
464

465
    // Fill the kernel level write buffer, to provoke error::broken_pipe.
466
    constexpr std::size_t size = 4096;
1✔
467
    std::unique_ptr<char[]> buffer(new char[size]);
1✔
468
    std::fill(buffer.get(), buffer.get() + size, 0);
1✔
469
    std::error_code ec;
1✔
470
    do {
2✔
471
        socket_2.write_some(buffer.get(), size, ec);
2✔
472
    } while (!ec);
2✔
473
#if REALM_PLATFORM_APPLE
474
    // Which error we get from writing to a closed socket seems to depend on
475
    // some asynchronous kernel state. If it notices that the socket is closed
476
    // before sending any data we get EPIPE, and if it's after trying to send
477
    // data it's ECONNRESET. EHOSTDOWN is not documented as an error code from
478
    // send() and may be worth investigating once the macOS 12 XNU source is
479
    // released.
480
    REALM_ASSERT(ec == error::broken_pipe || ec == error::connection_reset || ec.value() == EHOSTDOWN);
481
#else
482
    REALM_ASSERT(ec == error::broken_pipe);
1✔
483
#endif
1✔
484

485
    CHECK_SYSTEM_ERROR(ssl_stream_2.handshake(), error::broken_pipe);
1✔
486
}
1✔
487
#endif
488

489

490
TEST(Util_Network_SSL_EndOfInputOnRead)
491
{
1✔
492
    network::Service service_1, service_2;
1✔
493
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
494
    network::ssl::Context ssl_context_1;
1✔
495
    network::ssl::Context ssl_context_2;
1✔
496
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
497
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
498
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
499
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
500
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
501
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
502
    ssl_stream_2.shutdown();
1✔
503
    socket_2.shutdown(network::Socket::shutdown_send);
1✔
504
    char ch;
1✔
505
    CHECK_SYSTEM_ERROR(ssl_stream_1.read_some(&ch, 1), MiscExtErrors::end_of_input);
1✔
506
}
1✔
507

508

509
TEST(Util_Network_SSL_PrematureEndOfInputOnRead)
510
{
1✔
511
    network::Service service_1, service_2;
1✔
512
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
513
    network::ssl::Context ssl_context_1;
1✔
514
    network::ssl::Context ssl_context_2;
1✔
515
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
516
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
517
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
518
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
519
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
520
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
521

522
    socket_2.shutdown(network::Socket::shutdown_send);
1✔
523
    char ch;
1✔
524
    CHECK_SYSTEM_ERROR(ssl_stream_1.read_some(&ch, 1), MiscExtErrors::premature_end_of_input);
1✔
525
}
1✔
526

527

528
#ifndef _WIN32 // FIXME: winsock doesn't have EPIPE, what's the equivalent?
529
TEST(Util_Network_SSL_BrokenPipeOnWrite)
530
{
1✔
531
    network::Service service;
1✔
532
    network::Socket socket_1{service}, socket_2{service};
1✔
533
    network::ssl::Context ssl_context_1;
1✔
534
    network::ssl::Context ssl_context_2;
1✔
535
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
536
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
537
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
538
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
539
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
540
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
541

542
    socket_1.close();
1✔
543

544
    // Fill the kernel level write buffer, to provoke error::broken_pipe.
545
    constexpr std::size_t size = 4096;
1✔
546
    std::unique_ptr<char[]> buffer(new char[size]);
1✔
547
    std::fill(buffer.get(), buffer.get() + size, 0);
1✔
548
    std::error_code ec;
1✔
549
    do {
2✔
550
        socket_2.write_some(buffer.get(), size, ec);
2✔
551
    } while (!ec);
2✔
552
#if REALM_PLATFORM_APPLE
553
    REALM_ASSERT(ec == error::broken_pipe || ec == error::connection_reset || ec.value() == EHOSTDOWN);
554
#else
555
    REALM_ASSERT(ec == error::broken_pipe);
1✔
556
#endif
1✔
557

558
    char ch = 0;
1✔
559
    CHECK_SYSTEM_ERROR(ssl_stream_2.write(&ch, 1), error::broken_pipe);
1✔
560
}
1✔
561

562

563
TEST(Util_Network_SSL_BrokenPipeOnShutdown)
564
{
1✔
565
    network::Service service;
1✔
566
    network::Socket socket_1{service}, socket_2{service};
1✔
567
    network::ssl::Context ssl_context_1;
1✔
568
    network::ssl::Context ssl_context_2;
1✔
569
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
570
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
571
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
572
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
573
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
574
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
575

576
    socket_1.close();
1✔
577

578
    // Fill the kernel level write buffer, to provoke error::broken_pipe.
579
    constexpr std::size_t size = 4096;
1✔
580
    std::unique_ptr<char[]> buffer(new char[size]);
1✔
581
    std::fill(buffer.get(), buffer.get() + size, 0);
1✔
582
    std::error_code ec;
1✔
583
    do {
2✔
584
        socket_2.write_some(buffer.get(), size, ec);
2✔
585
    } while (!ec);
2✔
586
#if REALM_PLATFORM_APPLE
587
    REALM_ASSERT(ec == error::broken_pipe || ec == error::connection_reset || ec.value() == EHOSTDOWN);
588
#else
589
    REALM_ASSERT(ec == error::broken_pipe);
1✔
590
#endif
1✔
591

592
    CHECK_SYSTEM_ERROR(ssl_stream_2.shutdown(), error::broken_pipe);
1✔
593
}
1✔
594
#endif
595

596

597
TEST(Util_Network_SSL_ShutdownBeforeCloseNotifyReceived)
598
{
1✔
599
    network::Service service;
1✔
600
    network::Socket socket_1{service}, socket_2{service};
1✔
601
    network::ssl::Context ssl_context_1;
1✔
602
    network::ssl::Context ssl_context_2;
1✔
603
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
604
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
605
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
606
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
607
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
608
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
609

610
    // Shut down peer 1's writing side before it has received a shutdown alert
611
    // from peer 2.
612
    ssl_stream_1.shutdown();
1✔
613
}
1✔
614

615

616
TEST(Util_Network_SSL_ShutdownAfterCloseNotifyReceived)
617
{
1✔
618
    network::Service service;
1✔
619
    network::Socket socket_1{service}, socket_2{service};
1✔
620
    network::ssl::Context ssl_context_1;
1✔
621
    network::ssl::Context ssl_context_2;
1✔
622
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
623
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
624
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
625
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
626
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
627
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
628

629
    // Make sure peer 2 gets an SSL shutdown alert.
630
    ssl_stream_1.shutdown();
1✔
631
    socket_1.shutdown(network::Socket::shutdown_send);
1✔
632

633
    // Make sure peer 2 received the shutdown alert from peer 1 before peer 2
634
    // writes.
635
    char ch;
1✔
636
    CHECK_SYSTEM_ERROR(ssl_stream_2.read_some(&ch, 1), MiscExtErrors::end_of_input);
1✔
637

638
    // Check that peer 2 can stil permform a shutdown operation.
639
    ssl_stream_2.shutdown();
1✔
640
}
1✔
641

642

643
TEST(Util_Network_SSL_WriteAfterCloseNotifyReceived)
644
{
1✔
645
    network::Service service;
1✔
646
    network::Socket socket_1{service}, socket_2{service};
1✔
647
    network::ssl::Context ssl_context_1;
1✔
648
    network::ssl::Context ssl_context_2;
1✔
649
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
650
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
651
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
652
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
653
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
654
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
655

656
    // Shut down peer 1's writing side, such that peer 2 gets an SSL shutdown
657
    // alert.
658
    ssl_stream_1.shutdown();
1✔
659
    socket_1.shutdown(network::Socket::shutdown_send);
1✔
660

661
    // Make sure peer 2 received the shutdown alert from peer 1 before peer 2
662
    // writes.
663
    char ch;
1✔
664
    CHECK_SYSTEM_ERROR(ssl_stream_2.read_some(&ch, 1), MiscExtErrors::end_of_input);
1✔
665

666
    // Make peer 2 Write a message, which must fail....????
667
    const char* message = "hello";
1✔
668
    CHECK_SYSTEM_ERROR(ssl_stream_2.write(message, std::strlen(message)), error::broken_pipe);
1✔
669
}
1✔
670

671
TEST(Util_Network_SSL_BasicSendAndReceive)
672
{
1✔
673
    network::Service service;
1✔
674
    network::Socket socket_1{service}, socket_2{service};
1✔
675
    network::ssl::Context ssl_context_1;
1✔
676
    network::ssl::Context ssl_context_2;
1✔
677
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
678
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
679
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
680
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
681
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
682
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
683

684
    // Make peer 2 Write a message.
685
    const char* message = "hello";
1✔
686
    ssl_stream_2.write(message, std::strlen(message));
1✔
687
    ssl_stream_2.shutdown();
1✔
688
    socket_2.shutdown(network::Socket::shutdown_send);
1✔
689

690
    // Check that peer 1 received the message correctly.
691
    char buffer[256];
1✔
692
    std::error_code ec;
1✔
693
    std::size_t n = ssl_stream_1.read(buffer, sizeof buffer, ec);
1✔
694
    CHECK_EQUAL(MiscExtErrors::end_of_input, ec);
1✔
695
    if (CHECK_EQUAL(std::strlen(message), n))
1✔
696
        CHECK(std::equal(buffer, buffer + n, message));
1✔
697
}
1✔
698

699

700
#if REALM_HAVE_SECURE_TRANSPORT
701

702
template <typename ReadHandler, typename ReadError>
703
void run_ssl_nonzero_length_test(test_util::unit_test::TestContext& test_context, ReadHandler&& read_handler,
704
                                 ReadError&& read_error_callback)
705
{
706
    network::Service service;
707
    network::DeadlineTimer run_timer{service};
708
    network::Socket socket_1{service};
709
    network::Socket socket_2{service};
710
    network::ssl::Context ssl_context_1;
711
    network::ssl::Context ssl_context_2;
712
    configure_server_ssl_context_for_test(ssl_context_1);
713
    network::ssl::Stream ssl_stream_1 = {socket_1, ssl_context_1, network::ssl::Stream::server};
714
    network::ssl::Stream ssl_stream_2 = {socket_2, ssl_context_2, network::ssl::Stream::client};
715
    ssl_stream_1.set_logger(test_context.logger.get());
716
    ssl_stream_2.set_logger(test_context.logger.get());
717
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
718
    network::ReadAheadBuffer rab;
719

720
    char buffer[50];
721

722
    auto service_thread = std::thread([&test_context, &service, &run_timer]() {
723
        run_timer.async_wait(std::chrono::seconds(10), [&test_context](Status status) {
724
            if (!status.is_ok())
725
                return;
726
            test_context.logger->info("run_ssl_nonzero_length_test: service timed out");
727
            abort(); // fail the test if the timer expires
728
        });
729
        service.run();
730
    });
731

732
    auto [r_promise, read_future] = util::make_promise_future<void>();
733
    auto async_read_handler = [&test_context, read_promise = std::move(r_promise),
734
                               handler = std::move(read_handler)](std::error_code ec, std::size_t n) mutable {
735
        CHECK_NOT_EQUAL(n, 50);
736
        handler(test_context, ec, n);
737
        read_promise.emplace_value();
738
    };
739

740
    // Set the error before the read
741
    service.post([&](Status status) {
742
        if (!status.is_ok())
743
            return;
744

745
        read_error_callback(test_context, ssl_stream_2);
746
        ssl_stream_2.async_read(buffer, 50, rab, std::move(async_read_handler));
747
    });
748
    read_future.get();
749

750
    // Shut down stream
751
    auto [s_promise, shutdown_future] = util::make_promise_future<void>();
752
    auto shutdown_handler = [&test_context, shutdown_promise = std::move(s_promise)](std::error_code ec) mutable {
753
        CHECK_EQUAL(std::error_code(), ec);
754
        shutdown_promise.emplace_value();
755
    };
756
    service.post([&](Status status) {
757
        if (!status.is_ok())
758
            return;
759

760
        ssl_stream_1.async_shutdown(std::move(shutdown_handler));
761
    });
762
    shutdown_future.get();
763

764
    // Stop the service thread after shutdown is complete
765
    service.stop();
766
    service_thread.join();
767
}
768

769
TEST(Util_Network_SSL_Nonzero_Length_Error)
770
{
771
    using MockSSLError = network::ssl::Stream::MockSSLError;
772
    auto&& read_handler = [](test_util::unit_test::TestContext& test_context, std::error_code ec, std::size_t n) {
773
        CHECK_EQUAL(util::MiscExtErrors::premature_end_of_input, ec);
774
        test_context.logger->info("Util_Network_SSL_Nonzero_Length_Error: n: %1", n);
775
        CHECK_EQUAL(0, n);
776
    };
777
    auto&& read_error_callback = [](test_util::unit_test::TestContext&, network::ssl::Stream& ssl_stream_2) {
778
        ssl_stream_2.set_mock_ssl_perform_error(
779
            std::make_unique<MockSSLError>(MockSSLError::Operation::read, static_cast<int>(errSSLClosedAbort), 0));
780
    };
781
    run_ssl_nonzero_length_test(test_context, std::move(read_handler), std::move(read_error_callback));
782
}
783

784

785
TEST(Util_Network_SSL_Nonzero_Length_EndOfInput)
786
{
787
    auto&& read_handler = [](test_util::unit_test::TestContext& test_context, std::error_code ec, std::size_t n) {
788
        CHECK_EQUAL(util::MiscExtErrors::end_of_input, ec);
789
        test_context.logger->info("Util_Network_SSL_Nonzero_Length_EndOfInput: n: %1", n);
790
        CHECK_EQUAL(6, n);
791
    };
792
    auto&& read_error_callback = [](test_util::unit_test::TestContext&, network::ssl::Stream& ssl_stream_2) {
793
        using MockSSLError = network::ssl::Stream::MockSSLError;
794
        ssl_stream_2.set_mock_ssl_perform_error(
795
            std::make_unique<MockSSLError>(MockSSLError::Operation::read, static_cast<int>(errSSLClosedGraceful), 6));
796
    };
797
    run_ssl_nonzero_length_test(test_context, std::move(read_handler), std::move(read_error_callback));
798
}
799
#endif
800

801

802
TEST(Util_Network_SSL_StressTest)
803
{
1✔
804
    network::Service service_1, service_2;
1✔
805
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
806
    network::ssl::Context ssl_context_1;
1✔
807
    network::ssl::Context ssl_context_2;
1✔
808
    configure_server_ssl_context_for_test(ssl_context_1);
1✔
809
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
810
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
811
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
812
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
813
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
1✔
814

815
    constexpr size_t original_size = 0x100000; // 1MiB
1✔
816
    std::unique_ptr<char[]> original_1, original_2;
1✔
817
    original_1.reset(new char[original_size]);
1✔
818
    original_2.reset(new char[original_size]);
1✔
819
    {
1✔
820
        std::mt19937_64 prng{std::random_device()()};
1✔
821
        using lim = std::numeric_limits<char>;
1✔
822
        std::uniform_int_distribution<short> dist{short(lim::min()), short(lim::max())};
1✔
823
        log("Initializing...");
1✔
824
        for (size_t i = 0; i < original_size; ++i)
1,048,577✔
825
            original_1[i] = char(dist(prng));
1,048,576✔
826
        for (size_t i = 0; i < original_size; ++i)
1,048,577✔
827
            original_2[i] = char(dist(prng));
1,048,576✔
828
        log("Initialized");
1✔
829
    }
1✔
830

831
    struct Stats {
1✔
832
        std::uint_fast64_t num_cancellations = 0;
1✔
833
        std::uint_fast64_t num_reads = 0, num_canceled_reads = 0;
1✔
834
        std::uint_fast64_t num_writes = 0, num_canceled_writes = 0;
1✔
835
    };
1✔
836

837
#ifdef _WIN32
838
    // With 512, it would take 9 minutes in 32-bit Debug mode on Windows
839
    constexpr int num_cycles = 32;
840
#else
841
    constexpr int num_cycles = 512;
1✔
842
#endif
1✔
843
    auto thread = [&](int id, network::ssl::Stream& ssl_stream, const char* read_original, const char* write_original,
1✔
844
                      Stats& stats) {
2✔
845
        std::unique_ptr<char[]> read_buffer{new char[original_size]};
2✔
846
        std::mt19937_64 prng{std::random_device()()};
2✔
847
        // Using range 1B -> 32KiB because that undershoots and overshoots in
848
        // equal amounts with respect to the SSL frame size of 16KiB.
849
        std::uniform_int_distribution<size_t> read_write_size_dist(1, 32 * 1024);
2✔
850
        std::uniform_int_distribution<int> delayed_read_write_dist(0, 49);
2✔
851
        network::Service& service = ssl_stream.lowest_layer().get_service();
2✔
852
        network::DeadlineTimer cancellation_timer{service};
2✔
853
        network::DeadlineTimer read_timer{service};
2✔
854
        network::DeadlineTimer write_timer{service};
2✔
855
        bool read_done = false, write_done = false;
2✔
856
        UniqueFunction<void()> shedule_cancellation = [&] {
2✔
857
            cancellation_timer.async_wait(std::chrono::microseconds(10), [&](Status status) {
×
858
                REALM_ASSERT(status.is_ok() || status == ErrorCodes::OperationAborted);
×
859
                if (status == ErrorCodes::OperationAborted)
×
860
                    return;
×
861
                if (read_done && write_done)
×
862
                    return;
×
863
                ssl_stream.lowest_layer().cancel();
×
864
                ++stats.num_cancellations;
×
865
                shedule_cancellation();
×
866
            });
×
867
        };
×
868
        //        shedule_cancellation();
869
        char* read_begin = read_buffer.get();
2✔
870
        char* read_end = read_buffer.get() + original_size;
2✔
871
        int num_read_cycles = 0;
2✔
872
        UniqueFunction<void()> read = [&] {
128,694✔
873
            if (read_begin == read_end) {
128,694✔
874
                log("<R%1>", id);
1,024✔
875
                CHECK(std::equal(read_original, read_original + original_size, read_buffer.get()));
1,024✔
876
                ++num_read_cycles;
1,024✔
877
                if (num_read_cycles == num_cycles) {
1,024✔
878
                    log("End of read %1", id);
2✔
879
                    read_done = true;
2✔
880
                    if (write_done)
2✔
881
                        cancellation_timer.cancel();
2✔
882
                    return;
2✔
883
                }
2✔
884
                read_begin = read_buffer.get();
1,022✔
885
                read_end = read_buffer.get() + original_size;
1,022✔
886
            }
1,022✔
887
            auto handler = [&](std::error_code ec, size_t n) {
128,724✔
888
                REALM_ASSERT(!ec || ec == error::operation_aborted);
128,724!
889
                ++stats.num_reads;
128,724✔
890
                if (ec == error::operation_aborted) {
128,724✔
891
                    ++stats.num_canceled_reads;
×
892
                }
×
893
                else {
128,724✔
894
                    read_begin += n;
128,724✔
895
                }
128,724✔
896
                if (delayed_read_write_dist(prng) == 0) {
128,724✔
897
                    read_timer.async_wait(std::chrono::microseconds(100), [&](Status status) {
2,587✔
898
                        REALM_ASSERT(status.is_ok());
2,587✔
899
                        read();
2,587✔
900
                    });
2,587✔
901
                }
2,587✔
902
                else {
126,137✔
903
                    read();
126,137✔
904
                }
126,137✔
905
            };
128,724✔
906
            char* buffer = read_begin;
128,692✔
907
            size_t size = read_write_size_dist(prng);
128,692✔
908
            size_t max_size = read_end - read_begin;
128,692✔
909
            if (size > max_size)
128,692✔
910
                size = max_size;
2,005✔
911
            ssl_stream.async_read_some(buffer, size, std::move(handler));
128,692✔
912
        };
128,692✔
913
        read();
2✔
914
        const char* write_begin = write_original;
2✔
915
        const char* write_end = write_original + original_size;
2✔
916
        int num_write_cycles = 0;
2✔
917
        UniqueFunction<void()> write = [&] {
87,476✔
918
            if (write_begin == write_end) {
87,476✔
919
                log("<W%1>", id);
1,024✔
920
                ++num_write_cycles;
1,024✔
921
                if (num_write_cycles == num_cycles) {
1,024✔
922
                    log("End of write %1", id);
2✔
923
                    write_done = true;
2✔
924
                    if (read_done)
2✔
925
                        cancellation_timer.cancel();
×
926
                    return;
2✔
927
                }
2✔
928
                write_begin = write_original;
1,022✔
929
                write_end = write_original + original_size;
1,022✔
930
            }
1,022✔
931
            auto handler = [&](std::error_code ec, size_t n) {
87,474✔
932
                REALM_ASSERT(!ec || ec == error::operation_aborted);
87,457!
933
                ++stats.num_writes;
87,457✔
934
                if (ec == error::operation_aborted) {
87,457✔
935
                    ++stats.num_canceled_writes;
×
936
                }
×
937
                else {
87,457✔
938
                    write_begin += n;
87,457✔
939
                }
87,457✔
940
                if (delayed_read_write_dist(prng) == 0) {
87,457✔
941
                    write_timer.async_wait(std::chrono::microseconds(100), [&](Status status) {
1,649✔
942
                        REALM_ASSERT(status.is_ok());
1,649✔
943
                        write();
1,649✔
944
                    });
1,649✔
945
                }
1,649✔
946
                else {
85,808✔
947
                    write();
85,808✔
948
                }
85,808✔
949
            };
87,457✔
950
            const char* data = write_begin;
87,474✔
951
            size_t size = read_write_size_dist(prng);
87,474✔
952
            size_t max_size = write_end - write_begin;
87,474✔
953
            if (size > max_size)
87,474✔
954
                size = max_size;
1,339✔
955
            ssl_stream.async_write_some(data, size, std::move(handler));
87,474✔
956
        };
87,474✔
957
        write();
2✔
958
        service.run();
2✔
959
    };
2✔
960

961
    Stats stats_1, stats_2;
1✔
962
    std::thread thread_1{[&] {
1✔
963
        thread(1, ssl_stream_1, original_1.get(), original_2.get(), stats_1);
1✔
964
    }};
1✔
965
    std::thread thread_2{[&] {
1✔
966
        thread(2, ssl_stream_2, original_2.get(), original_1.get(), stats_2);
1✔
967
    }};
1✔
968
    thread_1.join();
1✔
969
    thread_2.join();
1✔
970

971
    ssl_stream_1.shutdown();
1✔
972
    ssl_stream_2.shutdown();
1✔
973

974
    char ch;
1✔
975
    CHECK_SYSTEM_ERROR(ssl_stream_1.read_some(&ch, 1), MiscExtErrors::end_of_input);
1✔
976
    CHECK_SYSTEM_ERROR(ssl_stream_2.read_some(&ch, 1), MiscExtErrors::end_of_input);
1✔
977

978
    log("Cancellations: %1, %2", stats_1.num_cancellations, stats_2.num_cancellations);
1✔
979
    log("Reads:  %1 (%2 canceled), %3 (%4 canceled)", stats_1.num_reads, stats_1.num_canceled_reads,
1✔
980
        stats_2.num_reads, stats_2.num_canceled_reads);
1✔
981
    log("Writes: %1 (%2 canceled), %3 (%4 canceled)", stats_1.num_writes, stats_1.num_canceled_writes,
1✔
982
        stats_2.num_writes, stats_2.num_canceled_writes);
1✔
983
}
1✔
984

985
// The host name is contained in both the
986
// Common Name and the Subject Alternartive Name
987
// section of the server certificate.
988
TEST(Util_Network_SSL_Certificate_CN_SAN)
989
{
1✔
990
    network::Service service_1, service_2;
1✔
991
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
992
    network::ssl::Context ssl_context_1;
1✔
993
    network::ssl::Context ssl_context_2;
1✔
994

995
    std::string ca_dir = get_test_resource_path();
1✔
996

997
    ssl_context_1.use_certificate_chain_file(ca_dir + "dns-chain.crt.pem");
1✔
998
    ssl_context_1.use_private_key_file(ca_dir + "dns-checked-server.key.pem");
1✔
999
    ssl_context_2.use_verify_file(ca_dir + "crt.pem");
1✔
1000

1001
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
1002
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
1003
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
1004
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
1005

1006
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
1✔
1007

1008
    // We expect success because the certificate is signed for www.example.com
1009
    // in both Common Name and SAN.
1010
    ssl_stream_2.set_host_name("www.example.com");
1✔
1011

1012
    connect_sockets(socket_1, socket_2);
1✔
1013

1014
    auto connector = [&] {
1✔
1015
        std::error_code ec;
1✔
1016
        ssl_stream_2.handshake(ec);
1✔
1017
        CHECK_EQUAL(std::error_code(), ec);
1✔
1018
    };
1✔
1019
    auto acceptor = [&] {
1✔
1020
        std::error_code ec;
1✔
1021
        ssl_stream_1.handshake(ec);
1✔
1022
        CHECK_EQUAL(std::error_code(), ec);
1✔
1023
    };
1✔
1024

1025
    std::thread thread_1(std::move(connector));
1✔
1026
    std::thread thread_2(std::move(acceptor));
1✔
1027
    thread_1.join();
1✔
1028
    thread_2.join();
1✔
1029
}
1✔
1030

1031
// The host name is only contained in the
1032
// Subject Alternative Name section of the certificate.
1033
TEST(Util_Network_SSL_Certificate_SAN)
1034
{
1✔
1035
    network::Service service_1, service_2;
1✔
1036
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
1037
    network::ssl::Context ssl_context_1;
1✔
1038
    network::ssl::Context ssl_context_2;
1✔
1039

1040
    std::string ca_dir = get_test_resource_path();
1✔
1041

1042
    ssl_context_1.use_certificate_chain_file(ca_dir + "dns-chain.crt.pem");
1✔
1043
    ssl_context_1.use_private_key_file(ca_dir + "dns-checked-server.key.pem");
1✔
1044
    ssl_context_2.use_verify_file(ca_dir + "crt.pem");
1✔
1045

1046
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
1047
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
1048
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
1049
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
1050

1051
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
1✔
1052

1053
    ssl_stream_2.set_host_name("support.example.com");
1✔
1054

1055
    connect_sockets(socket_1, socket_2);
1✔
1056

1057
    auto connector = [&] {
1✔
1058
        std::error_code ec;
1✔
1059
        ssl_stream_2.handshake(ec);
1✔
1060
        CHECK_EQUAL(std::error_code(), ec);
1✔
1061
    };
1✔
1062
    auto acceptor = [&] {
1✔
1063
        std::error_code ec;
1✔
1064
        ssl_stream_1.handshake(ec);
1✔
1065
        CHECK_EQUAL(std::error_code(), ec);
1✔
1066
    };
1✔
1067

1068
    std::thread thread_1(std::move(connector));
1✔
1069
    std::thread thread_2(std::move(acceptor));
1✔
1070
    thread_1.join();
1✔
1071
    thread_2.join();
1✔
1072
}
1✔
1073

1074

1075
// FIXME: Verification of peer against Common Name is no longer supported in
1076
// Catalina (macOS).
1077
#if REALM_HAVE_OPENSSL || !REALM_HAVE_SECURE_TRANSPORT
1078

1079
// The host name www.example.com is contained in Common Name but not in SAN.
1080
TEST(Util_Network_SSL_Certificate_CN)
1081
{
1✔
1082
    network::Service service_1, service_2;
1✔
1083
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
1084
    network::ssl::Context ssl_context_1;
1✔
1085
    network::ssl::Context ssl_context_2;
1✔
1086

1087
    std::string ca_dir = get_test_resource_path();
1✔
1088

1089
    ssl_context_1.use_certificate_chain_file(ca_dir + "ip-chain.crt.pem");
1✔
1090
    ssl_context_1.use_private_key_file(ca_dir + "ip-server.key.pem");
1✔
1091
    ssl_context_2.use_verify_file(ca_dir + "crt.pem");
1✔
1092

1093
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
1094
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
1095
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
1096
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
1097

1098
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
1✔
1099

1100
    ssl_stream_2.set_host_name("www.example.com");
1✔
1101

1102
    connect_sockets(socket_1, socket_2);
1✔
1103

1104
    auto connector = [&] {
1✔
1105
        std::error_code ec;
1✔
1106
        ssl_stream_2.handshake(ec);
1✔
1107
        CHECK_EQUAL(std::error_code(), ec);
1✔
1108
    };
1✔
1109
    auto acceptor = [&] {
1✔
1110
        std::error_code ec;
1✔
1111
        ssl_stream_1.handshake(ec);
1✔
1112
        CHECK_EQUAL(std::error_code(), ec);
1✔
1113
    };
1✔
1114

1115
    std::thread thread_1(std::move(connector));
1✔
1116
    std::thread thread_2(std::move(acceptor));
1✔
1117
    thread_1.join();
1✔
1118
    thread_2.join();
1✔
1119
}
1✔
1120

1121
#endif // REALM_HAVE_OPENSSL || !REALM_HAVE_SECURE_TRANSPORT
1122

1123
// The ip address is contained in the IP SAN section
1124
// of the certificate. For OpenSSL, we expect failure because we only
1125
// check for DNS. For Secure Transport we get success because the
1126
// ip section is checked. This discrepancy could be resolved in the
1127
// future if deemed important.
1128
TEST(Util_Network_SSL_Certificate_IP)
1129
{
1✔
1130
    network::Service service_1, service_2;
1✔
1131
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
1132
    network::ssl::Context ssl_context_1;
1✔
1133
    network::ssl::Context ssl_context_2;
1✔
1134

1135
    std::string ca_dir = get_test_resource_path();
1✔
1136

1137
    ssl_context_1.use_certificate_chain_file(ca_dir + "ip-chain.crt.pem");
1✔
1138
    ssl_context_1.use_private_key_file(ca_dir + "ip-server.key.pem");
1✔
1139
    ssl_context_2.use_verify_file(ca_dir + "crt.pem");
1✔
1140

1141
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
1142
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
1143
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
1144
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
1145

1146
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
1✔
1147

1148
    ssl_stream_2.set_host_name("127.0.0.1");
1✔
1149

1150
    connect_sockets(socket_1, socket_2);
1✔
1151

1152
    auto connector = [&] {
1✔
1153
        std::error_code ec;
1✔
1154
        ssl_stream_2.handshake(ec);
1✔
1155
#if REALM_HAVE_OPENSSL
1✔
1156
        CHECK_NOT_EQUAL(std::error_code(), ec);
1✔
1157
#elif REALM_HAVE_SECURE_TRANSPORT
1158
        CHECK_EQUAL(std::error_code(), ec);
1159
#endif
1160
    };
1✔
1161
    auto acceptor = [&] {
1✔
1162
        std::error_code ec;
1✔
1163
        ssl_stream_1.handshake(ec);
1✔
1164
#if REALM_HAVE_OPENSSL
1✔
1165
        CHECK_NOT_EQUAL(std::error_code(), ec);
1✔
1166
#elif REALM_HAVE_SECURE_TRANSPORT
1167
        CHECK_EQUAL(std::error_code(), ec);
1168
#endif
1169
    };
1✔
1170

1171
    std::thread thread_1(std::move(connector));
1✔
1172
    std::thread thread_2(std::move(acceptor));
1✔
1173
    thread_1.join();
1✔
1174
    thread_2.join();
1✔
1175
}
1✔
1176

1177
// The certificate contains incorrect host names.
1178
// We expect the handshake to fail.
1179
TEST(Util_Network_SSL_Certificate_Failure)
1180
{
1✔
1181
    network::Service service_1, service_2;
1✔
1182
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
1183
    network::ssl::Context ssl_context_1;
1✔
1184
    network::ssl::Context ssl_context_2;
1✔
1185

1186
    std::string ca_dir = get_test_resource_path();
1✔
1187

1188
    ssl_context_1.use_certificate_chain_file(ca_dir + "dns-chain.crt.pem");
1✔
1189
    ssl_context_1.use_private_key_file(ca_dir + "dns-checked-server.key.pem");
1✔
1190
    ssl_context_2.use_verify_file(ca_dir + "crt.pem");
1✔
1191

1192
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
1193
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
1194
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
1195
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
1196

1197
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
1✔
1198

1199
    // We expect failure because the certificate is signed for www.example.com
1200
    ssl_stream_2.set_host_name("www.another-example.com");
1✔
1201

1202
    connect_sockets(socket_1, socket_2);
1✔
1203

1204
    auto connector = [&] {
1✔
1205
        std::error_code ec;
1✔
1206
        ssl_stream_2.handshake(ec);
1✔
1207
        // Refine this
1208
        CHECK_NOT_EQUAL(std::error_code(), ec);
1✔
1209
    };
1✔
1210
    auto acceptor = [&] {
1✔
1211
        std::error_code ec;
1✔
1212
        ssl_stream_1.handshake(ec);
1✔
1213
        // Refine this
1214
        CHECK_NOT_EQUAL(std::error_code(), ec);
1✔
1215
    };
1✔
1216

1217
    std::thread thread_1(std::move(connector));
1✔
1218
    std::thread thread_2(std::move(acceptor));
1✔
1219
    thread_1.join();
1✔
1220
    thread_2.join();
1✔
1221
}
1✔
1222

1223
#ifdef _WIN32
1224

1225
// Adding a trusted root certificate causes a blocking popup to appear so only run this test
1226
// if in an interactive session with a debugger attached, otherwise CI machines will block.
1227
// TODO: maybe use the CI environment variable for the condition?
1228
TEST_IF(Util_Network_SSL_Certificate_From_Windows_Cert_Store, IsDebuggerPresent())
1229
{
1230
    std::string ca_dir = get_test_resource_path();
1231

1232
    BIO* file = BIO_new_file((ca_dir + "crt.pem").c_str(), "rb");
1233
    X509* cert = PEM_read_bio_X509(file, NULL, 0, NULL);
1234
    BIO_free(file);
1235

1236
    BIO* mem = BIO_new(BIO_s_mem());
1237
    int size = i2d_X509_bio(mem, cert);
1238
    BUF_MEM* buffer;
1239
    BIO_get_mem_ptr(mem, &buffer);
1240

1241
    PCCERT_CONTEXT cert_context;
1242
    HCERTSTORE store = CertOpenSystemStoreA(NULL, "ROOT");
1243
    CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(buffer->data),
1244
                                     static_cast<DWORD>(buffer->length), CERT_STORE_ADD_USE_EXISTING, &cert_context);
1245
    BIO_free(mem);
1246

1247
    network::Service service_1, service_2;
1248
    network::Socket socket_1{service_1}, socket_2{service_2};
1249
    network::ssl::Context ssl_context_1;
1250
    network::ssl::Context ssl_context_2;
1251

1252
    ssl_context_1.use_certificate_chain_file(ca_dir + "dns-chain.crt.pem");
1253
    ssl_context_1.use_private_key_file(ca_dir + "dns-checked-server.key.pem");
1254
    ssl_context_2
1255
        .use_default_verify(); // this will import the Windows certificates in the context's certificate store
1256

1257
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1258
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1259
    ssl_stream_1.set_logger(test_context.logger.get());
1260
    ssl_stream_2.set_logger(test_context.logger.get());
1261

1262
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
1263

1264
    // We expect success because the certificate is signed for www.example.com
1265
    // in both Common Name and SAN.
1266
    ssl_stream_2.set_host_name("www.example.com");
1267

1268
    connect_sockets(socket_1, socket_2);
1269

1270
    auto connector = [&] {
1271
        std::error_code ec;
1272
        ssl_stream_2.handshake(ec);
1273
        CHECK_EQUAL(std::error_code(), ec);
1274
    };
1275
    auto acceptor = [&] {
1276
        std::error_code ec;
1277
        ssl_stream_1.handshake(ec);
1278
        CHECK_EQUAL(std::error_code(), ec);
1279
    };
1280

1281
    std::thread thread_1(std::move(connector));
1282
    std::thread thread_2(std::move(acceptor));
1283
    thread_1.join();
1284
    thread_2.join();
1285

1286
    CertDeleteCertificateFromStore(cert_context);
1287
    CertCloseStore(store, 0);
1288
}
1289

1290
#endif // _WIN32
1291

1292
#endif // !REALM_MOBILE
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc