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

randombit / botan / 16271721753

14 Jul 2025 11:26AM UTC coverage: 90.622% (-0.003%) from 90.625%
16271721753

push

github

web-flow
Merge pull request #4989 from randombit/jack/fix-more-named-parameters

Fix more readability-named-parameter warnings from clang-tidy

99624 of 109934 relevant lines covered (90.62%)

12378967.04 hits per line

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

98.85
/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_compat.h>
14
   #if defined(BOTAN_FOUND_COMPATIBLE_BOOST_ASIO_VERSION)
15

16
      #include <botan/asio_stream.h>
17
      #include <botan/tls_callbacks.h>
18
      #include <botan/tls_session_manager_noop.h>
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
      explicit MockChannel(std::shared_ptr<Botan::TLS::Callbacks> core) :
26✔
41
            m_callbacks(std::move(core)),
26✔
42
            m_bytes_till_complete_record(TEST_DATA_SIZE),
26✔
43
            m_active(false),
26✔
44
            m_close_notify_received(false) {}
26✔
45

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

57
      void send(std::span<const uint8_t> buf) { m_callbacks->tls_emit_data(buf); }
15✔
58

59
      bool is_active() const { return m_active; }
8✔
60

61
      bool is_handshake_complete() const { return m_active; }
13✔
62

63
      bool is_closed_for_reading() const { return m_close_notify_received; }
64

65
      bool is_closed_for_writing() const { return m_close_notify_received; }
66

67
      void received_close_notify() {
2✔
68
         m_close_notify_received = true;
2✔
69
         m_callbacks->tls_alert(Botan::TLS::AlertType::CloseNotify);
2✔
70
         const auto close_notify_record = Botan::hex_decode("15030300020100");
2✔
71
         send(close_notify_record);
2✔
72
      }
2✔
73

74
   private:
75
      std::shared_ptr<Botan::TLS::Callbacks> m_callbacks;
76
      std::size_t m_bytes_till_complete_record;  // number of bytes still to read before tls record is completed
77
      bool m_active;
78
      bool m_close_notify_received;
79
};
80

81
class ThrowingMockChannel : public MockChannel {
12✔
82
   public:
83
      static boost::system::error_code expected_ec() { return Botan::TLS::Alert::UnexpectedMessage; }
12✔
84

85
      explicit ThrowingMockChannel(std::shared_ptr<Botan::TLS::Callbacks> core) : MockChannel(std::move(core)) {}
6✔
86

87
      std::size_t received_data(std::span<const uint8_t> /*data*/) {
4✔
88
         throw Botan::TLS::Unexpected_Message("test_error");
4✔
89
      }
90

91
      void send(std::span<const uint8_t> /*data*/) { throw Botan::TLS::Unexpected_Message("test_error"); }
2✔
92
};
93

94
class CancellingMockChannel : public MockChannel {
4✔
95
   public:
96
      explicit CancellingMockChannel(std::shared_ptr<Botan::TLS::Callbacks> core) : MockChannel(std::move(core)) {}
2✔
97

98
      std::size_t received_data(std::span<const uint8_t> /*data*/) {
2✔
99
         received_close_notify();
2✔
100
         return 0;
×
101
      }
102
};
103

104
// Unfortunately, boost::beast::test::stream keeps lowest_layer_type private and
105
// only friends boost::asio::ssl::stream. We need to make our own.
106
class TestStream : public boost::beast::test::stream {
27✔
107
   public:
108
      // NOLINTNEXTLINE(modernize-type-traits)
109
      using boost::beast::test::stream::stream;
27✔
110
      using lowest_layer_type = boost::beast::test::stream;
111
};
112

113
using FailCount = boost::beast::test::fail_count;
114

115
class AsioStream : public Botan::TLS::Stream<TestStream, MockChannel> {
×
116
   public:
117
      template <typename... Args>
118
      explicit AsioStream(std::shared_ptr<Botan::TLS::Context> context, Args&&... args) :
18✔
119
            Stream(std::move(context), std::forward<Args>(args)...) {
18✔
120
         m_native_handle = std::make_unique<MockChannel>(m_core);
18✔
121
      }
18✔
122
};
123

124
class ThrowingAsioStream : public Botan::TLS::Stream<TestStream, ThrowingMockChannel> {
12✔
125
   public:
126
      template <typename... Args>
127
      explicit ThrowingAsioStream(std::shared_ptr<Botan::TLS::Context> context, Args&&... args) :
6✔
128
            Stream(std::move(context), std::forward<Args>(args)...) {
6✔
129
         m_native_handle = std::make_unique<ThrowingMockChannel>(m_core);
6✔
130
      }
6✔
131
};
132

133
class CancellingAsioStream : public Botan::TLS::Stream<TestStream, CancellingMockChannel> {
4✔
134
   public:
135
      template <typename... Args>
136
      explicit CancellingAsioStream(std::shared_ptr<Botan::TLS::Context> context, Args&&... args) :
2✔
137
            Stream(std::move(context), std::forward<Args>(args)...) {
2✔
138
         m_native_handle = std::make_unique<CancellingMockChannel>(m_core);
2✔
139
      }
2✔
140
};
141

142
/**
143
 * Synchronous tests for Botan::Stream.
144
 *
145
 * This test validates the asynchronous behavior Botan::Stream, including its utility classes StreamCore and Async_*_Op.
146
 * The stream's channel, i.e. TLS_Client or TLS_Server, is mocked and pretends to perform TLS operations (noop) and
147
 * provides the test data to the stream.
148
 * The underlying network socket, claiming it read / wrote a number of bytes.
149
 */
150
class Asio_Stream_Tests final : public Test {
×
151
      std::shared_ptr<Botan::TLS::Context> get_context() {
27✔
152
         return std::make_shared<Botan::TLS::Context>(std::make_shared<Botan::Credentials_Manager>(),
54✔
153
                                                      std::make_shared<Botan::Null_RNG>(),
27✔
154
                                                      std::make_shared<Botan::TLS::Session_Manager_Noop>(),
27✔
155
                                                      std::make_shared<Botan::TLS::Default_Policy>());
54✔
156
      }
157

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

161
      boost::string_view test_data() const {
14✔
162
         return boost::string_view(reinterpret_cast<const char*>(TEST_DATA), TEST_DATA_SIZE);
14✔
163
      }
164

165
      void test_sync_handshake(std::vector<Test::Result>& results) {
1✔
166
         net::io_context ioc;
1✔
167
         auto ctx = get_context();
1✔
168
         AsioStream ssl(ctx, ioc, test_data());
1✔
169

170
         ssl.handshake(Botan::TLS::Connection_Side::Client);
1✔
171

172
         Test::Result result("sync TLS handshake");
1✔
173
         result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true);
1✔
174
         results.push_back(result);
1✔
175
      }
2✔
176

177
      void test_sync_handshake_error(std::vector<Test::Result>& results) {
1✔
178
         net::io_context ioc;
1✔
179
         // fail right away
180
         FailCount fc{0, net::error::no_recovery};
1✔
181
         TestStream remote{ioc};
1✔
182

183
         auto ctx = get_context();
1✔
184
         AsioStream ssl(ctx, ioc, fc);
1✔
185
         ssl.next_layer().connect(remote);
1✔
186

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

190
         error_code ec;
1✔
191
         ssl.handshake(Botan::TLS::Connection_Side::Client, ec);
1✔
192

193
         Test::Result result("sync TLS handshake error");
1✔
194
         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
195
         result.confirm("propagates error code", ec == net::error::no_recovery);
2✔
196
         results.push_back(result);
1✔
197
      }
2✔
198

199
      void test_sync_handshake_cancellation(std::vector<Test::Result>& results) {
1✔
200
         net::io_context ioc;
1✔
201
         TestStream remote{ioc};
1✔
202

203
         auto ctx = get_context();
1✔
204
         CancellingAsioStream ssl(ctx, ioc, test_data());
1✔
205
         ssl.next_layer().connect(remote);
1✔
206

207
         // mimic handshake initialization
208
         ssl.native_handle()->send(TEST_DATA);
1✔
209

210
         error_code ec;
1✔
211
         ssl.handshake(Botan::TLS::Connection_Side::Client, ec);
1✔
212

213
         Test::Result result("sync TLS handshake cancellation");
1✔
214
         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
215
         result.test_eq("does not finish handshake", ssl.native_handle()->is_handshake_complete(), false);
1✔
216
         result.confirm("cancelled handshake means EOF", ec == net::error::eof);
2✔
217
         results.push_back(result);
1✔
218
      }
2✔
219

220
      void test_sync_handshake_throw(std::vector<Test::Result>& results) {
1✔
221
         net::io_context ioc;
1✔
222
         TestStream remote{ioc};
1✔
223

224
         auto ctx = get_context();
1✔
225
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
226
         ssl.next_layer().connect(remote);
1✔
227

228
         error_code ec;
1✔
229
         ssl.handshake(Botan::TLS::Connection_Side::Client, ec);
1✔
230

231
         Test::Result result("sync TLS handshake error");
1✔
232
         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
233
         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
2✔
234
         results.push_back(result);
1✔
235
      }
2✔
236

237
      void test_async_handshake(std::vector<Test::Result>& results) {
1✔
238
         net::io_context ioc;
1✔
239
         TestStream remote{ioc};
1✔
240

241
         auto ctx = get_context();
1✔
242
         AsioStream ssl(ctx, ioc, test_data());
1✔
243
         ssl.next_layer().connect(remote);
1✔
244

245
         // mimic handshake initialization
246
         ssl.native_handle()->send(TEST_DATA);
1✔
247

248
         Test::Result result("async TLS handshake");
1✔
249

250
         auto handler = [&](const error_code&) {
2✔
251
            result.confirm("reads from socket", ssl.next_layer().nread() > 0);
2✔
252
            result.confirm("writes from socket", ssl.next_layer().nwrite() > 0);
2✔
253
            result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true);
1✔
254
         };
1✔
255

256
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
257

258
         ssl.next_layer().close_remote();
1✔
259
         ioc.run();
1✔
260
         results.push_back(result);
1✔
261
      }
2✔
262

263
      void test_async_handshake_error(std::vector<Test::Result>& results) {
1✔
264
         net::io_context ioc;
1✔
265
         // fail right away
266
         FailCount fc{0, net::error::no_recovery};
1✔
267
         TestStream remote{ioc};
1✔
268

269
         auto ctx = get_context();
1✔
270
         AsioStream ssl(ctx, ioc, fc);
1✔
271
         ssl.next_layer().connect(remote);
1✔
272

273
         // mimic handshake initialization
274
         ssl.native_handle()->send(TEST_DATA);
1✔
275

276
         Test::Result result("async TLS handshake error");
1✔
277

278
         auto handler = [&](const error_code& ec) {
2✔
279
            result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
280
            result.confirm("propagates error code", ec == net::error::no_recovery);
2✔
281
         };
1✔
282

283
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
284

285
         ioc.run();
1✔
286
         results.push_back(result);
1✔
287
      }
2✔
288

289
      void test_async_handshake_cancellation(std::vector<Test::Result>& results) {
1✔
290
         net::io_context ioc;
1✔
291
         TestStream remote{ioc};
1✔
292

293
         auto ctx = get_context();
1✔
294
         CancellingAsioStream ssl(ctx, ioc, test_data());
1✔
295
         ssl.next_layer().connect(remote);
1✔
296

297
         // mimic handshake initialization
298
         ssl.native_handle()->send(TEST_DATA);
1✔
299

300
         Test::Result result("async TLS handshake cancellation");
1✔
301

302
         auto handler = [&](const error_code& ec) {
2✔
303
            result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
304
            result.test_eq("does not finish handshake", ssl.native_handle()->is_handshake_complete(), false);
1✔
305
            result.confirm("cancelled handshake means EOF", ec == net::error::eof);
2✔
306
         };
1✔
307

308
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
309

310
         ioc.run();
1✔
311
         results.push_back(result);
1✔
312
      }
2✔
313

314
      void test_async_handshake_throw(std::vector<Test::Result>& results) {
1✔
315
         net::io_context ioc;
1✔
316
         TestStream remote{ioc};
1✔
317

318
         auto ctx = get_context();
1✔
319
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
320
         ssl.next_layer().connect(remote);
1✔
321

322
         Test::Result result("async TLS handshake throw");
1✔
323

324
         auto handler = [&](const error_code& ec) {
2✔
325
            result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
326
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
2✔
327
         };
1✔
328

329
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
330

331
         ioc.run();
1✔
332
         results.push_back(result);
1✔
333
      }
2✔
334

335
      void test_sync_read_some_success(std::vector<Test::Result>& results) {
1✔
336
         net::io_context ioc;
1✔
337

338
         auto ctx = get_context();
1✔
339
         AsioStream ssl(ctx, ioc, test_data());
1✔
340

341
         const std::size_t buf_size = 128;
1✔
342
         uint8_t buf[buf_size];
1✔
343
         error_code ec;
1✔
344

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

347
         Test::Result result("sync read_some success");
1✔
348
         result.confirm("reads the correct data", contains(buf, TEST_DATA, buf_size));
2✔
349
         result.test_eq("reads the correct amount of data", bytes_transferred, buf_size);
1✔
350
         result.confirm("does not report an error", !ec);
2✔
351

352
         results.push_back(result);
1✔
353
      }
2✔
354

355
      void test_sync_read_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
356
         net::io_context ioc;
1✔
357

358
         auto ctx = get_context();
1✔
359
         AsioStream ssl(ctx, ioc, test_data());
1✔
360
         error_code ec;
1✔
361

362
         std::vector<net::mutable_buffer> data;
1✔
363
         uint8_t buf1[TEST_DATA_SIZE / 2];
1✔
364
         uint8_t buf2[TEST_DATA_SIZE / 2];
1✔
365
         data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE / 2));
1✔
366
         data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE / 2));
1✔
367

368
         auto bytes_transferred = net::read(ssl, data, ec);
1✔
369

370
         Test::Result result("sync read_some buffer sequence");
1✔
371

372
         result.confirm("reads the correct data",
2✔
373
                        contains(buf1, TEST_DATA, TEST_DATA_SIZE / 2) &&
1✔
374
                           contains(buf2, TEST_DATA + TEST_DATA_SIZE / 2, TEST_DATA_SIZE / 2));
1✔
375
         result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
376
         result.confirm("does not report an error", !ec);
2✔
377

378
         results.push_back(result);
1✔
379
      }
2✔
380

381
      void test_sync_read_some_error(std::vector<Test::Result>& results) {
1✔
382
         net::io_context ioc;
1✔
383
         // fail right away
384
         FailCount fc{0, net::error::no_recovery};
1✔
385
         TestStream remote{ioc};
1✔
386

387
         auto ctx = get_context();
1✔
388
         AsioStream ssl(ctx, ioc, fc);
1✔
389
         ssl.next_layer().connect(remote);
1✔
390

391
         uint8_t buf[128];
1✔
392
         error_code ec;
1✔
393

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

396
         Test::Result result("sync read_some error");
1✔
397
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
398
         result.confirm("propagates error code", ec == net::error::no_recovery);
2✔
399

400
         results.push_back(result);
1✔
401
      }
2✔
402

403
      void test_sync_read_some_throw(std::vector<Test::Result>& results) {
1✔
404
         net::io_context ioc;
1✔
405
         TestStream remote{ioc};
1✔
406

407
         auto ctx = get_context();
1✔
408
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
409
         ssl.next_layer().connect(remote);
1✔
410

411
         uint8_t buf[128];
1✔
412
         error_code ec;
1✔
413

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

416
         Test::Result result("sync read_some throw");
1✔
417
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
418
         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
2✔
419

420
         results.push_back(result);
1✔
421
      }
2✔
422

423
      void test_sync_read_zero_buffer(std::vector<Test::Result>& results) {
1✔
424
         net::io_context ioc;
1✔
425

426
         auto ctx = get_context();
1✔
427
         AsioStream ssl(ctx, ioc);
1✔
428

429
         const std::size_t buf_size = 128;
1✔
430
         uint8_t buf[buf_size];
1✔
431
         error_code ec;
1✔
432

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

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

441
         results.push_back(result);
1✔
442
      }
2✔
443

444
      void test_async_read_some_success(std::vector<Test::Result>& results) {
1✔
445
         net::io_context ioc;
1✔
446
         TestStream remote{ioc};
1✔
447

448
         auto ctx = get_context();
1✔
449
         AsioStream ssl(ctx, ioc, test_data());
1✔
450
         uint8_t data[TEST_DATA_SIZE];
1✔
451

452
         Test::Result result("async read_some success");
1✔
453

454
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
455
            result.confirm("reads the correct data", contains(data, TEST_DATA, TEST_DATA_SIZE));
2✔
456
            result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
457
            result.confirm("does not report an error", !ec);
2✔
458
         };
1✔
459

460
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
461
         net::async_read(ssl, buf, read_handler);
1✔
462

463
         ssl.next_layer().close_remote();
1✔
464
         ioc.run();
1✔
465
         results.push_back(result);
1✔
466
      }
2✔
467

468
      void test_async_read_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
469
         net::io_context ioc;
1✔
470
         auto ctx = get_context();
1✔
471
         AsioStream ssl(ctx, ioc, test_data());
1✔
472

473
         std::vector<net::mutable_buffer> data;
1✔
474
         uint8_t buf1[TEST_DATA_SIZE / 2];
1✔
475
         uint8_t buf2[TEST_DATA_SIZE / 2];
1✔
476
         data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE / 2));
1✔
477
         data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE / 2));
1✔
478

479
         Test::Result result("async read_some buffer sequence");
1✔
480

481
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
482
            result.confirm("reads the correct data",
2✔
483
                           contains(buf1, TEST_DATA, TEST_DATA_SIZE / 2) &&
1✔
484
                              contains(buf2, TEST_DATA + TEST_DATA_SIZE / 2, TEST_DATA_SIZE / 2));
1✔
485
            result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
486
            result.confirm("does not report an error", !ec);
2✔
487
         };
1✔
488

489
         net::async_read(ssl, data, read_handler);
1✔
490

491
         ssl.next_layer().close_remote();
1✔
492
         ioc.run();
1✔
493
         results.push_back(result);
1✔
494
      }
2✔
495

496
      void test_async_read_some_error(std::vector<Test::Result>& results) {
1✔
497
         net::io_context ioc;
1✔
498
         // fail right away
499
         FailCount fc{0, net::error::no_recovery};
1✔
500
         auto ctx = get_context();
1✔
501
         AsioStream ssl(ctx, ioc, fc);
1✔
502
         uint8_t data[TEST_DATA_SIZE];
1✔
503

504
         Test::Result result("async read_some error");
1✔
505

506
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
507
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
508
            result.confirm("propagates error code", ec == net::error::no_recovery);
2✔
509
         };
1✔
510

511
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
512
         net::async_read(ssl, buf, read_handler);
1✔
513

514
         ssl.next_layer().close_remote();
1✔
515
         ioc.run();
1✔
516
         results.push_back(result);
1✔
517
      }
2✔
518

519
      void test_async_read_some_throw(std::vector<Test::Result>& results) {
1✔
520
         net::io_context ioc;
1✔
521
         auto ctx = get_context();
1✔
522
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
523
         uint8_t data[TEST_DATA_SIZE];
1✔
524

525
         Test::Result result("async read_some throw");
1✔
526

527
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
528
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
529
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
2✔
530
         };
1✔
531

532
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
533
         net::async_read(ssl, buf, read_handler);
1✔
534

535
         ssl.next_layer().close_remote();
1✔
536
         ioc.run();
1✔
537
         results.push_back(result);
1✔
538
      }
2✔
539

540
      void test_async_read_zero_buffer(std::vector<Test::Result>& results) {
1✔
541
         net::io_context ioc;
1✔
542
         TestStream remote{ioc};
1✔
543

544
         auto ctx = get_context();
1✔
545
         AsioStream ssl(ctx, ioc);
1✔
546
         uint8_t data[TEST_DATA_SIZE];
1✔
547

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

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

557
         net::mutable_buffer buf{data, std::size_t(0)};
1✔
558
         net::async_read(ssl, buf, read_handler);
1✔
559

560
         ssl.next_layer().close_remote();
1✔
561
         ioc.run();
1✔
562
         results.push_back(result);
1✔
563
      }
2✔
564

565
      void test_sync_write_some_success(std::vector<Test::Result>& results) {
1✔
566
         net::io_context ioc;
1✔
567
         TestStream remote{ioc};
1✔
568

569
         auto ctx = get_context();
1✔
570
         AsioStream ssl(ctx, ioc);
1✔
571
         ssl.next_layer().connect(remote);
1✔
572
         error_code ec;
1✔
573

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

576
         Test::Result result("sync write_some success");
1✔
577
         result.confirm("writes the correct data", remote.str() == test_data());
3✔
578
         result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
579
         result.confirm("does not report an error", !ec);
2✔
580

581
         results.push_back(result);
1✔
582
      }
2✔
583

584
      void test_sync_no_handshake(std::vector<Test::Result>& results) {
1✔
585
         net::io_context ioc;
1✔
586
         TestStream remote{ioc};
1✔
587

588
         auto ctx = get_context();
1✔
589
         Botan::TLS::Stream<TestStream> ssl(ctx, ioc);  // Note that we're not using MockChannel here
1✔
590
         ssl.next_layer().connect(remote);
1✔
591
         error_code ec;
1✔
592

593
         net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
1✔
594

595
         Test::Result result("sync write_some without handshake fails gracefully");
1✔
596
         result.confirm("reports an error", ec.failed());
3✔
597

598
         results.push_back(result);
1✔
599
      }
2✔
600

601
      void test_sync_write_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
602
         net::io_context ioc;
1✔
603
         TestStream remote{ioc};
1✔
604

605
         auto ctx = get_context();
1✔
606
         AsioStream ssl(ctx, ioc);
1✔
607
         ssl.next_layer().connect(remote);
1✔
608
         error_code ec;
1✔
609

610
         // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1
611
         std::array<uint8_t, 17 * 1024 + 1> random_data{};
1✔
612
         random_data.fill('4');  // chosen by fair dice roll
1✔
613
         random_data.back() = '5';
1✔
614

615
         std::vector<net::const_buffer> data;
1✔
616
         data.emplace_back(net::const_buffer(random_data.data(), 1));
1✔
617
         for(std::size_t i = 1; i < random_data.size(); i += 1024) {
18✔
618
            data.emplace_back(net::const_buffer(random_data.data() + i, 1024));
17✔
619
         }
620

621
         auto bytes_transferred = net::write(ssl, data, ec);
1✔
622

623
         Test::Result result("sync write_some buffer sequence");
1✔
624

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

628
         result.confirm("writes the correct data",
2✔
629
                        contains(remote.buffer().data().data(), random_data.data(), random_data.size()));
1✔
630
         result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size());
1✔
631
         result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2);
1✔
632
         result.confirm("does not report an error", !ec);
2✔
633

634
         results.push_back(result);
1✔
635
      }
2✔
636

637
      void test_sync_write_some_error(std::vector<Test::Result>& results) {
1✔
638
         net::io_context ioc;
1✔
639
         // fail right away
640
         FailCount fc{0, net::error::no_recovery};
1✔
641
         TestStream remote{ioc};
1✔
642

643
         auto ctx = get_context();
1✔
644
         AsioStream ssl(ctx, ioc, fc);
1✔
645
         ssl.next_layer().connect(remote);
1✔
646

647
         error_code ec;
1✔
648

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

651
         Test::Result result("sync write_some error");
1✔
652
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
653
         result.confirm("propagates error code", ec == net::error::no_recovery);
2✔
654

655
         results.push_back(result);
1✔
656
      }
2✔
657

658
      void test_sync_write_some_throw(std::vector<Test::Result>& results) {
1✔
659
         net::io_context ioc;
1✔
660
         TestStream remote{ioc};
1✔
661

662
         auto ctx = get_context();
1✔
663
         ThrowingAsioStream ssl(ctx, ioc);
1✔
664
         ssl.next_layer().connect(remote);
1✔
665
         error_code ec;
1✔
666

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

669
         Test::Result result("sync write_some throw");
1✔
670
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
671
         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
2✔
672

673
         results.push_back(result);
1✔
674
      }
2✔
675

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

680
         auto ctx = get_context();
1✔
681
         AsioStream ssl(ctx, ioc);
1✔
682
         ssl.next_layer().connect(remote);
1✔
683

684
         Test::Result result("async write_some success");
1✔
685

686
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
687
            result.confirm("writes the correct data", remote.str() == test_data());
3✔
688
            result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
689
            result.confirm("does not report an error", !ec);
2✔
690
         };
1✔
691

692
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
693

694
         ioc.run();
1✔
695
         results.push_back(result);
1✔
696
      }
2✔
697

698
      void test_async_write_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
699
         net::io_context ioc;
1✔
700
         TestStream remote{ioc};
1✔
701

702
         auto ctx = get_context();
1✔
703
         AsioStream ssl(ctx, ioc);
1✔
704
         ssl.next_layer().connect(remote);
1✔
705

706
         // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1
707
         std::array<uint8_t, 17 * 1024 + 1> random_data{};
1✔
708
         random_data.fill('4');  // chosen by fair dice roll
1✔
709
         random_data.back() = '5';
1✔
710

711
         std::vector<net::const_buffer> src;
1✔
712
         src.emplace_back(net::const_buffer(random_data.data(), 1));
1✔
713
         for(std::size_t i = 1; i < random_data.size(); i += 1024) {
18✔
714
            src.emplace_back(net::const_buffer(random_data.data() + i, 1024));
17✔
715
         }
716

717
         Test::Result result("async write_some buffer sequence");
1✔
718

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

722
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
723
            result.confirm("writes the correct data",
2✔
724
                           contains(remote.buffer().data().data(), random_data.data(), random_data.size()));
1✔
725
            result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size());
1✔
726
            result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2);
1✔
727
            result.confirm("does not report an error", !ec);
2✔
728
         };
1✔
729

730
         net::async_write(ssl, src, write_handler);
1✔
731

732
         ioc.run();
1✔
733
         results.push_back(result);
1✔
734
      }
2✔
735

736
      void test_async_write_some_error(std::vector<Test::Result>& results) {
1✔
737
         net::io_context ioc;
1✔
738
         // fail right away
739
         FailCount fc{0, net::error::no_recovery};
1✔
740
         TestStream remote{ioc};
1✔
741

742
         auto ctx = get_context();
1✔
743
         AsioStream ssl(ctx, ioc, fc);
1✔
744
         ssl.next_layer().connect(remote);
1✔
745

746
         Test::Result result("async write_some error");
1✔
747

748
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
749
            result.test_eq("committed some bytes to the core", bytes_transferred, TEST_DATA_SIZE);
1✔
750
            result.confirm("propagates error code", ec == net::error::no_recovery);
2✔
751
         };
1✔
752

753
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
754

755
         ioc.run();
1✔
756
         results.push_back(result);
1✔
757
      }
2✔
758

759
      void test_async_write_throw(std::vector<Test::Result>& results) {
1✔
760
         net::io_context ioc;
1✔
761
         TestStream remote{ioc};
1✔
762

763
         auto ctx = get_context();
1✔
764
         ThrowingAsioStream ssl(ctx, ioc);
1✔
765
         ssl.next_layer().connect(remote);
1✔
766

767
         Test::Result result("async write_some throw");
1✔
768

769
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
770
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
771
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
2✔
772
         };
1✔
773

774
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
775

776
         ioc.run();
1✔
777
         results.push_back(result);
1✔
778
      }
2✔
779

780
   public:
781
      std::vector<Test::Result> run() override {
1✔
782
         std::vector<Test::Result> results;
1✔
783

784
         test_sync_no_handshake(results);
1✔
785

786
         test_sync_handshake(results);
1✔
787
         test_sync_handshake_error(results);
1✔
788
         test_sync_handshake_cancellation(results);
1✔
789
         test_sync_handshake_throw(results);
1✔
790

791
         test_async_handshake(results);
1✔
792
         test_async_handshake_error(results);
1✔
793
         test_async_handshake_cancellation(results);
1✔
794
         test_async_handshake_throw(results);
1✔
795

796
         test_sync_read_some_success(results);
1✔
797
         test_sync_read_some_buffer_sequence(results);
1✔
798
         test_sync_read_some_error(results);
1✔
799
         test_sync_read_some_throw(results);
1✔
800
         test_sync_read_zero_buffer(results);
1✔
801

802
         test_async_read_some_success(results);
1✔
803
         test_async_read_some_buffer_sequence(results);
1✔
804
         test_async_read_some_error(results);
1✔
805
         test_async_read_some_throw(results);
1✔
806
         test_async_read_zero_buffer(results);
1✔
807

808
         test_sync_write_some_success(results);
1✔
809
         test_sync_write_some_buffer_sequence(results);
1✔
810
         test_sync_write_some_error(results);
1✔
811
         test_sync_write_some_throw(results);
1✔
812

813
         test_async_write_some_success(results);
1✔
814
         test_async_write_some_buffer_sequence(results);
1✔
815
         test_async_write_some_error(results);
1✔
816
         test_async_write_throw(results);
1✔
817

818
         return results;
1✔
819
      }
×
820
};
821

822
BOTAN_REGISTER_TEST("tls", "tls_asio_stream", Asio_Stream_Tests);
823

824
}  // namespace Botan_Tests
825

826
   #endif
827
#endif  // BOTAN_HAS_TLS && BOTAN_HAS_TLS_ASIO_STREAM
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

© 2026 Coveralls, Inc