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

randombit / botan / 6564375290

18 Oct 2023 12:18PM UTC coverage: 91.715% (+0.01%) from 91.704%
6564375290

push

github

web-flow
Merge pull request #3764 from Rohde-Schwarz/refactor/modernize_asio_stream

Refactor: Modernize asio TLS Stream

80132 of 87371 relevant lines covered (91.71%)

8636754.93 hits per line

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

98.93
/src/tests/unit_asio_stream.cpp
1
/*
2
* TLS ASIO Stream Unit Tests
3
* (C) 2018-2020 Jack Lloyd
4
*     2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include "tests.h"
10

11
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_TLS_ASIO_STREAM)
12

13
   #include <botan/asio_stream.h>
14
   #include <botan/tls_callbacks.h>
15
   #include <botan/tls_session_manager_noop.h>
16

17
   #include <boost/version.hpp>
18
   #if BOOST_VERSION >= 107300
19

20
      #include <boost/beast/_experimental/test/stream.hpp>
21
      #include <boost/bind.hpp>
22
      #include <utility>
23

24
namespace Botan_Tests {
25

26
namespace net = boost::asio;
27
using error_code = boost::system::error_code;
28

29
constexpr uint8_t TEST_DATA[] =
30
   "The story so far: In the beginning the Universe was created. "
31
   "This has made a lot of people very angry and been widely regarded as a bad move.";
32
constexpr std::size_t TEST_DATA_SIZE = 142;
33
static_assert(sizeof(TEST_DATA) == TEST_DATA_SIZE, "size of TEST_DATA must match TEST_DATA_SIZE");
34

35
/**
36
 * Mocked Botan::TLS::Channel. Pretends to perform TLS operations and triggers appropriate callbacks in StreamCore.
37
 */
38
class MockChannel {
36✔
39
   public:
40
      MockChannel(std::shared_ptr<Botan::TLS::Callbacks> core) :
24✔
41
            m_callbacks(std::move(core)), m_bytes_till_complete_record(TEST_DATA_SIZE), m_active(false) {}
24✔
42

43
   public:
44
      std::size_t received_data(std::span<const uint8_t> data) {
6✔
45
         if(m_bytes_till_complete_record <= data.size()) {
6✔
46
            m_callbacks->tls_record_received(0, TEST_DATA);
6✔
47
            m_active = true;  // claim to be active once a full record has been received (for handshake test)
6✔
48
            return 0;
6✔
49
         }
50
         m_bytes_till_complete_record -= data.size();
×
51
         return m_bytes_till_complete_record;
×
52
      }
53

54
      void send(std::span<const uint8_t> buf) { m_callbacks->tls_emit_data(buf); }
43✔
55

56
      bool is_active() const { return m_active; }
6✔
57

58
      bool is_handshake_complete() const { return m_active; }
10✔
59

60
   private:
61
      std::shared_ptr<Botan::TLS::Callbacks> m_callbacks;
62
      std::size_t m_bytes_till_complete_record;  // number of bytes still to read before tls record is completed
63
      bool m_active;
64
};
65

66
class ThrowingMockChannel : public MockChannel {
12✔
67
   public:
68
      static boost::system::error_code expected_ec() { return Botan::TLS::Alert::UnexpectedMessage; }
12✔
69

70
      ThrowingMockChannel(std::shared_ptr<Botan::TLS::Callbacks> core) : MockChannel(std::move(core)) {}
6✔
71

72
      std::size_t received_data(std::span<const uint8_t>) { throw Botan::TLS::Unexpected_Message("test_error"); }
4✔
73

74
      void send(std::span<const uint8_t>) { throw Botan::TLS::Unexpected_Message("test_error"); }
2✔
75
};
76

77
// Unfortunately, boost::beast::test::stream keeps lowest_layer_type private and
78
// only friends boost::asio::ssl::stream. We need to make our own.
79
class TestStream : public boost::beast::test::stream {
25✔
80
   public:
81
      using boost::beast::test::stream::stream;
25✔
82
      using lowest_layer_type = boost::beast::test::stream;
83
};
84

85
using FailCount = boost::beast::test::fail_count;
86

87
class AsioStream : public Botan::TLS::Stream<TestStream, MockChannel> {
×
88
   public:
89
      template <typename... Args>
90
      AsioStream(std::shared_ptr<Botan::TLS::Context> context, Args&&... args) : Stream(context, args...) {
18✔
91
         m_native_handle = std::make_unique<MockChannel>(m_core);
18✔
92
      }
18✔
93
};
94

95
class ThrowingAsioStream : public Botan::TLS::Stream<TestStream, ThrowingMockChannel> {
12✔
96
   public:
97
      template <typename... Args>
98
      ThrowingAsioStream(std::shared_ptr<Botan::TLS::Context> context, Args&&... args) : Stream(context, args...) {
6✔
99
         m_native_handle = std::make_unique<ThrowingMockChannel>(m_core);
6✔
100
      }
6✔
101
};
102

103
/**
104
 * Synchronous tests for Botan::Stream.
105
 *
106
 * This test validates the asynchronous behavior Botan::Stream, including its utility classes StreamCore and Async_*_Op.
107
 * The stream's channel, i.e. TLS_Client or TLS_Server, is mocked and pretends to perform TLS operations (noop) and
108
 * provides the test data to the stream.
109
 * The underlying network socket, claiming it read / wrote a number of bytes.
110
 */
111
class Asio_Stream_Tests final : public Test {
×
112
      std::shared_ptr<Botan::TLS::Context> get_context() {
25✔
113
         return std::make_shared<Botan::TLS::Context>(std::make_shared<Botan::Credentials_Manager>(),
50✔
114
                                                      std::make_shared<Botan::Null_RNG>(),
25✔
115
                                                      std::make_shared<Botan::TLS::Session_Manager_Noop>(),
25✔
116
                                                      std::make_shared<Botan::TLS::Default_Policy>());
50✔
117
      }
118

119
      // use memcmp to check if the data in a is a prefix of the data in b
120
      bool contains(const void* a, const void* b, const std::size_t size) { return memcmp(a, b, size) == 0; }
8✔
121

122
      boost::string_view test_data() const {
12✔
123
         return boost::string_view(reinterpret_cast<const char*>(TEST_DATA), TEST_DATA_SIZE);
12✔
124
      }
125

126
      void test_sync_handshake(std::vector<Test::Result>& results) {
1✔
127
         net::io_context ioc;
1✔
128
         auto ctx = get_context();
1✔
129
         AsioStream ssl(ctx, ioc, test_data());
1✔
130

131
         ssl.handshake(Botan::TLS::Connection_Side::Client);
1✔
132

133
         Test::Result result("sync TLS handshake");
1✔
134
         result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true);
1✔
135
         results.push_back(result);
1✔
136
      }
2✔
137

138
      void test_sync_handshake_error(std::vector<Test::Result>& results) {
1✔
139
         net::io_context ioc;
1✔
140
         // fail right away
141
         FailCount fc{0, net::error::no_recovery};
1✔
142
         TestStream remote{ioc};
1✔
143

144
         auto ctx = get_context();
1✔
145
         AsioStream ssl(ctx, ioc, fc);
1✔
146
         ssl.next_layer().connect(remote);
1✔
147

148
         // mimic handshake initialization
149
         ssl.native_handle()->send(TEST_DATA);
1✔
150

151
         error_code ec;
1✔
152
         ssl.handshake(Botan::TLS::Connection_Side::Client, ec);
1✔
153

154
         Test::Result result("sync TLS handshake error");
1✔
155
         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
156
         result.confirm("propagates error code", ec == net::error::no_recovery);
4✔
157
         results.push_back(result);
1✔
158
      }
3✔
159

160
      void test_sync_handshake_throw(std::vector<Test::Result>& results) {
1✔
161
         net::io_context ioc;
1✔
162
         TestStream remote{ioc};
1✔
163

164
         auto ctx = get_context();
1✔
165
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
166
         ssl.next_layer().connect(remote);
1✔
167

168
         error_code ec;
1✔
169
         ssl.handshake(Botan::TLS::Connection_Side::Client, ec);
1✔
170

171
         Test::Result result("sync TLS handshake error");
1✔
172
         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
173
         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
174
         results.push_back(result);
1✔
175
      }
2✔
176

177
      void test_async_handshake(std::vector<Test::Result>& results) {
1✔
178
         net::io_context ioc;
1✔
179
         TestStream remote{ioc};
1✔
180

181
         auto ctx = get_context();
1✔
182
         AsioStream ssl(ctx, ioc, test_data());
1✔
183
         ssl.next_layer().connect(remote);
1✔
184

185
         // mimic handshake initialization
186
         ssl.native_handle()->send(TEST_DATA);
1✔
187

188
         Test::Result result("async TLS handshake");
1✔
189

190
         auto handler = [&](const error_code&) {
2✔
191
            result.confirm("reads from socket", ssl.next_layer().nread() > 0);
2✔
192
            result.confirm("writes from socket", ssl.next_layer().nwrite() > 0);
2✔
193
            result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true);
1✔
194
         };
1✔
195

196
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
197

198
         ssl.next_layer().close_remote();
1✔
199
         ioc.run();
1✔
200
         results.push_back(result);
1✔
201
      }
2✔
202

203
      void test_async_handshake_error(std::vector<Test::Result>& results) {
1✔
204
         net::io_context ioc;
1✔
205
         // fail right away
206
         FailCount fc{0, net::error::no_recovery};
1✔
207
         TestStream remote{ioc};
1✔
208

209
         auto ctx = get_context();
1✔
210
         AsioStream ssl(ctx, ioc, fc);
1✔
211
         ssl.next_layer().connect(remote);
1✔
212

213
         // mimic handshake initialization
214
         ssl.native_handle()->send(TEST_DATA);
1✔
215

216
         Test::Result result("async TLS handshake error");
1✔
217

218
         auto handler = [&](const error_code& ec) {
2✔
219
            result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
220
            result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
221
         };
1✔
222

223
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
224

225
         ioc.run();
1✔
226
         results.push_back(result);
1✔
227
      }
2✔
228

229
      void test_async_handshake_throw(std::vector<Test::Result>& results) {
1✔
230
         net::io_context ioc;
1✔
231
         TestStream remote{ioc};
1✔
232

233
         auto ctx = get_context();
1✔
234
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
235
         ssl.next_layer().connect(remote);
1✔
236

237
         Test::Result result("async TLS handshake throw");
1✔
238

239
         auto handler = [&](const error_code& ec) {
2✔
240
            result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
241
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
242
         };
1✔
243

244
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
245

246
         ioc.run();
1✔
247
         results.push_back(result);
1✔
248
      }
2✔
249

250
      void test_sync_read_some_success(std::vector<Test::Result>& results) {
1✔
251
         net::io_context ioc;
1✔
252

253
         auto ctx = get_context();
1✔
254
         AsioStream ssl(ctx, ioc, test_data());
1✔
255

256
         const std::size_t buf_size = 128;
1✔
257
         uint8_t buf[buf_size];
1✔
258
         error_code ec;
1✔
259

260
         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec);
1✔
261

262
         Test::Result result("sync read_some success");
1✔
263
         result.confirm("reads the correct data", contains(buf, TEST_DATA, buf_size));
2✔
264
         result.test_eq("reads the correct amount of data", bytes_transferred, buf_size);
1✔
265
         result.confirm("does not report an error", !ec);
2✔
266

267
         results.push_back(result);
1✔
268
      }
2✔
269

270
      void test_sync_read_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
271
         net::io_context ioc;
1✔
272

273
         auto ctx = get_context();
1✔
274
         AsioStream ssl(ctx, ioc, test_data());
1✔
275
         error_code ec;
1✔
276

277
         std::vector<net::mutable_buffer> data;
1✔
278
         uint8_t buf1[TEST_DATA_SIZE / 2];
1✔
279
         uint8_t buf2[TEST_DATA_SIZE / 2];
1✔
280
         data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE / 2));
1✔
281
         data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE / 2));
1✔
282

283
         auto bytes_transferred = net::read(ssl, data, ec);
1✔
284

285
         Test::Result result("sync read_some buffer sequence");
1✔
286

287
         result.confirm("reads the correct data",
2✔
288
                        contains(buf1, TEST_DATA, TEST_DATA_SIZE / 2) &&
1✔
289
                           contains(buf2, TEST_DATA + TEST_DATA_SIZE / 2, TEST_DATA_SIZE / 2));
1✔
290
         result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
291
         result.confirm("does not report an error", !ec);
2✔
292

293
         results.push_back(result);
1✔
294
      }
3✔
295

296
      void test_sync_read_some_error(std::vector<Test::Result>& results) {
1✔
297
         net::io_context ioc;
1✔
298
         // fail right away
299
         FailCount fc{0, net::error::no_recovery};
1✔
300
         TestStream remote{ioc};
1✔
301

302
         auto ctx = get_context();
1✔
303
         AsioStream ssl(ctx, ioc, fc);
1✔
304
         ssl.next_layer().connect(remote);
1✔
305

306
         uint8_t buf[128];
1✔
307
         error_code ec;
1✔
308

309
         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec);
1✔
310

311
         Test::Result result("sync read_some error");
1✔
312
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
313
         result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
314

315
         results.push_back(result);
1✔
316
      }
2✔
317

318
      void test_sync_read_some_throw(std::vector<Test::Result>& results) {
1✔
319
         net::io_context ioc;
1✔
320
         TestStream remote{ioc};
1✔
321

322
         auto ctx = get_context();
1✔
323
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
324
         ssl.next_layer().connect(remote);
1✔
325

326
         uint8_t buf[128];
1✔
327
         error_code ec;
1✔
328

329
         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec);
1✔
330

331
         Test::Result result("sync read_some throw");
1✔
332
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
333
         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
334

335
         results.push_back(result);
1✔
336
      }
2✔
337

338
      void test_sync_read_zero_buffer(std::vector<Test::Result>& results) {
1✔
339
         net::io_context ioc;
1✔
340

341
         auto ctx = get_context();
1✔
342
         AsioStream ssl(ctx, ioc);
1✔
343

344
         const std::size_t buf_size = 128;
1✔
345
         uint8_t buf[buf_size];
1✔
346
         error_code ec;
1✔
347

348
         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, std::size_t(0)), ec);
1✔
349

350
         Test::Result result("sync read_some into zero-size buffer");
1✔
351
         result.test_eq("reads the correct amount of data", bytes_transferred, 0);
1✔
352
         // This relies on an implementation detail of TestStream: A "real" asio::tcp::stream
353
         // would block here. TestStream sets error_code::eof.
354
         result.confirm("does not report an error", !ec);
2✔
355

356
         results.push_back(result);
1✔
357
      }
2✔
358

359
      void test_async_read_some_success(std::vector<Test::Result>& results) {
1✔
360
         net::io_context ioc;
1✔
361
         TestStream remote{ioc};
1✔
362

363
         auto ctx = get_context();
1✔
364
         AsioStream ssl(ctx, ioc, test_data());
1✔
365
         uint8_t data[TEST_DATA_SIZE];
1✔
366

367
         Test::Result result("async read_some success");
1✔
368

369
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
370
            result.confirm("reads the correct data", contains(data, TEST_DATA, TEST_DATA_SIZE));
2✔
371
            result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
372
            result.confirm("does not report an error", !ec);
2✔
373
         };
1✔
374

375
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
376
         net::async_read(ssl, buf, read_handler);
1✔
377

378
         ssl.next_layer().close_remote();
1✔
379
         ioc.run();
1✔
380
         results.push_back(result);
1✔
381
      }
2✔
382

383
      void test_async_read_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
384
         net::io_context ioc;
1✔
385
         auto ctx = get_context();
1✔
386
         AsioStream ssl(ctx, ioc, test_data());
1✔
387

388
         std::vector<net::mutable_buffer> data;
1✔
389
         uint8_t buf1[TEST_DATA_SIZE / 2];
1✔
390
         uint8_t buf2[TEST_DATA_SIZE / 2];
1✔
391
         data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE / 2));
1✔
392
         data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE / 2));
1✔
393

394
         Test::Result result("async read_some buffer sequence");
1✔
395

396
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
397
            result.confirm("reads the correct data",
2✔
398
                           contains(buf1, TEST_DATA, TEST_DATA_SIZE / 2) &&
1✔
399
                              contains(buf2, TEST_DATA + TEST_DATA_SIZE / 2, TEST_DATA_SIZE / 2));
1✔
400
            result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
401
            result.confirm("does not report an error", !ec);
2✔
402
         };
1✔
403

404
         net::async_read(ssl, data, read_handler);
1✔
405

406
         ssl.next_layer().close_remote();
1✔
407
         ioc.run();
1✔
408
         results.push_back(result);
1✔
409
      }
3✔
410

411
      void test_async_read_some_error(std::vector<Test::Result>& results) {
1✔
412
         net::io_context ioc;
1✔
413
         // fail right away
414
         FailCount fc{0, net::error::no_recovery};
1✔
415
         auto ctx = get_context();
1✔
416
         AsioStream ssl(ctx, ioc, fc);
1✔
417
         uint8_t data[TEST_DATA_SIZE];
1✔
418

419
         Test::Result result("async read_some error");
1✔
420

421
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
422
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
423
            result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
424
         };
1✔
425

426
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
427
         net::async_read(ssl, buf, read_handler);
1✔
428

429
         ssl.next_layer().close_remote();
1✔
430
         ioc.run();
1✔
431
         results.push_back(result);
1✔
432
      }
2✔
433

434
      void test_async_read_some_throw(std::vector<Test::Result>& results) {
1✔
435
         net::io_context ioc;
1✔
436
         auto ctx = get_context();
1✔
437
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
438
         uint8_t data[TEST_DATA_SIZE];
1✔
439

440
         Test::Result result("async read_some throw");
1✔
441

442
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
443
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
444
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
445
         };
1✔
446

447
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
448
         net::async_read(ssl, buf, read_handler);
1✔
449

450
         ssl.next_layer().close_remote();
1✔
451
         ioc.run();
1✔
452
         results.push_back(result);
1✔
453
      }
2✔
454

455
      void test_async_read_zero_buffer(std::vector<Test::Result>& results) {
1✔
456
         net::io_context ioc;
1✔
457
         TestStream remote{ioc};
1✔
458

459
         auto ctx = get_context();
1✔
460
         AsioStream ssl(ctx, ioc);
1✔
461
         uint8_t data[TEST_DATA_SIZE];
1✔
462

463
         Test::Result result("async read_some into zero-size buffer");
1✔
464

465
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
466
            result.test_eq("reads the correct amount of data", bytes_transferred, 0);
1✔
467
            // This relies on an implementation detail of TestStream: A "real" asio::tcp::stream
468
            // would block here. TestStream sets error_code::eof.
469
            result.confirm("does not report an error", !ec);
2✔
470
         };
1✔
471

472
         net::mutable_buffer buf{data, std::size_t(0)};
1✔
473
         net::async_read(ssl, buf, read_handler);
1✔
474

475
         ssl.next_layer().close_remote();
1✔
476
         ioc.run();
1✔
477
         results.push_back(result);
1✔
478
      }
2✔
479

480
      void test_sync_write_some_success(std::vector<Test::Result>& results) {
1✔
481
         net::io_context ioc;
1✔
482
         TestStream remote{ioc};
1✔
483

484
         auto ctx = get_context();
1✔
485
         AsioStream ssl(ctx, ioc);
1✔
486
         ssl.next_layer().connect(remote);
1✔
487
         error_code ec;
1✔
488

489
         auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
1✔
490

491
         Test::Result result("sync write_some success");
1✔
492
         result.confirm("writes the correct data", remote.str() == test_data());
3✔
493
         result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
494
         result.confirm("does not report an error", !ec);
2✔
495

496
         results.push_back(result);
1✔
497
      }
2✔
498

499
      void test_sync_no_handshake(std::vector<Test::Result>& results) {
1✔
500
         net::io_context ioc;
1✔
501
         TestStream remote{ioc};
1✔
502

503
         auto ctx = get_context();
1✔
504
         Botan::TLS::Stream<TestStream> ssl(ctx, ioc);  // Note that we're not using MockChannel here
1✔
505
         ssl.next_layer().connect(remote);
1✔
506
         error_code ec;
1✔
507

508
         net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
1✔
509

510
         Test::Result result("sync write_some without handshake fails gracefully");
1✔
511
         result.confirm("reports an error", ec.failed());
2✔
512

513
         results.push_back(result);
1✔
514
      }
2✔
515

516
      void test_sync_write_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
517
         net::io_context ioc;
1✔
518
         TestStream remote{ioc};
1✔
519

520
         auto ctx = get_context();
1✔
521
         AsioStream ssl(ctx, ioc);
1✔
522
         ssl.next_layer().connect(remote);
1✔
523
         error_code ec;
1✔
524

525
         // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1
526
         std::array<uint8_t, 17 * 1024 + 1> random_data;
1✔
527
         random_data.fill('4');  // chosen by fair dice roll
1✔
528
         random_data.back() = '5';
1✔
529

530
         std::vector<net::const_buffer> data;
1✔
531
         data.emplace_back(net::const_buffer(random_data.data(), 1));
1✔
532
         for(std::size_t i = 1; i < random_data.size(); i += 1024) {
18✔
533
            data.emplace_back(net::const_buffer(random_data.data() + i, 1024));
17✔
534
         }
535

536
         auto bytes_transferred = net::write(ssl, data, ec);
1✔
537

538
         Test::Result result("sync write_some buffer sequence");
1✔
539

540
         result.confirm("[precondition] MAX_PLAINTEXT_SIZE is still smaller than random_data.size()",
2✔
541
                        Botan::TLS::MAX_PLAINTEXT_SIZE < random_data.size());
542

543
         result.confirm("writes the correct data",
2✔
544
                        contains(remote.buffer().data().data(), random_data.data(), random_data.size()));
1✔
545
         result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size());
1✔
546
         result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2);
1✔
547
         result.confirm("does not report an error", !ec);
2✔
548

549
         results.push_back(result);
1✔
550
      }
3✔
551

552
      void test_sync_write_some_error(std::vector<Test::Result>& results) {
1✔
553
         net::io_context ioc;
1✔
554
         // fail right away
555
         FailCount fc{0, net::error::no_recovery};
1✔
556
         TestStream remote{ioc};
1✔
557

558
         auto ctx = get_context();
1✔
559
         AsioStream ssl(ctx, ioc, fc);
1✔
560
         ssl.next_layer().connect(remote);
1✔
561

562
         error_code ec;
1✔
563

564
         auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
1✔
565

566
         Test::Result result("sync write_some error");
1✔
567
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
568
         result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
569

570
         results.push_back(result);
1✔
571
      }
2✔
572

573
      void test_sync_write_some_throw(std::vector<Test::Result>& results) {
1✔
574
         net::io_context ioc;
1✔
575
         TestStream remote{ioc};
1✔
576

577
         auto ctx = get_context();
1✔
578
         ThrowingAsioStream ssl(ctx, ioc);
1✔
579
         ssl.next_layer().connect(remote);
1✔
580
         error_code ec;
1✔
581

582
         auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
1✔
583

584
         Test::Result result("sync write_some throw");
1✔
585
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
586
         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
587

588
         results.push_back(result);
1✔
589
      }
2✔
590

591
      void test_async_write_some_success(std::vector<Test::Result>& results) {
1✔
592
         net::io_context ioc;
1✔
593
         TestStream remote{ioc};
1✔
594

595
         auto ctx = get_context();
1✔
596
         AsioStream ssl(ctx, ioc);
1✔
597
         ssl.next_layer().connect(remote);
1✔
598

599
         Test::Result result("async write_some success");
1✔
600

601
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
602
            result.confirm("writes the correct data", remote.str() == test_data());
3✔
603
            result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
604
            result.confirm("does not report an error", !ec);
2✔
605
         };
1✔
606

607
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
608

609
         ioc.run();
1✔
610
         results.push_back(result);
1✔
611
      }
2✔
612

613
      void test_async_write_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
614
         net::io_context ioc;
1✔
615
         TestStream remote{ioc};
1✔
616

617
         auto ctx = get_context();
1✔
618
         AsioStream ssl(ctx, ioc);
1✔
619
         ssl.next_layer().connect(remote);
1✔
620

621
         // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1
622
         std::array<uint8_t, 17 * 1024 + 1> random_data;
1✔
623
         random_data.fill('4');  // chosen by fair dice roll
1✔
624
         random_data.back() = '5';
1✔
625

626
         std::vector<net::const_buffer> src;
1✔
627
         src.emplace_back(net::const_buffer(random_data.data(), 1));
1✔
628
         for(std::size_t i = 1; i < random_data.size(); i += 1024) {
18✔
629
            src.emplace_back(net::const_buffer(random_data.data() + i, 1024));
17✔
630
         }
631

632
         Test::Result result("async write_some buffer sequence");
1✔
633

634
         result.confirm("[precondition] MAX_PLAINTEXT_SIZE is still smaller than random_data.size()",
2✔
635
                        Botan::TLS::MAX_PLAINTEXT_SIZE < random_data.size());
636

637
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
638
            result.confirm("writes the correct data",
2✔
639
                           contains(remote.buffer().data().data(), random_data.data(), random_data.size()));
1✔
640
            result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size());
1✔
641
            result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2);
1✔
642
            result.confirm("does not report an error", !ec);
2✔
643
         };
1✔
644

645
         net::async_write(ssl, src, write_handler);
1✔
646

647
         ioc.run();
1✔
648
         results.push_back(result);
1✔
649
      }
3✔
650

651
      void test_async_write_some_error(std::vector<Test::Result>& results) {
1✔
652
         net::io_context ioc;
1✔
653
         // fail right away
654
         FailCount fc{0, net::error::no_recovery};
1✔
655
         TestStream remote{ioc};
1✔
656

657
         auto ctx = get_context();
1✔
658
         AsioStream ssl(ctx, ioc, fc);
1✔
659
         ssl.next_layer().connect(remote);
1✔
660

661
         Test::Result result("async write_some error");
1✔
662

663
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
664
            result.test_eq("committed some bytes to the core", bytes_transferred, TEST_DATA_SIZE);
1✔
665
            result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
666
         };
1✔
667

668
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
669

670
         ioc.run();
1✔
671
         results.push_back(result);
1✔
672
      }
2✔
673

674
      void test_async_write_throw(std::vector<Test::Result>& results) {
1✔
675
         net::io_context ioc;
1✔
676
         TestStream remote{ioc};
1✔
677

678
         auto ctx = get_context();
1✔
679
         ThrowingAsioStream ssl(ctx, ioc);
1✔
680
         ssl.next_layer().connect(remote);
1✔
681

682
         Test::Result result("async write_some throw");
1✔
683

684
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
685
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
686
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
687
         };
1✔
688

689
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
690

691
         ioc.run();
1✔
692
         results.push_back(result);
1✔
693
      }
2✔
694

695
   public:
696
      std::vector<Test::Result> run() override {
1✔
697
         std::vector<Test::Result> results;
1✔
698

699
         test_sync_no_handshake(results);
1✔
700

701
         test_sync_handshake(results);
1✔
702
         test_sync_handshake_error(results);
1✔
703
         test_sync_handshake_throw(results);
1✔
704

705
         test_async_handshake(results);
1✔
706
         test_async_handshake_error(results);
1✔
707
         test_async_handshake_throw(results);
1✔
708

709
         test_sync_read_some_success(results);
1✔
710
         test_sync_read_some_buffer_sequence(results);
1✔
711
         test_sync_read_some_error(results);
1✔
712
         test_sync_read_some_throw(results);
1✔
713
         test_sync_read_zero_buffer(results);
1✔
714

715
         test_async_read_some_success(results);
1✔
716
         test_async_read_some_buffer_sequence(results);
1✔
717
         test_async_read_some_error(results);
1✔
718
         test_async_read_some_throw(results);
1✔
719
         test_async_read_zero_buffer(results);
1✔
720

721
         test_sync_write_some_success(results);
1✔
722
         test_sync_write_some_buffer_sequence(results);
1✔
723
         test_sync_write_some_error(results);
1✔
724
         test_sync_write_some_throw(results);
1✔
725

726
         test_async_write_some_success(results);
1✔
727
         test_async_write_some_buffer_sequence(results);
1✔
728
         test_async_write_some_error(results);
1✔
729
         test_async_write_throw(results);
1✔
730

731
         return results;
1✔
732
      }
×
733
};
734

735
BOTAN_REGISTER_TEST("tls", "tls_asio_stream", Asio_Stream_Tests);
736

737
}  // namespace Botan_Tests
738

739
   #endif  // BOOST_VERSION
740
#endif     // BOTAN_HAS_TLS && BOTAN_HAS_BOOST_ASIO
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