From ffdc21ec06d2747c65779e78a7bdfbfbb4936053 Mon Sep 17 00:00:00 2001 From: Takatoshi Kondo Date: Tue, 8 Sep 2020 22:32:20 +0900 Subject: [PATCH] Added topic alias support. TopicAlias lifetime is the same as Session lifetime It is different from MQTT v5 spec but practical choice. See https://lists.oasis-open.org/archives/mqtt-comment/202009/msg00000.html Related fix: When topic is empty and no topic alias entry is found, then protocol_error. The broker send DISCONNECT packet with protocol_error reason code. process_disconnect implementation was simply doesn't call `on_mqtt_message_processed()` but it was bad. It caused exit from ioc.run() loop because the next async_read was not called. The correct behavior is calling `on_mqtt_message_processed()` and close socket. The endpoint can detect the socket close by on_error(). Fixed topic alias. --- include/mqtt/client.hpp | 8 +- include/mqtt/constant.hpp | 1 + include/mqtt/endpoint.hpp | 97 ++++- include/mqtt/setup_log.hpp | 1 + include/mqtt/topic_alias_recv.hpp | 70 +++ include/mqtt/type.hpp | 1 + test/CMakeLists.txt | 1 + test/as_buffer_async_pubsub_1.cpp | 6 + test/as_buffer_async_pubsub_2.cpp | 6 + test/as_buffer_pubsub.cpp | 11 + test/as_buffer_sub.cpp | 6 + test/async_pubsub_1.cpp | 6 + test/async_pubsub_2.cpp | 19 +- test/connect.cpp | 18 +- test/length_check.cpp | 1 + test/manual_publish.cpp | 1 + test/multi_sub.cpp | 1 + test/pubsub.cpp | 26 +- test/pubsub_no_strand.cpp | 10 + test/remaining_length.cpp | 3 + test/resend.cpp | 16 +- test/retain_1.cpp | 2 + test/retain_2.cpp | 12 +- test/sub.cpp | 9 + test/test_broker.hpp | 60 ++- test/topic_alias_recv.cpp | 684 ++++++++++++++++++++++++++++++ 26 files changed, 1021 insertions(+), 55 deletions(-) create mode 100644 include/mqtt/topic_alias_recv.hpp create mode 100644 test/topic_alias_recv.cpp diff --git a/include/mqtt/client.hpp b/include/mqtt/client.hpp index d783cb453..38c89fa99 100644 --- a/include/mqtt/client.hpp +++ b/include/mqtt/client.hpp @@ -1389,20 +1389,18 @@ class client : public endpoint { } static optional get_session_expiry_interval_by_props(v5::properties const& props) { - bool finish = false; - optional val; + optional val; for (auto const& prop : props) { MQTT_NS::visit( make_lambda_visitor( - [&finish, &val](v5::property::session_expiry_interval const& p) { + [&val](v5::property::session_expiry_interval const& p) { val = p.val(); - finish = true; }, [](auto&&) { } ), prop ); - if (finish) break; + if (val) break; } return val; } diff --git a/include/mqtt/constant.hpp b/include/mqtt/constant.hpp index a44827389..125ead17e 100644 --- a/include/mqtt/constant.hpp +++ b/include/mqtt/constant.hpp @@ -12,6 +12,7 @@ namespace MQTT_NS { static constexpr session_expiry_interval_t const session_never_expire = 0xffffffffUL; +static constexpr topic_alias_t const topic_alias_max = 0xffff; } // namespace MQTT_NS diff --git a/include/mqtt/endpoint.hpp b/include/mqtt/endpoint.hpp index 108185416..1bfdb5253 100644 --- a/include/mqtt/endpoint.hpp +++ b/include/mqtt/endpoint.hpp @@ -64,6 +64,7 @@ #include #include #include +#include #if defined(MQTT_USE_WS) #include @@ -990,9 +991,7 @@ class endpoint : public std::enable_shared_from_this lck (topic_alias_recv_mtx_); + return topic_alias_recv_; + } + + /** + * @brief restore topic alias recv container. + * @param con topic alias recv container to restore + * + * This function for dump/restore topic alias recv container. + */ + void restore_topic_alias_recv_container(topic_alias_recv_map_t con) { + LockGuard lck (topic_alias_recv_mtx_); + topic_alias_recv_ = MQTT_NS::force_move(con); + } + protected: /** @@ -4568,9 +4589,15 @@ class endpoint : public std::enable_shared_from_this lck (store_mtx_); - store_.clear(); - packet_id_.clear(); + { + LockGuard lck (store_mtx_); + store_.clear(); + packet_id_.clear(); + } + { + LockGuard lck (topic_alias_recv_mtx_); + clear_topic_alias(topic_alias_recv_); + } } private: @@ -4602,7 +4629,10 @@ class endpoint : public std::enable_shared_from_this - void shutdown_from_client(T& socket) { + void shutdown(T& socket) { + connected_ = false; + mqtt_connected_ = false; + boost::system::error_code ec; socket.lowest_layer().close(ec); } @@ -7045,6 +7075,31 @@ class endpoint : public std::enable_shared_from_this lck (topic_alias_recv_mtx_); + return find_topic_by_alias(topic_alias_recv_, topic_alias.value()); + }(); + if (topic_name.empty()) { + MQTT_LOG("mqtt_cb", error) + << MQTT_ADD_VALUE(address, this) + << "no matching topic alias: " + << topic_alias.value(); + call_protocol_error_handlers(); + return false; + } + else { + info.topic_name = allocate_buffer(topic_name); + } + } + } + else { + if (auto topic_alias = get_topic_alias_by_props(info.props)) { + LockGuard lck (topic_alias_recv_mtx_); + register_topic_alias(topic_alias_recv_, info.topic_name, topic_alias.value()); + } + } if (on_v5_publish( info.packet_id, publish_options(fixed_header_), @@ -8585,6 +8640,8 @@ class endpoint : public std::enable_shared_from_this get_topic_alias_by_prop(v5::property_variant const& prop) { + optional val; + MQTT_NS::visit( + make_lambda_visitor( + [&val](v5::property::topic_alias const& p) { + val = p.val(); + }, + [](auto&&) { + } + ), prop + ); + return val; + } + + static optional get_topic_alias_by_props(v5::properties const& props) { + for (auto const& prop : props) { + if (auto val = get_topic_alias_by_prop(prop)) { + return val; + } + } + return nullopt; + } + protected: // Ensure that only code that knows the *exact* type of an object // inheriting from this abstract base class can destruct it. @@ -9976,6 +10056,9 @@ class endpoint : public std::enable_shared_from_this +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace MQTT_NS { + +using topic_alias_recv_map_t = std::map; + +inline void register_topic_alias(topic_alias_recv_map_t& m, string_view topic, topic_alias_t alias) { + BOOST_ASSERT(alias > 0 && alias <= topic_alias_max); + + MQTT_LOG("mqtt_impl", info) + << MQTT_ADD_VALUE(address, &m) + << "register_topic_alias" + << " topic:" << topic + << " alias:" << alias; + + if (topic.empty()) { + m.erase(alias); + } + else { + m[alias] = std::string(topic); // overwrite + } +} + +inline std::string find_topic_by_alias(topic_alias_recv_map_t const& m, topic_alias_t alias) { + BOOST_ASSERT(alias > 0 && alias <= topic_alias_max); + + std::string topic; + auto it = m.find(alias); + if (it != m.end()) topic = it->second; + + MQTT_LOG("mqtt_impl", info) + << MQTT_ADD_VALUE(address, &m) + << "find_topic_by_alias" + << " alias:" << alias + << " topic:" << topic; + + return topic; +} + +inline void clear_topic_alias(topic_alias_recv_map_t& m) { + MQTT_LOG("mqtt_impl", info) + << MQTT_ADD_VALUE(address, &m) + << "clear_topic_alias"; + + m.clear(); +} + +} // namespace MQTT_NS + +#endif // MQTT_TOPIC_ALIAS_RECV_HPP diff --git a/include/mqtt/type.hpp b/include/mqtt/type.hpp index 1040f23c4..7a284eb3a 100644 --- a/include/mqtt/type.hpp +++ b/include/mqtt/type.hpp @@ -12,6 +12,7 @@ namespace MQTT_NS { using session_expiry_interval_t = std::uint32_t; +using topic_alias_t = std::uint16_t; } // namespace MQTT_NS diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f161b7ada..d045cf325 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,7 @@ IF (MQTT_TEST_1) LIST (APPEND check_PROGRAMS connect.cpp underlying_timeout.cpp + topic_alias_recv.cpp ) ENDIF () diff --git a/test/as_buffer_async_pubsub_1.cpp b/test/as_buffer_async_pubsub_1.cpp index 8912189a9..63940731f 100644 --- a/test/as_buffer_async_pubsub_1.cpp +++ b/test/as_buffer_async_pubsub_1.cpp @@ -19,6 +19,7 @@ BOOST_AUTO_TEST_SUITE(test_as_buffer_async_pubsub_1) BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_sub; @@ -233,6 +234,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_pub; @@ -459,6 +461,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_pub; @@ -687,6 +690,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_sub; @@ -900,6 +904,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos1 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_pub; @@ -1124,6 +1129,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos1 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_pub; diff --git a/test/as_buffer_async_pubsub_2.cpp b/test/as_buffer_async_pubsub_2.cpp index 6ea6d21c6..466e149fd 100644 --- a/test/as_buffer_async_pubsub_2.cpp +++ b/test/as_buffer_async_pubsub_2.cpp @@ -19,6 +19,7 @@ BOOST_AUTO_TEST_SUITE(test_as_buffer_async_pubsub_2) BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -237,6 +238,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos2 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -470,6 +472,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos2 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -705,6 +708,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos2 ) { BOOST_AUTO_TEST_CASE( publish_function ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -917,6 +921,7 @@ BOOST_AUTO_TEST_CASE( publish_function ) { BOOST_AUTO_TEST_CASE( publish_function_buffer ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -1123,6 +1128,7 @@ BOOST_AUTO_TEST_CASE( publish_function_buffer ) { BOOST_AUTO_TEST_CASE( publish_dup_function ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; diff --git a/test/as_buffer_pubsub.cpp b/test/as_buffer_pubsub.cpp index cdc1697e4..27664a693 100644 --- a/test/as_buffer_pubsub.cpp +++ b/test/as_buffer_pubsub.cpp @@ -16,6 +16,7 @@ BOOST_AUTO_TEST_SUITE(test_as_buffer_pubsub) BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -195,6 +196,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); bool pub_seq_finished = false; @@ -383,6 +385,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -572,6 +575,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -750,6 +754,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos1 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -943,6 +948,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos1 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -1139,6 +1145,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos1 ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -1318,6 +1325,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos2 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -1511,6 +1519,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos2 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -1707,6 +1716,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos2 ) { BOOST_AUTO_TEST_CASE( publish_function ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -1881,6 +1891,7 @@ BOOST_AUTO_TEST_CASE( publish_function ) { BOOST_AUTO_TEST_CASE( publish_dup_function ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; diff --git a/test/as_buffer_sub.cpp b/test/as_buffer_sub.cpp index b0e269439..445072fb7 100644 --- a/test/as_buffer_sub.cpp +++ b/test/as_buffer_sub.cpp @@ -18,6 +18,7 @@ using namespace std::literals::string_literals; BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_single ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -109,6 +110,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_single ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_arg ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -212,6 +214,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_arg ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_vec ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -313,6 +316,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_vec ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_single_async ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); checker chk = { @@ -417,6 +421,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_single_async ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_arg_async ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -536,6 +541,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_arg_async ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_vec_async ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); diff --git a/test/async_pubsub_1.cpp b/test/async_pubsub_1.cpp index d24905077..0329f8e92 100644 --- a/test/async_pubsub_1.cpp +++ b/test/async_pubsub_1.cpp @@ -19,6 +19,7 @@ BOOST_AUTO_TEST_SUITE(test_async_pubsub_1) BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -202,6 +203,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -401,6 +403,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -593,6 +596,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -776,6 +780,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos1 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -969,6 +974,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos1 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; diff --git a/test/async_pubsub_2.cpp b/test/async_pubsub_2.cpp index 9b01bb760..562d465c1 100644 --- a/test/async_pubsub_2.cpp +++ b/test/async_pubsub_2.cpp @@ -21,6 +21,7 @@ using namespace MQTT_NS::literals; BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -204,6 +205,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos2 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -400,6 +402,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos2 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -598,6 +601,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos2 ) { BOOST_AUTO_TEST_CASE( publish_function ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -776,6 +780,7 @@ BOOST_AUTO_TEST_CASE( publish_function ) { BOOST_AUTO_TEST_CASE( publish_dup_function ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -959,6 +964,7 @@ BOOST_AUTO_TEST_CASE( publish_dup_function ) { BOOST_AUTO_TEST_CASE( publish_function_buffer ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -1147,6 +1153,7 @@ BOOST_AUTO_TEST_CASE( pub_sub_prop ) { } using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -1224,7 +1231,12 @@ BOOST_AUTO_TEST_CASE( pub_sub_prop ) { BOOST_TEST(topic == "topic1"); BOOST_TEST(contents == "topic1_contents"); - BOOST_TEST(props.size() == prop_size); + // -1 means TopicAlias + // TopicAlias is not forwarded + // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901113 + // A receiver MUST NOT carry forward any Topic Alias mappings from + // one Network Connection to another [MQTT-3.3.2-7]. + BOOST_TEST(props.size() == prop_size - 1); for (auto const& p : props) { MQTT_NS::visit( @@ -1235,9 +1247,6 @@ BOOST_AUTO_TEST_CASE( pub_sub_prop ) { [&](MQTT_NS::v5::property::message_expiry_interval const& t) { BOOST_TEST(t.val() == 0x12345678UL); }, - [&](MQTT_NS::v5::property::topic_alias const& t) { - BOOST_TEST(t.val() == 0x1234U); - }, [&](MQTT_NS::v5::property::response_topic const& t) { BOOST_TEST(t.val() == "response topic"); }, @@ -1323,6 +1332,7 @@ BOOST_AUTO_TEST_CASE( puback_props ) { } using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); c->set_auto_pub_response(false); @@ -1504,6 +1514,7 @@ BOOST_AUTO_TEST_CASE( pubrec_rel_comp_prop ) { // * test target using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); c->set_auto_pub_response(false); diff --git a/test/connect.cpp b/test/connect.cpp index b69728263..df678ae69 100644 --- a/test/connect.cpp +++ b/test/connect.cpp @@ -1198,10 +1198,11 @@ BOOST_AUTO_TEST_CASE( async_pingresp_timeout ) { BOOST_AUTO_TEST_CASE( connect_prop ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& b) { - if (c->get_protocol_version() != MQTT_NS::protocol_version::v5) { - finish(); - return; - } + if (c->get_protocol_version() != MQTT_NS::protocol_version::v5) { + finish(); + return; + } + c->set_client_id("cid1"); c->set_clean_session(true); BOOST_TEST(c->connected() == false); @@ -1319,10 +1320,11 @@ BOOST_AUTO_TEST_CASE( connect_prop ) { BOOST_AUTO_TEST_CASE( disconnect_prop ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& b) { - if (c->get_protocol_version() != MQTT_NS::protocol_version::v5) { - finish(); - return; - } + if (c->get_protocol_version() != MQTT_NS::protocol_version::v5) { + finish(); + return; + } + c->set_client_id("cid1"); c->set_clean_session(true); BOOST_TEST(c->connected() == false); diff --git a/test/length_check.cpp b/test/length_check.cpp index 80012acd8..6f0366b3b 100644 --- a/test/length_check.cpp +++ b/test/length_check.cpp @@ -21,6 +21,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { } using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); diff --git a/test/manual_publish.cpp b/test/manual_publish.cpp index 30524ed3d..8673aa3cf 100644 --- a/test/manual_publish.cpp +++ b/test/manual_publish.cpp @@ -16,6 +16,7 @@ BOOST_AUTO_TEST_SUITE(test_manual_publish) BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); diff --git a/test/multi_sub.cpp b/test/multi_sub.cpp index a08e3ca3b..02e799cb3 100644 --- a/test/multi_sub.cpp +++ b/test/multi_sub.cpp @@ -23,6 +23,7 @@ BOOST_AUTO_TEST_CASE( multi_channel ) { } using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; diff --git a/test/pubsub.cpp b/test/pubsub.cpp index 6202998a2..1557ad992 100644 --- a/test/pubsub.cpp +++ b/test/pubsub.cpp @@ -18,6 +18,7 @@ using namespace MQTT_NS::literals; BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_sub; @@ -196,6 +197,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); bool pub_seq_finished = false; @@ -392,6 +394,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos0 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_pub; @@ -581,6 +584,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos0 ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_sub; @@ -759,6 +763,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos1 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_pub; @@ -951,6 +956,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos1 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos1 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_pub; @@ -1147,6 +1153,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos1 ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_sub; @@ -1327,6 +1334,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos2 ) { BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_pub; @@ -1520,6 +1528,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos2 ) { BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos2 ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_pub; @@ -1716,6 +1725,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos2 ) { BOOST_AUTO_TEST_CASE( publish_function ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_sub; @@ -1890,6 +1900,7 @@ BOOST_AUTO_TEST_CASE( publish_function ) { BOOST_AUTO_TEST_CASE( publish_function_buffer ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_sub; @@ -2064,6 +2075,7 @@ BOOST_AUTO_TEST_CASE( publish_function_buffer ) { BOOST_AUTO_TEST_CASE( publish_dup_function ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_sub; @@ -2243,6 +2255,7 @@ BOOST_AUTO_TEST_CASE( publish_dup_function ) { BOOST_AUTO_TEST_CASE( publish_dup_function_buffer ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_sub; @@ -2427,6 +2440,7 @@ BOOST_AUTO_TEST_CASE( pub_sub_prop ) { } using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_sub; @@ -2521,7 +2535,12 @@ BOOST_AUTO_TEST_CASE( pub_sub_prop ) { BOOST_TEST(topic == "topic1"); BOOST_TEST(contents == "topic1_contents"); - BOOST_TEST(props.size() == prop_size); + // -1 means TopicAlias + // TopicAlias is not forwarded + // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901113 + // A receiver MUST NOT carry forward any Topic Alias mappings from + // one Network Connection to another [MQTT-3.3.2-7]. + BOOST_TEST(props.size() == prop_size - 1); for (auto const& p : props) { MQTT_NS::visit( @@ -2535,9 +2554,6 @@ BOOST_AUTO_TEST_CASE( pub_sub_prop ) { [&](MQTT_NS::v5::property::message_expiry_interval const& t) { BOOST_TEST(t.val() == 0x12345678UL); }, - [&](MQTT_NS::v5::property::topic_alias const& t) { - BOOST_TEST(t.val() == 0x1234U); - }, [&](MQTT_NS::v5::property::response_topic const& t) { BOOST_TEST(t.val() == "response topic"); }, @@ -2604,6 +2620,7 @@ BOOST_AUTO_TEST_CASE( puback_prop ) { } using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); packet_id_t pid_pub; @@ -2768,6 +2785,7 @@ BOOST_AUTO_TEST_CASE( pubrec_rel_comp_prop ) { } using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); c->set_auto_pub_response(false); diff --git a/test/pubsub_no_strand.cpp b/test/pubsub_no_strand.cpp index cb8fbbfde..49fa7dbb0 100644 --- a/test/pubsub_no_strand.cpp +++ b/test/pubsub_no_strand.cpp @@ -43,6 +43,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { boost::asio::io_context ioc; auto c = MQTT_NS::make_client_no_strand(ioc, broker_url, broker_notls_port); using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -168,6 +169,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos0 ) { boost::asio::io_context ioc; auto c = MQTT_NS::make_client_no_strand(ioc, broker_url, broker_notls_port); using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -293,6 +295,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos0 ) { boost::asio::io_context ioc; auto c = MQTT_NS::make_client_no_strand(ioc, broker_url, broker_notls_port); using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -420,6 +423,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos1 ) { boost::asio::io_context ioc; auto c = MQTT_NS::make_client_no_strand(ioc, broker_url, broker_notls_port); using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -542,6 +546,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos1 ) { boost::asio::io_context ioc; auto c = MQTT_NS::make_client_no_strand(ioc, broker_url, broker_notls_port); using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -676,6 +681,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos1 ) { boost::asio::io_context ioc; auto c = MQTT_NS::make_client_no_strand(ioc, broker_url, broker_notls_port); using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -813,6 +819,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos2 ) { boost::asio::io_context ioc; auto c = MQTT_NS::make_client_no_strand(ioc, broker_url, broker_notls_port); using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -936,6 +943,7 @@ BOOST_AUTO_TEST_CASE( pub_qos1_sub_qos2 ) { boost::asio::io_context ioc; auto c = MQTT_NS::make_client_no_strand(ioc, broker_url, broker_notls_port); using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -1071,6 +1079,7 @@ BOOST_AUTO_TEST_CASE( pub_qos2_sub_qos2 ) { boost::asio::io_context ioc; auto c = MQTT_NS::make_client_no_strand(ioc, broker_url, broker_notls_port); using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_pub; @@ -1208,6 +1217,7 @@ BOOST_AUTO_TEST_CASE( publish_function ) { boost::asio::io_context ioc; auto c = MQTT_NS::make_client_no_strand(ioc, broker_url, broker_notls_port); using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; diff --git a/test/remaining_length.cpp b/test/remaining_length.cpp index b34485c59..de6a5e082 100644 --- a/test/remaining_length.cpp +++ b/test/remaining_length.cpp @@ -26,6 +26,7 @@ BOOST_AUTO_TEST_CASE( pub_sub_over_127 ) { test_contents.push_back(static_cast(i)); } + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -136,6 +137,7 @@ BOOST_AUTO_TEST_CASE( pub_sub_over_16384 ) { test_contents.push_back(static_cast(i & 0xff)); } + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -248,6 +250,7 @@ BOOST_AUTO_TEST_CASE( pub_sub_over_2097152 ) { test_contents.push_back(static_cast(i % 0xff)); } + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; diff --git a/test/resend.cpp b/test/resend.cpp index 8fda8e77f..2ed405d76 100644 --- a/test/resend.cpp +++ b/test/resend.cpp @@ -20,7 +20,7 @@ BOOST_AUTO_TEST_CASE( publish_qos1 ) { c->set_client_id("cid1"); c->set_clean_session(true); - std::uint16_t pid_pub; + packet_id_t pid_pub; boost::asio::steady_timer tim(ioc); @@ -244,7 +244,7 @@ BOOST_AUTO_TEST_CASE( publish_qos2 ) { c->set_client_id("cid1"); c->set_clean_session(true); - std::uint16_t pid_pub; + packet_id_t pid_pub; boost::asio::steady_timer tim(ioc); @@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE( pubrel_qos2 ) { c->set_client_id("cid1"); c->set_clean_session(true); - std::uint16_t pid_pub; + packet_id_t pid_pub; boost::asio::steady_timer tim(ioc); @@ -633,7 +633,7 @@ BOOST_AUTO_TEST_CASE( publish_pubrel_qos2 ) { c->set_client_id("cid1"); c->set_clean_session(true); - std::uint16_t pid_pub; + packet_id_t pid_pub; boost::asio::steady_timer tim(ioc); @@ -842,8 +842,8 @@ BOOST_AUTO_TEST_CASE( multi_publish_qos1 ) { c->set_client_id("cid1"); c->set_clean_session(true); - std::uint16_t pid_pub1; - std::uint16_t pid_pub2; + packet_id_t pid_pub1; + packet_id_t pid_pub2; boost::asio::steady_timer tim(ioc); @@ -1028,7 +1028,7 @@ BOOST_AUTO_TEST_CASE( publish_session_before_expire ) { c->set_client_id("cid1"); c->set_clean_session(true); - std::uint16_t pid_pub; + packet_id_t pid_pub; boost::asio::steady_timer tim(ioc); @@ -1162,7 +1162,7 @@ BOOST_AUTO_TEST_CASE( publish_session_after_expire ) { c->set_client_id("cid1"); c->set_clean_session(true); - std::uint16_t pid_pub; + packet_id_t pid_pub; boost::asio::steady_timer tim(ioc); diff --git a/test/retain_1.cpp b/test/retain_1.cpp index ae311900a..943e92459 100644 --- a/test/retain_1.cpp +++ b/test/retain_1.cpp @@ -18,6 +18,7 @@ using namespace MQTT_NS::literals; BOOST_AUTO_TEST_CASE( simple ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -196,6 +197,7 @@ BOOST_AUTO_TEST_CASE( simple ) { BOOST_AUTO_TEST_CASE( overwrite ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; diff --git a/test/retain_2.cpp b/test/retain_2.cpp index 72d5c9de3..59341e20f 100644 --- a/test/retain_2.cpp +++ b/test/retain_2.cpp @@ -18,6 +18,7 @@ using namespace MQTT_NS::literals; BOOST_AUTO_TEST_CASE( retain_and_publish ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -267,6 +268,7 @@ BOOST_AUTO_TEST_CASE( prop ) { } using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); std::uint16_t pid_sub; @@ -363,7 +365,12 @@ BOOST_AUTO_TEST_CASE( prop ) { BOOST_TEST(topic == "topic1"); BOOST_TEST(contents == "retained_contents"); - BOOST_TEST(props.size() == prop_size); + // -1 means TopicAlias + // TopicAlias is not forwarded + // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901113 + // A receiver MUST NOT carry forward any Topic Alias mappings from + // one Network Connection to another [MQTT-3.3.2-7]. + BOOST_TEST(props.size() == prop_size - 1); for (auto const& p : props) { MQTT_NS::visit( @@ -374,9 +381,6 @@ BOOST_AUTO_TEST_CASE( prop ) { [&](MQTT_NS::v5::property::message_expiry_interval const& t) { BOOST_TEST(t.val() == 0x12345678UL); }, - [&](MQTT_NS::v5::property::topic_alias const& t) { - BOOST_TEST(t.val() == 0x1234U); - }, [&](MQTT_NS::v5::property::response_topic const& t) { BOOST_TEST(t.val() == "response topic"); }, diff --git a/test/sub.cpp b/test/sub.cpp index 5a3475203..53195c49e 100644 --- a/test/sub.cpp +++ b/test/sub.cpp @@ -20,6 +20,7 @@ using namespace std::literals::string_literals; BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_single ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -111,6 +112,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_single ) { BOOST_AUTO_TEST_CASE( sub_v5_options ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -205,6 +207,7 @@ BOOST_AUTO_TEST_CASE( sub_v5_options ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_arg ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -308,6 +311,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_arg ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_vec ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -415,6 +419,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_vec ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_single_async ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -506,6 +511,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_single_async ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_arg_async ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -623,6 +629,7 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_arg_async ) { BOOST_AUTO_TEST_CASE( pub_qos0_sub_string_multi_vec_async ) { auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -747,6 +754,7 @@ BOOST_AUTO_TEST_CASE( sub_unsub_prop ) { } using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); @@ -889,6 +897,7 @@ BOOST_AUTO_TEST_CASE( suback_unsuback_prop ) { } using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); c->set_clean_session(true); diff --git a/test/test_broker.hpp b/test/test_broker.hpp index 739fcfa4a..2df7b533f 100644 --- a/test/test_broker.hpp +++ b/test/test_broker.hpp @@ -104,7 +104,6 @@ class test_broker { (MQTT_NS::error_code ec){ con_sp_t sp = wp.lock(); BOOST_ASSERT(sp); - // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#S4_13_Errors if (ec == boost::system::errc::protocol_error) { if (sp->connected()) { @@ -114,7 +113,6 @@ class test_broker { sp->connack(false, MQTT_NS::v5::connect_reason_code::protocol_error); } } - close_proc(MQTT_NS::force_move(sp), true); }); @@ -565,6 +563,22 @@ class test_broker { BOOST_ASSERT(act_sess_it == act_sess_idx.find(client_id)); } else { + + if (non_act_sess_it->topic_alias_recv) { + + // TopicAlias lifetime is the same as Session lifetime + // It is different from MQTT v5 spec but practical choice. + // See + // https://lists.oasis-open.org/archives/mqtt-comment/202009/msg00000.html + // + // When minimum boost requirement will update to 1.74.0, + // the following code is updated to move semantics + // using multi_index extract functionality. + // See + // https://github.com/boostorg/multi_index/commit/e69466039d64dd49ecf8fc8a181d9f24d5f82386 + ep.restore_topic_alias_recv_container(non_act_sess_it->topic_alias_recv.value()); + } + session_state state; non_act_sess_idx.modify(non_act_sess_it, [&](session_state & val) { state = val; }, @@ -648,19 +662,9 @@ class test_broker { ) { if (delay_disconnect_) { tim_disconnect_.expires_after(delay_disconnect_.value()); - tim_disconnect_.async_wait( - [&, wp = con_wp_t(MQTT_NS::force_move(spep))](MQTT_NS::error_code ec) { - if (!ec) { - if (con_sp_t sp = wp.lock()) { - close_proc(MQTT_NS::force_move(sp), false); - } - } - } - ); - } - else { - close_proc(MQTT_NS::force_move(spep), false); + tim_disconnect_.wait(); } + close_proc(MQTT_NS::force_move(spep), false); } bool publish_handler( @@ -671,12 +675,31 @@ class test_broker { MQTT_NS::buffer contents, MQTT_NS::v5::properties props) { + MQTT_NS::v5::properties forward_props; + + for (auto&& p : props) { + MQTT_NS::visit( + MQTT_NS::make_lambda_visitor( + [](MQTT_NS::v5::property::topic_alias&&) { + // TopicAlias is not forwarded + // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901113 + // A receiver MUST NOT carry forward any Topic Alias mappings from + // one Network Connection to another [MQTT-3.3.2-7]. + }, + [&forward_props](auto&& p) { + forward_props.push_back(MQTT_NS::force_move(p)); + } + ), + MQTT_NS::force_move(p) + ); + } + auto& ep = *spep; do_publish( MQTT_NS::force_move(topic_name), MQTT_NS::force_move(contents), pubopts.get_qos() | pubopts.get_retain(), // remove dup flag - MQTT_NS::force_move(props)); + MQTT_NS::force_move(forward_props)); switch (ep.get_protocol_version()) { case MQTT_NS::protocol_version::v3_1_1: @@ -1042,6 +1065,12 @@ class test_broker { ); } + // TopicAlias lifetime is the same as Session lifetime + // It is different from MQTT v5 spec but practical choice. + // See + // https://lists.oasis-open.org/archives/mqtt-comment/202009/msg00000.html + state.topic_alias_recv = ep.get_topic_alias_recv_container(); + auto const& ret = non_active_sessions_.insert(MQTT_NS::force_move(state)); (void)ret; BOOST_ASSERT(ret.second); @@ -1140,6 +1169,7 @@ class test_broker { MQTT_NS::optional will_delay; MQTT_NS::optional session_expiry_interval; std::shared_ptr tim_session_expiry; + MQTT_NS::optional topic_alias_recv; }; // The mi_active_sessions container holds the relevant data about an active connection with the broker. diff --git a/test/topic_alias_recv.cpp b/test/topic_alias_recv.cpp new file mode 100644 index 000000000..fc0948caf --- /dev/null +++ b/test/topic_alias_recv.cpp @@ -0,0 +1,684 @@ +// Copyright Takatoshi Kondo 2020 +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "test_main.hpp" +#include "combi_test.hpp" +#include "checker.hpp" +#include "test_util.hpp" +#include "global_fixture.hpp" + +#include + +BOOST_AUTO_TEST_SUITE(test_topic_alias_recv) + +using namespace MQTT_NS::literals; + +BOOST_AUTO_TEST_CASE( pubsub ) { + auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { + + if (c->get_protocol_version() != MQTT_NS::protocol_version::v5) { + finish(); + return; + } + + using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); + c->set_clean_session(true); + + checker chk = { + // connect + cont("h_connack"), + // subscribe topic1 QoS0 + cont("h_suback"), + // publish topic1 alias1 QoS0 + // publish alias1 QoS0 + cont("h_publsh1"), + cont("h_publish2"), + cont("h_unsuback"), + // disconnect + cont("h_close"), + }; + + switch (c->get_protocol_version()) { + case MQTT_NS::protocol_version::v5: + c->set_v5_connack_handler( + [&chk, &c] + (bool sp, MQTT_NS::v5::connect_reason_code connack_return_code, MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_connack"); + BOOST_TEST(sp == false); + BOOST_TEST(connack_return_code == MQTT_NS::v5::connect_reason_code::success); + c->subscribe("topic1", MQTT_NS::qos::at_most_once); + return true; + }); + c->set_v5_puback_handler( + [] + (packet_id_t, MQTT_NS::v5::puback_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_pubrec_handler( + [] + (packet_id_t, MQTT_NS::v5::pubrec_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_pubcomp_handler( + [] + (packet_id_t, MQTT_NS::v5::pubcomp_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_suback_handler( + [&chk, &c] + (packet_id_t packet_id, std::vector reasons, MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_suback"); + BOOST_TEST(reasons.size() == 1U); + BOOST_TEST(reasons[0] == MQTT_NS::v5::suback_reason_code::granted_qos_0); + // register topic alias + c->publish( + "topic1", + "topic1_contents_1", + MQTT_NS::qos::at_most_once, + MQTT_NS::v5::properties { + MQTT_NS::v5::property::topic_alias(0x1U) + } + ); + // use topic alias + c->publish( + "", + "topic1_contents_2", + MQTT_NS::qos::at_most_once, + MQTT_NS::v5::properties { + MQTT_NS::v5::property::topic_alias(0x1U) + } + ); + return true; + }); + c->set_v5_unsuback_handler( + [&chk, &c] + (packet_id_t packet_id, std::vector reasons, MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_unsuback"); + BOOST_TEST(reasons.size() == 1U); + BOOST_TEST(reasons[0] == MQTT_NS::v5::unsuback_reason_code::success); + c->disconnect(); + return true; + }); + c->set_v5_publish_handler( + [&chk, &c] + (MQTT_NS::optional packet_id, + MQTT_NS::publish_options pubopts, + MQTT_NS::buffer topic, + MQTT_NS::buffer contents, + MQTT_NS::v5::properties /*props*/) { + auto ret = chk.match( + "h_suback", + [&] { + MQTT_CHK("h_publsh1"); + BOOST_TEST(pubopts.get_dup() == MQTT_NS::dup::no); + BOOST_TEST(pubopts.get_qos() == MQTT_NS::qos::at_most_once); + BOOST_TEST(pubopts.get_retain() == MQTT_NS::retain::no); + BOOST_CHECK(!packet_id); + BOOST_TEST(topic == "topic1"); + BOOST_TEST(contents == "topic1_contents_1"); + }, + "h_publsh1", + [&] { + MQTT_CHK("h_publish2"); + BOOST_TEST(pubopts.get_dup() == MQTT_NS::dup::no); + BOOST_TEST(pubopts.get_qos() == MQTT_NS::qos::at_most_once); + BOOST_TEST(pubopts.get_retain() == MQTT_NS::retain::no); + BOOST_CHECK(!packet_id); + BOOST_TEST(topic == "topic1"); + BOOST_TEST(contents == "topic1_contents_2"); + c->unsubscribe("topic1"); + } + ); + BOOST_TEST(ret); + return true; + }); + break; + default: + BOOST_CHECK(false); + break; + } + + c->set_close_handler( + [&chk, &finish] + () { + MQTT_CHK("h_close"); + finish(); + }); + c->set_error_handler( + [] + (MQTT_NS::error_code) { + BOOST_CHECK(false); + }); + c->set_pub_res_sent_handler( + [] + (packet_id_t) { + BOOST_CHECK(false); + }); + c->connect(); + ioc.run(); + BOOST_TEST(chk.all()); + }; + do_combi_test_sync(test); +} + +BOOST_AUTO_TEST_CASE( overwrite ) { + auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { + + if (c->get_protocol_version() != MQTT_NS::protocol_version::v5) { + finish(); + return; + } + + using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); + c->set_clean_session(true); + + checker chk = { + // connect + cont("h_connack"), + // subscribe topic1 QoS0 + cont("h_suback1"), + cont("h_suback2"), + // publish topic1 alias1 QoS0 + // publish topic2 alias1 QoS0 + // publish alias1 QoS0 + cont("h_publish1"), + cont("h_publish2"), + cont("h_publish3"), + cont("h_unsuback1"), + cont("h_unsuback2"), + // disconnect + cont("h_close"), + }; + + switch (c->get_protocol_version()) { + case MQTT_NS::protocol_version::v5: + c->set_v5_connack_handler( + [&chk, &c] + (bool sp, MQTT_NS::v5::connect_reason_code connack_return_code, MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_connack"); + BOOST_TEST(sp == false); + BOOST_TEST(connack_return_code == MQTT_NS::v5::connect_reason_code::success); + c->subscribe("topic1", MQTT_NS::qos::at_most_once); + c->subscribe("topic2", MQTT_NS::qos::at_most_once); + return true; + }); + c->set_v5_puback_handler( + [] + (packet_id_t, MQTT_NS::v5::puback_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_pubrec_handler( + [] + (packet_id_t, MQTT_NS::v5::pubrec_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_pubcomp_handler( + [] + (packet_id_t, MQTT_NS::v5::pubcomp_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_suback_handler( + [&chk, &c] + (packet_id_t packet_id, std::vector reasons, MQTT_NS::v5::properties /*props*/) { + auto ret = chk.match( + "h_connack", + [&] { + MQTT_CHK("h_suback1"); + }, + "h_suback1", + [&] { + MQTT_CHK("h_suback2"); + BOOST_TEST(reasons.size() == 1U); + BOOST_TEST(reasons[0] == MQTT_NS::v5::suback_reason_code::granted_qos_0); + // register topic alias + c->publish( + "topic1", + "topic1_contents_1", + MQTT_NS::qos::at_most_once, + MQTT_NS::v5::properties { + MQTT_NS::v5::property::topic_alias(0x1U) + } + ); + // overwrite topic alias + c->publish( + "topic2", + "topic1_contents_2", + MQTT_NS::qos::at_most_once, + MQTT_NS::v5::properties { + MQTT_NS::v5::property::topic_alias(0x1U) + } + ); + // use topic alias + c->publish( + "", + "topic1_contents_3", + MQTT_NS::qos::at_most_once, + MQTT_NS::v5::properties { + MQTT_NS::v5::property::topic_alias(0x1U) + } + ); + } + ); + BOOST_TEST(ret); + return true; + }); + c->set_v5_unsuback_handler( + [&chk, &c] + (packet_id_t packet_id, std::vector reasons, MQTT_NS::v5::properties /*props*/) { + auto ret = chk.match( + "h_publish3", + [&] { + MQTT_CHK("h_unsuback1"); + BOOST_TEST(reasons.size() == 1U); + BOOST_TEST(reasons[0] == MQTT_NS::v5::unsuback_reason_code::success); + }, + "h_unsuback1", + [&] { + MQTT_CHK("h_unsuback2"); + BOOST_TEST(reasons.size() == 1U); + BOOST_TEST(reasons[0] == MQTT_NS::v5::unsuback_reason_code::success); + c->disconnect(); + } + ); + BOOST_TEST(ret); + return true; + }); + c->set_v5_publish_handler( + [&chk, &c] + (MQTT_NS::optional packet_id, + MQTT_NS::publish_options pubopts, + MQTT_NS::buffer topic, + MQTT_NS::buffer contents, + MQTT_NS::v5::properties /*props*/) { + auto ret = chk.match( + "h_suback1", + [&] { + MQTT_CHK("h_publish1"); + BOOST_TEST(pubopts.get_dup() == MQTT_NS::dup::no); + BOOST_TEST(pubopts.get_qos() == MQTT_NS::qos::at_most_once); + BOOST_TEST(pubopts.get_retain() == MQTT_NS::retain::no); + BOOST_CHECK(!packet_id); + BOOST_TEST(topic == "topic1"); + BOOST_TEST(contents == "topic1_contents_1"); + }, + "h_publish1", + [&] { + MQTT_CHK("h_publish2"); + BOOST_TEST(pubopts.get_dup() == MQTT_NS::dup::no); + BOOST_TEST(pubopts.get_qos() == MQTT_NS::qos::at_most_once); + BOOST_TEST(pubopts.get_retain() == MQTT_NS::retain::no); + BOOST_CHECK(!packet_id); + BOOST_TEST(topic == "topic2"); + BOOST_TEST(contents == "topic1_contents_2"); + }, + "h_publish2", + [&] { + MQTT_CHK("h_publish3"); + BOOST_TEST(pubopts.get_dup() == MQTT_NS::dup::no); + BOOST_TEST(pubopts.get_qos() == MQTT_NS::qos::at_most_once); + BOOST_TEST(pubopts.get_retain() == MQTT_NS::retain::no); + BOOST_CHECK(!packet_id); + BOOST_TEST(topic == "topic2"); + BOOST_TEST(contents == "topic1_contents_3"); + c->unsubscribe("topic1"); + c->unsubscribe("topic2"); + } + ); + BOOST_TEST(ret); + return true; + }); + break; + default: + BOOST_CHECK(false); + break; + } + + c->set_close_handler( + [&chk, &finish] + () { + MQTT_CHK("h_close"); + finish(); + }); + c->set_error_handler( + [] + (MQTT_NS::error_code) { + BOOST_CHECK(false); + }); + c->set_pub_res_sent_handler( + [] + (packet_id_t) { + BOOST_CHECK(false); + }); + c->connect(); + ioc.run(); + BOOST_TEST(chk.all()); + }; + do_combi_test_sync(test); +} + +BOOST_AUTO_TEST_CASE( no_entry ) { + auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { + + if (c->get_protocol_version() != MQTT_NS::protocol_version::v5) { + finish(); + return; + } + + using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); + c->set_clean_session(true); + + checker chk = { + // connect + cont("h_connack"), + // publish topic_alias1 QoS0 + cont("h_suback"), + // publish QoS0 + cont("h_disconnect"), + // disconnect + cont("h_error"), + }; + + switch (c->get_protocol_version()) { + case MQTT_NS::protocol_version::v5: + c->set_v5_connack_handler( + [&chk, &c] + (bool sp, MQTT_NS::v5::connect_reason_code connack_return_code, MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_connack"); + BOOST_TEST(sp == false); + BOOST_TEST(connack_return_code == MQTT_NS::v5::connect_reason_code::success); + c->subscribe("topic1", MQTT_NS::qos::at_most_once); + return true; + }); + c->set_v5_puback_handler( + [] + (packet_id_t, MQTT_NS::v5::puback_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_pubrec_handler( + [] + (packet_id_t, MQTT_NS::v5::pubrec_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_pubcomp_handler( + [] + (packet_id_t, MQTT_NS::v5::pubcomp_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_suback_handler( + [&chk, &c] + (packet_id_t packet_id, std::vector reasons, MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_suback"); + BOOST_TEST(reasons.size() == 1U); + BOOST_TEST(reasons[0] == MQTT_NS::v5::suback_reason_code::granted_qos_0); + // use no existing topic alias + c->publish( + "", + "topic1_contents", + MQTT_NS::qos::at_most_once, + MQTT_NS::v5::properties { + MQTT_NS::v5::property::topic_alias(0x1U) + } + ); + return true; + }); + c->set_v5_unsuback_handler( + [&chk] + (packet_id_t /*packet_id*/, std::vector /*reasons*/, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_publish_handler( + [&chk] + (MQTT_NS::optional /*packet_id*/, + MQTT_NS::publish_options /*pubopts*/, + MQTT_NS::buffer /*topic*/, + MQTT_NS::buffer /*contents*/, + MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_disconnect_handler( + [&chk] + (MQTT_NS::v5::disconnect_reason_code reason_code, MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_disconnect"); + BOOST_TEST(reason_code == MQTT_NS::v5::disconnect_reason_code::protocol_error); + } + ); + break; + default: + BOOST_CHECK(false); + break; + } + + c->set_close_handler( + [] + () { + BOOST_CHECK(false); + }); + c->set_error_handler( + [&chk, &finish] + (MQTT_NS::error_code) { + MQTT_CHK("h_error"); + finish(); + }); + c->set_pub_res_sent_handler( + [] + (packet_id_t) { + BOOST_CHECK(false); + }); + c->connect(); + ioc.run(); + BOOST_TEST(chk.all()); + }; + do_combi_test_sync(test); +} + +BOOST_AUTO_TEST_CASE( resend_publish ) { + auto test = [](boost::asio::io_context& ioc, auto& c, auto finish, auto& /*b*/) { + + if (c->get_protocol_version() != MQTT_NS::protocol_version::v5) { + finish(); + return; + } + + using packet_id_t = typename std::remove_reference_t::packet_id_t; + c->set_client_id("cid1"); + c->set_clean_session(true); + + boost::asio::steady_timer tim(ioc); + + checker chk = { + cont("start"), + // connect + cont("h_connack1"), + // disconnect + cont("h_close1"), + // connect + cont("h_connack2"), + // subscribe topic1 QoS0 + cont("h_suback"), + // publish topic1 alias1 QoS0 + // publish alias1 QoS1 + deps("h_error", "h_suback"), + cont("h_connack3"), + cont("h_puback"), + deps("h_publish", "h_connack3"), + cont("h_unsuback"), + // disconnect + cont("h_close2"), + }; + + switch (c->get_protocol_version()) { + case MQTT_NS::protocol_version::v5: + c->set_v5_connack_handler( + [&chk, &c] + (bool sp, MQTT_NS::v5::connect_reason_code connack_return_code, MQTT_NS::v5::properties /*props*/) { + auto ret = chk.match( + "start", + [&] { + MQTT_CHK("h_connack1"); + BOOST_TEST(sp == false); + c->disconnect(); + }, + "h_close1", + [&] { + MQTT_CHK("h_connack2"); + BOOST_TEST(sp == false); + BOOST_TEST(connack_return_code == MQTT_NS::v5::connect_reason_code::success); + c->subscribe("topic1", MQTT_NS::qos::at_least_once); + }, + "h_error", + [&] { + MQTT_CHK("h_connack3"); + BOOST_TEST(sp == true); + BOOST_TEST(connack_return_code == MQTT_NS::v5::connect_reason_code::success); + } + ); + return true; + }); + c->set_v5_puback_handler( + [&chk, &c] + (packet_id_t packet_id, MQTT_NS::v5::puback_reason_code, MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_puback"); + c->disconnect(); + return true; + }); + c->set_v5_pubrec_handler( + [] + (packet_id_t, MQTT_NS::v5::pubrec_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_pubcomp_handler( + [] + (packet_id_t, MQTT_NS::v5::pubcomp_reason_code, MQTT_NS::v5::properties /*props*/) { + BOOST_CHECK(false); + return true; + }); + c->set_v5_suback_handler( + [&chk, &c] + (packet_id_t packet_id, std::vector reasons, MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_suback"); + BOOST_TEST(reasons.size() == 1U); + BOOST_TEST(reasons[0] == MQTT_NS::v5::suback_reason_code::granted_qos_1); + // register topic alias + c->publish( + "topic1", + "topic1_contents_1", + MQTT_NS::qos::at_most_once, + MQTT_NS::v5::properties { + MQTT_NS::v5::property::topic_alias(0x1U) + } + ); + // use topic alias + c->publish( + "", + "topic1_contents_2", + MQTT_NS::qos::at_least_once, + MQTT_NS::v5::properties { + MQTT_NS::v5::property::topic_alias(0x1U) + } + ); + + // 1st publish is lost because QoS0 but topic_alias is registered + // 2nd publish will be resent in the future. + // However, it contains empty topic and topic_alias. + // mqtt_cpp keeps topic_alias map's life time as the same as session lifetime + // so resend would be successfully finished. + // See https://lists.oasis-open.org/archives/mqtt-comment/202009/msg00000.html + c->force_disconnect(); + + return true; + }); + c->set_v5_unsuback_handler( + [&chk, &c] + (packet_id_t packet_id, std::vector reasons, MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_unsuback"); + BOOST_TEST(reasons.size() == 1U); + BOOST_TEST(reasons[0] == MQTT_NS::v5::unsuback_reason_code::success); + c->disconnect(); + return true; + }); + c->set_v5_publish_handler( + [&chk, &c] + (MQTT_NS::optional packet_id, + MQTT_NS::publish_options pubopts, + MQTT_NS::buffer topic, + MQTT_NS::buffer contents, + MQTT_NS::v5::properties /*props*/) { + MQTT_CHK("h_publish"); + BOOST_TEST(pubopts.get_dup() == MQTT_NS::dup::no); + BOOST_TEST(pubopts.get_qos() == MQTT_NS::qos::at_least_once); + BOOST_TEST(pubopts.get_retain() == MQTT_NS::retain::no); + BOOST_TEST(topic == "topic1"); + BOOST_TEST(contents == "topic1_contents_2"); + c->unsubscribe("topic1"); + return true; + }); + break; + default: + BOOST_CHECK(false); + break; + } + + c->set_close_handler( + [&chk, &c, &finish] + () { + auto ret = chk.match( + "h_connack1", + [&] { + MQTT_CHK("h_close1"); + connect_no_clean(c); + }, + "h_puback", + [&] { + MQTT_CHK("h_close2"); + finish(); + } + ); + BOOST_TEST(ret); + }); + c->set_error_handler( + [&chk, &c, &tim] + (MQTT_NS::error_code) { + MQTT_CHK("h_error"); + // TCP level disconnection detecting timing is unpredictable. + // Sometimes broker first, sometimes the client (this test) first. + // This test assume that the broker detects first, so I set timer. + // If client detect the disconnection first, then reconnect with + // existing client id. And it is overwritten at broker. + // Then error handler in the broker called, assertion failed due to + // no corresponding connection exists + tim.expires_after(std::chrono::milliseconds(100)); + tim.async_wait( + [&c] (MQTT_NS::error_code ec) { + BOOST_ASSERT( ! ec); + connect_no_clean(c); + } + ); + }); + MQTT_CHK("start"); + c->connect(); + ioc.run(); + BOOST_TEST(chk.all()); + }; + do_combi_test_sync(test); +} + + +BOOST_AUTO_TEST_SUITE_END()