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

randombit / botan / 21119274491

18 Jan 2026 01:15PM UTC coverage: 91.944% (+1.5%) from 90.401%
21119274491

push

github

web-flow
Merge pull request #5241 from KaganCanSit/fix-boost-depracate-bind-include-warnings

Replace deprecated boost/bind.hpp with boost/bind/bind.hpp

104187 of 113316 relevant lines covered (91.94%)

12626365.97 hits per line

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

99.05
/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 <utility>
22

23
namespace Botan_Tests {
24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

646
         error_code ec;
1✔
647

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

783
         test_sync_no_handshake(results);
1✔
784

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

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

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

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

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

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

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

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

823
}  // namespace Botan_Tests
824

825
   #endif
826
#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