stream.hpp 20 KB

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