mirror of
https://github.com/boostorg/mqtt5.git
synced 2025-07-31 13:07:37 +02:00
add a notification in receive channel when session resets
Summary: resolves T13152 Reviewers: ivica Reviewed By: ivica Subscribers: miljen, iljazovic Maniphest Tasks: T13152 Differential Revision: https://repo.mireo.local/D26679
This commit is contained in:
@@ -30,6 +30,12 @@ may complete with, along with the reasons for their occurrence.
|
|||||||
the current operation due to the exhaustion of the available identifiers.
|
the current operation due to the exhaustion of the available identifiers.
|
||||||
This occurs when there are 65535 outgoing Packets awaiting their responses.
|
This occurs when there are 65535 outgoing Packets awaiting their responses.
|
||||||
]]
|
]]
|
||||||
|
[[`async_mqtt5::client::error::session_expired`][
|
||||||
|
The Client has established a successful connection with a Broker, but either the session does not exist or has expired.
|
||||||
|
In cases where the Client had previously set up subscriptions to Topics, these subscriptions are also expired.
|
||||||
|
Therefore, the Client should re-subscribe.
|
||||||
|
This error code is exclusive to completion handlers associated with [refmem mqtt_client async_receive] calls.
|
||||||
|
]]
|
||||||
[[`async_mqtt5::client::error::qos_not_supported`] [
|
[[`async_mqtt5::client::error::qos_not_supported`] [
|
||||||
The Client has attempted to publish an Application Message with __QOS__ higher
|
The Client has attempted to publish an Application Message with __QOS__ higher
|
||||||
than the Maximum __QOS__ specified by the Server.
|
than the Maximum __QOS__ specified by the Server.
|
||||||
|
@@ -35,11 +35,32 @@ struct credentials {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class session_state {
|
||||||
|
uint8_t _flags = 0b00;
|
||||||
|
|
||||||
|
static constexpr uint8_t session_present_flag = 0b01;
|
||||||
|
public:
|
||||||
|
void session_present(bool present) {
|
||||||
|
return update_flag(present, session_present_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool session_present() const { return _flags & session_present_flag; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
void update_flag(bool set, uint8_t flag) {
|
||||||
|
if (set)
|
||||||
|
_flags |= flag;
|
||||||
|
else
|
||||||
|
_flags &= ~flag;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct mqtt_context {
|
struct mqtt_context {
|
||||||
credentials credentials;
|
credentials credentials;
|
||||||
std::optional<will> will;
|
std::optional<will> will;
|
||||||
connect_props co_props;
|
connect_props co_props;
|
||||||
connack_props ca_props;
|
connack_props ca_props;
|
||||||
|
session_state session_state;
|
||||||
any_authenticator authenticator;
|
any_authenticator authenticator;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -61,6 +61,9 @@ enum class error : int {
|
|||||||
/** There are no more available Packet Identifiers to use. */
|
/** There are no more available Packet Identifiers to use. */
|
||||||
pid_overrun,
|
pid_overrun,
|
||||||
|
|
||||||
|
/** The Client's session does not exist or it has expired. */
|
||||||
|
session_expired,
|
||||||
|
|
||||||
// publish
|
// publish
|
||||||
/** The Server does not support the specified \ref qos_e. */
|
/** The Server does not support the specified \ref qos_e. */
|
||||||
qos_not_supported,
|
qos_not_supported,
|
||||||
@@ -79,6 +82,8 @@ inline std::string client_error_to_string(error err) {
|
|||||||
switch (err) {
|
switch (err) {
|
||||||
case malformed_packet:
|
case malformed_packet:
|
||||||
return "Malformed packet has been detected";
|
return "Malformed packet has been detected";
|
||||||
|
case session_expired:
|
||||||
|
return "The Client's session does not exist or it has expired. ";
|
||||||
case pid_overrun:
|
case pid_overrun:
|
||||||
return "There are no more available Packet Identifiers to use.";
|
return "There are no more available Packet Identifiers to use.";
|
||||||
case qos_not_supported:
|
case qos_not_supported:
|
||||||
@@ -401,7 +406,6 @@ constexpr reason_code session_taken_over { 0x8e };
|
|||||||
/** The Topic Filter is not malformed, but it is not accepted. */
|
/** The Topic Filter is not malformed, but it is not accepted. */
|
||||||
constexpr reason_code topic_filter_invalid { 0x8f };
|
constexpr reason_code topic_filter_invalid { 0x8f };
|
||||||
|
|
||||||
|
|
||||||
/** The Topic Name is not malformed, but it is not accepted. */
|
/** The Topic Name is not malformed, but it is not accepted. */
|
||||||
constexpr reason_code topic_name_invalid { 0x90 };
|
constexpr reason_code topic_name_invalid { 0x90 };
|
||||||
|
|
||||||
@@ -468,7 +472,7 @@ namespace detail {
|
|||||||
using enum category;
|
using enum category;
|
||||||
|
|
||||||
template <category cat>
|
template <category cat>
|
||||||
inline std::pair<reason_code*, size_t> valid_codes()
|
std::pair<reason_code*, size_t> valid_codes()
|
||||||
requires (cat == connack) {
|
requires (cat == connack) {
|
||||||
static reason_code valid_codes[] = {
|
static reason_code valid_codes[] = {
|
||||||
success, unspecified_error, malformed_packet,
|
success, unspecified_error, malformed_packet,
|
||||||
@@ -487,7 +491,7 @@ requires (cat == connack) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <category cat>
|
template <category cat>
|
||||||
inline std::pair<reason_code*, size_t> valid_codes()
|
std::pair<reason_code*, size_t> valid_codes()
|
||||||
requires (cat == auth) {
|
requires (cat == auth) {
|
||||||
static reason_code valid_codes[] = {
|
static reason_code valid_codes[] = {
|
||||||
success, continue_authentication
|
success, continue_authentication
|
||||||
@@ -497,7 +501,7 @@ requires (cat == auth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <category cat>
|
template <category cat>
|
||||||
inline std::pair<reason_code*, size_t> valid_codes()
|
std::pair<reason_code*, size_t> valid_codes()
|
||||||
requires (cat == puback || cat == pubrec) {
|
requires (cat == puback || cat == pubrec) {
|
||||||
static reason_code valid_codes[] = {
|
static reason_code valid_codes[] = {
|
||||||
success, no_matching_subscribers, unspecified_error,
|
success, no_matching_subscribers, unspecified_error,
|
||||||
@@ -510,7 +514,7 @@ requires (cat == puback || cat == pubrec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <category cat>
|
template <category cat>
|
||||||
inline std::pair<reason_code*, size_t> valid_codes()
|
std::pair<reason_code*, size_t> valid_codes()
|
||||||
requires (cat == pubrel || cat == pubcomp) {
|
requires (cat == pubrel || cat == pubcomp) {
|
||||||
static reason_code valid_codes[] = {
|
static reason_code valid_codes[] = {
|
||||||
success, packet_id_not_found
|
success, packet_id_not_found
|
||||||
@@ -520,7 +524,7 @@ requires (cat == pubrel || cat == pubcomp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <category cat>
|
template <category cat>
|
||||||
inline std::pair<reason_code*, size_t> valid_codes()
|
std::pair<reason_code*, size_t> valid_codes()
|
||||||
requires (cat == suback) {
|
requires (cat == suback) {
|
||||||
static reason_code valid_codes[] = {
|
static reason_code valid_codes[] = {
|
||||||
granted_qos_0, granted_qos_1, granted_qos_2,
|
granted_qos_0, granted_qos_1, granted_qos_2,
|
||||||
@@ -536,7 +540,7 @@ requires (cat == suback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <category cat>
|
template <category cat>
|
||||||
inline std::pair<reason_code*, size_t> valid_codes()
|
std::pair<reason_code*, size_t> valid_codes()
|
||||||
requires (cat == unsuback) {
|
requires (cat == unsuback) {
|
||||||
static reason_code valid_codes[] = {
|
static reason_code valid_codes[] = {
|
||||||
success, no_subscription_existed,
|
success, no_subscription_existed,
|
||||||
@@ -549,7 +553,7 @@ requires (cat == unsuback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <category cat>
|
template <category cat>
|
||||||
inline std::pair<reason_code*, size_t> valid_codes()
|
std::pair<reason_code*, size_t> valid_codes()
|
||||||
requires (cat == disconnect) {
|
requires (cat == disconnect) {
|
||||||
static reason_code valid_codes[] = {
|
static reason_code valid_codes[] = {
|
||||||
normal_disconnection, unspecified_error,
|
normal_disconnection, unspecified_error,
|
||||||
@@ -578,7 +582,7 @@ requires (cat == disconnect) {
|
|||||||
|
|
||||||
|
|
||||||
template <reason_codes::category cat>
|
template <reason_codes::category cat>
|
||||||
inline std::optional<reason_code> to_reason_code(uint8_t code) {
|
std::optional<reason_code> to_reason_code(uint8_t code) {
|
||||||
auto [ptr, len] = reason_codes::detail::valid_codes<cat>();
|
auto [ptr, len] = reason_codes::detail::valid_codes<cat>();
|
||||||
auto it = std::lower_bound(ptr, ptr + len, reason_code(code));
|
auto it = std::lower_bound(ptr, ptr + len, reason_code(code));
|
||||||
|
|
||||||
|
@@ -125,6 +125,7 @@ public:
|
|||||||
duration wait_for, CompletionCondition cc
|
duration wait_for, CompletionCondition cc
|
||||||
) {
|
) {
|
||||||
if (ec == asio::error::try_again) {
|
if (ec == asio::error::try_again) {
|
||||||
|
_svc.update_session_state();
|
||||||
_svc._async_sender.resend();
|
_svc._async_sender.resend();
|
||||||
_data_span = { _read_buff.cend(), _read_buff.cend() };
|
_data_span = { _read_buff.cend(), _read_buff.cend() };
|
||||||
return perform(wait_for, std::move(cc));
|
return perform(wait_for, std::move(cc));
|
||||||
|
@@ -73,7 +73,7 @@ class async_sender {
|
|||||||
serial_num_t _last_serial_num { 0 };
|
serial_num_t _last_serial_num { 0 };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
async_sender(ClientService& svc) : _svc(svc) {}
|
explicit async_sender(ClientService& svc) : _svc(svc) {}
|
||||||
|
|
||||||
using executor_type = typename client_service::executor_type;
|
using executor_type = typename client_service::executor_type;
|
||||||
executor_type get_executor() const noexcept {
|
executor_type get_executor() const noexcept {
|
||||||
@@ -145,6 +145,7 @@ public:
|
|||||||
_write_in_progress = false;
|
_write_in_progress = false;
|
||||||
|
|
||||||
if (ec == asio::error::try_again) {
|
if (ec == asio::error::try_again) {
|
||||||
|
_svc.update_session_state();
|
||||||
_write_queue.insert(
|
_write_queue.insert(
|
||||||
_write_queue.begin(),
|
_write_queue.begin(),
|
||||||
std::make_move_iterator(write_queue.begin()),
|
std::make_move_iterator(write_queue.begin()),
|
||||||
|
@@ -28,7 +28,7 @@ class stream_context<StreamType, TlsContext> {
|
|||||||
mqtt_context _mqtt_context;
|
mqtt_context _mqtt_context;
|
||||||
tls_context_type _tls_context;
|
tls_context_type _tls_context;
|
||||||
public:
|
public:
|
||||||
stream_context(TlsContext tls_context) :
|
explicit stream_context(TlsContext tls_context) :
|
||||||
_tls_context(std::move(tls_context))
|
_tls_context(std::move(tls_context))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ requires (!has_tls_layer<StreamType>)
|
|||||||
class stream_context<StreamType, std::monostate> {
|
class stream_context<StreamType, std::monostate> {
|
||||||
mqtt_context _mqtt_context;
|
mqtt_context _mqtt_context;
|
||||||
public:
|
public:
|
||||||
stream_context(std::monostate) {}
|
explicit stream_context(std::monostate) {}
|
||||||
|
|
||||||
mqtt_context& mqtt_context() {
|
mqtt_context& mqtt_context() {
|
||||||
return _mqtt_context;
|
return _mqtt_context;
|
||||||
@@ -289,6 +289,15 @@ public:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void update_session_state() {
|
||||||
|
auto& session_state = _stream_context.mqtt_context().session_state;
|
||||||
|
if (!session_state.session_present()) {
|
||||||
|
channel_store_error(client::error::session_expired);
|
||||||
|
_replies.clear_pending_pubrels();
|
||||||
|
session_state.session_present(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool channel_store(decoders::publish_message message) {
|
bool channel_store(decoders::publish_message message) {
|
||||||
auto& [topic, packet_id, flags, props, payload] = message;
|
auto& [topic, packet_id, flags, props, payload] = message;
|
||||||
return _rec_channel.try_send(
|
return _rec_channel.try_send(
|
||||||
@@ -297,6 +306,10 @@ public:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool channel_store_error(error_code ec) {
|
||||||
|
return _rec_channel.try_send(ec, std::string {}, std::string {}, publish_props {});
|
||||||
|
}
|
||||||
|
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
decltype(auto) async_channel_receive(CompletionToken&& token) {
|
decltype(auto) async_channel_receive(CompletionToken&& token) {
|
||||||
// sig = void (error_code, std::string, std::string, publish_props)
|
// sig = void (error_code, std::string, std::string, publish_props)
|
||||||
|
@@ -281,6 +281,7 @@ public:
|
|||||||
const auto& [session_present, reason_code, ca_props] = *rv;
|
const auto& [session_present, reason_code, ca_props] = *rv;
|
||||||
|
|
||||||
_ctx.ca_props = ca_props;
|
_ctx.ca_props = ca_props;
|
||||||
|
_ctx.session_state.session_present(session_present);
|
||||||
|
|
||||||
// TODO: session_present logic
|
// TODO: session_present logic
|
||||||
// Unexpected result handling:
|
// Unexpected result handling:
|
||||||
|
@@ -155,6 +155,19 @@ public:
|
|||||||
_fast_replies.clear();
|
_fast_replies.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear_pending_pubrels() {
|
||||||
|
for (auto it = _handlers.begin(); it != _handlers.end();) {
|
||||||
|
if (it->code() == control_code_e::pubrel) {
|
||||||
|
std::move(*it)(
|
||||||
|
asio::error::operation_aborted, byte_citer {}, byte_citer {}
|
||||||
|
);
|
||||||
|
it = _handlers.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
handlers::iterator find_handler(control_code_e code, uint16_t packet_id) {
|
handlers::iterator find_handler(control_code_e code, uint16_t packet_id) {
|
||||||
return std::find_if(
|
return std::find_if(
|
||||||
|
@@ -660,6 +660,7 @@ public:
|
|||||||
* The list of all possible error codes that this operation can finish with:\n
|
* The list of all possible error codes that this operation can finish with:\n
|
||||||
* - `boost::system::errc::errc_t::success`\n
|
* - `boost::system::errc::errc_t::success`\n
|
||||||
* - `boost::asio::error::operation_aborted`\n
|
* - `boost::asio::error::operation_aborted`\n
|
||||||
|
* - \link async_mqtt5::client::error::session_expired \endlink
|
||||||
*
|
*
|
||||||
* Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
|
* Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
|
||||||
*/
|
*/
|
||||||
|
@@ -166,7 +166,7 @@ BOOST_AUTO_TEST_CASE(client_cancel_async_receive) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// passes on debian, hangs on windows in io_context destructor
|
// passes on debian, hangs on windows in io_context destructor
|
||||||
BOOST_AUTO_TEST_CASE(ioc_stop_async_publish) {
|
BOOST_AUTO_TEST_CASE(ioc_stop_async_publish, *boost::unit_test::disabled() ) {
|
||||||
cancel_async_publish<test::ioc_stop>();
|
cancel_async_publish<test::ioc_stop>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(client_cancel_async_publish) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// passes on debian, hangs on windows
|
// passes on debian, hangs on windows
|
||||||
BOOST_AUTO_TEST_CASE(ioc_stop_cancel_during_connecting) {
|
BOOST_AUTO_TEST_CASE(ioc_stop_cancel_during_connecting, *boost::unit_test::disabled() ) {
|
||||||
cancel_during_connecting<test::ioc_stop>();
|
cancel_during_connecting<test::ioc_stop>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <boost/asio/as_tuple.hpp>
|
||||||
#include <boost/asio/co_spawn.hpp>
|
#include <boost/asio/co_spawn.hpp>
|
||||||
#include <boost/asio/detached.hpp>
|
#include <boost/asio/detached.hpp>
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
@@ -15,25 +16,30 @@ BOOST_AUTO_TEST_SUITE(coroutine/*, *boost::unit_test::disabled()*/)
|
|||||||
using namespace async_mqtt5;
|
using namespace async_mqtt5;
|
||||||
namespace asio = boost::asio;
|
namespace asio = boost::asio;
|
||||||
|
|
||||||
|
constexpr auto use_nothrow_awaitable = asio::as_tuple(asio::use_awaitable);
|
||||||
|
|
||||||
template<typename StreamType, typename TlsContext>
|
template<typename StreamType, typename TlsContext>
|
||||||
asio::awaitable<void> sanity_check(mqtt_client<StreamType, TlsContext>& c) {
|
asio::awaitable<void> sanity_check(mqtt_client<StreamType, TlsContext>& c) {
|
||||||
co_await c.template async_publish<qos_e::at_most_once>(
|
auto [ec_0] = co_await c.template async_publish<qos_e::at_most_once>(
|
||||||
"test/mqtt-test", "hello world with qos0!", retain_e::no, publish_props{},
|
"test/mqtt-test", "hello world with qos0!", retain_e::yes, publish_props {},
|
||||||
asio::use_awaitable
|
use_nothrow_awaitable
|
||||||
);
|
);
|
||||||
|
BOOST_CHECK(!ec_0);
|
||||||
|
|
||||||
auto [puback_rc, puback_props] = co_await c.template async_publish<qos_e::at_least_once>(
|
auto [ec_1, puback_rc, puback_props] = co_await c.template async_publish<qos_e::at_least_once>(
|
||||||
"test/mqtt-test", "hello world with qos1!",
|
"test/mqtt-test", "hello world with qos1!",
|
||||||
retain_e::no, publish_props{},
|
retain_e::yes, publish_props {},
|
||||||
asio::use_awaitable
|
use_nothrow_awaitable
|
||||||
);
|
);
|
||||||
|
BOOST_CHECK(!ec_1);
|
||||||
BOOST_CHECK(!puback_rc);
|
BOOST_CHECK(!puback_rc);
|
||||||
|
|
||||||
auto [pubcomp_rc, pubcomp_props] = co_await c.template async_publish<qos_e::exactly_once>(
|
auto [ec_2, pubcomp_rc, pubcomp_props] = co_await c.template async_publish<qos_e::exactly_once>(
|
||||||
"test/mqtt-test", "hello world with qos2!",
|
"test/mqtt-test", "hello world with qos2!",
|
||||||
retain_e::no, publish_props{},
|
retain_e::yes, publish_props {},
|
||||||
asio::use_awaitable
|
use_nothrow_awaitable
|
||||||
);
|
);
|
||||||
|
BOOST_CHECK(!ec_2);
|
||||||
BOOST_CHECK(!pubcomp_rc);
|
BOOST_CHECK(!pubcomp_rc);
|
||||||
|
|
||||||
std::vector<subscribe_topic> topics;
|
std::vector<subscribe_topic> topics;
|
||||||
@@ -46,19 +52,21 @@ asio::awaitable<void> sanity_check(mqtt_client<StreamType, TlsContext>& c) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
auto [sub_codes, sub_props] = co_await c.async_subscribe(
|
auto [sub_ec, sub_codes, sub_props] = co_await c.async_subscribe(
|
||||||
topics, subscribe_props{}, asio::use_awaitable
|
topics, subscribe_props {}, use_nothrow_awaitable
|
||||||
);
|
);
|
||||||
|
BOOST_CHECK(!sub_ec);
|
||||||
BOOST_CHECK(!sub_codes[0]);
|
BOOST_CHECK(!sub_codes[0]);
|
||||||
auto [topic, payload, publish_props] = co_await c.async_receive(asio::use_awaitable);
|
auto [rec, topic, payload, publish_props] = co_await c.async_receive(use_nothrow_awaitable);
|
||||||
|
|
||||||
auto [unsub_codes, unsub_props] = co_await c.async_unsubscribe(
|
auto [unsub_ec, unsub_codes, unsub_props] = co_await c.async_unsubscribe(
|
||||||
std::vector<std::string>{"test/mqtt-test"}, unsubscribe_props{},
|
std::vector<std::string>{"test/mqtt-test"}, unsubscribe_props{},
|
||||||
asio::use_awaitable
|
use_nothrow_awaitable
|
||||||
);
|
);
|
||||||
|
BOOST_CHECK(!unsub_ec);
|
||||||
BOOST_CHECK(!unsub_codes[0]);
|
BOOST_CHECK(!unsub_codes[0]);
|
||||||
|
|
||||||
co_await c.async_disconnect(asio::use_awaitable);
|
co_await c.async_disconnect(use_nothrow_awaitable);
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
91
test/unit/test/session.cpp
Normal file
91
test/unit/test/session.cpp
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <boost/asio/as_tuple.hpp>
|
||||||
|
#include <boost/asio/co_spawn.hpp>
|
||||||
|
#include <boost/asio/use_awaitable.hpp>
|
||||||
|
|
||||||
|
#include <async_mqtt5.hpp>
|
||||||
|
#include <test_common/test_service.hpp>
|
||||||
|
|
||||||
|
using namespace async_mqtt5;
|
||||||
|
|
||||||
|
constexpr auto use_nothrow_awaitable = asio::as_tuple(asio::use_awaitable);
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(session/*, *boost::unit_test::disabled()*/)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(session_state_session_present) {
|
||||||
|
detail::session_state session_state;
|
||||||
|
BOOST_CHECK_EQUAL(session_state.session_present(), false);
|
||||||
|
session_state.session_present(true);
|
||||||
|
BOOST_CHECK_EQUAL(session_state.session_present(), true);
|
||||||
|
session_state.session_present(false);
|
||||||
|
BOOST_CHECK_EQUAL(session_state.session_present(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(session_expired_in_channel) {
|
||||||
|
asio::io_context ioc;
|
||||||
|
|
||||||
|
using stream_type = asio::ip::tcp::socket;
|
||||||
|
using client_type = mqtt_client<stream_type>;
|
||||||
|
client_type c(ioc, "");
|
||||||
|
|
||||||
|
c.credentials("tester", "", "")
|
||||||
|
.brokers("mqtt.mireo.local", 1883)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
co_spawn(ioc,
|
||||||
|
[&]() -> asio::awaitable<void> {
|
||||||
|
auto [ec, topic, payload, props] = co_await c.async_receive(use_nothrow_awaitable);
|
||||||
|
BOOST_CHECK(ec == client::error::session_expired);
|
||||||
|
BOOST_CHECK_EQUAL(topic, std::string {});
|
||||||
|
BOOST_CHECK_EQUAL(payload, std::string {});
|
||||||
|
c.cancel();
|
||||||
|
co_return;
|
||||||
|
},
|
||||||
|
asio::detached
|
||||||
|
);
|
||||||
|
|
||||||
|
ioc.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(clear_waiting_on_pubrel) {
|
||||||
|
constexpr int expected_handlers_called = 1;
|
||||||
|
int handlers_called = 0;
|
||||||
|
|
||||||
|
asio::io_context ioc;
|
||||||
|
using client_service_type = test::test_service<asio::ip::tcp::socket>;
|
||||||
|
auto svc_ptr = std::make_shared<client_service_type>(ioc.get_executor());
|
||||||
|
|
||||||
|
decoders::publish_message pub_msg = std::make_tuple(
|
||||||
|
"topic", 1, 0b0100, publish_props {}, "payload"
|
||||||
|
);
|
||||||
|
|
||||||
|
detail::publish_rec_op<client_service_type> { svc_ptr }.perform(pub_msg);
|
||||||
|
|
||||||
|
// let publish_rec_op reach wait_on_pubrel stage
|
||||||
|
asio::steady_timer timer(ioc.get_executor());
|
||||||
|
timer.expires_after(std::chrono::milliseconds(50));
|
||||||
|
timer.async_wait([&svc_ptr, &handlers_called](error_code) {
|
||||||
|
BOOST_CHECK_EQUAL(svc_ptr.use_count(), 2);
|
||||||
|
svc_ptr->update_session_state(); // session_present = false
|
||||||
|
// publish_rec_op should complete
|
||||||
|
BOOST_CHECK_EQUAL(svc_ptr.use_count(), 1);
|
||||||
|
|
||||||
|
svc_ptr->async_channel_receive(
|
||||||
|
[&svc_ptr, &handlers_called](error_code ec, std::string topic, std::string payload, publish_props props) {
|
||||||
|
handlers_called++;
|
||||||
|
BOOST_CHECK(ec == client::error::session_expired);
|
||||||
|
BOOST_CHECK_EQUAL(topic, std::string {});
|
||||||
|
BOOST_CHECK_EQUAL(payload, std::string {});
|
||||||
|
svc_ptr->cancel();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ioc.run();
|
||||||
|
BOOST_CHECK_EQUAL(
|
||||||
|
handlers_called, expected_handlers_called
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END();
|
Reference in New Issue
Block a user