diff --git a/include/async_mqtt5/detail/async_traits.hpp b/include/async_mqtt5/detail/async_traits.hpp index 453b9a1..781ed64 100644 --- a/include/async_mqtt5/detail/async_traits.hpp +++ b/include/async_mqtt5/detail/async_traits.hpp @@ -15,8 +15,6 @@ #include #include -#include - #include #include #include @@ -35,6 +33,8 @@ void assign_tls_sni(const authority_path& ap, TlsContext& ctx, TlsStream& s); namespace detail { +// tracking executor + template using tracking_type = std::decay_t< typename asio::prefer_result< @@ -52,18 +52,8 @@ tracking_executor(const Handler& handler, const DfltExecutor& ex) { ); } -template -using async_write_sig = decltype( - std::declval().async_write(std::declval()...) -); - -constexpr auto write_handler_t = [](error_code, size_t) {}; - -template -constexpr bool has_async_write = boost::is_detected< - async_write_sig, T, B, decltype(write_handler_t) ->::value; +// tls handshake constexpr auto handshake_handler_t = [](error_code) {}; @@ -84,6 +74,7 @@ constexpr bool has_tls_handshake = boost::is_detected< decltype(handshake_handler_t) >::value; +// websocket handshake template using async_ws_handshake_sig = decltype( @@ -97,60 +88,68 @@ constexpr bool has_ws_handshake = boost::is_detected< decltype(handshake_handler_t) >::value; +// next layer template -using tls_context_sig = decltype( - std::declval().tls_context() -); - -template -constexpr bool has_tls_context = boost::is_detected< - tls_context_sig, T ->::value; - - -template -using next_layer_sig = decltype( - std::declval().next_layer() -); +using next_layer_sig = decltype(std::declval().next_layer()); template constexpr bool has_next_layer = boost::is_detected< - next_layer_sig, T + next_layer_sig, boost::remove_cv_ref_t >::value; template -struct next_layer_type { +struct next_layer_type_impl { using type = T; }; template -struct next_layer_type< - T, std::enable_if_t> -> { - using type = typename std::remove_reference_t::next_layer_type; +struct next_layer_type_impl>> { + using type = typename T::next_layer_type; }; template -typename next_layer_type>>::type& -next_layer(T&& a) { - return a; -} +using next_layer_type = typename next_layer_type_impl< + boost::remove_cv_ref_t +>::type; template -typename next_layer_type>>::type& -next_layer(T&& a) { - return a.next_layer(); +next_layer_type& next_layer(T&& a) { + if constexpr (has_next_layer) + return a.next_layer(); + else + return std::forward(a); } -template -using lowest_layer_type = typename boost::beast::lowest_layer_type; +// lowest layer -template -lowest_layer_type& lowest_layer(S&& a) { - return boost::beast::get_lowest_layer(std::forward(a)); +template +struct lowest_layer_type_impl { + using type = T; +}; + +template +struct lowest_layer_type_impl>> { + using type = typename lowest_layer_type_impl< + next_layer_type + >::type; +}; + +template +using lowest_layer_type = typename lowest_layer_type_impl< + boost::remove_cv_ref_t +>::type; + +template +lowest_layer_type& lowest_layer(T&& a) { + if constexpr (has_next_layer) + return lowest_layer(a.next_layer()); + else + return std::forward(a); } +// tls layer + template struct has_tls_layer_impl : std::false_type {}; @@ -171,6 +170,41 @@ constexpr bool has_tls_layer = has_tls_layer_impl< boost::remove_cv_ref_t >::value; +// tls context + +template +using tls_context_sig = decltype( + std::declval().tls_context() +); + +template +constexpr bool has_tls_context = boost::is_detected< + tls_context_sig, T +>::value; + +// setup_tls_sni + +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)); +} + +// async_write + +template +using async_write_sig = decltype( + std::declval().async_write(std::declval()...) +); + +constexpr auto write_handler_t = [](error_code, size_t) {}; + +template +constexpr bool has_async_write = boost::is_detected< + async_write_sig, T, B, decltype(write_handler_t) +>::value; template < typename Stream, @@ -190,13 +224,6 @@ decltype(auto) async_write( ); } -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 diff --git a/include/async_mqtt5/detail/rebind_executor.hpp b/include/async_mqtt5/detail/rebind_executor.hpp index 7886077..b44605d 100644 --- a/include/async_mqtt5/detail/rebind_executor.hpp +++ b/include/async_mqtt5/detail/rebind_executor.hpp @@ -8,8 +8,6 @@ #ifndef ASYNC_MQTT5_REBIND_EXECUTOR_HPP #define ASYNC_MQTT5_REBIND_EXECUTOR_HPP -#include - namespace boost::asio::ssl { // forward declare to preserve optional OpenSSL dependency @@ -18,6 +16,15 @@ class stream; } // end namespace boost::asio::ssl +// forward declare to avoid Beast dependency + +namespace boost::beast::websocket { + +template +class stream; + +}// end namespace boost::beast::websocket + namespace async_mqtt5::detail { namespace asio = boost::asio; @@ -30,14 +37,19 @@ struct rebind_executor { // asio::ssl::stream does not define a rebind_executor member type template struct rebind_executor, Executor> { - using other = typename asio::ssl::stream::other>; + using other = typename asio::ssl::stream< + typename rebind_executor::other + >; }; -template -struct rebind_executor>, Executor> { +template +struct rebind_executor< + boost::beast::websocket::stream, deflate_supported>, + Executor +> { using other = typename boost::beast::websocket::stream< asio::ssl::stream::other>, - boost::beast::websocket::stream>::is_deflate_supported::value + deflate_supported >; }; diff --git a/include/async_mqtt5/impl/connect_op.hpp b/include/async_mqtt5/impl/connect_op.hpp index 4ea4f56..de69d26 100644 --- a/include/async_mqtt5/impl/connect_op.hpp +++ b/include/async_mqtt5/impl/connect_op.hpp @@ -138,10 +138,10 @@ public: ); } else if constexpr ( - has_tls_handshake::type> + has_tls_handshake> ) { _stream.next_layer().async_handshake( - tls_handshake_type::type>::client, + tls_handshake_type>::client, asio::append( asio::prepend(std::move(*this), on_tls_handshake {}), std::move(ep), std::move(ap) diff --git a/test/integration/mqtt_features.cpp b/test/integration/mqtt_features.cpp index 762a717..4e111a9 100644 --- a/test/integration/mqtt_features.cpp +++ b/test/integration/mqtt_features.cpp @@ -23,6 +23,8 @@ #include +#include + #include BOOST_AUTO_TEST_SUITE(mqtt_features/*, *boost::unit_test::disabled()*/) @@ -34,9 +36,9 @@ constexpr auto use_nothrow_awaitable = asio::as_tuple(asio::use_awaitable); constexpr auto test_duration = std::chrono::seconds(5); -using stream_type = asio::ip::tcp::socket; +using stream_type = boost::beast::websocket::stream; -constexpr auto broker = "broker.hivemq.com"; +constexpr auto broker = "broker.hivemq.com/mqtt"; constexpr auto connect_wait_dur = std::chrono::milliseconds(200); constexpr auto topic = "async-mqtt5/test"; constexpr auto share_topic = "$share/sharename/async-mqtt5/test"; @@ -62,7 +64,7 @@ asio::awaitable test_manual_use_topic_alias() { auto ex = co_await asio::this_coro::executor; mqtt_client client(ex); - client.brokers(broker) + client.brokers(broker, 8000) .connect_property(prop::topic_alias_maximum, uint16_t(10)) .async_run(asio::detached); @@ -94,7 +96,7 @@ asio::awaitable test_subscription_identifiers() { auto ex = co_await asio::this_coro::executor; mqtt_client client(ex); - client.brokers(broker) + client.brokers(broker, 8000) .async_run(asio::detached); publish_props pprops; @@ -109,7 +111,7 @@ asio::awaitable test_subscription_identifiers() { sprops[prop::subscription_identifier] = sub_id; subscribe_options sub_opts = { .no_local = no_local_e::no }; - subscribe_topic sub_topic = { share_topic, sub_opts }; + subscribe_topic sub_topic = { topic, sub_opts }; auto&& [ec_2, rcs, __] = co_await client.async_subscribe( sub_topic, sprops, use_nothrow_awaitable ); @@ -135,7 +137,7 @@ asio::awaitable test_shared_subscription() { auto ex = co_await asio::this_coro::executor; mqtt_client client(ex); - client.brokers(broker) + client.brokers(broker, 8000) .async_run(asio::detached); subscribe_options sub_opts = { .no_local = no_local_e::no }; @@ -170,7 +172,7 @@ asio::awaitable test_user_property() { auto ex = co_await asio::this_coro::executor; mqtt_client client(ex); - client.brokers(broker) + client.brokers(broker, 8000) .async_run(asio::detached); publish_props pprops; diff --git a/test/unit/traits.cpp b/test/unit/traits.cpp index 80dfc73..49598d4 100644 --- a/test/unit/traits.cpp +++ b/test/unit/traits.cpp @@ -9,11 +9,12 @@ #include #include +#include #include #include #include -#include +#include #include @@ -26,8 +27,6 @@ using namespace async_mqtt5; -BOOST_AUTO_TEST_SUITE(traits/*, *boost::unit_test::disabled()*/) - struct good_authenticator { good_authenticator() = default; @@ -79,7 +78,6 @@ BOOST_STATIC_ASSERT(detail::has_next_layer); BOOST_STATIC_ASSERT(detail::has_next_layer); BOOST_STATIC_ASSERT(detail::has_next_layer); - BOOST_STATIC_ASSERT(!detail::has_tls_layer); BOOST_STATIC_ASSERT(detail::has_tls_layer); BOOST_STATIC_ASSERT(!detail::has_tls_layer); @@ -90,10 +88,68 @@ BOOST_STATIC_ASSERT(detail::has_tls_handshake); BOOST_STATIC_ASSERT(!detail::has_tls_handshake); BOOST_STATIC_ASSERT(!detail::has_tls_handshake); - BOOST_STATIC_ASSERT(!detail::has_ws_handshake); BOOST_STATIC_ASSERT(!detail::has_ws_handshake); BOOST_STATIC_ASSERT(detail::has_ws_handshake); BOOST_STATIC_ASSERT(detail::has_ws_handshake); -BOOST_AUTO_TEST_SUITE_END(); +BOOST_STATIC_ASSERT(!detail::has_next_layer); +BOOST_STATIC_ASSERT(detail::has_next_layer); +BOOST_STATIC_ASSERT(detail::has_next_layer); +BOOST_STATIC_ASSERT(detail::has_next_layer); + +BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); +BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); +BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); +BOOST_STATIC_ASSERT(std::is_same_v, tls_layer>); + +BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); +BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); +BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); +BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); + +void tcp_layers_test() { + asio::system_executor ex; + tcp_layer layer(ex); + + detail::next_layer_type& nlayer = detail::next_layer(layer); + BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); + + detail::lowest_layer_type& llayer = detail::lowest_layer(layer); + BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); +} + +void tls_layers_test() { + asio::system_executor ex; + asio::ssl::context ctx(asio::ssl::context::tls_client); + tls_layer layer(ex, ctx); + + detail::next_layer_type& nlayer = detail::next_layer(layer); + BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); + + detail::lowest_layer_type& llayer = detail::lowest_layer(layer); + BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); +} + +void websocket_tcp_layers_test() { + asio::system_executor ex; + websocket_tcp_layer layer(ex); + + detail::next_layer_type& nlayer = detail::next_layer(layer); + BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); + + detail::lowest_layer_type& llayer = detail::lowest_layer(layer); + BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); +} + +void websocket_tls_layers_test() { + asio::system_executor ex; + asio::ssl::context ctx(asio::ssl::context::tls_client); + websocket_tls_layer layer(ex, ctx); + + detail::next_layer_type& nlayer = detail::next_layer(layer); + BOOST_STATIC_ASSERT(std::is_same_v, tls_layer>); + + detail::lowest_layer_type& llayer = detail::lowest_layer(layer); + BOOST_STATIC_ASSERT(std::is_same_v, tcp_layer>); +}