mirror of
https://github.com/boostorg/mqtt5.git
synced 2025-07-30 04:27:34 +02:00
Gracefully shutdown Websocket and TLS streams
Summary: Resolves #18 Ref T15241 Reviewers: ivica Reviewed By: ivica Subscribers: korina, miljen Maniphest Tasks: T15241 Differential Revision: https://repo.mireo.local/D33395
This commit is contained in:
@ -95,7 +95,6 @@ public:
|
||||
|
||||
void close() {
|
||||
error_code ec;
|
||||
detail::lowest_layer(*_stream_ptr).shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
||||
detail::lowest_layer(*_stream_ptr).close(ec);
|
||||
}
|
||||
|
||||
|
@ -224,6 +224,7 @@ public:
|
||||
auto initiation = [this](
|
||||
auto handler, const MutableBuffer& buffer
|
||||
) {
|
||||
_pending_read.complete(_ex, asio::error::operation_aborted, 0);
|
||||
_pending_read = pending_read(buffer, std::move(handler));
|
||||
complete_read();
|
||||
};
|
||||
|
@ -79,10 +79,6 @@ public:
|
||||
_test_broker = nullptr;
|
||||
}
|
||||
|
||||
void shutdown(asio::ip::tcp::socket::shutdown_type, error_code& ec) {
|
||||
ec = {};
|
||||
}
|
||||
|
||||
void connect(const endpoint_type& ep, error_code& ec) {
|
||||
ec = {};
|
||||
_remote_ep = ep;
|
||||
@ -165,8 +161,6 @@ public:
|
||||
}
|
||||
|
||||
void operator()(on_read, error_code ec, size_t bytes_read) {
|
||||
if (ec)
|
||||
_stream_impl->disconnect();
|
||||
complete(ec, bytes_read);
|
||||
}
|
||||
|
||||
@ -223,8 +217,6 @@ public:
|
||||
}
|
||||
|
||||
void operator()(on_write, error_code ec, size_t bytes_written) {
|
||||
if (ec)
|
||||
_stream_impl->disconnect();
|
||||
complete(ec, bytes_written);
|
||||
}
|
||||
|
||||
@ -300,10 +292,6 @@ public:
|
||||
return _impl->is_connected();
|
||||
}
|
||||
|
||||
void shutdown(asio::ip::tcp::socket::shutdown_type st, error_code& ec) {
|
||||
return _impl->shutdown(st, ec);
|
||||
}
|
||||
|
||||
endpoint_type remote_endpoint(error_code& ec) {
|
||||
return _impl->remote_endpoint(ec);
|
||||
}
|
||||
@ -368,6 +356,10 @@ public:
|
||||
|
||||
};
|
||||
|
||||
template <typename ShutdownHandler>
|
||||
void async_shutdown(test_stream&, ShutdownHandler&& handler) {
|
||||
return std::move(handler)(error_code {});
|
||||
}
|
||||
|
||||
} // end namespace boost::mqtt5::test
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
#include <boost/mqtt5.hpp>
|
||||
#include <boost/mqtt5/websocket.hpp>
|
||||
#include <boost/mqtt5/websocket_ssl.hpp>
|
||||
|
||||
#include <boost/asio/as_tuple.hpp>
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
@ -19,7 +19,6 @@
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/beast/ssl/ssl_stream.hpp> // async_teardown specialization for websocket ssl stream
|
||||
#include <boost/beast/websocket.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/test/data/test_case.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <chrono>
|
||||
@ -311,4 +312,67 @@ BOOST_FIXTURE_TEST_CASE(omit_props, shared_test_data) {
|
||||
BOOST_TEST(handlers_called == expected_handlers_called);
|
||||
}
|
||||
|
||||
struct long_shutdown_stream : public test::test_stream {
|
||||
long_shutdown_stream(typename test::test_stream::executor_type ex) :
|
||||
test::test_stream(std::move(ex)) {}
|
||||
};
|
||||
|
||||
template <typename ShutdownHandler>
|
||||
void async_shutdown(long_shutdown_stream& stream, ShutdownHandler&& handler) {
|
||||
auto timer = std::make_shared<asio::steady_timer>(stream.get_executor());
|
||||
timer->expires_after(std::chrono::seconds(10));
|
||||
timer->async_wait(asio::consign(std::move(handler), std::move(timer)));
|
||||
}
|
||||
|
||||
BOOST_DATA_TEST_CASE_F(
|
||||
shared_test_data, cancel_disconnect_in_shutdown,
|
||||
boost::unit_test::data::make({ 100, 8000 }), cancel_delay_ms
|
||||
) {
|
||||
asio::io_context ioc;
|
||||
auto executor = ioc.get_executor();
|
||||
|
||||
constexpr int expected_handlers_called = 1;
|
||||
int handlers_called = 0;
|
||||
|
||||
test::msg_exchange broker_side;
|
||||
broker_side
|
||||
.expect(connect)
|
||||
.complete_with(success, after(0ms))
|
||||
.reply_with(connack, after(0ms))
|
||||
.expect(disconnect)
|
||||
.complete_with(success, after(0ms));
|
||||
|
||||
auto& broker = asio::make_service<test::test_broker>(
|
||||
ioc, executor, std::move(broker_side)
|
||||
);
|
||||
|
||||
asio::steady_timer timer(executor);
|
||||
mqtt_client<long_shutdown_stream> c(executor);
|
||||
c.brokers("127.0.0.1")
|
||||
.async_run(asio::detached);
|
||||
|
||||
asio::cancellation_signal signal;
|
||||
|
||||
c.async_disconnect(
|
||||
asio::bind_cancellation_slot(
|
||||
signal.slot(),
|
||||
[&](error_code ec) {
|
||||
handlers_called++;
|
||||
BOOST_TEST(ec == asio::error::operation_aborted);
|
||||
timer.cancel();
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
timer.expires_after(std::chrono::milliseconds(cancel_delay_ms));
|
||||
timer.async_wait([&signal](error_code) {
|
||||
signal.emit(asio::cancellation_type::all);
|
||||
});
|
||||
|
||||
ioc.run_for(6s);
|
||||
|
||||
BOOST_TEST(broker.received_all_expected());
|
||||
BOOST_TEST(handlers_called == expected_handlers_called);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -172,6 +172,52 @@ BOOST_FIXTURE_TEST_CASE(receive_disconnect, shared_test_data) {
|
||||
BOOST_TEST(broker.received_all_expected());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(receive_disconnect_while_reconnecting, shared_test_data) {
|
||||
// packets
|
||||
auto disconnect = encoders::encode_disconnect(0x00, {});
|
||||
constexpr int expected_handlers_called = 1;
|
||||
int handlers_called = 0;
|
||||
|
||||
test::msg_exchange broker_side;
|
||||
broker_side
|
||||
.expect(connect)
|
||||
.complete_with(success, after(1ms))
|
||||
.reply_with(connack, after(2ms))
|
||||
.expect(publish)
|
||||
.complete_with(fail, after(20ms))
|
||||
.send(disconnect, after(30ms))
|
||||
.expect(connect)
|
||||
.complete_with(success, after(20ms))
|
||||
.reply_with(connack, after(30ms))
|
||||
.expect(publish)
|
||||
.complete_with(success, after(0ms));
|
||||
|
||||
asio::io_context ioc;
|
||||
auto executor = ioc.get_executor();
|
||||
auto& broker = asio::make_service<test::test_broker>(
|
||||
ioc, executor, std::move(broker_side)
|
||||
);
|
||||
|
||||
using client_type = mqtt_client<test::test_stream>;
|
||||
client_type c(executor);
|
||||
c.brokers("127.0.0.1,127.0.0.1") // to avoid reconnect backoff
|
||||
.async_run(asio::detached);
|
||||
|
||||
c.async_publish<qos_e::at_most_once>(
|
||||
topic, payload, retain_e::no, {},
|
||||
[&](error_code ec) {
|
||||
BOOST_TEST(handlers_called == 0);
|
||||
handlers_called++;
|
||||
BOOST_TEST(!ec);
|
||||
c.cancel();
|
||||
}
|
||||
);
|
||||
|
||||
ioc.run_for(1s);
|
||||
BOOST_TEST(handlers_called == expected_handlers_called);
|
||||
BOOST_TEST(broker.received_all_expected());
|
||||
}
|
||||
|
||||
template <typename VerifyFun>
|
||||
void run_receive_test(
|
||||
test::msg_exchange broker_side, int num_of_receives,
|
||||
|
@ -533,7 +533,7 @@ BOOST_FIXTURE_TEST_CASE(cancel_resending_publish, shared_test_data) {
|
||||
[&handlers_called, &c](error_code ec, reason_code rc, puback_props) {
|
||||
++handlers_called;
|
||||
|
||||
BOOST_TEST(ec = asio::error::operation_aborted);
|
||||
BOOST_TEST(ec == asio::error::operation_aborted);
|
||||
BOOST_TEST(rc == reason_codes::empty);
|
||||
|
||||
c.cancel();
|
||||
|
@ -11,13 +11,12 @@
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
#include <boost/mqtt5.hpp>
|
||||
#include <boost/mqtt5/websocket.hpp>
|
||||
#include <boost/mqtt5/websocket_ssl.hpp>
|
||||
|
||||
#include <boost/asio/awaitable.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/beast/websocket/ssl.hpp> // async_teardown for asio::ssl::socket
|
||||
#include <boost/beast/websocket/stream.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <boost/mqtt5/logger.hpp>
|
||||
#include <boost/mqtt5/logger_traits.hpp>
|
||||
#include <boost/mqtt5/mqtt_client.hpp>
|
||||
#include <boost/mqtt5/websocket.hpp>
|
||||
#include <boost/mqtt5/websocket_ssl.hpp>
|
||||
|
||||
#include <boost/mqtt5/detail/log_invoke.hpp>
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ssl/stream.hpp>
|
||||
#include <boost/beast/ssl/ssl_stream.hpp> // async_teardown specialization for websocket ssl stream
|
||||
#include <boost/beast/websocket/stream.hpp>
|
||||
#include <boost/test/tools/output_test_stream.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
@ -84,6 +84,11 @@ struct test_tcp_stream : public test::test_stream {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ShutdownHandler>
|
||||
void async_shutdown(test_tcp_stream&, ShutdownHandler&& handler) {
|
||||
return std::move(handler)(error_code {});
|
||||
}
|
||||
|
||||
using underlying_stream = test_tcp_stream;
|
||||
using stream_context = detail::stream_context<underlying_stream, std::monostate>;
|
||||
using astream = test::test_autoconnect_stream<underlying_stream, stream_context>;
|
||||
|
Reference in New Issue
Block a user