#ifndef ASYNC_MQTT5_ASYNC_TRAITS_HPP #define ASYNC_MQTT5_ASYNC_TRAITS_HPP #include #include #include #include #include #include #include namespace async_mqtt5 { namespace asio = boost::asio; // TODO: move tls_handshake_type and assign_tls_sni to // separate header template struct tls_handshake_type {}; template void assign_tls_sni(const authority_path& ap, TlsContext& ctx, TlsStream& s); namespace detail { template decltype(auto) tracking_executor(const Handler& handler, const DfltExecutor& ex) { return asio::prefer( asio::get_associated_executor(handler, ex), asio::execution::outstanding_work.tracked ); } template using tracking_type = typename asio::prefer_result< asio::associated_executor_t, asio::execution::outstanding_work_t::tracked_t >::type; template concept has_async_write = requires(T a) { a.async_write( std::declval(), [](error_code, size_t) {} ); }; template concept has_tls_handshake = requires(T a) { a.async_handshake( typename T::handshake_type{}, [](error_code) {} ); }; template concept has_ws_handshake = requires(T a) { a.async_handshake( std::declval(), std::declval(), [](error_code) {} ); }; template concept has_tls_context = requires(T a) { a.tls_context(); }; template concept has_next_layer = requires(T a) { a.next_layer(); }; template struct next_layer_type { using type = T; }; template requires has_next_layer struct next_layer_type { using type = typename std::remove_reference_t::next_layer_type; }; template requires (!has_next_layer) typename next_layer_type::type& next_layer(T&& a) { return a; } template requires has_next_layer typename next_layer_type::type& next_layer(T&& a) { return a.next_layer(); } template using lowest_layer_type = typename boost::beast::lowest_layer_type; template lowest_layer_type& lowest_layer(S&& a) { return boost::beast::get_lowest_layer(std::forward(a)); } template struct has_tls_layer_impl : std::false_type {}; template requires has_tls_handshake struct has_tls_layer_impl : std::true_type {}; template requires (!has_tls_handshake && has_next_layer) struct has_tls_layer_impl : has_tls_layer_impl< std::remove_cvref_t().next_layer())> > {}; template concept has_tls_layer = has_tls_layer_impl>::value; //TODO: move to appropriate place template < typename Stream, typename ConstBufferSequence, typename CompletionToken > decltype(auto) async_write( Stream& stream, const ConstBufferSequence& buff, CompletionToken&& token ) { // TODO: find layer that has async write method if constexpr (has_async_write) return stream.async_write( buff, std::forward(token) ); else return asio::async_write( stream, buff, std::forward(token) ); } template void setup_tls_sni(const authority_path& ap, TlsContext& ctx, Stream& s) { if constexpr (has_tls_handshake) assign_tls_sni(ap, ctx, s); else if constexpr (has_next_layer) setup_tls_sni(ap, ctx, next_layer(s)); } } // end namespace detail } // end namespace async_mqtt5 #endif // !ASYNC_MQTT5_ASYNC_TRAITS_HPP