Async.MQTT5 -> Boost.MQTT5

Summary:
related to T15996

folder structure include/async_mqtt5 -> include/boost/mqtt5
namespace async_mqtt5 -> namespace boost::mqtt5
all tabs replaced with 4 spaces (because tabs are banned)
boost-like order of includes

TODO: fix all docs

Reviewers: ivica

Reviewed By: ivica

Subscribers: iljazovic, miljen

Differential Revision: https://repo.mireo.local/D33152
This commit is contained in:
Korina Šimičević committed 2025-01-14 08:57:44 +01:00
1 parent 1225cc778a
commit afc270f10e
136 files changed
+17834 -17859

No files matched your search

+73 -74
View File
@@ -5,10 +5,8 @@
// (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASYNC_MQTT5_TEST_DELAYED_OP_HPP
#define ASYNC_MQTT5_TEST_DELAYED_OP_HPP
#include <chrono>
#ifndef BOOST_MQTT5_TEST_DELAYED_OP_HPP
#define BOOST_MQTT5_TEST_DELAYED_OP_HPP
#include <boost/asio/append.hpp>
#include <boost/asio/bind_cancellation_slot.hpp>
@@ -17,10 +15,11 @@
#include <boost/asio/prepend.hpp>
#include <boost/asio/recycling_allocator.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/type_traits/remove_cv_ref.hpp>
namespace async_mqtt5::test {
#include <chrono>
namespace boost::mqtt5::test {
namespace asio = boost::asio;
@@ -30,96 +29,96 @@ using duration = time_stamp::duration;
template <typename ...BoundArgs>
class delayed_op {
struct on_timer {};
struct on_timer {};
std::unique_ptr<asio::steady_timer> _timer;
time_stamp::duration _delay;
asio::cancellation_slot _cancel_slot;
std::unique_ptr<asio::steady_timer> _timer;
time_stamp::duration _delay;
asio::cancellation_slot _cancel_slot;
std::tuple<BoundArgs...> _args;
std::tuple<BoundArgs...> _args;
public:
template <typename Executor, typename ...Args>
delayed_op(
const Executor& ex, time_stamp::duration delay, Args&& ...args
) :
_timer(new asio::steady_timer(ex)), _delay(delay),
_args(std::move(args)...)
{}
template <typename Executor, typename ...Args>
delayed_op(
const Executor& ex, time_stamp::duration delay, Args&& ...args
) :
_timer(new asio::steady_timer(ex)), _delay(delay),
_args(std::move(args)...)
{}
delayed_op(delayed_op&&) = default;
delayed_op(const delayed_op&) = delete;
delayed_op(delayed_op&&) = default;
delayed_op(const delayed_op&) = delete;
delayed_op& operator=(delayed_op&&) = default;
delayed_op& operator=(const delayed_op&) = delete;
delayed_op& operator=(delayed_op&&) = default;
delayed_op& operator=(const delayed_op&) = delete;
using allocator_type = asio::recycling_allocator<void>;
allocator_type get_allocator() const noexcept {
return allocator_type {};
}
using allocator_type = asio::recycling_allocator<void>;
allocator_type get_allocator() const noexcept {
return allocator_type {};
}
using cancellation_slot_type = asio::cancellation_slot;
asio::cancellation_slot get_cancellation_slot() const noexcept {
return _cancel_slot;
}
using cancellation_slot_type = asio::cancellation_slot;
asio::cancellation_slot get_cancellation_slot() const noexcept {
return _cancel_slot;
}
using executor_type = asio::steady_timer::executor_type;
executor_type get_executor() const noexcept {
return _timer->get_executor();
}
using executor_type = asio::steady_timer::executor_type;
executor_type get_executor() const noexcept {
return _timer->get_executor();
}
template <typename CompletionHandler>
void perform(CompletionHandler&& handler) {
_cancel_slot = asio::get_associated_cancellation_slot(handler);
template <typename CompletionHandler>
void perform(CompletionHandler&& handler) {
_cancel_slot = asio::get_associated_cancellation_slot(handler);
_timer->expires_after(_delay);
_timer->async_wait(
asio::prepend(std::move(*this), on_timer {}, std::move(handler))
);
}
_timer->expires_after(_delay);
_timer->async_wait(
asio::prepend(std::move(*this), on_timer {}, std::move(handler))
);
}
template <typename CompletionHandler>
void operator()(on_timer, CompletionHandler&& h, error_code ec) {
// The timer places a handler into the cancellation slot
// and does not clear it. Therefore, we need to clear it explicitly
// to properly remove the corresponding cancellation signal
// in the test_broker.
get_cancellation_slot().clear();
template <typename CompletionHandler>
void operator()(on_timer, CompletionHandler&& h, error_code ec) {
// The timer places a handler into the cancellation slot
// and does not clear it. Therefore, we need to clear it explicitly
// to properly remove the corresponding cancellation signal
// in the test_broker.
get_cancellation_slot().clear();
auto bh = std::apply(
[h = std::move(h)](auto&&... args) mutable {
return asio::append(std::move(h), std::move(args)...);
},
_args
);
auto bh = std::apply(
[h = std::move(h)](auto&&... args) mutable {
return asio::append(std::move(h), std::move(args)...);
},
_args
);
asio::dispatch(asio::prepend(std::move(bh), ec));
}
asio::dispatch(asio::prepend(std::move(bh), ec));
}
};
template <typename CompletionToken, typename ...BoundArgs>
decltype(auto) async_delay(
asio::cancellation_slot cancel_slot,
delayed_op<BoundArgs...>&& op,
CompletionToken&& token
asio::cancellation_slot cancel_slot,
delayed_op<BoundArgs...>&& op,
CompletionToken&& token
) {
using Signature = void (error_code, boost::remove_cv_ref_t<BoundArgs>...);
using Signature = void (error_code, boost::remove_cv_ref_t<BoundArgs>...);
auto initiation = [](
auto handler, asio::cancellation_slot cancel_slot,
delayed_op<BoundArgs...> op
) {
op.perform(
asio::bind_cancellation_slot(cancel_slot, std::move(handler))
);
};
auto initiation = [](
auto handler, asio::cancellation_slot cancel_slot,
delayed_op<BoundArgs...> op
) {
op.perform(
asio::bind_cancellation_slot(cancel_slot, std::move(handler))
);
};
return asio::async_initiate<CompletionToken, Signature>(
std::move(initiation), token, cancel_slot, std::move(op)
);
return asio::async_initiate<CompletionToken, Signature>(
std::move(initiation), token, cancel_slot, std::move(op)
);
}
} // end namespace async_mqtt5::test
} // end namespace boost::mqtt5::test
#endif // ASYNC_MQTT5_TEST_DELAYED_OP_HPP
#endif // BOOST_MQTT5_TEST_DELAYED_OP_HPP
+195 -195
View File
@@ -5,8 +5,11 @@
// (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASYNC_MQTT5_TEST_MESSAGE_EXCHANGE_HPP
#define ASYNC_MQTT5_TEST_MESSAGE_EXCHANGE_HPP
#ifndef BOOST_MQTT5_TEST_MESSAGE_EXCHANGE_HPP
#define BOOST_MQTT5_TEST_MESSAGE_EXCHANGE_HPP
#include <boost/system/error_code.hpp>
#include <boost/type_traits/remove_cv_ref.hpp>
#include <algorithm>
#include <chrono>
@@ -17,12 +20,9 @@
#include <type_traits>
#include <vector>
#include <boost/system/error_code.hpp>
#include <boost/type_traits/remove_cv_ref.hpp>
#include "test_common/delayed_op.hpp"
namespace async_mqtt5::test {
namespace boost::mqtt5::test {
using error_code = boost::system::error_code;
using time_stamp = std::chrono::time_point<std::chrono::steady_clock>;
@@ -38,273 +38,273 @@ using namespace std::chrono_literals;
namespace detail {
class stream_message {
error_code _ec;
duration _after { 0 };
std::vector<uint8_t> _content;
error_code _ec;
duration _after { 0 };
std::vector<uint8_t> _content;
public:
template <typename ...Args>
stream_message(error_code ec, duration after, Args&& ...args) :
_ec(ec), _after(after)
{
(_content.insert(_content.end(), args.begin(), args.end()), ...);
}
template <typename ...Args>
stream_message(error_code ec, duration after, Args&& ...args) :
_ec(ec), _after(after)
{
(_content.insert(_content.end(), args.begin(), args.end()), ...);
}
stream_message(const stream_message&) = delete;
stream_message(stream_message&&) = default;
stream_message(const stream_message&) = delete;
stream_message(stream_message&&) = default;
stream_message& operator=(stream_message&&) = default;
stream_message& operator=(const stream_message&) = delete;
stream_message& operator=(stream_message&&) = default;
stream_message& operator=(const stream_message&) = delete;
template <typename Executor>
auto to_operation(const Executor& ex) {
return delayed_op<error_code, std::vector<uint8_t>> {
ex, _after, _ec, std::move(_content)
};
}
template <typename Executor>
auto to_operation(const Executor& ex) {
return delayed_op<error_code, std::vector<uint8_t>> {
ex, _after, _ec, std::move(_content)
};
}
};
} // end namespace detail
class client_message {
msg_exchange* _owner;
msg_exchange* _owner;
error_code _write_ec;
duration _complete_after { 0 };
std::vector<std::string> _expected_packets;
error_code _write_ec;
duration _complete_after { 0 };
std::vector<std::string> _expected_packets;
std::vector<detail::stream_message> _replies;
std::vector<detail::stream_message> _replies;
public:
template <typename ...Args>
client_message(msg_exchange* owner, Args&&... args) :
_owner(owner),
_expected_packets({ std::forward<Args>(args)... })
{}
template <typename ...Args>
client_message(msg_exchange* owner, Args&&... args) :
_owner(owner),
_expected_packets({ std::forward<Args>(args)... })
{}
client_message(client_message&&) = default;
client_message(const client_message&) = delete;
client_message(client_message&&) = default;
client_message(const client_message&) = delete;
client_message& operator=(client_message&&) = default;
client_message& operator=(const client_message&) = delete;
client_message& operator=(client_message&&) = default;
client_message& operator=(const client_message&) = delete;
client_message& complete_with(error_code ec, duration af) {
_write_ec = ec;
_complete_after = af;
return *this;
}
client_message& complete_with(error_code ec, duration af) {
_write_ec = ec;
_complete_after = af;
return *this;
}
template <typename ...Args>
client_message& reply_with(Args&& ...args) {
return reply_with_dur(std::make_tuple(std::forward<Args>(args)...));
}
template <typename ...Args>
client_message& reply_with(Args&& ...args) {
return reply_with_dur(std::make_tuple(std::forward<Args>(args)...));
}
template <typename ...Args>
client_message& expect(Args&& ...args);
template <typename ...Args>
client_message& expect(Args&& ...args);
template <typename ...Args>
broker_message& send(Args&& ...args);
template <typename ...Args>
broker_message& send(Args&& ...args);
template <typename Executor>
decltype(auto) write_completion(const Executor& ex) const {
return delayed_op<error_code>(ex, _complete_after, _write_ec);
}
template <typename Executor>
decltype(auto) write_completion(const Executor& ex) const {
return delayed_op<error_code>(ex, _complete_after, _write_ec);
}
template <typename Executor>
decltype(auto) pop_reply_ops(const Executor& ex) {
std::vector<delayed_op<error_code, std::vector<uint8_t>>> ret;
std::transform(
_replies.begin(), _replies.end(), std::back_inserter(ret),
[&ex](auto& r) { return r.to_operation(ex); }
);
_replies.clear();
return ret;
}
template <typename Executor>
decltype(auto) pop_reply_ops(const Executor& ex) {
std::vector<delayed_op<error_code, std::vector<uint8_t>>> ret;
std::transform(
_replies.begin(), _replies.end(), std::back_inserter(ret),
[&ex](auto& r) { return r.to_operation(ex); }
);
_replies.clear();
return ret;
}
decltype(auto) expected_packets() {
return _expected_packets;
}
decltype(auto) expected_packets() {
return _expected_packets;
}
private:
template <typename Tuple>
client_message& reply_with_dur(const Tuple& t) {
using indices = std::make_index_sequence<std::tuple_size_v<Tuple> - 1>;
return reply_with_dur(t, indices {});
}
template <typename Tuple>
client_message& reply_with_dur(const Tuple& t) {
using indices = std::make_index_sequence<std::tuple_size_v<Tuple> - 1>;
return reply_with_dur(t, indices {});
}
template<typename Tuple, size_t ...I>
client_message& reply_with_dur(const Tuple& t, std::index_sequence<I...>) {
return reply_with_impl(
std::get<std::tuple_size_v<Tuple> - 1>(t),
std::get<I>(t)...
);
}
template<typename Tuple, size_t ...I>
client_message& reply_with_dur(const Tuple& t, std::index_sequence<I...>) {
return reply_with_impl(
std::get<std::tuple_size_v<Tuple> - 1>(t),
std::get<I>(t)...
);
}
template <
typename ...Args,
std::enable_if_t<
(std::is_same_v<boost::remove_cv_ref_t<Args>, std::string> && ...),
bool
> = true
>
client_message& reply_with_impl(duration af, Args&& ...args) {
_replies.emplace_back(
error_code {}, af, std::forward<Args>(args)...
);
return *this;
}
template <
typename ...Args,
std::enable_if_t<
(std::is_same_v<boost::remove_cv_ref_t<Args>, std::string> && ...),
bool
> = true
>
client_message& reply_with_impl(duration af, Args&& ...args) {
_replies.emplace_back(
error_code {}, af, std::forward<Args>(args)...
);
return *this;
}
client_message& reply_with_impl(duration af, error_code ec) {
_replies.emplace_back(ec, af);
return *this;
}
client_message& reply_with_impl(duration af, error_code ec) {
_replies.emplace_back(ec, af);
return *this;
}
};
class broker_message {
msg_exchange* _owner;
detail::stream_message _message;
msg_exchange* _owner;
detail::stream_message _message;
public:
template <typename ...Args>
broker_message(
msg_exchange* owner, error_code ec, duration af, Args&&... args
) :
_owner(owner), _message(ec, af, std::forward<Args>(args) ...)
{}
template <typename ...Args>
broker_message(
msg_exchange* owner, error_code ec, duration af, Args&&... args
) :
_owner(owner), _message(ec, af, std::forward<Args>(args) ...)
{}
broker_message(broker_message&&) = default;
broker_message(const broker_message&) = delete;
broker_message(broker_message&&) = default;
broker_message(const broker_message&) = delete;
broker_message& operator=(broker_message&&) = default;
broker_message& operator=(const broker_message&) = delete;
broker_message& operator=(broker_message&&) = default;
broker_message& operator=(const broker_message&) = delete;
template <typename ...Args>
client_message& expect(Args&& ...args);
template <typename ...Args>
client_message& expect(Args&& ...args);
template <typename ...Args>
broker_message& send(Args&& ...args);
template <typename ...Args>
broker_message& send(Args&& ...args);
template <typename Executor>
decltype(auto) pop_send_op(const Executor& ex) {
return _message.to_operation(ex);
}
template <typename Executor>
decltype(auto) pop_send_op(const Executor& ex) {
return _message.to_operation(ex);
}
};
class msg_exchange {
std::deque<client_message> _to_broker;
std::vector<broker_message> _from_broker;
std::deque<client_message> _to_broker;
std::vector<broker_message> _from_broker;
public:
msg_exchange() = default;
msg_exchange() = default;
msg_exchange(msg_exchange&&) = default;
msg_exchange(const msg_exchange&) = delete;
msg_exchange(msg_exchange&&) = default;
msg_exchange(const msg_exchange&) = delete;
msg_exchange& operator=(msg_exchange&&) = default;
msg_exchange& operator=(const msg_exchange&) = delete;
msg_exchange& operator=(msg_exchange&&) = default;
msg_exchange& operator=(const msg_exchange&) = delete;
template <
typename ...Args,
std::enable_if_t<
(std::is_same_v<boost::remove_cv_ref_t<Args>, std::string> && ...),
bool
> = true
>
client_message& expect(Args&& ...args) {
_to_broker.emplace_back(this, std::forward<Args>(args)...);
return _to_broker.back();
}
template <
typename ...Args,
std::enable_if_t<
(std::is_same_v<boost::remove_cv_ref_t<Args>, std::string> && ...),
bool
> = true
>
client_message& expect(Args&& ...args) {
_to_broker.emplace_back(this, std::forward<Args>(args)...);
return _to_broker.back();
}
template <typename ...Args>
broker_message& send(Args&& ...args) {
return send_with_dur(std::make_tuple(std::forward<Args>(args)...));
}
template <typename ...Args>
broker_message& send(Args&& ...args) {
return send_with_dur(std::make_tuple(std::forward<Args>(args)...));
}
std::optional<client_message> pop_reply_action() {
if (_to_broker.empty())
return std::nullopt;
std::optional<client_message> pop_reply_action() {
if (_to_broker.empty())
return std::nullopt;
auto rv = std::move(_to_broker.front());
_to_broker.pop_front();
return rv;
}
auto rv = std::move(_to_broker.front());
_to_broker.pop_front();
return rv;
}
template <typename Executor>
auto pop_broker_ops(const Executor& ex) {
std::vector<delayed_op<error_code, std::vector<uint8_t>>> ret;
std::transform(
_from_broker.begin(), _from_broker.end(), std::back_inserter(ret),
[&ex](auto& s) { return s.pop_send_op(ex); }
);
_from_broker.clear();
return ret;
}
template <typename Executor>
auto pop_broker_ops(const Executor& ex) {
std::vector<delayed_op<error_code, std::vector<uint8_t>>> ret;
std::transform(
_from_broker.begin(), _from_broker.end(), std::back_inserter(ret),
[&ex](auto& s) { return s.pop_send_op(ex); }
);
_from_broker.clear();
return ret;
}
bool has_remaining_messages() const {
return !_to_broker.empty() || !_from_broker.empty();
}
bool has_remaining_messages() const {
return !_to_broker.empty() || !_from_broker.empty();
}
private:
template <typename Tuple>
broker_message& send_with_dur(const Tuple& t) {
using indices = std::make_index_sequence<std::tuple_size_v<Tuple> - 1>;
return send_with_dur(t, indices {});
}
template <typename Tuple>
broker_message& send_with_dur(const Tuple& t) {
using indices = std::make_index_sequence<std::tuple_size_v<Tuple> - 1>;
return send_with_dur(t, indices {});
}
template<typename Tuple, size_t ...I>
broker_message& send_with_dur(const Tuple& t, std::index_sequence<I...>) {
return send_impl(
std::get<std::tuple_size_v<Tuple> - 1>(t),
std::get<I>(t)...
);
}
template<typename Tuple, size_t ...I>
broker_message& send_with_dur(const Tuple& t, std::index_sequence<I...>) {
return send_impl(
std::get<std::tuple_size_v<Tuple> - 1>(t),
std::get<I>(t)...
);
}
template <
typename ...Args,
std::enable_if_t<
(std::is_same_v<boost::remove_cv_ref_t<Args>, std::string> && ...),
bool
> = true
>
broker_message& send_impl(duration after, Args&& ...args) {
_from_broker.emplace_back(
this, error_code {}, after, std::forward<Args>(args)...
);
return _from_broker.back();
}
template <
typename ...Args,
std::enable_if_t<
(std::is_same_v<boost::remove_cv_ref_t<Args>, std::string> && ...),
bool
> = true
>
broker_message& send_impl(duration after, Args&& ...args) {
_from_broker.emplace_back(
this, error_code {}, after, std::forward<Args>(args)...
);
return _from_broker.back();
}
broker_message& send_impl(duration after, error_code ec) {
_from_broker.emplace_back(this, ec, after);
return _from_broker.back();
}
broker_message& send_impl(duration after, error_code ec) {
_from_broker.emplace_back(this, ec, after);
return _from_broker.back();
}
};
template <typename ...Args>
client_message& client_message::expect(Args&& ...args) {
return _owner->expect(std::forward<Args>(args)...);
return _owner->expect(std::forward<Args>(args)...);
}
template <typename ...Args>
broker_message& client_message::send(Args&& ...args) {
return _owner->send(std::forward<Args>(args)...);
return _owner->send(std::forward<Args>(args)...);
}
template <typename ...Args>
client_message& broker_message::expect(Args&& ...args) {
return _owner->expect(std::forward<Args>(args)...);
return _owner->expect(std::forward<Args>(args)...);
}
template <typename ...Args>
broker_message& broker_message::send(Args&& ...args) {
return _owner->send(std::forward<Args>(args)...);
return _owner->send(std::forward<Args>(args)...);
}
} // end namespace async_mqtt5::test
} // end namespace boost::mqtt5::test
#endif // ASYNC_MQTT5_TEST_MESSAGE_EXCHANGE_HPP
#endif // BOOST_MQTT5_TEST_MESSAGE_EXCHANGE_HPP
+275 -274
View File
@@ -5,8 +5,18 @@
// (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASYNC_MQTT5_TEST_PACKET_UTIL_HPP
#define ASYNC_MQTT5_TEST_PACKET_UTIL_HPP
#ifndef BOOST_MQTT5_TEST_PACKET_UTIL_HPP
#define BOOST_MQTT5_TEST_PACKET_UTIL_HPP
#include <boost/mqtt5/detail/control_packet.hpp>
#include <boost/mqtt5/detail/traits.hpp>
#include <boost/mqtt5/impl/codecs/message_decoders.hpp>
#include <boost/mqtt5/impl/codecs/message_encoders.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/range/algorithm/transform.hpp>
#include <bitset>
#include <cstdint>
@@ -15,383 +25,374 @@
#include <type_traits>
#include <vector>
#include <boost/algorithm/string/join.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/range/algorithm/transform.hpp>
namespace boost::mqtt5::test {
#include <async_mqtt5/detail/control_packet.hpp>
#include <async_mqtt5/detail/traits.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
namespace async_mqtt5::test {
using control_code_e = async_mqtt5::detail::control_code_e;
using control_code_e = boost::mqtt5::detail::control_code_e;
template <typename ...Strings>
std::string concat_strings(Strings&&... strings) {
std::ostringstream stream;
(stream << ... << std::forward<Strings>(strings));
return stream.str();
std::ostringstream stream;
(stream << ... << std::forward<Strings>(strings));
return stream.str();
}
namespace detail {
inline qos_e extract_qos(uint8_t flags) {
auto byte = (flags & 0b0110) >> 1;
return qos_e(byte);
auto byte = (flags & 0b0110) >> 1;
return qos_e(byte);
}
inline control_code_e extract_code(uint8_t control_byte) {
constexpr uint8_t mask = 0b11110000;
constexpr uint8_t publish_bits = 0b0011;
constexpr uint8_t special_mask = 0b00000010;
constexpr control_code_e codes_with_non_zero_end[] = {
control_code_e::pubrel, control_code_e::subscribe,
control_code_e::unsubscribe
};
constexpr uint8_t mask = 0b11110000;
constexpr uint8_t publish_bits = 0b0011;
constexpr uint8_t special_mask = 0b00000010;
constexpr control_code_e codes_with_non_zero_end[] = {
control_code_e::pubrel, control_code_e::subscribe,
control_code_e::unsubscribe
};
if ((control_byte >> 4) == publish_bits)
return control_code_e::publish;
if ((control_byte & mask) == control_byte)
return control_code_e(control_byte & mask);
if ((control_byte >> 4) == publish_bits)
return control_code_e::publish;
if ((control_byte & mask) == control_byte)
return control_code_e(control_byte & mask);
for (const auto& special_code : codes_with_non_zero_end)
if (control_byte == (uint8_t(special_code) | special_mask))
return special_code;
for (const auto& special_code : codes_with_non_zero_end)
if (control_byte == (uint8_t(special_code) | special_mask))
return special_code;
return control_code_e::no_packet;
return control_code_e::no_packet;
}
inline std::string_view code_to_str(control_code_e code) {
switch (code) {
case control_code_e::connect: return "CONNECT";
case control_code_e::connack: return "CONNACK";
case control_code_e::publish: return "PUBLISH";
case control_code_e::puback: return "PUBACK";
case control_code_e::pubrec: return "PUBREC";
case control_code_e::pubrel: return "PUBREL";
case control_code_e::pubcomp: return "PUBCOMP";
case control_code_e::subscribe: return "SUBSCRIBE";
case control_code_e::suback: return "SUBACK";
case control_code_e::unsubscribe: return "UNSUBSCRIBE";
case control_code_e::unsuback: return "UNSUBACK";
case control_code_e::auth: return "AUTH";
case control_code_e::disconnect: return "DISCONNECT";
case control_code_e::pingreq: return "PINGREQ";
case control_code_e::pingresp: return "PINGRESP";
default: return "NO PACKET";
}
switch (code) {
case control_code_e::connect: return "CONNECT";
case control_code_e::connack: return "CONNACK";
case control_code_e::publish: return "PUBLISH";
case control_code_e::puback: return "PUBACK";
case control_code_e::pubrec: return "PUBREC";
case control_code_e::pubrel: return "PUBREL";
case control_code_e::pubcomp: return "PUBCOMP";
case control_code_e::subscribe: return "SUBSCRIBE";
case control_code_e::suback: return "SUBACK";
case control_code_e::unsubscribe: return "UNSUBSCRIBE";
case control_code_e::unsuback: return "UNSUBACK";
case control_code_e::auth: return "AUTH";
case control_code_e::disconnect: return "DISCONNECT";
case control_code_e::pingreq: return "PINGREQ";
case control_code_e::pingresp: return "PINGRESP";
default: return "NO PACKET";
}
}
template <typename Props>
inline std::string to_readable_props(Props props) {
std::ostringstream stream;
props.visit([&stream](const auto&, const auto& v) -> bool {
using namespace async_mqtt5::detail;
if constexpr (is_optional<decltype(v)>)
if (v.has_value())
stream << *v << " ";
if constexpr (is_vector<decltype(v)>)
for (size_t i = 0; i < v.size(); i++) {
if constexpr (is_pair<decltype(v[i])>)
stream << "(" << v[i].first << ", " << v[i].second << ")";
else
stream << v[i];
if (i + 1 < v.size())
stream << ", ";
}
return true;
});
return stream.str();
std::ostringstream stream;
props.visit([&stream](const auto&, const auto& v) -> bool {
using namespace boost::mqtt5::detail;
if constexpr (is_optional<decltype(v)>)
if (v.has_value())
stream << *v << " ";
if constexpr (is_vector<decltype(v)>)
for (size_t i = 0; i < v.size(); i++) {
if constexpr (is_pair<decltype(v[i])>)
stream << "(" << v[i].first << ", " << v[i].second << ")";
else
stream << v[i];
if (i + 1 < v.size())
stream << ", ";
}
return true;
});
return stream.str();
}
using byte_citer = std::string::const_iterator;
template <
control_code_e code,
std::enable_if_t<code == control_code_e::connect, bool> = true
control_code_e code,
std::enable_if_t<code == control_code_e::connect, bool> = true
>
inline std::string to_string(uint32_t remain_length, byte_citer& it) {
auto connect = decoders::decode_connect(remain_length, it);
if (!connect.has_value())
return "Cannot decode Connect packet!";
auto& [cli_id, uname, pwd, keep_alive, clean_start, props, will] = *connect;
auto connect = decoders::decode_connect(remain_length, it);
if (!connect.has_value())
return "Cannot decode Connect packet!";
auto& [cli_id, uname, pwd, keep_alive, clean_start, props, will] = *connect;
return concat_strings(
code_to_str(code),
" uname: ", uname.value_or(""), " pwd: ", pwd.value_or(""),
" keep_alive: ", keep_alive, " clean_start: ", clean_start,
" props: ", to_readable_props(props)
);
return concat_strings(
code_to_str(code),
" uname: ", uname.value_or(""), " pwd: ", pwd.value_or(""),
" keep_alive: ", keep_alive, " clean_start: ", clean_start,
" props: ", to_readable_props(props)
);
}
template <
control_code_e code,
std::enable_if_t<code == control_code_e::connack, bool> = true
control_code_e code,
std::enable_if_t<code == control_code_e::connack, bool> = true
>
inline std::string to_string(uint32_t remain_length, byte_citer& it) {
auto connack = decoders::decode_connack(remain_length, it);
if (!connack.has_value())
return "Cannot decode Connack packet!";
auto& [session_present, reason_code, props] = *connack;
auto connack = decoders::decode_connack(remain_length, it);
if (!connack.has_value())
return "Cannot decode Connack packet!";
auto& [session_present, reason_code, props] = *connack;
return concat_strings(
code_to_str(code),
" session_present: ", session_present, " reason_code: ", reason_code,
" props: ", to_readable_props(props)
);
return concat_strings(
code_to_str(code),
" session_present: ", session_present, " reason_code: ", reason_code,
" props: ", to_readable_props(props)
);
}
template <
control_code_e code,
std::enable_if_t<code == control_code_e::disconnect, bool> = true
control_code_e code,
std::enable_if_t<code == control_code_e::disconnect, bool> = true
>
inline std::string to_string(uint32_t remain_length, byte_citer& it) {
auto disconnect = decoders::decode_disconnect(remain_length, it);
if (!disconnect.has_value())
return "Cannot decode Disconnect packet!";
auto& [reason_code, props] = *disconnect;
auto disconnect = decoders::decode_disconnect(remain_length, it);
if (!disconnect.has_value())
return "Cannot decode Disconnect packet!";
auto& [reason_code, props] = *disconnect;
return concat_strings(
code_to_str(code),
" reason_code: ", std::to_string(uint8_t(reason_code)),
" props: ", to_readable_props(props)
);
return concat_strings(
code_to_str(code),
" reason_code: ", std::to_string(uint8_t(reason_code)),
" props: ", to_readable_props(props)
);
}
template <
control_code_e code,
std::enable_if_t<code == control_code_e::publish, bool> = true
control_code_e code,
std::enable_if_t<code == control_code_e::publish, bool> = true
>
inline std::string to_string(
uint8_t control_byte, uint32_t remain_length, byte_citer& it
uint8_t control_byte, uint32_t remain_length, byte_citer& it
) {
auto publish = decoders::decode_publish(control_byte, remain_length, it);
if (!publish.has_value())
return "Cannot decode Publish packet!";
auto& [topic, packet_id, flags, props, payload] = *publish;
auto publish = decoders::decode_publish(control_byte, remain_length, it);
if (!publish.has_value())
return "Cannot decode Publish packet!";
auto& [topic, packet_id, flags, props, payload] = *publish;
return concat_strings(
code_to_str(code), (packet_id ? " " + std::to_string(*packet_id) : ""),
" flags: ", std::bitset<8>(flags),
" topic: ", topic, " payload: ", payload,
" props: ", to_readable_props(props)
);
return concat_strings(
code_to_str(code), (packet_id ? " " + std::to_string(*packet_id) : ""),
" flags: ", std::bitset<8>(flags),
" topic: ", topic, " payload: ", payload,
" props: ", to_readable_props(props)
);
}
template <
control_code_e code,
std::enable_if_t<
code == control_code_e::puback || code == control_code_e::pubrec ||
code == control_code_e::pubrel || code == control_code_e::pubcomp,
bool> = true
control_code_e code,
std::enable_if_t<
code == control_code_e::puback || code == control_code_e::pubrec ||
code == control_code_e::pubrel || code == control_code_e::pubcomp,
bool> = true
>
inline std::string to_string(uint32_t remain_length, byte_citer& it) {
const auto packet_id = decoders::decode_packet_id(it).value_or(0);
remain_length -= sizeof(uint16_t);
uint8_t reason_code = remain_length == 0 ? 0 : uint8_t(*it);
return concat_strings(
code_to_str(code),
" packet_id: ", packet_id, " reason_code: ", std::to_string(reason_code)
);
const auto packet_id = decoders::decode_packet_id(it).value_or(0);
remain_length -= sizeof(uint16_t);
uint8_t reason_code = remain_length == 0 ? 0 : uint8_t(*it);
return concat_strings(
code_to_str(code),
" packet_id: ", packet_id, " reason_code: ", std::to_string(reason_code)
);
}
template <
control_code_e code,
std::enable_if_t<code == control_code_e::auth, bool> = true
control_code_e code,
std::enable_if_t<code == control_code_e::auth, bool> = true
>
inline std::string to_string(uint32_t remain_length, byte_citer& it) {
auto auth = decoders::decode_auth(remain_length, it);
if (!auth.has_value())
return "Cannot decode Auth packet!";
auto& [reason_code, props] = *auth;
auto auth = decoders::decode_auth(remain_length, it);
if (!auth.has_value())
return "Cannot decode Auth packet!";
auto& [reason_code, props] = *auth;
return concat_strings(
code_to_str(code),
" reason_code: ", std::to_string(uint8_t(reason_code)),
" props: ", to_readable_props(props)
);
return concat_strings(
code_to_str(code),
" reason_code: ", std::to_string(uint8_t(reason_code)),
" props: ", to_readable_props(props)
);
}
template <
control_code_e code,
std::enable_if_t<code == control_code_e::subscribe, bool> = true
control_code_e code,
std::enable_if_t<code == control_code_e::subscribe, bool> = true
>
inline std::string to_string(uint32_t remain_length, byte_citer& it) {
const auto packet_id = decoders::decode_packet_id(it).value_or(0);
remain_length -= sizeof(uint16_t);
auto subscribe = decoders::decode_subscribe(remain_length, it);
if (!subscribe.has_value())
return "Cannot decode Subscribe packet!";
auto& [props, topics] = *subscribe;
const auto packet_id = decoders::decode_packet_id(it).value_or(0);
remain_length -= sizeof(uint16_t);
auto subscribe = decoders::decode_subscribe(remain_length, it);
if (!subscribe.has_value())
return "Cannot decode Subscribe packet!";
auto& [props, topics] = *subscribe;
std::vector<std::string> topics_str;
topics_str.resize(topics.size());
boost::transform(
boost::make_iterator_range(topics.cbegin(), topics.cend()),
topics_str.begin(),
[](const auto& tuple) {
auto& [topic, options] = tuple;
return concat_strings(topic, " ", std::bitset<8>(options));
}
);
return concat_strings(
code_to_str(code),
" packet_id: ", packet_id,
" topics: ", boost::algorithm::join(topics_str, ","),
" props: ", to_readable_props(props)
);
std::vector<std::string> topics_str;
topics_str.resize(topics.size());
boost::transform(
boost::make_iterator_range(topics.cbegin(), topics.cend()),
topics_str.begin(),
[](const auto& tuple) {
auto& [topic, options] = tuple;
return concat_strings(topic, " ", std::bitset<8>(options));
}
);
return concat_strings(
code_to_str(code),
" packet_id: ", packet_id,
" topics: ", boost::algorithm::join(topics_str, ","),
" props: ", to_readable_props(props)
);
}
template <
control_code_e code,
std::enable_if_t<code == control_code_e::unsubscribe, bool> = true
control_code_e code,
std::enable_if_t<code == control_code_e::unsubscribe, bool> = true
>
inline std::string to_string(uint32_t remain_length, byte_citer& it) {
const auto packet_id = decoders::decode_packet_id(it).value_or(0);
remain_length -= sizeof(uint16_t);
auto unsubscribe = decoders::decode_unsubscribe(remain_length, it);
if (!unsubscribe.has_value())
return "Cannot decode Unsubscribe packet!";
auto& [props, topics] = *unsubscribe;
const auto packet_id = decoders::decode_packet_id(it).value_or(0);
remain_length -= sizeof(uint16_t);
auto unsubscribe = decoders::decode_unsubscribe(remain_length, it);
if (!unsubscribe.has_value())
return "Cannot decode Unsubscribe packet!";
auto& [props, topics] = *unsubscribe;
return concat_strings(
code_to_str(code),
" packet_id: ", packet_id,
" topics: ", boost::algorithm::join(topics, ","),
" props: ", to_readable_props(props)
);
return concat_strings(
code_to_str(code),
" packet_id: ", packet_id,
" topics: ", boost::algorithm::join(topics, ","),
" props: ", to_readable_props(props)
);
}
inline std::string reason_codes_to_string(const std::vector<uint8_t>& rcs) {
std::vector<std::string> rcs_str;
rcs_str.resize(rcs.size());
boost::transform(
boost::make_iterator_range(rcs.cbegin(), rcs.cend()),
rcs_str.begin(),
[](const auto& rc) { return std::to_string(rc); }
);
return boost::algorithm::join(rcs_str, ",");
std::vector<std::string> rcs_str;
rcs_str.resize(rcs.size());
boost::transform(
boost::make_iterator_range(rcs.cbegin(), rcs.cend()),
rcs_str.begin(),
[](const auto& rc) { return std::to_string(rc); }
);
return boost::algorithm::join(rcs_str, ",");
}
template <
control_code_e code,
std::enable_if_t<code == control_code_e::suback, bool> = true
control_code_e code,
std::enable_if_t<code == control_code_e::suback, bool> = true
>
inline std::string to_string(uint32_t remain_length, byte_citer& it) {
const auto packet_id = decoders::decode_packet_id(it).value_or(0);
remain_length -= sizeof(uint16_t);
auto suback = decoders::decode_suback(remain_length, it);
if (!suback.has_value())
return "Cannot decode Suback packet!";
auto& [props, reason_codes] = *suback;
const auto packet_id = decoders::decode_packet_id(it).value_or(0);
remain_length -= sizeof(uint16_t);
auto suback = decoders::decode_suback(remain_length, it);
if (!suback.has_value())
return "Cannot decode Suback packet!";
auto& [props, reason_codes] = *suback;
return concat_strings(
code_to_str(code),
" packet_id: ", packet_id,
" reason_codes: ", reason_codes_to_string(reason_codes),
" props: ", to_readable_props(props)
);
return concat_strings(
code_to_str(code),
" packet_id: ", packet_id,
" reason_codes: ", reason_codes_to_string(reason_codes),
" props: ", to_readable_props(props)
);
}
template <
control_code_e code,
std::enable_if_t<code == control_code_e::unsuback, bool> = true
control_code_e code,
std::enable_if_t<code == control_code_e::unsuback, bool> = true
>
inline std::string to_string(uint32_t remain_length, byte_citer& it) {
const auto packet_id = decoders::decode_packet_id(it).value_or(0);
remain_length -= sizeof(uint16_t);
auto unsuback = decoders::decode_unsuback(remain_length, it);
if (!unsuback.has_value())
return "Cannot decode Unuback packet!";
auto& [props, reason_codes] = *unsuback;
const auto packet_id = decoders::decode_packet_id(it).value_or(0);
remain_length -= sizeof(uint16_t);
auto unsuback = decoders::decode_unsuback(remain_length, it);
if (!unsuback.has_value())
return "Cannot decode Unuback packet!";
auto& [props, reason_codes] = *unsuback;
return concat_strings(
code_to_str(code),
" packet_id: ", packet_id,
" reason_codes: ", reason_codes_to_string(reason_codes),
" props: ", to_readable_props(props)
);
return concat_strings(
code_to_str(code),
" packet_id: ", packet_id,
" reason_codes: ", reason_codes_to_string(reason_codes),
" props: ", to_readable_props(props)
);
}
} // end namespace detail
inline std::string to_readable_packet(std::string packet) {
auto control_byte = uint8_t(*packet.data());
auto code = detail::extract_code(control_byte);
auto control_byte = uint8_t(*packet.data());
auto code = detail::extract_code(control_byte);
if (
code == control_code_e::pingreq ||
code == control_code_e::pingresp
) {
return concat_strings(detail::code_to_str(code));
}
if (
code == control_code_e::pingreq ||
code == control_code_e::pingresp
) {
return concat_strings(detail::code_to_str(code));
}
auto begin = ++packet.cbegin();
auto varlen = decoders::type_parse(
begin, packet.cend(), decoders::basic::varint_
);
auto begin = ++packet.cbegin();
auto varlen = decoders::type_parse(
begin, packet.cend(), decoders::basic::varint_
);
switch (code) {
case control_code_e::connect:
return detail::to_string<control_code_e::connect>(*varlen, begin);
case control_code_e::connack:
return detail::to_string<control_code_e::connack>(*varlen, begin);
case control_code_e::disconnect:
return detail::to_string<control_code_e::disconnect>(*varlen, begin);
case control_code_e::publish:
return detail::to_string<control_code_e::publish>(
control_byte, *varlen, begin
);
case control_code_e::puback:
return detail::to_string<control_code_e::puback>(*varlen, begin);
case control_code_e::pubrec:
return detail::to_string<control_code_e::pubrec>(*varlen, begin);
case control_code_e::pubrel:
return detail::to_string<control_code_e::pubrel>(*varlen, begin);
case control_code_e::pubcomp:
return detail::to_string<control_code_e::pubcomp>(*varlen, begin);
case control_code_e::auth:
return detail::to_string<control_code_e::auth>(*varlen, begin);
case control_code_e::subscribe:
return detail::to_string<control_code_e::subscribe>(*varlen, begin);
case control_code_e::suback:
return detail::to_string<control_code_e::suback>(*varlen, begin);
case control_code_e::unsubscribe:
return detail::to_string<control_code_e::unsubscribe>(*varlen, begin);
case control_code_e::unsuback:
return detail::to_string<control_code_e::unsuback>(*varlen, begin);
default:
return "";
}
switch (code) {
case control_code_e::connect:
return detail::to_string<control_code_e::connect>(*varlen, begin);
case control_code_e::connack:
return detail::to_string<control_code_e::connack>(*varlen, begin);
case control_code_e::disconnect:
return detail::to_string<control_code_e::disconnect>(*varlen, begin);
case control_code_e::publish:
return detail::to_string<control_code_e::publish>(
control_byte, *varlen, begin
);
case control_code_e::puback:
return detail::to_string<control_code_e::puback>(*varlen, begin);
case control_code_e::pubrec:
return detail::to_string<control_code_e::pubrec>(*varlen, begin);
case control_code_e::pubrel:
return detail::to_string<control_code_e::pubrel>(*varlen, begin);
case control_code_e::pubcomp:
return detail::to_string<control_code_e::pubcomp>(*varlen, begin);
case control_code_e::auth:
return detail::to_string<control_code_e::auth>(*varlen, begin);
case control_code_e::subscribe:
return detail::to_string<control_code_e::subscribe>(*varlen, begin);
case control_code_e::suback:
return detail::to_string<control_code_e::suback>(*varlen, begin);
case control_code_e::unsubscribe:
return detail::to_string<control_code_e::unsubscribe>(*varlen, begin);
case control_code_e::unsuback:
return detail::to_string<control_code_e::unsuback>(*varlen, begin);
default:
return "";
}
}
template <typename ConstBufferSequence>
std::vector<std::string> to_readable_packets(const ConstBufferSequence& buffers) {
namespace asio = boost::asio;
namespace asio = boost::asio;
std::vector<std::string> content;
for (
auto it = asio::buffer_sequence_begin(buffers);
it != asio::buffer_sequence_end(buffers);
it++
)
content.push_back(
to_readable_packet(std::string { (const char*)it->data(), it->size() })
);
std::vector<std::string> content;
for (
auto it = asio::buffer_sequence_begin(buffers);
it != asio::buffer_sequence_end(buffers);
it++
)
content.push_back(
to_readable_packet(std::string { (const char*)it->data(), it->size() })
);
return content;
return content;
}
inline disconnect_props dprops_with_reason_string(const std::string& reason_string) {
disconnect_props dprops;
dprops[prop::reason_string] = reason_string;
return dprops;
disconnect_props dprops;
dprops[prop::reason_string] = reason_string;
return dprops;
}
} // end namespace async_mqtt5::test
} // end namespace boost::mqtt5::test
#endif // ASYNC_MQTT5_TEST_PACKET_UTIL_HPP
#endif // BOOST_MQTT5_TEST_PACKET_UTIL_HPP
@@ -5,82 +5,82 @@
// (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASYNC_MQTT5_TEST_TEST_AUTHENTICATORS_HPP
#define ASYNC_MQTT5_TEST_TEST_AUTHENTICATORS_HPP
#ifndef BOOST_MQTT5_TEST_TEST_AUTHENTICATORS_HPP
#define BOOST_MQTT5_TEST_TEST_AUTHENTICATORS_HPP
#include <string>
#include <string_view>
#include <boost/mqtt5/types.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/prepend.hpp>
#include <boost/system/error_code.hpp>
#include <async_mqtt5/types.hpp>
#include <string>
#include <string_view>
namespace async_mqtt5::test {
namespace boost::mqtt5::test {
namespace asio = boost::asio;
struct test_authenticator {
test_authenticator() = default;
test_authenticator() = default;
template <typename CompletionToken>
decltype(auto) async_auth(
auth_step_e step, std::string data,
CompletionToken&& token
) {
using error_code = boost::system::error_code;
using Signature = void (error_code, std::string);
template <typename CompletionToken>
decltype(auto) async_auth(
auth_step_e step, std::string data,
CompletionToken&& token
) {
using error_code = boost::system::error_code;
using Signature = void (error_code, std::string);
auto initiate = [](auto handler, auth_step_e, std::string) {
asio::dispatch(
asio::prepend(std::move(handler), error_code {}, "")
);
};
auto initiate = [](auto handler, auth_step_e, std::string) {
asio::dispatch(
asio::prepend(std::move(handler), error_code {}, "")
);
};
return asio::async_initiate<CompletionToken, Signature>(
initiate, token, step, std::move(data)
);
}
return asio::async_initiate<CompletionToken, Signature>(
initiate, token, step, std::move(data)
);
}
std::string_view method() const {
return "method";
}
std::string_view method() const {
return "method";
}
};
template <auth_step_e fail_on_step>
struct fail_test_authenticator {
fail_test_authenticator() = default;
fail_test_authenticator() = default;
template <typename CompletionToken>
decltype(auto) async_auth(
auth_step_e step, std::string data,
CompletionToken&& token
) {
using error_code = boost::system::error_code;
using Signature = void (error_code, std::string);
template <typename CompletionToken>
decltype(auto) async_auth(
auth_step_e step, std::string data,
CompletionToken&& token
) {
using error_code = boost::system::error_code;
using Signature = void (error_code, std::string);
auto initiate = [](auto handler, auth_step_e step, std::string) {
error_code ec;
if (fail_on_step == step)
ec = asio::error::no_recovery;
auto initiate = [](auto handler, auth_step_e step, std::string) {
error_code ec;
if (fail_on_step == step)
ec = asio::error::no_recovery;
asio::dispatch(
asio::prepend(std::move(handler), ec, "")
);
};
asio::dispatch(
asio::prepend(std::move(handler), ec, "")
);
};
return asio::async_initiate<CompletionToken, Signature>(
initiate, token, step, std::move(data)
);
}
return asio::async_initiate<CompletionToken, Signature>(
initiate, token, step, std::move(data)
);
}
std::string_view method() const {
return "method";
}
std::string_view method() const {
return "method";
}
};
} // end namespace async_mqtt5::test
} // end namespace boost::mqtt5::test
#endif // ASYNC_MQTT5_TEST_TEST_AUTHENTICATORS_HPP
#endif // BOOST_MQTT5_TEST_TEST_AUTHENTICATORS_HPP
@@ -4,131 +4,131 @@
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASYNC_MQTT5_TEST_AUTOCONNECT_STREAM_HPP
#define ASYNC_MQTT5_TEST_AUTOCONNECT_STREAM_HPP
#ifndef BOOST_MQTT5_TEST_AUTOCONNECT_STREAM_HPP
#define BOOST_MQTT5_TEST_AUTOCONNECT_STREAM_HPP
#include <boost/mqtt5/detail/async_mutex.hpp>
#include <boost/mqtt5/detail/async_traits.hpp>
#include <boost/mqtt5/detail/log_invoke.hpp>
#include <boost/mqtt5/impl/endpoints.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/system/error_code.hpp>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/system/error_code.hpp>
#include <async_mqtt5/detail/async_mutex.hpp>
#include <async_mqtt5/detail/async_traits.hpp>
#include <async_mqtt5/detail/log_invoke.hpp>
#include <async_mqtt5/impl/endpoints.hpp>
namespace async_mqtt5::test {
namespace boost::mqtt5::test {
namespace asio = boost::asio;
using error_code = boost::system::error_code;
template <
typename StreamType,
typename StreamContext = std::monostate,
typename LoggerType = async_mqtt5::noop_logger
typename StreamType,
typename StreamContext = std::monostate,
typename LoggerType = boost::mqtt5::noop_logger
>
class test_autoconnect_stream {
public:
using stream_type = StreamType;
using stream_ptr = std::shared_ptr<stream_type>;
using stream_context_type = StreamContext;
using executor_type = typename stream_type::executor_type;
using logger_type = LoggerType;
using stream_type = StreamType;
using stream_ptr = std::shared_ptr<stream_type>;
using stream_context_type = StreamContext;
using executor_type = typename stream_type::executor_type;
using logger_type = LoggerType;
private:
executor_type _stream_executor;
detail::async_mutex _conn_mtx;
asio::steady_timer _connect_timer;
detail::endpoints<logger_type> _endpoints;
executor_type _stream_executor;
detail::async_mutex _conn_mtx;
asio::steady_timer _connect_timer;
detail::endpoints<logger_type> _endpoints;
stream_ptr _stream_ptr;
stream_context_type& _stream_context;
stream_ptr _stream_ptr;
stream_context_type& _stream_context;
detail::log_invoke<logger_type> _log;
detail::log_invoke<logger_type> _log;
template <typename Stream>
friend class async_mqtt5::detail::reconnect_op;
template <typename Stream>
friend class boost::mqtt5::detail::reconnect_op;
public:
test_autoconnect_stream(
const executor_type& ex,
stream_context_type& context,
detail::log_invoke<logger_type>& log
) :
_stream_executor(ex),
_conn_mtx(_stream_executor),
_connect_timer(_stream_executor),
_endpoints(_stream_executor, _connect_timer, log),
_stream_context(context),
_log(log)
{
replace_next_layer(construct_next_layer());
open_lowest_layer(_stream_ptr, asio::ip::tcp::v4());
}
test_autoconnect_stream(
const executor_type& ex,
stream_context_type& context,
detail::log_invoke<logger_type>& log
) :
_stream_executor(ex),
_conn_mtx(_stream_executor),
_connect_timer(_stream_executor),
_endpoints(_stream_executor, _connect_timer, log),
_stream_context(context),
_log(log)
{
replace_next_layer(construct_next_layer());
open_lowest_layer(_stream_ptr, asio::ip::tcp::v4());
}
test_autoconnect_stream(const test_autoconnect_stream&) = delete;
test_autoconnect_stream& operator=(const test_autoconnect_stream&) = delete;
test_autoconnect_stream(const test_autoconnect_stream&) = delete;
test_autoconnect_stream& operator=(const test_autoconnect_stream&) = delete;
stream_ptr stream_pointer() const {
return _stream_ptr;
}
stream_ptr stream_pointer() const {
return _stream_ptr;
}
bool is_open() const noexcept {
return detail::lowest_layer(*_stream_ptr).is_open();
}
bool is_open() const noexcept {
return detail::lowest_layer(*_stream_ptr).is_open();
}
void brokers(std::string hosts, uint16_t default_port) {
_endpoints.brokers(std::move(hosts), default_port);
}
void brokers(std::string hosts, uint16_t default_port) {
_endpoints.brokers(std::move(hosts), default_port);
}
static void open_lowest_layer(const stream_ptr& sptr, asio::ip::tcp protocol) {
error_code ec;
auto& layer = detail::lowest_layer(*sptr);
layer.open(protocol, ec);
layer.set_option(asio::socket_base::reuse_address(true), ec);
layer.set_option(asio::ip::tcp::no_delay(true), ec);
}
static void open_lowest_layer(const stream_ptr& sptr, asio::ip::tcp protocol) {
error_code ec;
auto& layer = detail::lowest_layer(*sptr);
layer.open(protocol, ec);
layer.set_option(asio::socket_base::reuse_address(true), ec);
layer.set_option(asio::ip::tcp::no_delay(true), ec);
}
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);
}
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);
}
stream_ptr construct_next_layer() const {
stream_ptr sptr;
if constexpr (detail::has_tls_context<StreamContext>)
sptr = std::make_shared<stream_type>(
_stream_executor, _stream_context.tls_context()
);
else
sptr = std::make_shared<stream_type>(_stream_executor);
stream_ptr construct_next_layer() const {
stream_ptr sptr;
if constexpr (detail::has_tls_context<StreamContext>)
sptr = std::make_shared<stream_type>(
_stream_executor, _stream_context.tls_context()
);
else
sptr = std::make_shared<stream_type>(_stream_executor);
return sptr;
}
return sptr;
}
stream_ptr construct_and_open_next_layer(asio::ip::tcp protocol) const {
auto sptr = construct_next_layer();
open_lowest_layer(sptr, protocol);
return sptr;
}
stream_ptr construct_and_open_next_layer(asio::ip::tcp protocol) const {
auto sptr = construct_next_layer();
open_lowest_layer(sptr, protocol);
return sptr;
}
void replace_next_layer(stream_ptr sptr) {
if (_stream_ptr)
close();
std::exchange(_stream_ptr, std::move(sptr));
}
void replace_next_layer(stream_ptr sptr) {
if (_stream_ptr)
close();
std::exchange(_stream_ptr, std::move(sptr));
}
private:
detail::log_invoke<logger_type>& log() {
return _log;
}
detail::log_invoke<logger_type>& log() {
return _log;
}
};
} // end namespace async_mqtt5::test
} // end namespace boost::mqtt5::test
#endif // !ASYNC_MQTT5_TEST_AUTOCONNECT_STREAM_HPP
#endif // !BOOST_MQTT5_TEST_AUTOCONNECT_STREAM_HPP
+238 -239
View File
@@ -5,11 +5,27 @@
// (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASYNC_MQTT5_TEST_TEST_BROKER_HPP
#define ASYNC_MQTT5_TEST_TEST_BROKER_HPP
#ifndef BOOST_MQTT5_TEST_TEST_BROKER_HPP
#define BOOST_MQTT5_TEST_TEST_BROKER_HPP
#include <boost/test/unit_test.hpp>
#include <boost/mqtt5/types.hpp>
#include <boost/mqtt5/impl/codecs/message_decoders.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/asio/any_completion_handler.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/cancellation_signal.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/prepend.hpp>
#include <boost/system/error_code.hpp>
#include <boost/test/tools/interface.hpp>
#include <boost/test/unit_test.hpp>
#include <algorithm>
#include <cstdint>
@@ -19,309 +35,292 @@
#include <string>
#include <vector>
#include <boost/algorithm/string/join.hpp>
#include <boost/asio/any_completion_handler.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/cancellation_signal.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/prepend.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/system/error_code.hpp>
#include <async_mqtt5/types.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include "test_common/message_exchange.hpp"
#include "test_common/packet_util.hpp"
namespace async_mqtt5::test {
namespace boost::mqtt5::test {
namespace asio = boost::asio;
using error_code = boost::system::error_code;
class pending_read {
void* _buffer_data { nullptr };
size_t _buffer_size { 0 };
asio::any_completion_handler<void (error_code, size_t)> _handler {};
void* _buffer_data { nullptr };
size_t _buffer_size { 0 };
asio::any_completion_handler<void (error_code, size_t)> _handler {};
public:
template <typename MutableBuffer, typename Handler>
pending_read(const MutableBuffer& buffer, Handler&& handler) :
_buffer_data(buffer.data()),
_buffer_size(buffer.size()),
_handler(std::move(handler))
{}
template <typename MutableBuffer, typename Handler>
pending_read(const MutableBuffer& buffer, Handler&& handler) :
_buffer_data(buffer.data()),
_buffer_size(buffer.size()),
_handler(std::move(handler))
{}
pending_read() = default;
pending_read() = default;
pending_read(pending_read&&) = default;
pending_read(const pending_read&) = delete;
pending_read(pending_read&&) = default;
pending_read(const pending_read&) = delete;
pending_read& operator=(pending_read&&) = default;
pending_read& operator=(const pending_read&) = delete;
pending_read& operator=(pending_read&&) = default;
pending_read& operator=(const pending_read&) = delete;
size_t consume(const std::vector<uint8_t>& data) {
size_t num_bytes = std::min(_buffer_size, data.size());
if (num_bytes == 0)
return 0;
std::memcpy(_buffer_data, data.data(), num_bytes);
return num_bytes;
}
size_t consume(const std::vector<uint8_t>& data) {
size_t num_bytes = std::min(_buffer_size, data.size());
if (num_bytes == 0)
return 0;
std::memcpy(_buffer_data, data.data(), num_bytes);
return num_bytes;
}
template <typename Executor>
void complete(const Executor& ex, error_code ec, size_t bytes_read) {
if (empty())
return;
if (ec || bytes_read || _buffer_size == 0)
asio::post(ex, asio::prepend(std::move(_handler), ec, bytes_read));
}
template <typename Executor>
void complete(const Executor& ex, error_code ec, size_t bytes_read) {
if (empty())
return;
if (ec || bytes_read || _buffer_size == 0)
asio::post(ex, asio::prepend(std::move(_handler), ec, bytes_read));
}
constexpr bool empty() const {
return !_handler;
}
constexpr bool empty() const {
return !_handler;
}
};
class test_broker : public asio::execution_context::service {
public:
using executor_type = asio::any_io_executor;
using protocol_type = asio::ip::tcp;
using endpoint_type = asio::ip::tcp::endpoint;
using executor_type = asio::any_io_executor;
using protocol_type = asio::ip::tcp;
using endpoint_type = asio::ip::tcp::endpoint;
static inline asio::execution_context::id id {};
static inline asio::execution_context::id id {};
private:
using base = asio::execution_context::service;
using base = asio::execution_context::service;
struct on_receive {};
struct on_delayed_complete {};
struct on_receive {};
struct on_delayed_complete {};
struct broker_data {
error_code ec;
std::vector<uint8_t> bytes;
};
struct broker_data {
error_code ec;
std::vector<uint8_t> bytes;
};
executor_type _ex;
std::deque<broker_data> _broker_data;
pending_read _pending_read;
executor_type _ex;
std::deque<broker_data> _broker_data;
pending_read _pending_read;
msg_exchange _broker_side;
std::vector<std::unique_ptr<asio::cancellation_signal>> _cancel_signals;
msg_exchange _broker_side;
std::vector<std::unique_ptr<asio::cancellation_signal>> _cancel_signals;
public:
explicit test_broker(
asio::execution_context& context,
asio::any_io_executor ex = {}, msg_exchange broker_side = {}
) :
base(context), _ex(std::move(ex)), _broker_side(std::move(broker_side))
{
launch_broker_ops();
}
explicit test_broker(
asio::execution_context& context,
asio::any_io_executor ex = {}, msg_exchange broker_side = {}
) :
base(context), _ex(std::move(ex)), _broker_side(std::move(broker_side))
{
launch_broker_ops();
}
test_broker(const test_broker&) = delete;
test_broker& operator=(const test_broker&) = delete;
test_broker(const test_broker&) = delete;
test_broker& operator=(const test_broker&) = delete;
executor_type get_executor() const noexcept {
return _ex;
}
executor_type get_executor() const noexcept {
return _ex;
}
void close_connection() {
_pending_read.complete(
get_executor(), asio::error::operation_aborted, 0
);
void close_connection() {
_pending_read.complete(
get_executor(), asio::error::operation_aborted, 0
);
for (auto& cs : _cancel_signals)
cs->emit(asio::cancellation_type::terminal);
for (auto& cs : _cancel_signals)
cs->emit(asio::cancellation_type::terminal);
_broker_data.clear();
}
_broker_data.clear();
}
bool received_all_expected() {
return !_broker_side.has_remaining_messages();
}
bool received_all_expected() {
return !_broker_side.has_remaining_messages();
}
template <typename ConstBufferSequence, typename WriteToken>
decltype(auto) write_to_network(
const ConstBufferSequence& buffers,
WriteToken&& token
) {
auto initiation = [this](
auto handler, const ConstBufferSequence& buffers
) {
auto reply_action = _broker_side.pop_reply_action();
template <typename ConstBufferSequence, typename WriteToken>
decltype(auto) write_to_network(
const ConstBufferSequence& buffers,
WriteToken&& token
) {
auto initiation = [this](
auto handler, const ConstBufferSequence& buffers
) {
auto reply_action = _broker_side.pop_reply_action();
size_t bytes_written = std::accumulate(
asio::buffer_sequence_begin(buffers),
asio::buffer_sequence_end(buffers),
size_t(0), [](size_t a, const auto& b) { return a + b.size(); }
);
size_t bytes_written = std::accumulate(
asio::buffer_sequence_begin(buffers),
asio::buffer_sequence_end(buffers),
size_t(0), [](size_t a, const auto& b) { return a + b.size(); }
);
executor_type ex = get_executor();
executor_type ex = get_executor();
if (reply_action) {
const auto& expected = reply_action->expected_packets();
if (reply_action) {
const auto& expected = reply_action->expected_packets();
size_t buffers_size = std::distance(
asio::buffer_sequence_begin(buffers), asio::buffer_sequence_end(buffers)
);
BOOST_TEST(buffers_size == expected.size());
size_t buffers_size = std::distance(
asio::buffer_sequence_begin(buffers), asio::buffer_sequence_end(buffers)
);
BOOST_TEST(buffers_size == expected.size());
size_t num_packets = std::min(buffers_size, expected.size());
auto it = asio::buffer_sequence_begin(buffers);
for (size_t i = 0; i < num_packets; ++i, ++it) {
BOOST_TEST(it->size() == expected[i].size());
size_t len = std::min(it->size(), expected[i].size());
if (memcmp(it->data(), expected[i].data(), len))
BOOST_TEST_MESSAGE(
concat_strings(
"Packet mismatch!\nExpected: ",
to_readable_packet(expected[i]),
"\nReceived: ",
to_readable_packet(std::string((const char*)it->data(), it->size()))
)
);
}
} else
BOOST_TEST_MESSAGE(
"Broker side did not expect: " <<
boost::algorithm::join(to_readable_packets(buffers), ",")
);
size_t num_packets = std::min(buffers_size, expected.size());
auto it = asio::buffer_sequence_begin(buffers);
for (size_t i = 0; i < num_packets; ++i, ++it) {
BOOST_TEST(it->size() == expected[i].size());
size_t len = std::min(it->size(), expected[i].size());
if (memcmp(it->data(), expected[i].data(), len))
BOOST_TEST_MESSAGE(
concat_strings(
"Packet mismatch!\nExpected: ",
to_readable_packet(expected[i]),
"\nReceived: ",
to_readable_packet(std::string((const char*)it->data(), it->size()))
)
);
}
} else
BOOST_TEST_MESSAGE(
"Broker side did not expect: " <<
boost::algorithm::join(to_readable_packets(buffers), ",")
);
auto complete_op = reply_action ?
reply_action->write_completion(ex) :
delayed_op<error_code>(ex, 0ms, error_code {});
auto complete_op = reply_action ?
reply_action->write_completion(ex) :
delayed_op<error_code>(ex, 0ms, error_code {});
async_delay(
make_cancel_slot(), std::move(complete_op),
asio::prepend(
std::ref(*this), on_delayed_complete {},
std::move(handler), bytes_written
)
);
async_delay(
make_cancel_slot(), std::move(complete_op),
asio::prepend(
std::ref(*this), on_delayed_complete {},
std::move(handler), bytes_written
)
);
if (!reply_action.has_value())
return;
if (!reply_action.has_value())
return;
for (auto& op : reply_action->pop_reply_ops(ex))
async_delay(
make_cancel_slot(), std::move(op),
asio::prepend(std::ref(*this), on_receive {})
);
};
for (auto& op : reply_action->pop_reply_ops(ex))
async_delay(
make_cancel_slot(), std::move(op),
asio::prepend(std::ref(*this), on_receive {})
);
};
return asio::async_initiate<WriteToken, void (error_code, size_t)>(
std::move(initiation), token, buffers
);
}
return asio::async_initiate<WriteToken, void (error_code, size_t)>(
std::move(initiation), token, buffers
);
}
template <typename MutableBuffer, typename ReadToken>
decltype(auto) read_from_network(
const MutableBuffer& buffer, ReadToken&& token
) {
auto initiation = [this](
auto handler, const MutableBuffer& buffer
) {
_pending_read = pending_read(buffer, std::move(handler));
complete_read();
};
template <typename MutableBuffer, typename ReadToken>
decltype(auto) read_from_network(
const MutableBuffer& buffer, ReadToken&& token
) {
auto initiation = [this](
auto handler, const MutableBuffer& buffer
) {
_pending_read = pending_read(buffer, std::move(handler));
complete_read();
};
return asio::async_initiate<ReadToken, void (error_code, size_t)>(
std::move(initiation), token, buffer
);
}
return asio::async_initiate<ReadToken, void (error_code, size_t)>(
std::move(initiation), token, buffer
);
}
void operator()(
on_receive, error_code delay_ec,
error_code ec, std::vector<uint8_t> bytes
) {
remove_cancel_signal();
void operator()(
on_receive, error_code delay_ec,
error_code ec, std::vector<uint8_t> bytes
) {
remove_cancel_signal();
if (delay_ec) // asio::operation_aborted
return;
if (delay_ec) // asio::operation_aborted
return;
_broker_data.push_back({ ec, std::move(bytes) });
complete_read();
}
_broker_data.push_back({ ec, std::move(bytes) });
complete_read();
}
template <typename Handler>
void operator()(
on_delayed_complete, Handler handler, size_t bytes,
error_code delay_ec, error_code ec
) {
remove_cancel_signal();
template <typename Handler>
void operator()(
on_delayed_complete, Handler handler, size_t bytes,
error_code delay_ec, error_code ec
) {
remove_cancel_signal();
if (delay_ec) { // asio::operation_aborted
ec = delay_ec;
bytes = 0;
}
if (delay_ec) { // asio::operation_aborted
ec = delay_ec;
bytes = 0;
}
asio::dispatch(asio::prepend(std::move(handler), ec, bytes));
}
asio::dispatch(asio::prepend(std::move(handler), ec, bytes));
}
void cancel_pending_read() {
_pending_read.complete(get_executor(), asio::error::operation_aborted, 0);
}
void cancel_pending_read() {
_pending_read.complete(get_executor(), asio::error::operation_aborted, 0);
}
private:
void shutdown() override {
cancel_pending_read();
}
void shutdown() override {
cancel_pending_read();
}
void launch_broker_ops() {
for (auto& op: _broker_side.pop_broker_ops(get_executor())) {
async_delay(
asio::cancellation_slot {},
std::move(op),
asio::prepend(std::ref(*this), on_receive {})
);
}
}
void launch_broker_ops() {
for (auto& op: _broker_side.pop_broker_ops(get_executor())) {
async_delay(
asio::cancellation_slot {},
std::move(op),
asio::prepend(std::ref(*this), on_receive {})
);
}
}
void complete_read() {
if (_pending_read.empty())
return;
void complete_read() {
if (_pending_read.empty())
return;
error_code ec = {};
size_t bytes_read = 0;
error_code ec = {};
size_t bytes_read = 0;
if (!_broker_data.empty()) {
auto& [read_ec, bytes] = _broker_data.front();
ec = read_ec;
bytes_read = _pending_read.consume(bytes);
if (!_broker_data.empty()) {
auto& [read_ec, bytes] = _broker_data.front();
ec = read_ec;
bytes_read = _pending_read.consume(bytes);
if (bytes_read == bytes.size())
_broker_data.pop_front();
else
bytes.erase(bytes.begin(), bytes.begin() + bytes_read);
}
if (bytes_read == bytes.size())
_broker_data.pop_front();
else
bytes.erase(bytes.begin(), bytes.begin() + bytes_read);
}
_pending_read.complete(get_executor(), ec, bytes_read);
}
_pending_read.complete(get_executor(), ec, bytes_read);
}
asio::cancellation_slot make_cancel_slot() {
_cancel_signals.push_back(
std::make_unique<asio::cancellation_signal>()
);
return _cancel_signals.back()->slot();
}
asio::cancellation_slot make_cancel_slot() {
_cancel_signals.push_back(
std::make_unique<asio::cancellation_signal>()
);
return _cancel_signals.back()->slot();
}
void remove_cancel_signal() {
_cancel_signals.erase(
std::remove_if(
_cancel_signals.begin(), _cancel_signals.end(),
[](auto& sig_ptr) { return !sig_ptr->slot().has_handler(); }
),
_cancel_signals.end()
);
}
void remove_cancel_signal() {
_cancel_signals.erase(
std::remove_if(
_cancel_signals.begin(), _cancel_signals.end(),
[](auto& sig_ptr) { return !sig_ptr->slot().has_handler(); }
),
_cancel_signals.end()
);
}
};
} // end namespace async_mqtt5::test
} // end namespace boost::mqtt5::test
#endif // ASYNC_MQTT5_TEST_TEST_BROKER_HPP
#endif // BOOST_MQTT5_TEST_TEST_BROKER_HPP
+53 -53
View File
@@ -5,11 +5,12 @@
// (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASYNC_MQTT5_TEST_TEST_SERVICE_HPP
#define ASYNC_MQTT5_TEST_TEST_SERVICE_HPP
#ifndef BOOST_MQTT5_TEST_TEST_SERVICE_HPP
#define BOOST_MQTT5_TEST_TEST_SERVICE_HPP
#include <cstdint>
#include <variant>
#include <boost/mqtt5/types.hpp>
#include <boost/mqtt5/impl/client_service.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/async_result.hpp>
@@ -17,77 +18,76 @@
#include <boost/asio/prepend.hpp>
#include <boost/system/error_code.hpp>
#include <async_mqtt5/types.hpp>
#include <cstdint>
#include <variant>
#include <async_mqtt5/impl/client_service.hpp>
namespace async_mqtt5::test {
namespace boost::mqtt5::test {
namespace asio = boost::asio;
template <
typename StreamType,
typename TlsContext = std::monostate
typename StreamType,
typename TlsContext = std::monostate
>
class test_service : public async_mqtt5::detail::client_service<StreamType, TlsContext> {
using error_code = boost::system::error_code;
using base = async_mqtt5::detail::client_service<StreamType, TlsContext>;
class test_service : public boost::mqtt5::detail::client_service<StreamType, TlsContext> {
using error_code = boost::system::error_code;
using base = boost::mqtt5::detail::client_service<StreamType, TlsContext>;
asio::any_io_executor _ex;
connack_props _test_props;
asio::any_io_executor _ex;
connack_props _test_props;
public:
explicit test_service(const asio::any_io_executor& ex)
: base(ex), _ex(ex)
{}
explicit test_service(const asio::any_io_executor& ex)
: base(ex), _ex(ex)
{}
test_service(const asio::any_io_executor& ex, connack_props props)
: base(ex), _ex(ex), _test_props(std::move(props))
{}
test_service(const asio::any_io_executor& ex, connack_props props)
: base(ex), _ex(ex), _test_props(std::move(props))
{}
template <typename BufferType, typename CompletionToken>
decltype(auto) async_send(
const BufferType&, uint32_t, unsigned,
CompletionToken&& token
) {
auto initiation = [this](auto handler) {
asio::post(_ex,
asio::prepend(std::move(handler), error_code {})
);
};
template <typename BufferType, typename CompletionToken>
decltype(auto) async_send(
const BufferType&, uint32_t, unsigned,
CompletionToken&& token
) {
auto initiation = [this](auto handler) {
asio::post(_ex,
asio::prepend(std::move(handler), error_code {})
);
};
return asio::async_initiate<CompletionToken, void (error_code)>(
std::move(initiation), token
);
}
return asio::async_initiate<CompletionToken, void (error_code)>(
std::move(initiation), token
);
}
template <typename Prop>
const auto& connack_property(Prop p) const {
return _test_props[p];
}
template <typename Prop>
const auto& connack_property(Prop p) const {
return _test_props[p];
}
const auto& connack_properties() {
return _test_props;
}
const auto& connack_properties() {
return _test_props;
}
};
template <
typename StreamType,
typename TlsContext = std::monostate
typename StreamType,
typename TlsContext = std::monostate
>
class overrun_client : public async_mqtt5::detail::client_service<StreamType, TlsContext> {
class overrun_client : public boost::mqtt5::detail::client_service<StreamType, TlsContext> {
public:
explicit overrun_client(const asio::any_io_executor& ex) :
async_mqtt5::detail::client_service<StreamType, TlsContext>(ex)
{}
explicit overrun_client(const asio::any_io_executor& ex) :
boost::mqtt5::detail::client_service<StreamType, TlsContext>(ex)
{}
uint16_t allocate_pid() {
return 0;
}
uint16_t allocate_pid() {
return 0;
}
};
} // end namespace async_mqtt5::test
} // end namespace boost::mqtt5::test
#endif // ASYNC_MQTT5_TEST_TEST_SERVICE_HPP
#endif // BOOST_MQTT5_TEST_TEST_SERVICE_HPP
+250 -250
View File
@@ -5,28 +5,28 @@
// (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASYNC_MQTT5_TEST_TEST_STREAM_HPP
#define ASYNC_MQTT5_TEST_TEST_STREAM_HPP
#ifndef BOOST_MQTT5_TEST_TEST_STREAM_HPP
#define BOOST_MQTT5_TEST_TEST_STREAM_HPP
#include <chrono>
#include <memory>
#include <boost/mqtt5/detail/cancellable_handler.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/prepend.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/recycling_allocator.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/system/error_code.hpp>
#include <async_mqtt5/detail/cancellable_handler.hpp>
#include <chrono>
#include <memory>
#include "test_common/test_broker.hpp"
namespace async_mqtt5::test {
namespace boost::mqtt5::test {
namespace asio = boost::asio;
@@ -38,337 +38,337 @@ namespace detail {
class test_stream_impl {
public:
using executor_type = test_broker::executor_type;
using protocol_type = test_broker::protocol_type;
using endpoint_type = test_broker::endpoint_type;
using executor_type = test_broker::executor_type;
using protocol_type = test_broker::protocol_type;
using endpoint_type = test_broker::endpoint_type;
private:
executor_type _ex;
test_broker* _test_broker { nullptr };
endpoint_type _remote_ep;
executor_type _ex;
test_broker* _test_broker { nullptr };
endpoint_type _remote_ep;
template <typename Handler>
friend class read_op;
template <typename Handler>
friend class read_op;
template <typename Handler>
friend class write_op;
template <typename Handler>
friend class write_op;
public:
explicit test_stream_impl(executor_type ex) : _ex(std::move(ex)) {}
explicit test_stream_impl(executor_type ex) : _ex(std::move(ex)) {}
test_stream_impl(test_stream_impl&&) = default;
test_stream_impl(const test_stream_impl&) = delete;
test_stream_impl(test_stream_impl&&) = default;
test_stream_impl(const test_stream_impl&) = delete;
test_stream_impl& operator=(test_stream_impl&&) = default;
test_stream_impl& operator=(const test_stream_impl&) = delete;
test_stream_impl& operator=(test_stream_impl&&) = default;
test_stream_impl& operator=(const test_stream_impl&) = delete;
executor_type get_executor() const noexcept {
return _ex;
}
executor_type get_executor() const noexcept {
return _ex;
}
void open(const protocol_type&, error_code& ec) {
ec = {};
_test_broker = &asio::use_service<test_broker>(_ex.context());
}
void open(const protocol_type&, error_code& ec) {
ec = {};
_test_broker = &asio::use_service<test_broker>(_ex.context());
}
void cancel(error_code&) {}
void cancel(error_code&) {}
void close(error_code& ec) {
disconnect();
ec = {};
_test_broker = nullptr;
}
void close(error_code& ec) {
disconnect();
ec = {};
_test_broker = nullptr;
}
void shutdown(asio::ip::tcp::socket::shutdown_type, error_code& ec) {
ec = {};
}
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;
}
void connect(const endpoint_type& ep, error_code& ec) {
ec = {};
_remote_ep = ep;
}
void disconnect() {
_remote_ep = {};
if (_test_broker)
_test_broker->close_connection();
}
void disconnect() {
_remote_ep = {};
if (_test_broker)
_test_broker->close_connection();
}
endpoint_type remote_endpoint(error_code& ec) {
if (_remote_ep == endpoint_type {})
ec = asio::error::not_connected;
else
ec = {};
return _remote_ep;
}
endpoint_type remote_endpoint(error_code& ec) {
if (_remote_ep == endpoint_type {})
ec = asio::error::not_connected;
else
ec = {};
return _remote_ep;
}
bool is_open() const {
return _test_broker != nullptr;
}
bool is_open() const {
return _test_broker != nullptr;
}
bool is_connected() const {
return _remote_ep != endpoint_type {};
}
bool is_connected() const {
return _remote_ep != endpoint_type {};
}
};
template <typename Handler>
class read_op {
struct on_read {};
std::shared_ptr<test_stream_impl> _stream_impl;
struct on_read {};
std::shared_ptr<test_stream_impl> _stream_impl;
using handler_type = async_mqtt5::detail::cancellable_handler<
Handler,
typename test_stream_impl::executor_type
>;
handler_type _handler;
using handler_type = boost::mqtt5::detail::cancellable_handler<
Handler,
typename test_stream_impl::executor_type
>;
handler_type _handler;
public:
read_op(
std::shared_ptr<test_stream_impl> stream_impl, Handler handler
) :
_stream_impl(std::move(stream_impl)),
_handler(std::move(handler), _stream_impl->get_executor())
{
auto slot = asio::get_associated_cancellation_slot(_handler);
if (slot.is_connected())
slot.assign([stream_impl = _stream_impl](asio::cancellation_type_t) {
stream_impl->_test_broker->cancel_pending_read();
});
}
read_op(
std::shared_ptr<test_stream_impl> stream_impl, Handler handler
) :
_stream_impl(std::move(stream_impl)),
_handler(std::move(handler), _stream_impl->get_executor())
{
auto slot = asio::get_associated_cancellation_slot(_handler);
if (slot.is_connected())
slot.assign([stream_impl = _stream_impl](asio::cancellation_type_t) {
stream_impl->_test_broker->cancel_pending_read();
});
}
read_op(read_op&&) = default;
read_op(const read_op&) = delete;
read_op(read_op&&) = default;
read_op(const read_op&) = delete;
read_op& operator=(read_op&&) noexcept = default;
read_op& operator=(const read_op&) = delete;
read_op& operator=(read_op&&) noexcept = default;
read_op& operator=(const read_op&) = delete;
using allocator_type = asio::recycling_allocator<void>;
allocator_type get_allocator() const noexcept {
return allocator_type {};
}
using allocator_type = asio::recycling_allocator<void>;
allocator_type get_allocator() const noexcept {
return allocator_type {};
}
using executor_type = test_stream_impl::executor_type;
executor_type get_executor() const noexcept {
return _stream_impl->get_executor();
}
using executor_type = test_stream_impl::executor_type;
executor_type get_executor() const noexcept {
return _stream_impl->get_executor();
}
template <typename BufferType>
void perform(const BufferType& buffer) {
if (!_stream_impl->is_open() || !_stream_impl->is_connected())
return complete_immediate(asio::error::not_connected, 0);
template <typename BufferType>
void perform(const BufferType& buffer) {
if (!_stream_impl->is_open() || !_stream_impl->is_connected())
return complete_immediate(asio::error::not_connected, 0);
_stream_impl->_test_broker->read_from_network(
buffer,
asio::prepend(std::move(*this), on_read {})
);
}
_stream_impl->_test_broker->read_from_network(
buffer,
asio::prepend(std::move(*this), on_read {})
);
}
void operator()(on_read, error_code ec, size_t bytes_read) {
if (ec)
_stream_impl->disconnect();
complete(ec, bytes_read);
}
void operator()(on_read, error_code ec, size_t bytes_read) {
if (ec)
_stream_impl->disconnect();
complete(ec, bytes_read);
}
private:
void complete_immediate(error_code ec, size_t bytes_read) {
_handler.complete_immediate(ec, bytes_read);
}
void complete_immediate(error_code ec, size_t bytes_read) {
_handler.complete_immediate(ec, bytes_read);
}
void complete(error_code ec, size_t bytes_read) {
_handler.complete(ec, bytes_read);
}
void complete(error_code ec, size_t bytes_read) {
_handler.complete(ec, bytes_read);
}
};
template <typename Handler>
class write_op {
struct on_write {};
struct on_write {};
std::shared_ptr<test_stream_impl> _stream_impl;
Handler _handler;
std::shared_ptr<test_stream_impl> _stream_impl;
Handler _handler;
public:
write_op(
std::shared_ptr<test_stream_impl> stream_impl, Handler handler
) :
_stream_impl(std::move(stream_impl)),
_handler(std::move(handler))
{}
write_op(
std::shared_ptr<test_stream_impl> stream_impl, Handler handler
) :
_stream_impl(std::move(stream_impl)),
_handler(std::move(handler))
{}
write_op(write_op&&) = default;
write_op(const write_op&) = delete;
write_op(write_op&&) = default;
write_op(const write_op&) = delete;
write_op& operator=(write_op&&) noexcept = default;
write_op& operator=(const write_op&) = delete;
write_op& operator=(write_op&&) noexcept = default;
write_op& operator=(const write_op&) = delete;
using allocator_type = asio::recycling_allocator<void>;
allocator_type get_allocator() const noexcept {
return allocator_type {};
}
using allocator_type = asio::recycling_allocator<void>;
allocator_type get_allocator() const noexcept {
return allocator_type {};
}
using executor_type = test_stream_impl::executor_type;
executor_type get_executor() const noexcept {
return _stream_impl->get_executor();
}
using executor_type = test_stream_impl::executor_type;
executor_type get_executor() const noexcept {
return _stream_impl->get_executor();
}
template <typename BufferType>
void perform(const BufferType& buffers) {
if (!_stream_impl->is_open() || !_stream_impl->is_connected())
return complete_post(asio::error::not_connected, 0);
template <typename BufferType>
void perform(const BufferType& buffers) {
if (!_stream_impl->is_open() || !_stream_impl->is_connected())
return complete_post(asio::error::not_connected, 0);
_stream_impl->_test_broker->write_to_network(
buffers,
asio::prepend(std::move(*this), on_write {})
);
}
_stream_impl->_test_broker->write_to_network(
buffers,
asio::prepend(std::move(*this), on_write {})
);
}
void operator()(on_write, error_code ec, size_t bytes_written) {
if (ec)
_stream_impl->disconnect();
complete(ec, bytes_written);
}
void operator()(on_write, error_code ec, size_t bytes_written) {
if (ec)
_stream_impl->disconnect();
complete(ec, bytes_written);
}
private:
void complete_post(error_code ec, size_t bytes_written) {
asio::post(
get_executor(),
asio::prepend(std::move(_handler), ec, bytes_written)
);
}
void complete_post(error_code ec, size_t bytes_written) {
asio::post(
get_executor(),
asio::prepend(std::move(_handler), ec, bytes_written)
);
}
void complete(error_code ec, size_t bytes_written) {
asio::dispatch(
get_executor(),
asio::prepend(std::move(_handler), ec, bytes_written)
);
}
void complete(error_code ec, size_t bytes_written) {
asio::dispatch(
get_executor(),
asio::prepend(std::move(_handler), ec, bytes_written)
);
}
};
} // end namespace detail
class test_stream {
public:
using executor_type = test_broker::executor_type;
using protocol_type = test_broker::protocol_type;
using endpoint_type = test_broker::endpoint_type;
using executor_type = test_broker::executor_type;
using protocol_type = test_broker::protocol_type;
using endpoint_type = test_broker::endpoint_type;
private:
std::shared_ptr<detail::test_stream_impl> _impl;
std::shared_ptr<detail::test_stream_impl> _impl;
public:
explicit test_stream(executor_type ex) :
_impl(std::make_shared<detail::test_stream_impl>(std::move(ex)))
{}
explicit test_stream(executor_type ex) :
_impl(std::make_shared<detail::test_stream_impl>(std::move(ex)))
{}
test_stream(const test_stream&) = delete;
test_stream& operator=(const test_stream&) = delete;
test_stream(const test_stream&) = delete;
test_stream& operator=(const test_stream&) = delete;
~test_stream() {
error_code ec;
close(ec); // cancel() would be more appropriate
}
~test_stream() {
error_code ec;
close(ec); // cancel() would be more appropriate
}
executor_type get_executor() const noexcept {
return _impl->get_executor();
}
executor_type get_executor() const noexcept {
return _impl->get_executor();
}
void open(const protocol_type& p, error_code& ec) {
_impl->open(p, ec);
}
void open(const protocol_type& p, error_code& ec) {
_impl->open(p, ec);
}
void cancel(error_code& ec) {
_impl->cancel(ec);
}
void cancel(error_code& ec) {
_impl->cancel(ec);
}
void close(error_code& ec) {
_impl->close(ec);
}
void close(error_code& ec) {
_impl->close(ec);
}
void connect(const endpoint_type& ep, error_code& ec) {
_impl->connect(ep, ec);
}
void connect(const endpoint_type& ep, error_code& ec) {
_impl->connect(ep, ec);
}
void disconnect() {
_impl->disconnect();
}
void disconnect() {
_impl->disconnect();
}
bool is_open() const {
return _impl->is_open();
}
bool is_open() const {
return _impl->is_open();
}
bool is_connected() const {
return _impl->is_connected();
}
bool is_connected() const {
return _impl->is_connected();
}
void shutdown(asio::ip::tcp::socket::shutdown_type st, error_code& ec) {
return _impl->shutdown(st, ec);
}
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);
}
endpoint_type remote_endpoint(error_code& ec) {
return _impl->remote_endpoint(ec);
}
template<typename SettableSocketOption>
void set_option(const SettableSocketOption&, error_code&) {}
template<typename SettableSocketOption>
void set_option(const SettableSocketOption&, error_code&) {}
template <typename ConnectToken>
decltype(auto) async_connect(
const endpoint_type& ep, ConnectToken&& token
) {
template <typename ConnectToken>
decltype(auto) async_connect(
const endpoint_type& ep, ConnectToken&& token
) {
auto initiation = [this](auto handler, const endpoint_type& ep) {
error_code ec;
open(asio::ip::tcp::v4(), ec);
auto initiation = [this](auto handler, const endpoint_type& ep) {
error_code ec;
open(asio::ip::tcp::v4(), ec);
if (!ec)
connect(ep, ec);
if (!ec)
connect(ep, ec);
asio::post(get_executor(), asio::prepend(std::move(handler), ec));
};
asio::post(get_executor(), asio::prepend(std::move(handler), ec));
};
return asio::async_initiate<ConnectToken, void (error_code)>(
std::move(initiation), token, ep
);
}
return asio::async_initiate<ConnectToken, void (error_code)>(
std::move(initiation), token, ep
);
}
template<typename ConstBufferSequence, typename WriteToken>
decltype(auto) async_write_some(
const ConstBufferSequence& buffers, WriteToken&& token
) {
using Signature = void (error_code, size_t);
template<typename ConstBufferSequence, typename WriteToken>
decltype(auto) async_write_some(
const ConstBufferSequence& buffers, WriteToken&& token
) {
using Signature = void (error_code, size_t);
auto initiation = [this](
auto handler, const ConstBufferSequence& buffers
) {
detail::write_op { _impl, std::move(handler) }.perform(buffers);
};
auto initiation = [this](
auto handler, const ConstBufferSequence& buffers
) {
detail::write_op { _impl, std::move(handler) }.perform(buffers);
};
return asio::async_initiate<WriteToken, Signature>(
std::move(initiation), token, buffers
);
}
return asio::async_initiate<WriteToken, Signature>(
std::move(initiation), token, buffers
);
}
template<typename MutableBufferSequence, typename ReadToken>
decltype(auto) async_read_some(
const MutableBufferSequence& buffers,
ReadToken&& token
) {
using Signature = void (error_code, size_t);
template<typename MutableBufferSequence, typename ReadToken>
decltype(auto) async_read_some(
const MutableBufferSequence& buffers,
ReadToken&& token
) {
using Signature = void (error_code, size_t);
auto initiation = [this](
auto handler, const MutableBufferSequence& buffers
) {
detail::read_op { _impl, std::move(handler) }.perform(buffers);
};
auto initiation = [this](
auto handler, const MutableBufferSequence& buffers
) {
detail::read_op { _impl, std::move(handler) }.perform(buffers);
};
return asio::async_initiate<ReadToken, Signature>(
std::move(initiation), token, buffers
);
}
return asio::async_initiate<ReadToken, Signature>(
std::move(initiation), token, buffers
);
}
};
} // end namespace async_mqtt5::test
} // end namespace boost::mqtt5::test
#endif // ASYNC_MQTT5_TEST_TEST_STREAM_HPP
#endif // BOOST_MQTT5_TEST_TEST_STREAM_HPP