2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 #ifndef BOOST_BEAST_TEST_STREAM_HPP
11 #define BOOST_BEAST_TEST_STREAM_HPP
13 #include <boost/beast/core/detail/config.hpp>
14 #include <boost/beast/core/bind_handler.hpp>
15 #include <boost/beast/core/flat_buffer.hpp>
16 #include <boost/beast/core/role.hpp>
17 #include <boost/beast/core/string.hpp>
18 #include <boost/beast/_experimental/test/fail_count.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/io_context.hpp>
24 #include <boost/asio/post.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>
35 #if ! BOOST_BEAST_DOXYGEN
39 template<typename> class stream;
49 /** A two-way socket useful for unit testing
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.
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.
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.
74 To facilitate testing, these streams support some additional
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.
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
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.
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.
104 @li <em>SyncReadStream</em>
105 @li <em>SyncWriteStream</em>
106 @li <em>AsyncReadStream</em>
107 @li <em>AsyncWriteStream</em>
113 boost::shared_ptr<state> in_;
114 boost::weak_ptr<state> out_;
127 virtual ~read_op_base() = default;
128 virtual void operator()(error_code ec) = 0;
135 net::io_context& ioc;
136 boost::weak_ptr<service_impl> wp;
139 std::condition_variable cv;
140 std::unique_ptr<read_op_base> op;
141 status code = status::ok;
142 fail_count* fc = nullptr;
143 std::size_t nread = 0;
144 std::size_t nread_bytes = 0;
145 std::size_t nwrite = 0;
146 std::size_t nwrite_bytes = 0;
147 std::size_t read_max =
148 (std::numeric_limits<std::size_t>::max)();
149 std::size_t write_max =
150 (std::numeric_limits<std::size_t>::max)();
154 net::io_context& ioc_,
155 boost::weak_ptr<service_impl> wp_,
175 template<class Handler, class Buffers>
185 boost::shared_ptr<state> const& in,
186 std::unique_ptr<read_op_base>&& op,
187 std::size_t buf_size);
189 #if ! BOOST_BEAST_DOXYGEN
190 // boost::asio::ssl::stream needs these
193 friend class boost::asio::ssl::stream;
195 using lowest_layer_type = stream;
198 lowest_layer() noexcept
203 lowest_layer_type const&
204 lowest_layer() const noexcept
211 using buffer_type = flat_buffer;
215 If an asynchronous read operation is pending, it will
216 simply be discarded with no notification to the completion
219 If a connection is established while the stream is destroyed,
220 the peer will see the error `net::error::connection_reset`
221 when performing any reads or writes.
228 Moving the stream while asynchronous operations are pending
229 results in undefined behavior.
232 stream(stream&& other);
236 Moving the stream while asynchronous operations are pending
237 results in undefined behavior.
241 operator=(stream&& other);
243 /** Construct a stream
245 The stream will be created in a disconnected state.
247 @param ioc The `io_context` object that the stream will use to
248 dispatch handlers for any asynchronous operations.
252 stream(net::io_context& ioc);
254 /** Construct a stream
256 The stream will be created in a disconnected state.
258 @param ioc The `io_context` object that the stream will use to
259 dispatch handlers for any asynchronous operations.
261 @param fc The @ref fail_count to associate with the stream.
262 Each I/O operation performed on the stream will increment the
263 fail count. When the fail count reaches its internal limit,
264 a simulated failure error will be raised.
268 net::io_context& ioc,
271 /** Construct a stream
273 The stream will be created in a disconnected state.
275 @param ioc The `io_context` object that the stream will use to
276 dispatch handlers for any asynchronous operations.
278 @param s A string which will be appended to the input area, not
279 including the null terminator.
283 net::io_context& ioc,
286 /** Construct a stream
288 The stream will be created in a disconnected state.
290 @param ioc The `io_context` object that the stream will use to
291 dispatch handlers for any asynchronous operations.
293 @param fc The @ref fail_count to associate with the stream.
294 Each I/O operation performed on the stream will increment the
295 fail count. When the fail count reaches its internal limit,
296 a simulated failure error will be raised.
298 @param s A string which will be appended to the input area, not
299 including the null terminator.
303 net::io_context& ioc,
307 /// Establish a connection
310 connect(stream& remote);
312 /// The type of the executor associated with the object.
313 using executor_type =
314 net::io_context::executor_type;
316 /// Return the executor associated with the object.
318 get_executor() noexcept
320 return in_->ioc.get_executor();
323 /// Set the maximum number of bytes returned by read_some
325 read_size(std::size_t n) noexcept
330 /// Set the maximum number of bytes returned by write_some
332 write_size(std::size_t n) noexcept
337 /// Direct input buffer access
344 /// Returns a string view representing the pending input data
349 /// Appends a string to the pending input data
352 append(string_view s);
354 /// Clear the pending input area
359 /// Return the number of reads
361 nread() const noexcept
366 /// Return the number of bytes read
368 nread_bytes() const noexcept
370 return in_->nread_bytes;
373 /// Return the number of writes
375 nwrite() const noexcept
380 /// Return the number of bytes written
382 nwrite_bytes() const noexcept
384 return in_->nwrite_bytes;
387 /** Close the stream.
389 The other end of the connection will see
390 `error::eof` after reading all the remaining data.
396 /** Close the other end of the stream.
398 This end of the connection will see
399 `error::eof` after reading all the remaining data.
405 /** Read some data from the stream.
407 This function is used to read data from the stream. The function call will
408 block until one or more bytes of data has been read successfully, or until
411 @param buffers The buffers into which the data will be read.
413 @returns The number of bytes read.
415 @throws boost::system::system_error Thrown on failure.
417 @note The `read_some` operation may not read all of the requested number of
418 bytes. Consider using the function `net::read` if you need to ensure
419 that the requested amount of data is read before the blocking operation
422 template<class MutableBufferSequence>
424 read_some(MutableBufferSequence const& buffers);
426 /** Read some data from the stream.
428 This function is used to read data from the stream. The function call will
429 block until one or more bytes of data has been read successfully, or until
432 @param buffers The buffers into which the data will be read.
434 @param ec Set to indicate what error occurred, if any.
436 @returns The number of bytes read.
438 @note The `read_some` operation may not read all of the requested number of
439 bytes. Consider using the function `net::read` if you need to ensure
440 that the requested amount of data is read before the blocking operation
443 template<class MutableBufferSequence>
445 read_some(MutableBufferSequence const& buffers,
448 /** Start an asynchronous read.
450 This function is used to asynchronously read one or more bytes of data from
451 the stream. The function call always returns immediately.
453 @param buffers The buffers into which the data will be read. Although the
454 buffers object may be copied as necessary, ownership of the underlying
455 buffers is retained by the caller, which must guarantee that they remain
456 valid until the handler is called.
458 @param handler The completion handler to invoke when the operation
459 completes. The implementation takes ownership of the handler by
460 performing a decay-copy. The equivalent function signature of
464 error_code const& ec, // Result of operation.
465 std::size_t bytes_transferred // Number of bytes read.
468 Regardless of whether the asynchronous operation completes
469 immediately or not, the handler will not be invoked from within
470 this function. Invocation of the handler will be performed in a
471 manner equivalent to using `net::post`.
473 @note The `async_read_some` operation may not read all of the requested number of
474 bytes. Consider using the function `net::async_read` if you need
475 to ensure that the requested amount of data is read before the asynchronous
479 class MutableBufferSequence,
480 BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
481 BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
483 MutableBufferSequence const& buffers,
484 ReadHandler&& handler);
486 /** Write some data to the stream.
488 This function is used to write data on the stream. The function call will
489 block until one or more bytes of data has been written successfully, or
490 until an error occurs.
492 @param buffers The data to be written.
494 @returns The number of bytes written.
496 @throws boost::system::system_error Thrown on failure.
498 @note The `write_some` operation may not transmit all of the data to the
499 peer. Consider using the function `net::write` if you need to
500 ensure that all data is written before the blocking operation completes.
502 template<class ConstBufferSequence>
504 write_some(ConstBufferSequence const& buffers);
506 /** Write some data to the stream.
508 This function is used to write data on the stream. The function call will
509 block until one or more bytes of data has been written successfully, or
510 until an error occurs.
512 @param buffers The data to be written.
514 @param ec Set to indicate what error occurred, if any.
516 @returns The number of bytes written.
518 @note The `write_some` operation may not transmit all of the data to the
519 peer. Consider using the function `net::write` if you need to
520 ensure that all data is written before the blocking operation completes.
522 template<class ConstBufferSequence>
525 ConstBufferSequence const& buffers, error_code& ec);
527 /** Start an asynchronous write.
529 This function is used to asynchronously write one or more bytes of data to
530 the stream. The function call always returns immediately.
532 @param buffers The data to be written to the stream. Although the buffers
533 object may be copied as necessary, ownership of the underlying buffers is
534 retained by the caller, which must guarantee that they remain valid until
535 the handler is called.
537 @param handler The completion handler to invoke when the operation
538 completes. The implementation takes ownership of the handler by
539 performing a decay-copy. The equivalent function signature of
543 error_code const& ec, // Result of operation.
544 std::size_t bytes_transferred // Number of bytes written.
547 Regardless of whether the asynchronous operation completes
548 immediately or not, the handler will not be invoked from within
549 this function. Invocation of the handler will be performed in a
550 manner equivalent to using `net::post`.
552 @note The `async_write_some` operation may not transmit all of the data to
553 the peer. Consider using the function `net::async_write` if you need
554 to ensure that all data is written before the asynchronous operation completes.
557 class ConstBufferSequence,
558 BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
559 BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
561 ConstBufferSequence const& buffers,
562 WriteHandler&& handler);
564 #if ! BOOST_BEAST_DOXYGEN
571 boost::system::error_code& ec);
573 template<class TeardownHandler>
580 TeardownHandler&& handler);
584 #if ! BOOST_BEAST_DOXYGEN
587 beast_close_socket(stream& s)
593 #if BOOST_BEAST_DOXYGEN
594 /** Return a new stream connected to the given stream
596 @param to The stream to connect to.
598 @param args Optional arguments forwarded to the new stream's constructor.
600 @return The new, connected stream.
602 template<class... Args>
604 connect(stream& to, Args&&... args);
613 connect(stream& s1, stream& s2);
615 template<class Arg1, class... ArgN>
617 connect(stream& to, Arg1&& arg1, ArgN&&... argn);
624 #include <boost/beast/_experimental/test/impl/stream.hpp>
625 #ifdef BOOST_BEAST_HEADER_ONLY
626 #include <boost/beast/_experimental/test/impl/stream.ipp>