async_traits.hpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. //
  2. // Copyright (c) 2023-2025 Ivica Siladic, Bruno Iljazovic, Korina Simicevic
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MQTT5_ASYNC_TRAITS_HPP
  8. #define BOOST_MQTT5_ASYNC_TRAITS_HPP
  9. #include <boost/mqtt5/types.hpp>
  10. #include <boost/asio/associated_executor.hpp>
  11. #include <boost/asio/execution.hpp>
  12. #include <boost/asio/prefer.hpp>
  13. #include <boost/asio/write.hpp>
  14. #include <boost/type_traits/detected_or.hpp>
  15. #include <boost/type_traits/is_detected.hpp>
  16. #include <boost/type_traits/remove_cv_ref.hpp>
  17. #include <type_traits>
  18. namespace boost::mqtt5 {
  19. namespace asio = boost::asio;
  20. // TLS
  21. template <typename StreamType>
  22. struct tls_handshake_type {};
  23. template <typename TlsContext, typename TlsStream>
  24. void assign_tls_sni(const authority_path& ap, TlsContext& ctx, TlsStream& s);
  25. // WebSocket
  26. template <typename Stream>
  27. struct ws_handshake_traits {};
  28. namespace detail {
  29. // tracking executor
  30. template <typename Handler, typename DfltExecutor>
  31. using tracking_type = std::decay_t<
  32. typename asio::prefer_result<
  33. asio::associated_executor_t<Handler, DfltExecutor>,
  34. asio::execution::outstanding_work_t::tracked_t
  35. >::type
  36. >;
  37. template <typename Handler, typename DfltExecutor>
  38. tracking_type<Handler, DfltExecutor>
  39. tracking_executor(const Handler& handler, const DfltExecutor& ex) {
  40. return asio::prefer(
  41. asio::get_associated_executor(handler, ex),
  42. asio::execution::outstanding_work.tracked
  43. );
  44. }
  45. // tls handshake
  46. constexpr auto handshake_handler_t = [](error_code) {};
  47. template <typename T>
  48. using tls_handshake_t = typename T::handshake_type;
  49. template <typename T>
  50. using tls_handshake_type_of = boost::detected_or_t<void, tls_handshake_t, T>;
  51. template <typename T, typename ...Ts>
  52. using async_tls_handshake_sig = decltype(
  53. std::declval<T&>().async_handshake(std::declval<Ts>()...)
  54. );
  55. template <typename T>
  56. constexpr bool has_tls_handshake = boost::is_detected<
  57. async_tls_handshake_sig, T, tls_handshake_type_of<T>,
  58. decltype(handshake_handler_t)
  59. >::value;
  60. // websocket handshake
  61. template <typename T, typename ...Ts>
  62. using async_ws_handshake_sig = decltype(
  63. std::declval<T&>().async_handshake(std::declval<Ts>()...)
  64. );
  65. template <typename T>
  66. constexpr bool has_ws_handshake = boost::is_detected<
  67. async_ws_handshake_sig, T,
  68. std::string_view, std::string_view,
  69. decltype(handshake_handler_t)
  70. >::value;
  71. // next layer
  72. template <typename T>
  73. using next_layer_sig = decltype(std::declval<T&>().next_layer());
  74. template <typename T>
  75. constexpr bool has_next_layer = boost::is_detected<
  76. next_layer_sig, boost::remove_cv_ref_t<T>
  77. >::value;
  78. template <typename T, typename Enable = void>
  79. struct next_layer_type_impl {
  80. using type = T;
  81. };
  82. template <typename T>
  83. struct next_layer_type_impl<T, std::enable_if_t<has_next_layer<T>>> {
  84. using type = typename T::next_layer_type;
  85. };
  86. template <typename T>
  87. using next_layer_type = typename next_layer_type_impl<
  88. boost::remove_cv_ref_t<T>
  89. >::type;
  90. template <typename T>
  91. next_layer_type<T>& next_layer(T&& a) {
  92. if constexpr (has_next_layer<T>)
  93. return a.next_layer();
  94. else
  95. return std::forward<T>(a);
  96. }
  97. // lowest layer
  98. template <typename T, typename Enable = void>
  99. struct lowest_layer_type_impl {
  100. using type = T;
  101. };
  102. template <typename T>
  103. struct lowest_layer_type_impl<T, std::enable_if_t<has_next_layer<T>>> {
  104. using type = typename lowest_layer_type_impl<
  105. next_layer_type<T>
  106. >::type;
  107. };
  108. template <typename T>
  109. using lowest_layer_type = typename lowest_layer_type_impl<
  110. boost::remove_cv_ref_t<T>
  111. >::type;
  112. template <typename T>
  113. lowest_layer_type<T>& lowest_layer(T&& a) {
  114. if constexpr (has_next_layer<T>)
  115. return lowest_layer(a.next_layer());
  116. else
  117. return std::forward<T>(a);
  118. }
  119. // tls layer
  120. template <typename T, typename Enable = void>
  121. struct has_tls_layer_impl : std::false_type {};
  122. template <typename T>
  123. struct has_tls_layer_impl<
  124. T, std::enable_if_t<has_tls_handshake<T>>
  125. > : std::true_type {};
  126. template <typename T>
  127. struct has_tls_layer_impl<
  128. T, std::enable_if_t<!has_tls_handshake<T> && has_next_layer<T>>
  129. > : has_tls_layer_impl<
  130. boost::remove_cv_ref_t<decltype(std::declval<T&>().next_layer())>
  131. > {};
  132. template <typename T>
  133. constexpr bool has_tls_layer = has_tls_layer_impl<
  134. boost::remove_cv_ref_t<T>
  135. >::value;
  136. // tls context
  137. template <typename T>
  138. using tls_context_sig = decltype(
  139. std::declval<T&>().tls_context()
  140. );
  141. template <typename T>
  142. constexpr bool has_tls_context = boost::is_detected<
  143. tls_context_sig, T
  144. >::value;
  145. // setup_tls_sni
  146. template <typename TlsContext, typename Stream>
  147. void setup_tls_sni(const authority_path& ap, TlsContext& ctx, Stream& s) {
  148. if constexpr (has_tls_handshake<Stream>)
  149. assign_tls_sni(ap, ctx, s);
  150. else if constexpr (has_next_layer<Stream>)
  151. setup_tls_sni(ap, ctx, next_layer(s));
  152. }
  153. // async_write
  154. template <typename T, typename ...Ts>
  155. using async_write_sig = decltype(
  156. std::declval<T&>().async_write(std::declval<Ts>()...)
  157. );
  158. constexpr auto write_handler_t = [](error_code, size_t) {};
  159. template <typename T, typename B>
  160. constexpr bool has_async_write = boost::is_detected<
  161. async_write_sig, T, B, decltype(write_handler_t)
  162. >::value;
  163. template <
  164. typename Stream,
  165. typename ConstBufferSequence,
  166. typename CompletionToken
  167. >
  168. decltype(auto) async_write(
  169. Stream& stream, const ConstBufferSequence& buff, CompletionToken&& token
  170. ) {
  171. if constexpr (has_async_write<Stream, ConstBufferSequence>)
  172. return stream.async_write(
  173. buff, std::forward<CompletionToken>(token)
  174. );
  175. else
  176. return asio::async_write(
  177. stream, buff, std::forward<CompletionToken>(token)
  178. );
  179. }
  180. } // end namespace detail
  181. } // end namespace boost::mqtt5
  182. #endif // !BOOST_MQTT5_ASYNC_TRAITS_HPP