engine_stream_adaptor.hpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. //
  2. // Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 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. #ifndef BOOST_MYSQL_DETAIL_ENGINE_STREAM_ADAPTOR_HPP
  8. #define BOOST_MYSQL_DETAIL_ENGINE_STREAM_ADAPTOR_HPP
  9. #include <boost/mysql/error_code.hpp>
  10. #include <boost/mysql/detail/config.hpp>
  11. #include <boost/mysql/detail/engine_impl.hpp>
  12. #include <boost/mysql/detail/socket_stream.hpp>
  13. #include <boost/mysql/detail/void_t.hpp>
  14. #include <boost/asio/any_io_executor.hpp>
  15. #include <boost/asio/ip/tcp.hpp>
  16. #include <boost/asio/ssl/stream.hpp>
  17. #include <boost/config.hpp>
  18. #include <boost/core/ignore_unused.hpp>
  19. #include <type_traits>
  20. // Adapts a regular Asio Stream to meet the EngineStream requirements
  21. // We only use callbacks with the async functions in this file, so no need to support arbitrary return types
  22. namespace boost {
  23. namespace mysql {
  24. namespace detail {
  25. // Connect and close helpers
  26. template <class Stream, class = void>
  27. struct endpoint_storage // prevent build errors for non socket streams
  28. {
  29. void store(const void*) {}
  30. };
  31. template <class Stream>
  32. struct endpoint_storage<Stream, void_t<typename Stream::lowest_layer_type::endpoint_type>>
  33. {
  34. using endpoint_type = typename Stream::lowest_layer_type::endpoint_type;
  35. endpoint_type value;
  36. void store(const void* v) { value = *static_cast<const endpoint_type*>(v); }
  37. };
  38. template <class Stream>
  39. void do_connect_impl(Stream&, const endpoint_storage<Stream>&, error_code&, std::false_type)
  40. {
  41. BOOST_ASSERT(false);
  42. }
  43. template <class Stream>
  44. void do_connect_impl(Stream& stream, const endpoint_storage<Stream>& ep, error_code& ec, std::true_type)
  45. {
  46. stream.lowest_layer().connect(ep.value, ec);
  47. }
  48. template <class Stream>
  49. void do_connect(Stream& stream, const endpoint_storage<Stream>& ep, error_code& ec)
  50. {
  51. do_connect_impl(stream, ep, ec, is_socket_stream<Stream>{});
  52. }
  53. template <class Stream, class CompletionToken>
  54. void do_async_connect_impl(Stream&, const endpoint_storage<Stream>&, CompletionToken&&, std::false_type)
  55. {
  56. BOOST_ASSERT(false);
  57. }
  58. template <class Stream, class CompletionToken>
  59. void do_async_connect_impl(
  60. Stream& stream,
  61. const endpoint_storage<Stream>& ep,
  62. CompletionToken&& token,
  63. std::true_type
  64. )
  65. {
  66. stream.lowest_layer().async_connect(ep.value, std::forward<CompletionToken>(token));
  67. }
  68. template <class Stream, class CompletionToken>
  69. void do_async_connect(Stream& stream, const endpoint_storage<Stream>& ep, CompletionToken&& token)
  70. {
  71. do_async_connect_impl(stream, ep, std::forward<CompletionToken>(token), is_socket_stream<Stream>{});
  72. }
  73. template <class Stream>
  74. void do_close_impl(Stream&, error_code&, std::false_type)
  75. {
  76. BOOST_ASSERT(false);
  77. }
  78. template <class Stream>
  79. void do_close_impl(Stream& stream, error_code& ec, std::true_type)
  80. {
  81. stream.lowest_layer().shutdown(asio::socket_base::shutdown_both, ec);
  82. stream.lowest_layer().close(ec);
  83. }
  84. template <class Stream>
  85. void do_close(Stream& stream, error_code& ec)
  86. {
  87. do_close_impl(stream, ec, is_socket_stream<Stream>{});
  88. }
  89. template <class Stream>
  90. class engine_stream_adaptor
  91. {
  92. Stream stream_;
  93. endpoint_storage<Stream> endpoint_;
  94. public:
  95. template <class... Args>
  96. engine_stream_adaptor(Args&&... args) : stream_(std::forward<Args>(args)...)
  97. {
  98. }
  99. Stream& stream() { return stream_; }
  100. const Stream& stream() const { return stream_; }
  101. bool supports_ssl() const { return false; }
  102. void set_endpoint(const void* val) { endpoint_.store(val); }
  103. using executor_type = asio::any_io_executor;
  104. executor_type get_executor() { return stream_.get_executor(); }
  105. // SSL
  106. void ssl_handshake(error_code&) { BOOST_ASSERT(false); }
  107. template <class CompletinToken>
  108. void async_ssl_handshake(CompletinToken&&)
  109. {
  110. BOOST_ASSERT(false);
  111. }
  112. void ssl_shutdown(error_code&) { BOOST_ASSERT(false); }
  113. template <class CompletionToken>
  114. void async_ssl_shutdown(CompletionToken&&)
  115. {
  116. BOOST_ASSERT(false);
  117. }
  118. // Reading
  119. std::size_t read_some(boost::asio::mutable_buffer buff, bool use_ssl, error_code& ec)
  120. {
  121. BOOST_ASSERT(!use_ssl);
  122. boost::ignore_unused(use_ssl);
  123. return stream_.read_some(buff, ec);
  124. }
  125. template <class CompletionToken>
  126. void async_read_some(boost::asio::mutable_buffer buff, bool use_ssl, CompletionToken&& token)
  127. {
  128. BOOST_ASSERT(!use_ssl);
  129. boost::ignore_unused(use_ssl);
  130. stream_.async_read_some(buff, std::forward<CompletionToken>(token));
  131. }
  132. // Writing
  133. std::size_t write_some(boost::asio::const_buffer buff, bool use_ssl, error_code& ec)
  134. {
  135. BOOST_ASSERT(!use_ssl);
  136. boost::ignore_unused(use_ssl);
  137. return stream_.write_some(buff, ec);
  138. }
  139. template <class CompletionToken>
  140. void async_write_some(boost::asio::const_buffer buff, bool use_ssl, CompletionToken&& token)
  141. {
  142. BOOST_ASSERT(!use_ssl);
  143. boost::ignore_unused(use_ssl);
  144. stream_.async_write_some(buff, std::forward<CompletionToken>(token));
  145. }
  146. // Connect and close
  147. void connect(error_code& ec) { do_connect(stream_, endpoint_, ec); }
  148. template <class CompletionToken>
  149. void async_connect(CompletionToken&& token)
  150. {
  151. do_async_connect(stream_, endpoint_, std::forward<CompletionToken>(token));
  152. }
  153. void close(error_code& ec) { do_close(stream_, ec); }
  154. };
  155. template <class Stream>
  156. class engine_stream_adaptor<asio::ssl::stream<Stream>>
  157. {
  158. asio::ssl::stream<Stream> stream_;
  159. endpoint_storage<asio::ssl::stream<Stream>> endpoint_;
  160. public:
  161. template <class... Args>
  162. engine_stream_adaptor(Args&&... args) : stream_(std::forward<Args>(args)...)
  163. {
  164. }
  165. asio::ssl::stream<Stream>& stream() { return stream_; }
  166. const asio::ssl::stream<Stream>& stream() const { return stream_; }
  167. bool supports_ssl() const { return true; }
  168. void set_endpoint(const void* val) { endpoint_.store(val); }
  169. using executor_type = asio::any_io_executor;
  170. executor_type get_executor() { return stream_.get_executor(); }
  171. // SSL
  172. void ssl_handshake(error_code& ec) { stream_.handshake(asio::ssl::stream_base::client, ec); }
  173. template <class CompletionToken>
  174. void async_ssl_handshake(CompletionToken&& token)
  175. {
  176. stream_.async_handshake(asio::ssl::stream_base::client, std::forward<CompletionToken>(token));
  177. }
  178. void ssl_shutdown(error_code& ec) { stream_.shutdown(ec); }
  179. template <class CompletionToken>
  180. void async_ssl_shutdown(CompletionToken&& token)
  181. {
  182. stream_.async_shutdown(std::forward<CompletionToken>(token));
  183. }
  184. // Reading
  185. std::size_t read_some(boost::asio::mutable_buffer buff, bool use_ssl, error_code& ec)
  186. {
  187. if (use_ssl)
  188. {
  189. return stream_.read_some(buff, ec);
  190. }
  191. else
  192. {
  193. return stream_.next_layer().read_some(buff, ec);
  194. }
  195. }
  196. template <class CompletionToken>
  197. void async_read_some(boost::asio::mutable_buffer buff, bool use_ssl, CompletionToken&& token)
  198. {
  199. if (use_ssl)
  200. {
  201. stream_.async_read_some(buff, std::forward<CompletionToken>(token));
  202. }
  203. else
  204. {
  205. stream_.next_layer().async_read_some(buff, std::forward<CompletionToken>(token));
  206. }
  207. }
  208. // Writing
  209. std::size_t write_some(boost::asio::const_buffer buff, bool use_ssl, error_code& ec)
  210. {
  211. if (use_ssl)
  212. {
  213. return stream_.write_some(buff, ec);
  214. }
  215. else
  216. {
  217. return stream_.next_layer().write_some(buff, ec);
  218. }
  219. }
  220. template <class CompletionToken>
  221. void async_write_some(boost::asio::const_buffer buff, bool use_ssl, CompletionToken&& token)
  222. {
  223. if (use_ssl)
  224. {
  225. stream_.async_write_some(buff, std::forward<CompletionToken>(token));
  226. }
  227. else
  228. {
  229. stream_.next_layer().async_write_some(buff, std::forward<CompletionToken>(token));
  230. }
  231. }
  232. // Connect and close
  233. void connect(error_code& ec) { do_connect(stream_, endpoint_, ec); }
  234. template <class CompletionToken>
  235. void async_connect(CompletionToken&& token)
  236. {
  237. do_async_connect(stream_, endpoint_, std::forward<CompletionToken>(token));
  238. }
  239. void close(error_code& ec) { do_close(stream_, ec); }
  240. };
  241. #ifdef BOOST_MYSQL_SEPARATE_COMPILATION
  242. extern template class engine_impl<engine_stream_adaptor<asio::ssl::stream<asio::ip::tcp::socket>>>;
  243. extern template class engine_impl<engine_stream_adaptor<asio::ip::tcp::socket>>;
  244. #endif
  245. template <class Stream, class... Args>
  246. std::unique_ptr<engine> make_engine(Args&&... args)
  247. {
  248. return std::unique_ptr<engine>(new engine_impl<engine_stream_adaptor<Stream>>(std::forward<Args>(args)...)
  249. );
  250. }
  251. // Use these only for engines created using make_engine
  252. template <class Stream>
  253. Stream& stream_from_engine(engine& eng)
  254. {
  255. using derived_t = engine_impl<engine_stream_adaptor<Stream>>;
  256. return static_cast<derived_t&>(eng).stream().stream();
  257. }
  258. template <class Stream>
  259. const Stream& stream_from_engine(const engine& eng)
  260. {
  261. using derived_t = engine_impl<engine_stream_adaptor<Stream>>;
  262. return static_cast<const derived_t&>(eng).stream().stream();
  263. }
  264. } // namespace detail
  265. } // namespace mysql
  266. } // namespace boost
  267. #endif