#include #include #ifdef BOOST_ASIO_HAS_CO_AWAIT #include #include #include #include #include #include #include #include namespace boost::beast::websocket { template void async_teardown( boost::beast::role_type /* role */, asio::ssl::stream& stream, TeardownHandler&& handler ) { return stream.async_shutdown(std::forward(handler)); } } // end namespace boost::beast::websocket namespace async_mqtt5 { template struct tls_handshake_type> { static constexpr auto client = asio::ssl::stream_base::client; static constexpr auto server = asio::ssl::stream_base::server; }; template void assign_tls_sni( const authority_path& ap, asio::ssl::context& /* ctx */, asio::ssl::stream& stream ) { SSL_set_tlsext_host_name(stream.native_handle(), ap.host.c_str()); } } // end namespace async_mqtt5 BOOST_AUTO_TEST_SUITE(coroutine/*, *boost::unit_test::disabled()*/) using namespace async_mqtt5; namespace asio = boost::asio; constexpr auto use_nothrow_awaitable = asio::as_tuple(asio::use_awaitable); template asio::awaitable sanity_check(mqtt_client& c) { auto [ec_0] = co_await c.template async_publish( "test/mqtt-test", "hello world with qos0!", retain_e::yes, publish_props {}, use_nothrow_awaitable ); BOOST_CHECK(!ec_0); auto [ec_1, puback_rc, puback_props] = co_await c.template async_publish( "test/mqtt-test", "hello world with qos1!", retain_e::yes, publish_props {}, use_nothrow_awaitable ); BOOST_CHECK(!ec_1); BOOST_CHECK(!puback_rc); auto [ec_2, pubcomp_rc, pubcomp_props] = co_await c.template async_publish( "test/mqtt-test", "hello world with qos2!", retain_e::yes, publish_props {}, use_nothrow_awaitable ); BOOST_CHECK(!ec_2); BOOST_CHECK(!pubcomp_rc); subscribe_topic sub_topic = subscribe_topic { "test/mqtt-test", async_mqtt5::subscribe_options { qos_e::exactly_once, subscribe_options::no_local_e::no, subscribe_options::retain_as_published_e::retain, subscribe_options::retain_handling_e::send } }; auto [sub_ec, sub_codes, sub_props] = co_await c.async_subscribe( sub_topic, subscribe_props {}, use_nothrow_awaitable ); BOOST_CHECK(!sub_ec); BOOST_CHECK(!sub_codes[0]); auto [rec, topic, payload, publish_props] = co_await c.async_receive(use_nothrow_awaitable); auto [unsub_ec, unsub_codes, unsub_props] = co_await c.async_unsubscribe( "test/mqtt-test", unsubscribe_props {}, use_nothrow_awaitable ); BOOST_CHECK(!unsub_ec); BOOST_CHECK(!unsub_codes[0]); co_await c.async_disconnect(use_nothrow_awaitable); co_return; } BOOST_AUTO_TEST_CASE(tcp_client_check) { asio::io_context ioc; using stream_type = asio::ip::tcp::socket; using client_type = mqtt_client; client_type c(ioc, ""); c.credentials("tcp-tester", "", "") .brokers("broker.hivemq.com", 1883) .will({ "test/mqtt-test", "Client disconnected!", qos_e::at_least_once }) .async_run(asio::detached); asio::steady_timer timer(ioc); timer.expires_after(std::chrono::seconds(5)); timer.async_wait( [&](boost::system::error_code ec) { BOOST_CHECK_MESSAGE(ec, "Failed to receive all the expected replies!"); c.cancel(); ioc.stop(); } ); co_spawn(ioc, [&]() -> asio::awaitable { co_await sanity_check(c); timer.cancel(); }, asio::detached ); ioc.run(); } BOOST_AUTO_TEST_CASE(websocket_tcp_client_check) { asio::io_context ioc; using stream_type = boost::beast::websocket::stream< asio::ip::tcp::socket >; using client_type = mqtt_client; client_type c(ioc, ""); c.brokers("broker.hivemq.com/mqtt", 8000) .will({ "test/mqtt-test", "Client disconnected!", qos_e::at_least_once }) .async_run(asio::detached); asio::steady_timer timer(ioc); timer.expires_after(std::chrono::seconds(5)); timer.async_wait( [&](boost::system::error_code ec) { BOOST_CHECK_MESSAGE(ec, "Failed to receive all the expected replies!"); c.cancel(); ioc.stop(); } ); co_spawn(ioc, [&]() -> asio::awaitable { co_await sanity_check(c); timer.cancel(); }, asio::detached ); ioc.run(); } BOOST_AUTO_TEST_CASE(openssl_tls_client_check) { asio::io_context ioc; using stream_type = asio::ssl::stream; asio::ssl::context tls_context(asio::ssl::context::tls_client); using client_type = mqtt_client; client_type c(ioc, "", std::move(tls_context)); c.brokers("broker.hivemq.com", 8883) .will({ "test/mqtt-test", "Client disconnected!", qos_e::at_least_once }) .async_run(asio::detached); asio::steady_timer timer(ioc); timer.expires_after(std::chrono::seconds(5)); timer.async_wait( [&](boost::system::error_code ec) { BOOST_CHECK_MESSAGE(ec, "Failed to receive all the expected replies!"); c.cancel(); ioc.stop(); } ); co_spawn(ioc, [&]() -> asio::awaitable { co_await sanity_check(c); timer.cancel(); }, asio::detached ); ioc.run(); } BOOST_AUTO_TEST_CASE(websocket_tls_client_check) { asio::io_context ioc; using stream_type = boost::beast::websocket::stream< asio::ssl::stream >; asio::ssl::context tls_context(asio::ssl::context::tls_client); using client_type = mqtt_client; client_type c(ioc, "", std::move(tls_context)); c.brokers("broker.hivemq.com/mqtt", 8884) .will({ "test/mqtt-test", "Client disconnected!", qos_e::at_least_once }) .async_run(asio::detached); asio::steady_timer timer(ioc); timer.expires_after(std::chrono::seconds(5)); timer.async_wait( [&](boost::system::error_code ec) { BOOST_CHECK_MESSAGE(ec, "Failed to receive all the expected replies!"); c.cancel(); ioc.stop(); } ); co_spawn(ioc, [&]() -> asio::awaitable { co_await sanity_check(c); timer.cancel(); }, asio::detached ); ioc.run(); } BOOST_AUTO_TEST_SUITE_END() #endif