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