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

randombit / botan / 5111374265

29 May 2023 11:19AM UTC coverage: 92.227% (+0.5%) from 91.723%
5111374265

push

github

randombit
Next release will be 3.1.0. Update release notes

75588 of 81959 relevant lines covered (92.23%)

11886470.91 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
   // The boost::beast::test::stream we use is available starting from boost
18
   // version 1.68, so we cannot run these tests with a smaller version.
19
   #include <boost/version.hpp>
20
   #if BOOST_VERSION >= 106800
21

22
      // boost::beast::test::stream's include path has been changed in boost version
23
      // 1.70.
24
      #if BOOST_VERSION < 107000
25
         #include <boost/beast/experimental/test/stream.hpp>
26
      #else
27
         #include <boost/beast/_experimental/test/stream.hpp>
28
      #endif
29

30
      #include <boost/bind.hpp>
31

32
namespace Botan_Tests {
33

34
namespace net = boost::asio;
35
using error_code = boost::system::error_code;
36

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

43
/**
44
 * Mocked Botan::TLS::Channel. Pretends to perform TLS operations and triggers appropriate callbacks in StreamCore.
45
 */
46
class MockChannel {
36✔
47
   public:
48
      MockChannel(std::shared_ptr<Botan::TLS::Callbacks> core) :
24✔
49
            m_callbacks(core), m_bytes_till_complete_record(TEST_DATA_SIZE), m_active(false) {}
18✔
50

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

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

64
      bool is_active() { return m_active; }
10✔
65

66
   protected:
67
      std::shared_ptr<Botan::TLS::Callbacks> m_callbacks;
68
      std::size_t m_bytes_till_complete_record;  // number of bytes still to read before tls record is completed
69
      bool m_active;
70
};
71

72
class ThrowingMockChannel : public MockChannel {
12✔
73
   public:
74
      static boost::system::error_code expected_ec() { return Botan::TLS::Alert::UnexpectedMessage; }
12✔
75

76
      ThrowingMockChannel(std::shared_ptr<Botan::TLS::Callbacks> core) : MockChannel(core) {}
6✔
77

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

80
      void send(std::span<const uint8_t>) { throw Botan::TLS::Unexpected_Message("test_error"); }
2✔
81
};
82

83
// Unfortunately, boost::beast::test::stream keeps lowest_layer_type private and
84
// only friends boost::asio::ssl::stream. We need to make our own.
85
class TestStream : public boost::beast::test::stream {
25✔
86
   public:
87
      using boost::beast::test::stream::stream;
25✔
88
      using lowest_layer_type = boost::beast::test::stream;
89
};
90

91
using FailCount = boost::beast::test::fail_count;
92

93
class AsioStream : public Botan::TLS::Stream<TestStream, MockChannel> {
94
   public:
95
      template <typename... Args>
96
      AsioStream(std::shared_ptr<Botan::TLS::Context> context, Args&&... args) : Stream(context, args...) {
18✔
97
         m_native_handle = std::make_unique<MockChannel>(m_core);
18✔
98
      }
18✔
99

100
      virtual ~AsioStream() = default;
×
101
};
102

103
class ThrowingAsioStream : public Botan::TLS::Stream<TestStream, ThrowingMockChannel> {
104
   public:
105
      template <typename... Args>
106
      ThrowingAsioStream(std::shared_ptr<Botan::TLS::Context> context, Args&&... args) : Stream(context, args...) {
6✔
107
         m_native_handle = std::make_unique<ThrowingMockChannel>(m_core);
6✔
108
      }
6✔
109

110
      virtual ~ThrowingAsioStream() = default;
12✔
111
};
112

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

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

132
      boost::string_view test_data() const {
12✔
133
         return boost::string_view(reinterpret_cast<const char*>(TEST_DATA), TEST_DATA_SIZE);
12✔
134
      }
135

136
      void test_sync_handshake(std::vector<Test::Result>& results) {
1✔
137
         net::io_context ioc;
1✔
138
         auto ctx = get_context();
1✔
139
         AsioStream ssl(ctx, ioc, test_data());
1✔
140

141
         ssl.handshake(Botan::TLS::Connection_Side::Client);
1✔
142

143
         Test::Result result("sync TLS handshake");
1✔
144
         result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true);
1✔
145
         results.push_back(result);
1✔
146
      }
2✔
147

148
      void test_sync_handshake_error(std::vector<Test::Result>& results) {
1✔
149
         net::io_context ioc;
1✔
150
         // fail right away
151
         FailCount fc{0, net::error::no_recovery};
1✔
152
         TestStream remote{ioc};
1✔
153

154
         auto ctx = get_context();
1✔
155
         AsioStream ssl(ctx, ioc, fc);
1✔
156
         ssl.next_layer().connect(remote);
1✔
157

158
         // mimic handshake initialization
159
         ssl.native_handle()->send(TEST_DATA);
1✔
160

161
         error_code ec;
1✔
162
         ssl.handshake(Botan::TLS::Connection_Side::Client, ec);
1✔
163

164
         Test::Result result("sync TLS handshake error");
1✔
165
         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
166
         result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
167
         results.push_back(result);
1✔
168
      }
3✔
169

170
      void test_sync_handshake_throw(std::vector<Test::Result>& results) {
1✔
171
         net::io_context ioc;
1✔
172
         TestStream remote{ioc};
1✔
173

174
         auto ctx = get_context();
1✔
175
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
176
         ssl.next_layer().connect(remote);
1✔
177

178
         error_code ec;
1✔
179
         ssl.handshake(Botan::TLS::Connection_Side::Client, ec);
1✔
180

181
         Test::Result result("sync TLS handshake error");
1✔
182
         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
183
         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
184
         results.push_back(result);
1✔
185
      }
2✔
186

187
      void test_async_handshake(std::vector<Test::Result>& results) {
1✔
188
         net::io_context ioc;
1✔
189
         TestStream remote{ioc};
1✔
190

191
         auto ctx = get_context();
1✔
192
         AsioStream ssl(ctx, ioc, test_data());
1✔
193
         ssl.next_layer().connect(remote);
1✔
194

195
         // mimic handshake initialization
196
         ssl.native_handle()->send(TEST_DATA);
1✔
197

198
         Test::Result result("async TLS handshake");
1✔
199

200
         auto handler = [&](const error_code&) {
2✔
201
            result.confirm("reads from socket", ssl.next_layer().nread() > 0);
2✔
202
            result.confirm("writes from socket", ssl.next_layer().nwrite() > 0);
2✔
203
            result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true);
1✔
204
         };
1✔
205

206
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
207

208
         ssl.next_layer().close_remote();
1✔
209
         ioc.run();
1✔
210
         results.push_back(result);
1✔
211
      }
2✔
212

213
      void test_async_handshake_error(std::vector<Test::Result>& results) {
1✔
214
         net::io_context ioc;
1✔
215
         // fail right away
216
         FailCount fc{0, net::error::no_recovery};
1✔
217
         TestStream remote{ioc};
1✔
218

219
         auto ctx = get_context();
1✔
220
         AsioStream ssl(ctx, ioc, fc);
1✔
221
         ssl.next_layer().connect(remote);
1✔
222

223
         // mimic handshake initialization
224
         ssl.native_handle()->send(TEST_DATA);
1✔
225

226
         Test::Result result("async TLS handshake error");
1✔
227

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

233
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
234

235
         ioc.run();
1✔
236
         results.push_back(result);
1✔
237
      }
2✔
238

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

243
         auto ctx = get_context();
1✔
244
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
245
         ssl.next_layer().connect(remote);
1✔
246

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

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

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

256
         ioc.run();
1✔
257
         results.push_back(result);
1✔
258
      }
2✔
259

260
      void test_sync_read_some_success(std::vector<Test::Result>& results) {
1✔
261
         net::io_context ioc;
1✔
262

263
         auto ctx = get_context();
1✔
264
         AsioStream ssl(ctx, ioc, test_data());
1✔
265

266
         const std::size_t buf_size = 128;
1✔
267
         uint8_t buf[buf_size];
1✔
268
         error_code ec;
1✔
269

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

272
         Test::Result result("sync read_some success");
1✔
273
         result.confirm("reads the correct data", contains(buf, TEST_DATA, buf_size));
2✔
274
         result.test_eq("reads the correct amount of data", bytes_transferred, buf_size);
1✔
275
         result.confirm("does not report an error", !ec);
2✔
276

277
         results.push_back(result);
1✔
278
      }
2✔
279

280
      void test_sync_read_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
281
         net::io_context ioc;
1✔
282

283
         auto ctx = get_context();
1✔
284
         AsioStream ssl(ctx, ioc, test_data());
1✔
285
         error_code ec;
1✔
286

287
         std::vector<net::mutable_buffer> data;
1✔
288
         uint8_t buf1[TEST_DATA_SIZE / 2];
1✔
289
         uint8_t buf2[TEST_DATA_SIZE / 2];
1✔
290
         data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE / 2));
1✔
291
         data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE / 2));
1✔
292

293
         auto bytes_transferred = net::read(ssl, data, ec);
1✔
294

295
         Test::Result result("sync read_some buffer sequence");
1✔
296

297
         result.confirm("reads the correct data",
2✔
298
                        contains(buf1, TEST_DATA, TEST_DATA_SIZE / 2) &&
1✔
299
                           contains(buf2, TEST_DATA + TEST_DATA_SIZE / 2, TEST_DATA_SIZE / 2));
1✔
300
         result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
301
         result.confirm("does not report an error", !ec);
2✔
302

303
         results.push_back(result);
1✔
304
      }
3✔
305

306
      void test_sync_read_some_error(std::vector<Test::Result>& results) {
1✔
307
         net::io_context ioc;
1✔
308
         // fail right away
309
         FailCount fc{0, net::error::no_recovery};
1✔
310
         TestStream remote{ioc};
1✔
311

312
         auto ctx = get_context();
1✔
313
         AsioStream ssl(ctx, ioc, fc);
1✔
314
         ssl.next_layer().connect(remote);
1✔
315

316
         uint8_t buf[128];
1✔
317
         error_code ec;
1✔
318

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

321
         Test::Result result("sync read_some error");
1✔
322
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
323
         result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
324

325
         results.push_back(result);
1✔
326
      }
2✔
327

328
      void test_sync_read_some_throw(std::vector<Test::Result>& results) {
1✔
329
         net::io_context ioc;
1✔
330
         TestStream remote{ioc};
1✔
331

332
         auto ctx = get_context();
1✔
333
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
334
         ssl.next_layer().connect(remote);
1✔
335

336
         uint8_t buf[128];
1✔
337
         error_code ec;
1✔
338

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

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

345
         results.push_back(result);
1✔
346
      }
2✔
347

348
      void test_sync_read_zero_buffer(std::vector<Test::Result>& results) {
1✔
349
         net::io_context ioc;
1✔
350

351
         auto ctx = get_context();
1✔
352
         AsioStream ssl(ctx, ioc);
1✔
353

354
         const std::size_t buf_size = 128;
1✔
355
         uint8_t buf[buf_size];
1✔
356
         error_code ec;
1✔
357

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

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

366
         results.push_back(result);
1✔
367
      }
2✔
368

369
      void test_async_read_some_success(std::vector<Test::Result>& results) {
1✔
370
         net::io_context ioc;
1✔
371
         TestStream remote{ioc};
1✔
372

373
         auto ctx = get_context();
1✔
374
         AsioStream ssl(ctx, ioc, test_data());
1✔
375
         uint8_t data[TEST_DATA_SIZE];
1✔
376

377
         Test::Result result("async read_some success");
1✔
378

379
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
380
            result.confirm("reads the correct data", contains(data, TEST_DATA, TEST_DATA_SIZE));
2✔
381
            result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
382
            result.confirm("does not report an error", !ec);
2✔
383
         };
1✔
384

385
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
386
         net::async_read(ssl, buf, read_handler);
1✔
387

388
         ssl.next_layer().close_remote();
1✔
389
         ioc.run();
1✔
390
         results.push_back(result);
1✔
391
      }
2✔
392

393
      void test_async_read_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
394
         net::io_context ioc;
1✔
395
         auto ctx = get_context();
1✔
396
         AsioStream ssl(ctx, ioc, test_data());
1✔
397

398
         std::vector<net::mutable_buffer> data;
1✔
399
         uint8_t buf1[TEST_DATA_SIZE / 2];
1✔
400
         uint8_t buf2[TEST_DATA_SIZE / 2];
1✔
401
         data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE / 2));
1✔
402
         data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE / 2));
1✔
403

404
         Test::Result result("async read_some buffer sequence");
1✔
405

406
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
407
            result.confirm("reads the correct data",
2✔
408
                           contains(buf1, TEST_DATA, TEST_DATA_SIZE / 2) &&
1✔
409
                              contains(buf2, TEST_DATA + TEST_DATA_SIZE / 2, TEST_DATA_SIZE / 2));
1✔
410
            result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
411
            result.confirm("does not report an error", !ec);
2✔
412
         };
1✔
413

414
         net::async_read(ssl, data, read_handler);
1✔
415

416
         ssl.next_layer().close_remote();
1✔
417
         ioc.run();
1✔
418
         results.push_back(result);
1✔
419
      }
3✔
420

421
      void test_async_read_some_error(std::vector<Test::Result>& results) {
1✔
422
         net::io_context ioc;
1✔
423
         // fail right away
424
         FailCount fc{0, net::error::no_recovery};
1✔
425
         auto ctx = get_context();
1✔
426
         AsioStream ssl(ctx, ioc, fc);
1✔
427
         uint8_t data[TEST_DATA_SIZE];
1✔
428

429
         Test::Result result("async read_some error");
1✔
430

431
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
432
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
433
            result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
434
         };
1✔
435

436
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
437
         net::async_read(ssl, buf, read_handler);
1✔
438

439
         ssl.next_layer().close_remote();
1✔
440
         ioc.run();
1✔
441
         results.push_back(result);
1✔
442
      }
2✔
443

444
      void test_async_read_some_throw(std::vector<Test::Result>& results) {
1✔
445
         net::io_context ioc;
1✔
446
         auto ctx = get_context();
1✔
447
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
448
         uint8_t data[TEST_DATA_SIZE];
1✔
449

450
         Test::Result result("async read_some throw");
1✔
451

452
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
453
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
454
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
455
         };
1✔
456

457
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
458
         net::async_read(ssl, buf, read_handler);
1✔
459

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

465
      void test_async_read_zero_buffer(std::vector<Test::Result>& results) {
1✔
466
         net::io_context ioc;
1✔
467
         TestStream remote{ioc};
1✔
468

469
         auto ctx = get_context();
1✔
470
         AsioStream ssl(ctx, ioc);
1✔
471
         uint8_t data[TEST_DATA_SIZE];
1✔
472

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

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

482
         net::mutable_buffer buf{data, std::size_t(0)};
1✔
483
         net::async_read(ssl, buf, read_handler);
1✔
484

485
         ssl.next_layer().close_remote();
1✔
486
         ioc.run();
1✔
487
         results.push_back(result);
1✔
488
      }
2✔
489

490
      void test_sync_write_some_success(std::vector<Test::Result>& results) {
1✔
491
         net::io_context ioc;
1✔
492
         TestStream remote{ioc};
1✔
493

494
         auto ctx = get_context();
1✔
495
         AsioStream ssl(ctx, ioc);
1✔
496
         ssl.next_layer().connect(remote);
1✔
497
         error_code ec;
1✔
498

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

501
         Test::Result result("sync write_some success");
1✔
502
         result.confirm("writes the correct data", remote.str() == test_data());
3✔
503
         result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
504
         result.confirm("does not report an error", !ec);
2✔
505

506
         results.push_back(result);
1✔
507
      }
2✔
508

509
      void test_sync_no_handshake(std::vector<Test::Result>& results) {
1✔
510
         net::io_context ioc;
1✔
511
         TestStream remote{ioc};
1✔
512

513
         auto ctx = get_context();
1✔
514
         Botan::TLS::Stream<TestStream> ssl(ctx, ioc);  // Note that we're not using MockChannel here
1✔
515
         ssl.next_layer().connect(remote);
1✔
516
         error_code ec;
1✔
517

518
         net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
1✔
519

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

523
         results.push_back(result);
1✔
524
      }
2✔
525

526
      void test_sync_write_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
527
         net::io_context ioc;
1✔
528
         TestStream remote{ioc};
1✔
529

530
         auto ctx = get_context();
1✔
531
         AsioStream ssl(ctx, ioc);
1✔
532
         ssl.next_layer().connect(remote);
1✔
533
         error_code ec;
1✔
534

535
         // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1
536
         std::array<uint8_t, 17 * 1024 + 1> random_data;
1✔
537
         random_data.fill('4');  // chosen by fair dice roll
1✔
538
         random_data.back() = '5';
1✔
539

540
         std::vector<net::const_buffer> data;
1✔
541
         data.emplace_back(net::const_buffer(random_data.data(), 1));
1✔
542
         for(std::size_t i = 1; i < random_data.size(); i += 1024) {
18✔
543
            data.emplace_back(net::const_buffer(random_data.data() + i, 1024));
17✔
544
         }
545

546
         auto bytes_transferred = net::write(ssl, data, ec);
1✔
547

548
         Test::Result result("sync write_some buffer sequence");
1✔
549

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

553
         result.confirm("writes the correct data",
2✔
554
                        contains(remote.buffer().data().data(), random_data.data(), random_data.size()));
1✔
555
         result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size());
1✔
556
         result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2);
1✔
557
         result.confirm("does not report an error", !ec);
2✔
558

559
         results.push_back(result);
1✔
560
      }
3✔
561

562
      void test_sync_write_some_error(std::vector<Test::Result>& results) {
1✔
563
         net::io_context ioc;
1✔
564
         // fail right away
565
         FailCount fc{0, net::error::no_recovery};
1✔
566
         TestStream remote{ioc};
1✔
567

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

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 error");
1✔
577
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
578
         result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
579

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

583
      void test_sync_write_some_throw(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
         ThrowingAsioStream ssl(ctx, ioc);
1✔
589
         ssl.next_layer().connect(remote);
1✔
590
         error_code ec;
1✔
591

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

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

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

601
      void test_async_write_some_success(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

609
         Test::Result result("async write_some success");
1✔
610

611
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
612
            result.confirm("writes the correct data", remote.str() == test_data());
3✔
613
            result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
614
            result.confirm("does not report an error", !ec);
2✔
615
         };
1✔
616

617
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
618

619
         ioc.run();
1✔
620
         results.push_back(result);
1✔
621
      }
2✔
622

623
      void test_async_write_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
624
         net::io_context ioc;
1✔
625
         TestStream remote{ioc};
1✔
626

627
         auto ctx = get_context();
1✔
628
         AsioStream ssl(ctx, ioc);
1✔
629
         ssl.next_layer().connect(remote);
1✔
630

631
         // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1
632
         std::array<uint8_t, 17 * 1024 + 1> random_data;
1✔
633
         random_data.fill('4');  // chosen by fair dice roll
1✔
634
         random_data.back() = '5';
1✔
635

636
         std::vector<net::const_buffer> src;
1✔
637
         src.emplace_back(net::const_buffer(random_data.data(), 1));
1✔
638
         for(std::size_t i = 1; i < random_data.size(); i += 1024) {
18✔
639
            src.emplace_back(net::const_buffer(random_data.data() + i, 1024));
17✔
640
         }
641

642
         Test::Result result("async write_some buffer sequence");
1✔
643

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

647
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
648
            result.confirm("writes the correct data",
2✔
649
                           contains(remote.buffer().data().data(), random_data.data(), random_data.size()));
1✔
650
            result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size());
1✔
651
            result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2);
1✔
652
            result.confirm("does not report an error", !ec);
2✔
653
         };
1✔
654

655
         net::async_write(ssl, src, write_handler);
1✔
656

657
         ioc.run();
1✔
658
         results.push_back(result);
1✔
659
      }
3✔
660

661
      void test_async_write_some_error(std::vector<Test::Result>& results) {
1✔
662
         net::io_context ioc;
1✔
663
         // fail right away
664
         FailCount fc{0, net::error::no_recovery};
1✔
665
         TestStream remote{ioc};
1✔
666

667
         auto ctx = get_context();
1✔
668
         AsioStream ssl(ctx, ioc, fc);
1✔
669
         ssl.next_layer().connect(remote);
1✔
670

671
         Test::Result result("async write_some error");
1✔
672

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

678
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
679

680
         ioc.run();
1✔
681
         results.push_back(result);
1✔
682
      }
2✔
683

684
      void test_async_write_throw(std::vector<Test::Result>& results) {
1✔
685
         net::io_context ioc;
1✔
686
         TestStream remote{ioc};
1✔
687

688
         auto ctx = get_context();
1✔
689
         ThrowingAsioStream ssl(ctx, ioc);
1✔
690
         ssl.next_layer().connect(remote);
1✔
691

692
         Test::Result result("async write_some throw");
1✔
693

694
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
695
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
696
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
697
         };
1✔
698

699
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
700

701
         ioc.run();
1✔
702
         results.push_back(result);
1✔
703
      }
2✔
704

705
   public:
706
      std::vector<Test::Result> run() override {
1✔
707
         std::vector<Test::Result> results;
1✔
708

709
         test_sync_no_handshake(results);
1✔
710

711
         test_sync_handshake(results);
1✔
712
         test_sync_handshake_error(results);
1✔
713
         test_sync_handshake_throw(results);
1✔
714

715
         test_async_handshake(results);
1✔
716
         test_async_handshake_error(results);
1✔
717
         test_async_handshake_throw(results);
1✔
718

719
         test_sync_read_some_success(results);
1✔
720
         test_sync_read_some_buffer_sequence(results);
1✔
721
         test_sync_read_some_error(results);
1✔
722
         test_sync_read_some_throw(results);
1✔
723
         test_sync_read_zero_buffer(results);
1✔
724

725
         test_async_read_some_success(results);
1✔
726
         test_async_read_some_buffer_sequence(results);
1✔
727
         test_async_read_some_error(results);
1✔
728
         test_async_read_some_throw(results);
1✔
729
         test_async_read_zero_buffer(results);
1✔
730

731
         test_sync_write_some_success(results);
1✔
732
         test_sync_write_some_buffer_sequence(results);
1✔
733
         test_sync_write_some_error(results);
1✔
734
         test_sync_write_some_throw(results);
1✔
735

736
         test_async_write_some_success(results);
1✔
737
         test_async_write_some_buffer_sequence(results);
1✔
738
         test_async_write_some_error(results);
1✔
739
         test_async_write_throw(results);
1✔
740

741
         return results;
1✔
742
      }
×
743
};
744

745
BOTAN_REGISTER_TEST("tls", "tls_asio_stream", Asio_Stream_Tests);
746

747
}  // namespace Botan_Tests
748

749
   #endif  // BOOST_VERSION
750
#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