forked from boostorg/mqtt5
thread-safe client
Reviewers: ivica Reviewed By: ivica Subscribers: korina Differential Revision: https://repo.mireo.local/D26864
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
#ifndef ASYNC_MQTT5_CONTROL_PACKET_HPP
|
#ifndef ASYNC_MQTT5_CONTROL_PACKET_HPP
|
||||||
#define ASYNC_MQTT5_CONTROL_PACKET_HPP
|
#define ASYNC_MQTT5_CONTROL_PACKET_HPP
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/smart_ptr/allocate_unique.hpp>
|
#include <boost/smart_ptr/allocate_unique.hpp>
|
||||||
@@ -115,7 +114,6 @@ class packet_id_allocator {
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::mutex _mtx;
|
|
||||||
std::vector<interval> _free_ids;
|
std::vector<interval> _free_ids;
|
||||||
static constexpr uint16_t MAX_PACKET_ID = 65535;
|
static constexpr uint16_t MAX_PACKET_ID = 65535;
|
||||||
|
|
||||||
@@ -125,7 +123,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint16_t allocate() {
|
uint16_t allocate() {
|
||||||
std::lock_guard _(_mtx);
|
|
||||||
if (_free_ids.empty()) return 0;
|
if (_free_ids.empty()) return 0;
|
||||||
auto& last = _free_ids.back();
|
auto& last = _free_ids.back();
|
||||||
if (last.start == ++last.end) {
|
if (last.start == ++last.end) {
|
||||||
@@ -137,7 +134,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void free(uint16_t pid) {
|
void free(uint16_t pid) {
|
||||||
std::lock_guard _(_mtx);
|
|
||||||
auto it = std::upper_bound(
|
auto it = std::upper_bound(
|
||||||
_free_ids.begin(), _free_ids.end(), pid,
|
_free_ids.begin(), _free_ids.end(), pid,
|
||||||
[](const uint16_t x, const interval& i) { return x > i.start; }
|
[](const uint16_t x, const interval& i) { return x > i.start; }
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
#include <async_mqtt5/detail/any_authenticator.hpp>
|
#include <async_mqtt5/detail/any_authenticator.hpp>
|
||||||
|
|
||||||
@@ -70,6 +71,7 @@ struct mqtt_ctx {
|
|||||||
credentials creds;
|
credentials creds;
|
||||||
std::optional<will> will_msg;
|
std::optional<will> will_msg;
|
||||||
connect_props co_props;
|
connect_props co_props;
|
||||||
|
std::shared_mutex ca_mtx;
|
||||||
connack_props ca_props;
|
connack_props ca_props;
|
||||||
session_state state;
|
session_state state;
|
||||||
any_authenticator authenticator;
|
any_authenticator authenticator;
|
||||||
|
@@ -61,9 +61,18 @@ public:
|
|||||||
|
|
||||||
template <typename Prop>
|
template <typename Prop>
|
||||||
decltype(auto) connack_prop(Prop p) {
|
decltype(auto) connack_prop(Prop p) {
|
||||||
|
std::shared_lock reader_lock(_mqtt_context.ca_mtx);
|
||||||
return _mqtt_context.ca_props[p];
|
return _mqtt_context.ca_props[p];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Prop0, typename ...Props>
|
||||||
|
decltype(auto) connack_props(Prop0 p0, Props ...props) {
|
||||||
|
std::shared_lock reader_lock(_mqtt_context.ca_mtx);
|
||||||
|
return std::make_tuple(
|
||||||
|
_mqtt_context.ca_props[p0], _mqtt_context.ca_props[props]...
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void credentials(
|
void credentials(
|
||||||
std::string client_id,
|
std::string client_id,
|
||||||
std::string username = "", std::string password = ""
|
std::string username = "", std::string password = ""
|
||||||
@@ -109,9 +118,18 @@ public:
|
|||||||
|
|
||||||
template <typename Prop>
|
template <typename Prop>
|
||||||
decltype(auto) connack_prop(Prop p) {
|
decltype(auto) connack_prop(Prop p) {
|
||||||
|
std::shared_lock reader_lock(_mqtt_context.ca_mtx);
|
||||||
return _mqtt_context.ca_props[p];
|
return _mqtt_context.ca_props[p];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Prop0, typename ...Props>
|
||||||
|
decltype(auto) connack_props(Prop0 p0, Props ...props) {
|
||||||
|
std::shared_lock reader_lock(_mqtt_context.ca_mtx);
|
||||||
|
return std::make_tuple(
|
||||||
|
_mqtt_context.ca_props[p0], _mqtt_context.ca_props[props]...
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void credentials(
|
void credentials(
|
||||||
std::string client_id,
|
std::string client_id,
|
||||||
std::string username = "", std::string password = ""
|
std::string username = "", std::string password = ""
|
||||||
@@ -243,6 +261,11 @@ public:
|
|||||||
return _stream_context.connack_prop(p);
|
return _stream_context.connack_prop(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Prop0, typename ...Props>
|
||||||
|
decltype(auto) connack_props(Prop0 p0, Props ...props) {
|
||||||
|
return _stream_context.connack_props(p0, props...);
|
||||||
|
}
|
||||||
|
|
||||||
void run() {
|
void run() {
|
||||||
_stream.open();
|
_stream.open();
|
||||||
_rec_channel.reset();
|
_rec_channel.reset();
|
||||||
|
@@ -283,7 +283,11 @@ public:
|
|||||||
return complete(client::error::malformed_packet);
|
return complete(client::error::malformed_packet);
|
||||||
const auto& [session_present, reason_code, ca_props] = *rv;
|
const auto& [session_present, reason_code, ca_props] = *rv;
|
||||||
|
|
||||||
_ctx.ca_props = ca_props;
|
{
|
||||||
|
std::unique_lock writer_lock(_ctx.ca_mtx);
|
||||||
|
_ctx.ca_props = ca_props;
|
||||||
|
}
|
||||||
|
|
||||||
_ctx.state.session_present(session_present);
|
_ctx.state.session_present(session_present);
|
||||||
|
|
||||||
// Unexpected result handling:
|
// Unexpected result handling:
|
||||||
|
@@ -66,10 +66,10 @@ public:
|
|||||||
static_cast<uint8_t>(_context.reason_code), _context.props
|
static_cast<uint8_t>(_context.reason_code), _context.props
|
||||||
);
|
);
|
||||||
|
|
||||||
send_disconnect(std::move(disconnect));
|
asio::dispatch(asio::prepend(std::move(*this), std::move(disconnect)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_disconnect(control_packet<allocator_type> disconnect) {
|
void operator()(control_packet<allocator_type> disconnect) {
|
||||||
const auto& wire_data = disconnect.wire_data();
|
const auto& wire_data = disconnect.wire_data();
|
||||||
|
|
||||||
_svc_ptr->async_send(
|
_svc_ptr->async_send(
|
||||||
|
@@ -103,6 +103,18 @@ public:
|
|||||||
if (ec)
|
if (ec)
|
||||||
return complete_post(ec);
|
return complete_post(ec);
|
||||||
|
|
||||||
|
asio::dispatch(
|
||||||
|
asio::prepend(
|
||||||
|
std::move(*this), std::move(topic),
|
||||||
|
std::move(payload), retain, props
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(
|
||||||
|
std::string topic, std::string payload,
|
||||||
|
retain_e retain, const publish_props& props
|
||||||
|
) {
|
||||||
uint16_t packet_id = 0;
|
uint16_t packet_id = 0;
|
||||||
if constexpr (qos_type != qos_e::at_most_once) {
|
if constexpr (qos_type != qos_e::at_most_once) {
|
||||||
packet_id = _svc_ptr->allocate_pid();
|
packet_id = _svc_ptr->allocate_pid();
|
||||||
@@ -334,15 +346,18 @@ private:
|
|||||||
if (!is_valid_utf8_topic(topic))
|
if (!is_valid_utf8_topic(topic))
|
||||||
return client::error::invalid_topic;
|
return client::error::invalid_topic;
|
||||||
|
|
||||||
auto max_qos = _svc_ptr->connack_prop(prop::maximum_qos);
|
const auto& [max_qos, retain_avail, topic_alias_max] =
|
||||||
|
_svc_ptr->connack_props(
|
||||||
|
prop::maximum_qos, prop::retain_available,
|
||||||
|
prop::topic_alias_maximum
|
||||||
|
);
|
||||||
|
|
||||||
if (max_qos && uint8_t(qos_type) > *max_qos)
|
if (max_qos && uint8_t(qos_type) > *max_qos)
|
||||||
return client::error::qos_not_supported;
|
return client::error::qos_not_supported;
|
||||||
|
|
||||||
auto retain_avail = _svc_ptr->connack_prop(prop::retain_available);
|
|
||||||
if (retain_avail && *retain_avail == 0 && retain == retain_e::yes)
|
if (retain_avail && *retain_avail == 0 && retain == retain_e::yes)
|
||||||
return client::error::retain_not_available;
|
return client::error::retain_not_available;
|
||||||
|
|
||||||
auto topic_alias_max = _svc_ptr->connack_prop(prop::topic_alias_maximum);
|
|
||||||
auto topic_alias = props[prop::topic_alias];
|
auto topic_alias = props[prop::topic_alias];
|
||||||
if (
|
if (
|
||||||
(!topic_alias_max || topic_alias_max && *topic_alias_max == 0) &&
|
(!topic_alias_max || topic_alias_max && *topic_alias_max == 0) &&
|
||||||
|
@@ -66,6 +66,15 @@ public:
|
|||||||
if (ec)
|
if (ec)
|
||||||
return complete_post(ec, topics.size());
|
return complete_post(ec, topics.size());
|
||||||
|
|
||||||
|
asio::dispatch(
|
||||||
|
asio::prepend(std::move(*this), topics, props)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(
|
||||||
|
const std::vector<subscribe_topic>& topics,
|
||||||
|
const subscribe_props& props
|
||||||
|
) {
|
||||||
uint16_t packet_id = _svc_ptr->allocate_pid();
|
uint16_t packet_id = _svc_ptr->allocate_pid();
|
||||||
if (packet_id == 0)
|
if (packet_id == 0)
|
||||||
return complete_post(client::error::pid_overrun, topics.size());
|
return complete_post(client::error::pid_overrun, topics.size());
|
||||||
|
@@ -63,6 +63,15 @@ public:
|
|||||||
if (ec)
|
if (ec)
|
||||||
return complete_post(ec, topics.size());
|
return complete_post(ec, topics.size());
|
||||||
|
|
||||||
|
asio::dispatch(
|
||||||
|
asio::prepend(std::move(*this), topics, props)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(
|
||||||
|
const std::vector<std::string>& topics,
|
||||||
|
const unsubscribe_props& props
|
||||||
|
) {
|
||||||
uint16_t packet_id = _svc_ptr->allocate_pid();
|
uint16_t packet_id = _svc_ptr->allocate_pid();
|
||||||
if (packet_id == 0)
|
if (packet_id == 0)
|
||||||
return complete_post(client::error::pid_overrun, topics.size());
|
return complete_post(client::error::pid_overrun, topics.size());
|
||||||
|
Reference in New Issue
Block a user