1//
2// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7// Official repository: https://github.com/boostorg/beast
8//
9
10#ifndef BOOST_BEAST_TEST_STREAM_HPP
11#define BOOST_BEAST_TEST_STREAM_HPP
12
13#include <boost/beast/core/detail/config.hpp>
14#include <boost/beast/core/flat_buffer.hpp>
15#include <boost/beast/core/role.hpp>
16#include <boost/beast/core/string.hpp>
17#include <boost/beast/_experimental/test/fail_count.hpp>
18#include <boost/beast/_experimental/test/detail/stream_state.hpp>
19#include <boost/asio/async_result.hpp>
20#include <boost/asio/buffer.hpp>
21#include <boost/asio/error.hpp>
22#include <boost/asio/executor_work_guard.hpp>
23#include <boost/asio/any_io_executor.hpp>
24#include <boost/asio/io_context.hpp>
25#include <boost/assert.hpp>
26#include <boost/shared_ptr.hpp>
27#include <boost/weak_ptr.hpp>
28#include <boost/throw_exception.hpp>
29#include <condition_variable>
30#include <limits>
31#include <memory>
32#include <mutex>
33#include <utility>
34
35#if ! BOOST_BEAST_DOXYGEN
36namespace boost {
37namespace asio {
38namespace ssl {
39template<typename> class stream;
40} // ssl
41} // asio
42} // boost
43#endif
44
45namespace boost {
46namespace beast {
47namespace test {
48
49/** A two-way socket useful for unit testing
50
51 An instance of this class simulates a traditional socket,
52 while also providing features useful for unit testing.
53 Each endpoint maintains an independent buffer called
54 the input area. Writes from one endpoint append data
55 to the peer's pending input area. When an endpoint performs
56 a read and data is present in the input area, the data is
57 delivered to the blocking or asynchronous operation. Otherwise
58 the operation is blocked or deferred until data is made
59 available, or until the endpoints become disconnected.
60
61 These streams may be used anywhere an algorithm accepts a
62 reference to a synchronous or asynchronous read or write
63 stream. It is possible to use a test stream in a call to
64 `net::read_until`, or in a call to
65 @ref boost::beast::http::async_write for example.
66
67 As with Boost.Asio I/O objects, a @ref stream constructs
68 with a reference to the `net::io_context` to use for
69 handling asynchronous I/O. For asynchronous operations, the
70 stream follows the same rules as a traditional asio socket
71 with respect to how completion handlers for asynchronous
72 operations are performed.
73
74 To facilitate testing, these streams support some additional
75 features:
76
77 @li The input area, represented by a @ref beast::basic_flat_buffer,
78 may be directly accessed by the caller to inspect the contents
79 before or after the remote endpoint writes data. This allows
80 a unit test to verify that the received data matches.
81
82 @li Data may be manually appended to the input area. This data
83 will delivered in the next call to
84 @ref stream::read_some or @ref stream::async_read_some.
85 This allows predefined test vectors to be set up for testing
86 read algorithms.
87
88 @li The stream may be constructed with a fail count. The
89 stream will eventually fail with a predefined error after a
90 certain number of operations, where the number of operations
91 is controlled by the test. When a test loops over a range of
92 operation counts, it is possible to exercise every possible
93 point of failure in the algorithm being tested. When used
94 correctly the technique allows the tests to reach a high
95 percentage of code coverage.
96
97 @par Thread Safety
98 @e Distinct @e objects: Safe.@n
99 @e Shared @e objects: Unsafe.
100 The application must also ensure that all asynchronous
101 operations are performed within the same implicit or explicit strand.
102
103 @par Concepts
104 @li <em>SyncReadStream</em>
105 @li <em>SyncWriteStream</em>
106 @li <em>AsyncReadStream</em>
107 @li <em>AsyncWriteStream</em>
108*/
109template<class Executor = net::any_io_executor>
110class basic_stream;
111
112template<class Executor>
113void
114teardown(
115 role_type,
116 basic_stream<Executor>& s,
117 boost::system::error_code& ec);
118
119template<class Executor, class TeardownHandler>
120void
121async_teardown(
122 role_type role,
123 basic_stream<Executor>& s,
124 TeardownHandler&& handler);
125
126template<class Executor>
127class basic_stream
128{
129public:
130 /// The type of the executor associated with the object.
131 using executor_type =
132 Executor;
133
134 /// Rebinds the socket type to another executor.
135 template <typename Executor1>
136 struct rebind_executor
137 {
138 /// The socket type when rebound to the specified executor.
139 typedef basic_stream<Executor1> other;
140 };
141
142private:
143 template<class Executor2>
144 friend class basic_stream;
145
146 boost::shared_ptr<detail::stream_state> in_;
147 boost::weak_ptr<detail::stream_state> out_;
148
149 template<class Handler, class Buffers>
150 class read_op;
151
152 struct run_read_op;
153 struct run_write_op;
154
155 static
156 void
157 initiate_read(
158 boost::shared_ptr<detail::stream_state> const& in,
159 std::unique_ptr<detail::stream_read_op_base>&& op,
160 std::size_t buf_size);
161
162#if ! BOOST_BEAST_DOXYGEN
163 // boost::asio::ssl::stream needs these
164 // DEPRECATED
165 template<class>
166 friend class boost::asio::ssl::stream;
167 // DEPRECATED
168 using lowest_layer_type = basic_stream;
169 // DEPRECATED
170 lowest_layer_type&
171 lowest_layer() noexcept
172 {
173 return *this;
174 }
175 // DEPRECATED
176 lowest_layer_type const&
177 lowest_layer() const noexcept
178 {
179 return *this;
180 }
181#endif
182
183public:
184 using buffer_type = flat_buffer;
185
186 /** Destructor
187
188 If an asynchronous read operation is pending, it will
189 simply be discarded with no notification to the completion
190 handler.
191
192 If a connection is established while the stream is destroyed,
193 the peer will see the error `net::error::connection_reset`
194 when performing any reads or writes.
195 */
196 ~basic_stream();
197
198 /** Move Constructor
199
200 Moving the stream while asynchronous operations are pending
201 results in undefined behavior.
202 */
203 basic_stream(basic_stream&& other);
204
205 /** Move Constructor
206
207 Moving the stream while asynchronous operations are pending
208 results in undefined behavior.
209 */
210 template<class Executor2>
211 basic_stream(basic_stream<Executor2>&& other)
212 : in_(std::move(other.in_))
213 , out_(std::move(other.out_))
214 {
215 BOOST_ASSERT(in_->exec.template target<Executor2>() != nullptr);
216 in_->exec = executor_type(*in_->exec.template target<Executor2>());
217 }
218
219 /** Move Assignment
220
221 Moving the stream while asynchronous operations are pending
222 results in undefined behavior.
223 */
224 basic_stream&
225 operator=(basic_stream&& other);
226
227 template<class Executor2>
228 basic_stream&
229 operator==(basic_stream<Executor2>&& other);
230
231 /** Construct a stream
232
233 The stream will be created in a disconnected state.
234
235 @param context The `io_context` object that the stream will use to
236 dispatch handlers for any asynchronous operations.
237 */
238 template <typename ExecutionContext>
239 explicit basic_stream(ExecutionContext& context,
240 typename std::enable_if<
241 std::is_convertible<ExecutionContext&, net::execution_context&>::value
242 >::type* = 0)
243 : basic_stream(context.get_executor())
244 {
245 }
246
247 /** Construct a stream
248
249 The stream will be created in a disconnected state.
250
251 @param exec The `executor` object that the stream will use to
252 dispatch handlers for any asynchronous operations.
253 */
254 explicit
255 basic_stream(executor_type exec);
256
257 /** Construct a stream
258
259 The stream will be created in a disconnected state.
260
261 @param ioc The `io_context` object that the stream will use to
262 dispatch handlers for any asynchronous operations.
263
264 @param fc The @ref fail_count to associate with the stream.
265 Each I/O operation performed on the stream will increment the
266 fail count. When the fail count reaches its internal limit,
267 a simulated failure error will be raised.
268 */
269 basic_stream(
270 net::io_context& ioc,
271 fail_count& fc);
272
273 /** Construct a stream
274
275 The stream will be created in a disconnected state.
276
277 @param ioc The `io_context` object that the stream will use to
278 dispatch handlers for any asynchronous operations.
279
280 @param s A string which will be appended to the input area, not
281 including the null terminator.
282 */
283 basic_stream(
284 net::io_context& ioc,
285 string_view s);
286
287 /** Construct a stream
288
289 The stream will be created in a disconnected state.
290
291 @param ioc The `io_context` object that the stream will use to
292 dispatch handlers for any asynchronous operations.
293
294 @param fc The @ref fail_count to associate with the stream.
295 Each I/O operation performed on the stream will increment the
296 fail count. When the fail count reaches its internal limit,
297 a simulated failure error will be raised.
298
299 @param s A string which will be appended to the input area, not
300 including the null terminator.
301 */
302 basic_stream(
303 net::io_context& ioc,
304 fail_count& fc,
305 string_view s);
306
307 /// Establish a connection
308 void
309 connect(basic_stream& remote);
310
311 /// Return the executor associated with the object.
312 executor_type
313 get_executor() noexcept;
314
315 /// Set the maximum number of bytes returned by read_some
316 void
317 read_size(std::size_t n) noexcept
318 {
319 in_->read_max = n;
320 }
321
322 /// Set the maximum number of bytes returned by write_some
323 void
324 write_size(std::size_t n) noexcept
325 {
326 in_->write_max = n;
327 }
328
329 /// Direct input buffer access
330 buffer_type&
331 buffer() noexcept
332 {
333 return in_->b;
334 }
335
336 /// Returns a string view representing the pending input data
337 string_view
338 str() const;
339
340 /// Appends a string to the pending input data
341 void
342 append(string_view s);
343
344 /// Clear the pending input area
345 void
346 clear();
347
348 /// Return the number of reads
349 std::size_t
350 nread() const noexcept
351 {
352 return in_->nread;
353 }
354
355 /// Return the number of bytes read
356 std::size_t
357 nread_bytes() const noexcept
358 {
359 return in_->nread_bytes;
360 }
361
362 /// Return the number of writes
363 std::size_t
364 nwrite() const noexcept
365 {
366 return in_->nwrite;
367 }
368
369 /// Return the number of bytes written
370 std::size_t
371 nwrite_bytes() const noexcept
372 {
373 return in_->nwrite_bytes;
374 }
375
376 /** Close the stream.
377
378 The other end of the connection will see
379 `error::eof` after reading all the remaining data.
380 */
381 void
382 close();
383
384 /** Close the other end of the stream.
385
386 This end of the connection will see
387 `error::eof` after reading all the remaining data.
388 */
389 void
390 close_remote();
391
392 /** Read some data from the stream.
393
394 This function is used to read data from the stream. The function call will
395 block until one or more bytes of data has been read successfully, or until
396 an error occurs.
397
398 @param buffers The buffers into which the data will be read.
399
400 @returns The number of bytes read.
401
402 @throws boost::system::system_error Thrown on failure.
403
404 @note The `read_some` operation may not read all of the requested number of
405 bytes. Consider using the function `net::read` if you need to ensure
406 that the requested amount of data is read before the blocking operation
407 completes.
408 */
409 template<class MutableBufferSequence>
410 std::size_t
411 read_some(MutableBufferSequence const& buffers);
412
413 /** Read some data from the stream.
414
415 This function is used to read data from the stream. The function call will
416 block until one or more bytes of data has been read successfully, or until
417 an error occurs.
418
419 @param buffers The buffers into which the data will be read.
420
421 @param ec Set to indicate what error occurred, if any.
422
423 @returns The number of bytes read.
424
425 @note The `read_some` operation may not read all of the requested number of
426 bytes. Consider using the function `net::read` if you need to ensure
427 that the requested amount of data is read before the blocking operation
428 completes.
429 */
430 template<class MutableBufferSequence>
431 std::size_t
432 read_some(MutableBufferSequence const& buffers,
433 error_code& ec);
434
435 /** Start an asynchronous read.
436
437 This function is used to asynchronously read one or more bytes of data from
438 the stream. The function call always returns immediately.
439
440 @param buffers The buffers into which the data will be read. Although the
441 buffers object may be copied as necessary, ownership of the underlying
442 buffers is retained by the caller, which must guarantee that they remain
443 valid until the handler is called.
444
445 @param handler The completion handler to invoke when the operation
446 completes. The implementation takes ownership of the handler by
447 performing a decay-copy. The equivalent function signature of
448 the handler must be:
449 @code
450 void handler(
451 error_code const& ec, // Result of operation.
452 std::size_t bytes_transferred // Number of bytes read.
453 );
454 @endcode
455 If the handler has an associated immediate executor,
456 an immediate completion will be dispatched to it.
457 Otherwise, the handler will not be invoked from within
458 this function. Invocation of the handler will be performed
459 by dispatching to the immediate executor. If no
460 immediate executor is specified, this is equivalent
461 to using `net::post`.
462 @note The `async_read_some` operation may not read all of the requested number of
463 bytes. Consider using the function `net::async_read` if you need
464 to ensure that the requested amount of data is read before the asynchronous
465 operation completes.
466 */
467 template<
468 class MutableBufferSequence,
469 BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, std::size_t)) ReadHandler
470 BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
471 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, void(error_code, std::size_t))
472 async_read_some(
473 MutableBufferSequence const& buffers,
474 ReadHandler&& handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type));
475
476 /** Write some data to the stream.
477
478 This function is used to write data on the stream. The function call will
479 block until one or more bytes of data has been written successfully, or
480 until an error occurs.
481
482 @param buffers The data to be written.
483
484 @returns The number of bytes written.
485
486 @throws boost::system::system_error Thrown on failure.
487
488 @note The `write_some` operation may not transmit all of the data to the
489 peer. Consider using the function `net::write` if you need to
490 ensure that all data is written before the blocking operation completes.
491 */
492 template<class ConstBufferSequence>
493 std::size_t
494 write_some(ConstBufferSequence const& buffers);
495
496 /** Write some data to the stream.
497
498 This function is used to write data on the stream. The function call will
499 block until one or more bytes of data has been written successfully, or
500 until an error occurs.
501
502 @param buffers The data to be written.
503
504 @param ec Set to indicate what error occurred, if any.
505
506 @returns The number of bytes written.
507
508 @note The `write_some` operation may not transmit all of the data to the
509 peer. Consider using the function `net::write` if you need to
510 ensure that all data is written before the blocking operation completes.
511 */
512 template<class ConstBufferSequence>
513 std::size_t
514 write_some(
515 ConstBufferSequence const& buffers, error_code& ec);
516
517 /** Start an asynchronous write.
518
519 This function is used to asynchronously write one or more bytes of data to
520 the stream. The function call always returns immediately.
521
522 @param buffers The data to be written to the stream. Although the buffers
523 object may be copied as necessary, ownership of the underlying buffers is
524 retained by the caller, which must guarantee that they remain valid until
525 the handler is called.
526
527 @param handler The completion handler to invoke when the operation
528 completes. The implementation takes ownership of the handler by
529 performing a decay-copy. The equivalent function signature of
530 the handler must be:
531 @code
532 void handler(
533 error_code const& ec, // Result of operation.
534 std::size_t bytes_transferred // Number of bytes written.
535 );
536 @endcode
537 If the handler has an associated immediate executor,
538 an immediate completion will be dispatched to it.
539 Otherwise, the handler will not be invoked from within
540 this function. Invocation of the handler will be performed
541 by dispatching to the immediate executor. If no
542 immediate executor is specified, this is equivalent
543 to using `net::post`.
544 @note The `async_write_some` operation may not transmit all of the data to
545 the peer. Consider using the function `net::async_write` if you need
546 to ensure that all data is written before the asynchronous operation completes.
547 */
548 template<
549 class ConstBufferSequence,
550 BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, std::size_t)) WriteHandler
551 BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
552 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, void(error_code, std::size_t))
553 async_write_some(
554 ConstBufferSequence const& buffers,
555 WriteHandler&& handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
556 );
557
558#if ! BOOST_BEAST_DOXYGEN
559 friend
560 void
561 teardown<>(
562 role_type,
563 basic_stream& s,
564 boost::system::error_code& ec);
565
566 template<class Ex2, class TeardownHandler>
567 friend
568 void
569 async_teardown(
570 role_type role,
571 basic_stream<Ex2>& s,
572 TeardownHandler&& handler);
573#endif
574};
575
576#if ! BOOST_BEAST_DOXYGEN
577template<class Executor>
578void
579beast_close_socket(basic_stream<Executor>& s)
580{
581 s.close();
582}
583#endif
584
585#if BOOST_BEAST_DOXYGEN
586/** Return a new stream connected to the given stream
587
588 @param to The stream to connect to.
589
590 @param args Optional arguments forwarded to the new stream's constructor.
591
592 @return The new, connected stream.
593*/
594template<class Executor>
595template<class... Args>
596basic_stream
597connect(basic_stream& to, Args&&... args);
598
599#else
600template<class Executor>
601basic_stream<Executor>
602connect(basic_stream<Executor>& to);
603
604template<class Executor>
605void
606connect(basic_stream<Executor>& s1, basic_stream<Executor>& s2);
607
608template<class Executor, class Arg1, class... ArgN>
609basic_stream<Executor>
610connect(basic_stream<Executor>& to, Arg1&& arg1, ArgN&&... argn);
611#endif
612
613using stream = basic_stream<>;
614
615} // test
616} // beast
617} // boost
618
619#include <boost/beast/_experimental/test/impl/stream.hpp>
620//#ifdef BOOST_BEAST_HEADER_ONLY
621#include <boost/beast/_experimental/test/impl/stream.ipp>
622//#endif
623
624#endif
625

source code of boost/libs/beast/include/boost/beast/_experimental/test/stream.hpp