teardown.ipp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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_TEARDOWN_IPP
  10. #define BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
  11. #include <boost/beast/core/bind_handler.hpp>
  12. #include <boost/beast/core/type_traits.hpp>
  13. #include <boost/asio/associated_allocator.hpp>
  14. #include <boost/asio/associated_executor.hpp>
  15. #include <boost/asio/coroutine.hpp>
  16. #include <boost/asio/executor_work_guard.hpp>
  17. #include <boost/asio/handler_continuation_hook.hpp>
  18. #include <boost/asio/handler_invoke_hook.hpp>
  19. #include <boost/asio/post.hpp>
  20. #include <memory>
  21. namespace boost {
  22. namespace beast {
  23. namespace websocket {
  24. namespace detail {
  25. template<class Handler>
  26. class teardown_tcp_op : public boost::asio::coroutine
  27. {
  28. using socket_type =
  29. boost::asio::ip::tcp::socket;
  30. Handler h_;
  31. socket_type& s_;
  32. boost::asio::executor_work_guard<decltype(std::declval<
  33. socket_type&>().get_executor())> wg_;
  34. role_type role_;
  35. bool nb_;
  36. public:
  37. teardown_tcp_op(teardown_tcp_op&& other) = default;
  38. teardown_tcp_op(teardown_tcp_op const& other) = default;
  39. template<class DeducedHandler>
  40. teardown_tcp_op(
  41. DeducedHandler&& h,
  42. socket_type& s,
  43. role_type role)
  44. : h_(std::forward<DeducedHandler>(h))
  45. , s_(s)
  46. , wg_(s_.get_executor())
  47. , role_(role)
  48. {
  49. }
  50. using allocator_type =
  51. boost::asio::associated_allocator_t<Handler>;
  52. allocator_type
  53. get_allocator() const noexcept
  54. {
  55. return (boost::asio::get_associated_allocator)(h_);
  56. }
  57. using executor_type = boost::asio::associated_executor_t<
  58. Handler, decltype(std::declval<socket_type&>().get_executor())>;
  59. executor_type
  60. get_executor() const noexcept
  61. {
  62. return (boost::asio::get_associated_executor)(
  63. h_, s_.get_executor());
  64. }
  65. void
  66. operator()(
  67. error_code ec = {},
  68. std::size_t bytes_transferred = 0);
  69. friend
  70. bool asio_handler_is_continuation(teardown_tcp_op* op)
  71. {
  72. using boost::asio::asio_handler_is_continuation;
  73. return asio_handler_is_continuation(
  74. std::addressof(op->h_));
  75. }
  76. template<class Function>
  77. friend
  78. void asio_handler_invoke(Function&& f, teardown_tcp_op* op)
  79. {
  80. using boost::asio::asio_handler_invoke;
  81. asio_handler_invoke(f, std::addressof(op->h_));
  82. }
  83. };
  84. template<class Handler>
  85. void
  86. teardown_tcp_op<Handler>::
  87. operator()(error_code ec, std::size_t bytes_transferred)
  88. {
  89. using boost::asio::buffer;
  90. using tcp = boost::asio::ip::tcp;
  91. BOOST_ASIO_CORO_REENTER(*this)
  92. {
  93. nb_ = s_.non_blocking();
  94. s_.non_blocking(true, ec);
  95. if(! ec)
  96. {
  97. if(role_ == role_type::server)
  98. s_.shutdown(tcp::socket::shutdown_send, ec);
  99. }
  100. if(ec)
  101. {
  102. BOOST_ASIO_CORO_YIELD
  103. boost::asio::post(
  104. s_.get_executor(),
  105. bind_handler(std::move(*this), ec, 0));
  106. goto upcall;
  107. }
  108. for(;;)
  109. {
  110. {
  111. char buf[2048];
  112. s_.read_some(
  113. boost::asio::buffer(buf), ec);
  114. }
  115. if(ec == boost::asio::error::would_block)
  116. {
  117. BOOST_ASIO_CORO_YIELD
  118. s_.async_wait(
  119. boost::asio::ip::tcp::socket::wait_read,
  120. std::move(*this));
  121. continue;
  122. }
  123. if(ec)
  124. {
  125. if(ec != boost::asio::error::eof)
  126. goto upcall;
  127. ec = {};
  128. break;
  129. }
  130. if(bytes_transferred == 0)
  131. {
  132. // happens sometimes
  133. break;
  134. }
  135. }
  136. if(role_ == role_type::client)
  137. s_.shutdown(tcp::socket::shutdown_send, ec);
  138. if(ec)
  139. goto upcall;
  140. s_.close(ec);
  141. upcall:
  142. {
  143. error_code ignored;
  144. s_.non_blocking(nb_, ignored);
  145. }
  146. h_(ec);
  147. }
  148. }
  149. } // detail
  150. //------------------------------------------------------------------------------
  151. inline
  152. void
  153. teardown(
  154. role_type role,
  155. boost::asio::ip::tcp::socket& socket,
  156. error_code& ec)
  157. {
  158. using boost::asio::buffer;
  159. if(role == role_type::server)
  160. socket.shutdown(
  161. boost::asio::ip::tcp::socket::shutdown_send, ec);
  162. if(ec)
  163. return;
  164. for(;;)
  165. {
  166. char buf[2048];
  167. auto const bytes_transferred =
  168. socket.read_some(buffer(buf), ec);
  169. if(ec)
  170. {
  171. if(ec != boost::asio::error::eof)
  172. return;
  173. ec = {};
  174. break;
  175. }
  176. if(bytes_transferred == 0)
  177. {
  178. // happens sometimes
  179. break;
  180. }
  181. }
  182. if(role == role_type::client)
  183. socket.shutdown(
  184. boost::asio::ip::tcp::socket::shutdown_send, ec);
  185. if(ec)
  186. return;
  187. socket.close(ec);
  188. }
  189. template<class TeardownHandler>
  190. inline
  191. void
  192. async_teardown(
  193. role_type role,
  194. boost::asio::ip::tcp::socket& socket,
  195. TeardownHandler&& handler)
  196. {
  197. static_assert(beast::is_completion_handler<
  198. TeardownHandler, void(error_code)>::value,
  199. "TeardownHandler requirements not met");
  200. detail::teardown_tcp_op<typename std::decay<
  201. TeardownHandler>::type>{std::forward<
  202. TeardownHandler>(handler), socket,
  203. role}();
  204. }
  205. } // websocket
  206. } // beast
  207. } // boost
  208. #endif