2023-10-05 13:59:32 +02:00
|
|
|
#ifndef ASYNC_MQTT5_CLIENT_SERVICE_HPP
|
|
|
|
#define ASYNC_MQTT5_CLIENT_SERVICE_HPP
|
|
|
|
|
2023-12-13 15:13:07 +01:00
|
|
|
#include <utility>
|
|
|
|
|
2023-11-15 10:44:09 +01:00
|
|
|
#include <boost/asio/experimental/basic_concurrent_channel.hpp>
|
2023-10-05 13:59:32 +02:00
|
|
|
|
2023-11-15 10:44:09 +01:00
|
|
|
#include <async_mqtt5/detail/channel_traits.hpp>
|
2023-12-01 15:46:53 +01:00
|
|
|
#include <async_mqtt5/detail/internal_types.hpp>
|
2023-10-05 13:59:32 +02:00
|
|
|
|
|
|
|
#include <async_mqtt5/impl/assemble_op.hpp>
|
2023-10-06 11:51:04 +02:00
|
|
|
#include <async_mqtt5/impl/async_sender.hpp>
|
|
|
|
#include <async_mqtt5/impl/autoconnect_stream.hpp>
|
2023-10-05 13:59:32 +02:00
|
|
|
#include <async_mqtt5/impl/ping_op.hpp>
|
2023-10-06 11:51:04 +02:00
|
|
|
#include <async_mqtt5/impl/replies.hpp>
|
2023-10-05 13:59:32 +02:00
|
|
|
#include <async_mqtt5/impl/sentry_op.hpp>
|
|
|
|
|
|
|
|
namespace async_mqtt5::detail {
|
|
|
|
|
2023-10-06 11:51:04 +02:00
|
|
|
namespace asio = boost::asio;
|
2023-10-05 13:59:32 +02:00
|
|
|
|
2023-12-07 09:32:34 +01:00
|
|
|
template <
|
|
|
|
typename StreamType, typename TlsContext,
|
|
|
|
typename Enable = void
|
|
|
|
>
|
2023-10-05 13:59:32 +02:00
|
|
|
class stream_context;
|
|
|
|
|
2023-12-07 09:32:34 +01:00
|
|
|
template <
|
|
|
|
typename StreamType, typename TlsContext
|
|
|
|
>
|
|
|
|
class stream_context<
|
|
|
|
StreamType, TlsContext,
|
|
|
|
std::enable_if_t<has_tls_layer<StreamType>>
|
|
|
|
> {
|
2023-10-05 13:59:32 +02:00
|
|
|
using tls_context_type = TlsContext;
|
|
|
|
|
2023-12-04 15:31:17 +01:00
|
|
|
mqtt_ctx _mqtt_context;
|
2023-10-05 13:59:32 +02:00
|
|
|
tls_context_type _tls_context;
|
|
|
|
public:
|
2023-11-23 15:36:06 +01:00
|
|
|
explicit stream_context(TlsContext tls_context) :
|
2023-10-05 13:59:32 +02:00
|
|
|
_tls_context(std::move(tls_context))
|
|
|
|
{}
|
|
|
|
|
2023-12-04 15:31:17 +01:00
|
|
|
auto& mqtt_context() {
|
2023-10-05 13:59:32 +02:00
|
|
|
return _mqtt_context;
|
|
|
|
}
|
|
|
|
|
2023-12-04 15:31:17 +01:00
|
|
|
auto& tls_context() {
|
2023-10-05 13:59:32 +02:00
|
|
|
return _tls_context;
|
|
|
|
}
|
|
|
|
|
2023-12-01 15:46:53 +01:00
|
|
|
auto& session_state() {
|
2023-12-04 15:31:17 +01:00
|
|
|
return _mqtt_context.state;
|
2023-12-01 15:46:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const auto& session_state() const {
|
2023-12-04 15:31:17 +01:00
|
|
|
return _mqtt_context.state;
|
2023-12-01 15:46:53 +01:00
|
|
|
}
|
|
|
|
|
2023-10-05 13:59:32 +02:00
|
|
|
void will(will will) {
|
2023-12-04 15:31:17 +01:00
|
|
|
_mqtt_context.will_msg = std::move(will);
|
2023-10-05 13:59:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Prop>
|
2023-12-22 09:17:45 +01:00
|
|
|
const auto& connack_prop(Prop p) const {
|
|
|
|
return _mqtt_context.ca_props[p];
|
2023-10-05 13:59:32 +02:00
|
|
|
}
|
|
|
|
|
2023-12-22 09:17:45 +01:00
|
|
|
const auto& connack_props() const {
|
|
|
|
return _mqtt_context.ca_props;
|
2023-12-07 12:18:28 +01:00
|
|
|
}
|
|
|
|
|
2023-12-22 13:48:26 +01:00
|
|
|
template <typename Prop>
|
|
|
|
const auto& connect_prop(Prop p) const {
|
|
|
|
return _mqtt_context.co_props[p];
|
|
|
|
}
|
|
|
|
|
|
|
|
void connect_props(connect_props props) {
|
|
|
|
_mqtt_context.co_props = std::move(props);
|
|
|
|
}
|
|
|
|
|
2023-10-05 13:59:32 +02:00
|
|
|
void credentials(
|
|
|
|
std::string client_id,
|
|
|
|
std::string username = "", std::string password = ""
|
|
|
|
) {
|
2023-12-04 15:31:17 +01:00
|
|
|
_mqtt_context.creds = {
|
2023-10-05 13:59:32 +02:00
|
|
|
std::move(client_id),
|
|
|
|
std::move(username), std::move(password)
|
|
|
|
};
|
|
|
|
}
|
2023-11-03 08:38:28 +01:00
|
|
|
|
|
|
|
template <typename Authenticator>
|
|
|
|
void authenticator(Authenticator&& authenticator) {
|
|
|
|
_mqtt_context.authenticator = any_authenticator(
|
|
|
|
std::forward<Authenticator>(authenticator)
|
|
|
|
);
|
|
|
|
}
|
2023-10-05 13:59:32 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template <typename StreamType>
|
2023-12-07 09:32:34 +01:00
|
|
|
class stream_context<
|
|
|
|
StreamType, std::monostate,
|
|
|
|
std::enable_if_t<!has_tls_layer<StreamType>>
|
|
|
|
> {
|
2023-12-04 15:31:17 +01:00
|
|
|
mqtt_ctx _mqtt_context;
|
2023-10-05 13:59:32 +02:00
|
|
|
public:
|
2023-11-23 15:36:06 +01:00
|
|
|
explicit stream_context(std::monostate) {}
|
2023-10-05 13:59:32 +02:00
|
|
|
|
2023-12-04 15:31:17 +01:00
|
|
|
auto& mqtt_context() {
|
2023-10-05 13:59:32 +02:00
|
|
|
return _mqtt_context;
|
|
|
|
}
|
|
|
|
|
2023-12-01 15:46:53 +01:00
|
|
|
auto& session_state() {
|
2023-12-04 15:31:17 +01:00
|
|
|
return _mqtt_context.state;
|
2023-12-01 15:46:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const auto& session_state() const {
|
2023-12-04 15:31:17 +01:00
|
|
|
return _mqtt_context.state;
|
2023-12-01 15:46:53 +01:00
|
|
|
}
|
|
|
|
|
2023-10-05 13:59:32 +02:00
|
|
|
void will(will will) {
|
2023-12-04 15:31:17 +01:00
|
|
|
_mqtt_context.will_msg = std::move(will);
|
2023-10-05 13:59:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Prop>
|
2023-12-22 09:17:45 +01:00
|
|
|
const auto& connack_prop(Prop p) const {
|
|
|
|
return _mqtt_context.ca_props[p];
|
2023-10-05 13:59:32 +02:00
|
|
|
}
|
|
|
|
|
2023-12-22 09:17:45 +01:00
|
|
|
const auto& connack_props() const {
|
|
|
|
return _mqtt_context.ca_props;
|
2023-12-07 12:18:28 +01:00
|
|
|
}
|
|
|
|
|
2023-12-22 13:48:26 +01:00
|
|
|
template <typename Prop>
|
|
|
|
const auto& connect_prop(Prop p) const {
|
|
|
|
return _mqtt_context.co_props[p];
|
|
|
|
}
|
|
|
|
|
|
|
|
void connect_props(connect_props props) {
|
|
|
|
_mqtt_context.co_props = std::move(props);
|
|
|
|
}
|
|
|
|
|
2023-10-05 13:59:32 +02:00
|
|
|
void credentials(
|
|
|
|
std::string client_id,
|
|
|
|
std::string username = "", std::string password = ""
|
|
|
|
) {
|
2023-12-04 15:31:17 +01:00
|
|
|
_mqtt_context.creds = {
|
2023-10-05 13:59:32 +02:00
|
|
|
std::move(client_id),
|
|
|
|
std::move(username), std::move(password)
|
|
|
|
};
|
|
|
|
}
|
2023-11-03 08:38:28 +01:00
|
|
|
|
|
|
|
template <typename Authenticator>
|
|
|
|
void authenticator(Authenticator&& authenticator) {
|
|
|
|
_mqtt_context.authenticator = any_authenticator(
|
|
|
|
std::forward<Authenticator>(authenticator)
|
|
|
|
);
|
|
|
|
}
|
2023-10-05 13:59:32 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template <
|
|
|
|
typename StreamType,
|
|
|
|
typename TlsContext = std::monostate
|
|
|
|
>
|
|
|
|
class client_service {
|
2023-12-03 11:52:04 +01:00
|
|
|
using self_type = client_service<StreamType, TlsContext>;
|
2023-10-06 11:51:04 +02:00
|
|
|
using stream_context_type = stream_context<StreamType, TlsContext>;
|
|
|
|
using stream_type = autoconnect_stream<
|
2023-10-05 13:59:32 +02:00
|
|
|
StreamType, stream_context_type
|
|
|
|
>;
|
|
|
|
public:
|
|
|
|
using executor_type = typename stream_type::executor_type;
|
|
|
|
private:
|
|
|
|
using tls_context_type = TlsContext;
|
2023-11-15 10:44:09 +01:00
|
|
|
using receive_channel = asio::experimental::basic_concurrent_channel<
|
|
|
|
asio::any_io_executor,
|
|
|
|
channel_traits<>,
|
2023-10-05 13:59:32 +02:00
|
|
|
void (error_code, std::string, std::string, publish_props)
|
|
|
|
>;
|
|
|
|
|
|
|
|
template <typename ClientService>
|
2023-10-06 11:51:04 +02:00
|
|
|
friend class async_sender;
|
2023-10-05 13:59:32 +02:00
|
|
|
|
|
|
|
template <typename ClientService, typename Handler>
|
2023-10-06 11:51:04 +02:00
|
|
|
friend class assemble_op;
|
2023-10-05 13:59:32 +02:00
|
|
|
|
|
|
|
template <typename ClientService>
|
2023-10-06 11:51:04 +02:00
|
|
|
friend class ping_op;
|
2023-10-05 13:59:32 +02:00
|
|
|
|
|
|
|
template <typename ClientService>
|
2023-10-06 11:51:04 +02:00
|
|
|
friend class sentry_op;
|
2023-10-05 13:59:32 +02:00
|
|
|
|
2023-11-08 08:49:28 +01:00
|
|
|
template <typename ClientService>
|
|
|
|
friend class re_auth_op;
|
|
|
|
|
2023-10-05 13:59:32 +02:00
|
|
|
stream_context_type _stream_context;
|
|
|
|
stream_type _stream;
|
|
|
|
|
|
|
|
packet_id_allocator _pid_allocator;
|
2023-10-06 11:51:04 +02:00
|
|
|
replies _replies;
|
|
|
|
async_sender<client_service> _async_sender;
|
2023-10-05 13:59:32 +02:00
|
|
|
|
|
|
|
std::string _read_buff;
|
2023-10-06 11:51:04 +02:00
|
|
|
data_span _active_span;
|
2023-10-05 13:59:32 +02:00
|
|
|
|
|
|
|
receive_channel _rec_channel;
|
|
|
|
|
|
|
|
asio::cancellation_signal _cancel_ping;
|
|
|
|
asio::cancellation_signal _cancel_sentry;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
client_service(
|
|
|
|
const executor_type& ex,
|
|
|
|
const std::string& cnf,
|
|
|
|
tls_context_type tls_context = {}
|
|
|
|
) :
|
|
|
|
_stream_context(std::move(tls_context)),
|
|
|
|
_stream(ex, _stream_context),
|
|
|
|
_async_sender(*this),
|
2023-11-13 15:55:56 +01:00
|
|
|
_active_span(_read_buff.cend(), _read_buff.cend()),
|
2023-11-15 10:44:09 +01:00
|
|
|
_rec_channel(ex, std::numeric_limits<size_t>::max())
|
2023-10-05 13:59:32 +02:00
|
|
|
{}
|
|
|
|
|
|
|
|
executor_type get_executor() const noexcept {
|
|
|
|
return _stream.get_executor();
|
|
|
|
}
|
|
|
|
|
2023-12-07 09:32:34 +01:00
|
|
|
template <
|
|
|
|
typename Ctx = TlsContext,
|
|
|
|
std::enable_if_t<!std::is_same_v<Ctx, std::monostate>, bool> = true
|
|
|
|
>
|
|
|
|
decltype(auto) tls_context() {
|
2023-10-05 13:59:32 +02:00
|
|
|
return _stream_context.tls_context();
|
|
|
|
}
|
|
|
|
|
|
|
|
void will(will will) {
|
|
|
|
if (!is_open())
|
|
|
|
_stream_context.will(std::move(will));
|
|
|
|
}
|
|
|
|
|
|
|
|
void credentials(
|
|
|
|
std::string client_id,
|
|
|
|
std::string username = "", std::string password = ""
|
|
|
|
) {
|
|
|
|
if (!is_open())
|
|
|
|
_stream_context.credentials(
|
|
|
|
std::move(client_id),
|
|
|
|
std::move(username), std::move(password)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void brokers(std::string hosts, uint16_t default_port) {
|
|
|
|
if (!is_open())
|
|
|
|
_stream.brokers(std::move(hosts), default_port);
|
|
|
|
}
|
|
|
|
|
2023-12-07 09:32:34 +01:00
|
|
|
template <
|
|
|
|
typename Authenticator,
|
|
|
|
std::enable_if_t<is_authenticator<Authenticator>, bool> = true
|
|
|
|
>
|
2023-11-03 08:38:28 +01:00
|
|
|
void authenticator(Authenticator&& authenticator) {
|
2023-11-06 12:13:44 +01:00
|
|
|
if (!is_open())
|
|
|
|
_stream_context.authenticator(
|
|
|
|
std::forward<Authenticator>(authenticator)
|
|
|
|
);
|
2023-11-03 08:38:28 +01:00
|
|
|
}
|
|
|
|
|
2023-12-22 13:48:26 +01:00
|
|
|
template <typename Prop>
|
|
|
|
const auto& connect_prop(Prop p) const {
|
|
|
|
return _stream_context.connect_prop(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void connect_props(connect_props props) {
|
|
|
|
if (!is_open())
|
|
|
|
_stream_context.connect_props(std::move(props));
|
|
|
|
}
|
2023-12-22 09:17:45 +01:00
|
|
|
|
2023-10-05 13:59:32 +02:00
|
|
|
template <typename Prop>
|
2023-12-22 09:17:45 +01:00
|
|
|
const auto& connack_prop(Prop p) const {
|
2023-10-05 13:59:32 +02:00
|
|
|
return _stream_context.connack_prop(p);
|
|
|
|
}
|
|
|
|
|
2023-12-22 09:17:45 +01:00
|
|
|
const auto& connack_props() const {
|
|
|
|
return _stream_context.connack_props();
|
2023-12-07 12:18:28 +01:00
|
|
|
}
|
|
|
|
|
2023-11-15 10:44:09 +01:00
|
|
|
void run() {
|
|
|
|
_stream.open();
|
|
|
|
_rec_channel.reset();
|
|
|
|
}
|
|
|
|
|
2023-10-05 13:59:32 +02:00
|
|
|
void open_stream() {
|
|
|
|
_stream.open();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_open() const {
|
|
|
|
return _stream.is_open();
|
|
|
|
}
|
|
|
|
|
|
|
|
void close_stream() {
|
|
|
|
_stream.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cancel() {
|
|
|
|
_cancel_ping.emit(asio::cancellation_type::terminal);
|
|
|
|
_cancel_sentry.emit(asio::cancellation_type::terminal);
|
|
|
|
|
2023-11-15 10:44:09 +01:00
|
|
|
_rec_channel.close();
|
2023-10-05 13:59:32 +02:00
|
|
|
_replies.cancel_unanswered();
|
|
|
|
_async_sender.cancel();
|
|
|
|
_stream.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t allocate_pid() {
|
|
|
|
return _pid_allocator.allocate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_pid(uint16_t pid, bool was_throttled = false) {
|
|
|
|
_pid_allocator.free(pid);
|
|
|
|
if (was_throttled)
|
|
|
|
_async_sender.throttled_op_done();
|
|
|
|
}
|
|
|
|
|
|
|
|
serial_num_t next_serial_num() {
|
|
|
|
return _async_sender.next_serial_num();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename BufferType, typename CompletionToken>
|
|
|
|
decltype(auto) async_send(
|
|
|
|
const BufferType& buffer,
|
|
|
|
serial_num_t serial_num, unsigned flags,
|
|
|
|
CompletionToken&& token
|
|
|
|
) {
|
|
|
|
return _async_sender.async_send(
|
|
|
|
buffer, serial_num, flags, std::forward<CompletionToken>(token)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CompletionToken>
|
|
|
|
decltype(auto) async_assemble(duration wait_for, CompletionToken&& token) {
|
2023-12-03 11:52:04 +01:00
|
|
|
using Signature = void (error_code, uint8_t, byte_citer, byte_citer);
|
|
|
|
|
|
|
|
auto initiation = [] (
|
|
|
|
auto handler, self_type& self,
|
|
|
|
duration wait_for, std::string& read_buff, data_span& active_span
|
|
|
|
) {
|
2023-10-06 11:51:04 +02:00
|
|
|
assemble_op {
|
2023-12-03 11:52:04 +01:00
|
|
|
self, std::move(handler), read_buff, active_span
|
2023-10-05 13:59:32 +02:00
|
|
|
}.perform(wait_for, asio::transfer_at_least(0));
|
|
|
|
};
|
|
|
|
|
2023-11-03 08:38:28 +01:00
|
|
|
return asio::async_initiate<CompletionToken, Signature> (
|
2023-12-04 18:42:57 +01:00
|
|
|
initiation, token, std::ref(*this),
|
2023-12-03 11:52:04 +01:00
|
|
|
wait_for, std::ref(_read_buff), std::ref(_active_span)
|
2023-10-05 13:59:32 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CompletionToken>
|
|
|
|
decltype(auto) async_wait_reply(
|
|
|
|
control_code_e code, uint16_t packet_id, CompletionToken&& token
|
|
|
|
) {
|
|
|
|
return _replies.async_wait_reply(
|
|
|
|
code, packet_id, std::forward<CompletionToken>(token)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-12-01 15:46:53 +01:00
|
|
|
bool subscriptions_present() const {
|
|
|
|
return _stream_context.session_state().subscriptions_present();
|
|
|
|
}
|
|
|
|
|
|
|
|
void subscriptions_present(bool present) {
|
|
|
|
_stream_context.session_state().subscriptions_present(present);
|
|
|
|
}
|
|
|
|
|
2023-11-23 15:36:06 +01:00
|
|
|
void update_session_state() {
|
2023-12-01 15:46:53 +01:00
|
|
|
auto& session_state = _stream_context.session_state();
|
|
|
|
|
2023-11-23 15:36:06 +01:00
|
|
|
if (!session_state.session_present()) {
|
|
|
|
_replies.clear_pending_pubrels();
|
|
|
|
session_state.session_present(true);
|
2023-12-01 15:46:53 +01:00
|
|
|
|
|
|
|
if (session_state.subscriptions_present()) {
|
|
|
|
channel_store_error(client::error::session_expired);
|
|
|
|
session_state.subscriptions_present(false);
|
|
|
|
}
|
2023-11-23 15:36:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-05 13:59:32 +02:00
|
|
|
bool channel_store(decoders::publish_message message) {
|
|
|
|
auto& [topic, packet_id, flags, props, payload] = message;
|
|
|
|
return _rec_channel.try_send(
|
|
|
|
error_code {}, std::move(topic),
|
|
|
|
std::move(payload), std::move(props)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-11-23 15:36:06 +01:00
|
|
|
bool channel_store_error(error_code ec) {
|
2023-12-07 09:32:34 +01:00
|
|
|
return _rec_channel.try_send(
|
|
|
|
ec, std::string {}, std::string {}, publish_props {}
|
|
|
|
);
|
2023-11-23 15:36:06 +01:00
|
|
|
}
|
|
|
|
|
2023-10-05 13:59:32 +02:00
|
|
|
template <typename CompletionToken>
|
|
|
|
decltype(auto) async_channel_receive(CompletionToken&& token) {
|
|
|
|
// sig = void (error_code, std::string, std::string, publish_props)
|
|
|
|
return _rec_channel.async_receive(
|
|
|
|
std::forward<CompletionToken>(token)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace async_mqtt5::detail
|
|
|
|
|
|
|
|
#endif // !ASYNC_MQTT5_CLIENT_SERVICE_HPP
|