ping.ipp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. //
  2. // Copyright (c) 2016-2017 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_WEBSOCKET_IMPL_PING_IPP
  10. #define BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP
  11. #include <boost/beast/core/bind_handler.hpp>
  12. #include <boost/beast/core/handler_ptr.hpp>
  13. #include <boost/beast/core/type_traits.hpp>
  14. #include <boost/beast/core/detail/config.hpp>
  15. #include <boost/beast/websocket/detail/frame.hpp>
  16. #include <boost/asio/associated_allocator.hpp>
  17. #include <boost/asio/associated_executor.hpp>
  18. #include <boost/asio/coroutine.hpp>
  19. #include <boost/asio/executor_work_guard.hpp>
  20. #include <boost/asio/handler_continuation_hook.hpp>
  21. #include <boost/asio/handler_invoke_hook.hpp>
  22. #include <boost/asio/post.hpp>
  23. #include <boost/throw_exception.hpp>
  24. #include <memory>
  25. namespace boost {
  26. namespace beast {
  27. namespace websocket {
  28. /*
  29. This composed operation handles sending ping and pong frames.
  30. It only sends the frames it does not make attempts to read
  31. any frame data.
  32. */
  33. template<class NextLayer, bool deflateSupported>
  34. template<class Handler>
  35. class stream<NextLayer, deflateSupported>::ping_op
  36. : public boost::asio::coroutine
  37. {
  38. struct state
  39. {
  40. stream<NextLayer, deflateSupported>& ws;
  41. boost::asio::executor_work_guard<decltype(std::declval<
  42. stream<NextLayer, deflateSupported>&>().get_executor())> wg;
  43. detail::frame_buffer fb;
  44. state(
  45. Handler const&,
  46. stream<NextLayer, deflateSupported>& ws_,
  47. detail::opcode op,
  48. ping_data const& payload)
  49. : ws(ws_)
  50. , wg(ws.get_executor())
  51. {
  52. // Serialize the control frame
  53. ws.template write_ping<
  54. flat_static_buffer_base>(
  55. fb, op, payload);
  56. }
  57. };
  58. handler_ptr<state, Handler> d_;
  59. public:
  60. static constexpr int id = 3; // for soft_mutex
  61. ping_op(ping_op&&) = default;
  62. ping_op(ping_op const&) = delete;
  63. template<class DeducedHandler>
  64. ping_op(
  65. DeducedHandler&& h,
  66. stream<NextLayer, deflateSupported>& ws,
  67. detail::opcode op,
  68. ping_data const& payload)
  69. : d_(std::forward<DeducedHandler>(h),
  70. ws, op, payload)
  71. {
  72. }
  73. using allocator_type =
  74. boost::asio::associated_allocator_t<Handler>;
  75. allocator_type
  76. get_allocator() const noexcept
  77. {
  78. return (boost::asio::get_associated_allocator)(d_.handler());
  79. }
  80. using executor_type = boost::asio::associated_executor_t<
  81. Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
  82. executor_type
  83. get_executor() const noexcept
  84. {
  85. return (boost::asio::get_associated_executor)(
  86. d_.handler(), d_->ws.get_executor());
  87. }
  88. void operator()(
  89. error_code ec = {},
  90. std::size_t bytes_transferred = 0);
  91. friend
  92. bool asio_handler_is_continuation(ping_op* op)
  93. {
  94. using boost::asio::asio_handler_is_continuation;
  95. return asio_handler_is_continuation(
  96. std::addressof(op->d_.handler()));
  97. }
  98. template<class Function>
  99. friend
  100. void asio_handler_invoke(Function&& f, ping_op* op)
  101. {
  102. using boost::asio::asio_handler_invoke;
  103. asio_handler_invoke(
  104. f, std::addressof(op->d_.handler()));
  105. }
  106. };
  107. template<class NextLayer, bool deflateSupported>
  108. template<class Handler>
  109. void
  110. stream<NextLayer, deflateSupported>::
  111. ping_op<Handler>::
  112. operator()(error_code ec, std::size_t)
  113. {
  114. auto& d = *d_;
  115. BOOST_ASIO_CORO_REENTER(*this)
  116. {
  117. // Maybe suspend
  118. if(d.ws.wr_block_.try_lock(this))
  119. {
  120. // Make sure the stream is open
  121. if(! d.ws.check_open(ec))
  122. {
  123. BOOST_ASIO_CORO_YIELD
  124. boost::asio::post(
  125. d.ws.get_executor(),
  126. bind_handler(std::move(*this), ec));
  127. goto upcall;
  128. }
  129. }
  130. else
  131. {
  132. // Suspend
  133. BOOST_ASIO_CORO_YIELD
  134. d.ws.paused_ping_.emplace(std::move(*this));
  135. // Acquire the write block
  136. d.ws.wr_block_.lock(this);
  137. // Resume
  138. BOOST_ASIO_CORO_YIELD
  139. boost::asio::post(
  140. d.ws.get_executor(), std::move(*this));
  141. BOOST_ASSERT(d.ws.wr_block_.is_locked(this));
  142. // Make sure the stream is open
  143. if(! d.ws.check_open(ec))
  144. goto upcall;
  145. }
  146. // Send ping frame
  147. BOOST_ASIO_CORO_YIELD
  148. boost::asio::async_write(d.ws.stream_,
  149. d.fb.data(), std::move(*this));
  150. if(! d.ws.check_ok(ec))
  151. goto upcall;
  152. upcall:
  153. d.ws.wr_block_.unlock(this);
  154. d.ws.paused_close_.maybe_invoke() ||
  155. d.ws.paused_rd_.maybe_invoke() ||
  156. d.ws.paused_wr_.maybe_invoke();
  157. {
  158. auto wg = std::move(d.wg);
  159. d_.invoke(ec);
  160. }
  161. }
  162. }
  163. //------------------------------------------------------------------------------
  164. template<class NextLayer, bool deflateSupported>
  165. void
  166. stream<NextLayer, deflateSupported>::
  167. ping(ping_data const& payload)
  168. {
  169. error_code ec;
  170. ping(payload, ec);
  171. if(ec)
  172. BOOST_THROW_EXCEPTION(system_error{ec});
  173. }
  174. template<class NextLayer, bool deflateSupported>
  175. void
  176. stream<NextLayer, deflateSupported>::
  177. ping(ping_data const& payload, error_code& ec)
  178. {
  179. // Make sure the stream is open
  180. if(! check_open(ec))
  181. return;
  182. detail::frame_buffer fb;
  183. write_ping<flat_static_buffer_base>(
  184. fb, detail::opcode::ping, payload);
  185. boost::asio::write(stream_, fb.data(), ec);
  186. if(! check_ok(ec))
  187. return;
  188. }
  189. template<class NextLayer, bool deflateSupported>
  190. void
  191. stream<NextLayer, deflateSupported>::
  192. pong(ping_data const& payload)
  193. {
  194. error_code ec;
  195. pong(payload, ec);
  196. if(ec)
  197. BOOST_THROW_EXCEPTION(system_error{ec});
  198. }
  199. template<class NextLayer, bool deflateSupported>
  200. void
  201. stream<NextLayer, deflateSupported>::
  202. pong(ping_data const& payload, error_code& ec)
  203. {
  204. // Make sure the stream is open
  205. if(! check_open(ec))
  206. return;
  207. detail::frame_buffer fb;
  208. write_ping<flat_static_buffer_base>(
  209. fb, detail::opcode::pong, payload);
  210. boost::asio::write(stream_, fb.data(), ec);
  211. if(! check_ok(ec))
  212. return;
  213. }
  214. template<class NextLayer, bool deflateSupported>
  215. template<class WriteHandler>
  216. BOOST_ASIO_INITFN_RESULT_TYPE(
  217. WriteHandler, void(error_code))
  218. stream<NextLayer, deflateSupported>::
  219. async_ping(ping_data const& payload, WriteHandler&& handler)
  220. {
  221. static_assert(is_async_stream<next_layer_type>::value,
  222. "AsyncStream requirements not met");
  223. BOOST_BEAST_HANDLER_INIT(
  224. WriteHandler, void(error_code));
  225. ping_op<BOOST_ASIO_HANDLER_TYPE(
  226. WriteHandler, void(error_code))>{
  227. std::move(init.completion_handler), *this,
  228. detail::opcode::ping, payload}();
  229. return init.result.get();
  230. }
  231. template<class NextLayer, bool deflateSupported>
  232. template<class WriteHandler>
  233. BOOST_ASIO_INITFN_RESULT_TYPE(
  234. WriteHandler, void(error_code))
  235. stream<NextLayer, deflateSupported>::
  236. async_pong(ping_data const& payload, WriteHandler&& handler)
  237. {
  238. static_assert(is_async_stream<next_layer_type>::value,
  239. "AsyncStream requirements not met");
  240. BOOST_BEAST_HANDLER_INIT(
  241. WriteHandler, void(error_code));
  242. ping_op<BOOST_ASIO_HANDLER_TYPE(
  243. WriteHandler, void(error_code))>{
  244. std::move(init.completion_handler), *this,
  245. detail::opcode::pong, payload}();
  246. return init.result.get();
  247. }
  248. } // websocket
  249. } // beast
  250. } // boost
  251. #endif