stream.hpp 19 KB

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