forked from boostorg/mqtt5
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:
1 parent
1225cc778a
commit
afc270f10e
136 files changed
+17834
-17859
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user