diff --git a/include/async_mqtt5.hpp b/include/async_mqtt5.hpp index 027dcfd..ca2032f 100644 --- a/include/async_mqtt5.hpp +++ b/include/async_mqtt5.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/include/async_mqtt5/detail/log_invoke.hpp b/include/async_mqtt5/detail/log_invoke.hpp index e8819bf..583f8ed 100644 --- a/include/async_mqtt5/detail/log_invoke.hpp +++ b/include/async_mqtt5/detail/log_invoke.hpp @@ -13,8 +13,8 @@ #include #include -#include +#include #include #include #include @@ -24,83 +24,12 @@ namespace async_mqtt5::detail { namespace asio = boost::asio; using boost::system::error_code; -// NOOP Logger -class noop_logger {}; - -// at_resolve - -template -using at_resolve_sig = decltype( - std::declval().at_resolve( - std::declval(), - std::declval(), std::declval(), - std::declval() - ) -); -template -constexpr bool has_at_resolve = boost::is_detected::value; - -// at_tcp_connect - -template -using at_tcp_connect_sig = decltype( - std::declval().at_tcp_connect( - std::declval(), std::declval() - ) -); -template -constexpr bool has_at_tcp_connect = boost::is_detected::value; - -// at_tls_handshake - -template -using at_tls_handshake_sig = decltype( - std::declval().at_tls_handshake( - std::declval(), std::declval() - ) -); -template -constexpr bool has_at_tls_handshake = boost::is_detected::value; - -// at_ws_handshake - -template -using at_ws_handshake_sig = decltype( - std::declval().at_ws_handshake( - std::declval(), std::declval() - ) -); -template -constexpr bool has_at_ws_handshake = boost::is_detected::value; - -// at_connack - -template -using at_connack_sig = decltype( - std::declval().at_connack( - std::declval(), - std::declval(), std::declval() - ) -); -template -constexpr bool has_at_connack = boost::is_detected::value; - -// at_disconnect -template -using at_disconnect_sig = decltype( - std::declval().at_disconnect( - std::declval(), std::declval() - ) -); -template -constexpr bool has_at_disconnect = boost::is_detected::value; - -template +template class log_invoke { LoggerType _logger; public: - explicit log_invoke(LoggerType&& logger = {}) : - _logger(std::forward(logger)) + explicit log_invoke(LoggerType logger = {}) : + _logger(std::move(logger)) {} void at_resolve( diff --git a/include/async_mqtt5/impl/codecs/traits.hpp b/include/async_mqtt5/detail/traits.hpp similarity index 95% rename from include/async_mqtt5/impl/codecs/traits.hpp rename to include/async_mqtt5/detail/traits.hpp index 715f265..5a47fa9 100644 --- a/include/async_mqtt5/impl/codecs/traits.hpp +++ b/include/async_mqtt5/detail/traits.hpp @@ -16,7 +16,7 @@ #include #include -namespace async_mqtt5 { +namespace async_mqtt5::detail { template constexpr bool is_optional_impl = false; @@ -58,6 +58,6 @@ constexpr bool is_boost_iterator = is_specialization< boost::remove_cv_ref_t, boost::iterator_range >; -} // end namespace async_mqtt5 +} // end namespace async_mqtt5::detail #endif // !ASYNC_MQTT5_TRAITS_HPP diff --git a/include/async_mqtt5/impl/codecs/base_decoders.hpp b/include/async_mqtt5/impl/codecs/base_decoders.hpp index 1bfe8e5..9c95391 100644 --- a/include/async_mqtt5/impl/codecs/base_decoders.hpp +++ b/include/async_mqtt5/impl/codecs/base_decoders.hpp @@ -19,7 +19,7 @@ #include #include -#include +#include namespace async_mqtt5::decoders { @@ -80,7 +80,7 @@ constexpr auto to(T& arg) { return [&](auto& ctx) { using ctx_type = decltype(ctx); using attr_type = decltype(x3::_attr(std::declval())); - if constexpr (is_boost_iterator) + if constexpr (detail::is_boost_iterator) arg = T { x3::_attr(ctx).begin(), x3::_attr(ctx).end() }; else arg = x3::_attr(ctx); @@ -289,8 +289,8 @@ struct len_prefix_parser : x3::parser { } }; -constexpr len_prefix_parser utf8_{}; -constexpr len_prefix_parser binary_{}; +constexpr len_prefix_parser utf8_ {}; +constexpr len_prefix_parser binary_ {}; /* Boost Spirit incorrectly deduces atribute type for a parser of the form @@ -362,6 +362,7 @@ bool parse_to_prop( It& iter, const It last, const Ctx& ctx, RCtx& rctx, Prop& prop ) { + using namespace async_mqtt5::detail; using prop_type = std::remove_reference_t; bool rv = false; diff --git a/include/async_mqtt5/impl/codecs/base_encoders.hpp b/include/async_mqtt5/impl/codecs/base_encoders.hpp index e30ee2d..db73163 100644 --- a/include/async_mqtt5/impl/codecs/base_encoders.hpp +++ b/include/async_mqtt5/impl/codecs/base_encoders.hpp @@ -20,7 +20,7 @@ #include #include -#include +#include namespace async_mqtt5::encoders { @@ -72,7 +72,7 @@ public: template < typename T, typename projection = boost::identity, - std::enable_if_t, bool> = true + std::enable_if_t, bool> = true > auto operator()(T&& value, projection proj = {}) const { if constexpr (std::is_same_v) { @@ -89,7 +89,7 @@ public: template < typename T, typename projection = boost::identity, - std::enable_if_t, bool> = true + std::enable_if_t, bool> = true > auto operator()(T&& value, projection proj = {}) const { auto val = static_cast(std::invoke(proj, value)); @@ -125,7 +125,7 @@ public: int_val(T val) : _val(val) {} size_t byte_size() const { - if constexpr (is_optional) { + if constexpr (detail::is_optional) { if (_val) return val_length(*_val); return 0; } @@ -134,7 +134,7 @@ public: } std::string& encode(std::string& s) const { - if constexpr (is_optional) { + if constexpr (detail::is_optional) { if (_val) return encode_val(s, *_val); return s; } @@ -176,7 +176,7 @@ public: template auto operator()(T&& val, projection proj) const { - if constexpr (is_optional) { + if constexpr (detail::is_optional) { using rv_type = std::invoke_result_t< projection, typename boost::remove_cv_ref_t::value_type >; @@ -211,14 +211,14 @@ public: } size_t byte_size() const { - if constexpr (is_optional) + if constexpr (detail::is_optional) return _val ? _with_length * 2 + val_length(*_val) : 0; else return _with_length * 2 + val_length(_val); } std::string& encode(std::string& s) const { - if constexpr (is_optional) { + if constexpr (detail::is_optional) { if (_val) return encode_val(s, *_val); return s; } @@ -268,7 +268,7 @@ public: template auto operator()(T&& val, projection proj) const { - if constexpr (is_optional) { + if constexpr (detail::is_optional) { using rv_type = std::invoke_result_t< projection, typename boost::remove_cv_ref_t::value_type >; @@ -346,7 +346,7 @@ auto encoder_for_prop_value(const T& val) { return basic::int_def{}(val); else if constexpr (std::is_same_v) return basic::utf8_def{}(val); - else if constexpr (is_pair) + else if constexpr (detail::is_pair) return encoder_for_prop_value(val.first) & encoder_for_prop_value(val.second); } @@ -359,7 +359,7 @@ template < > class prop_val< T, p, - std::enable_if_t && is_optional> + std::enable_if_t && detail::is_optional> > : public basic::encoder { // allows T to be reference type to std::optional static inline boost::remove_cv_ref_t nulltype; @@ -388,7 +388,7 @@ template < > class prop_val< T, p, - std::enable_if_t || is_small_vector> + std::enable_if_t || detail::is_small_vector> > : public basic::encoder { // allows T to be reference type to std::vector static inline boost::remove_cv_ref_t nulltype; @@ -490,7 +490,7 @@ class props_def { public: template auto operator()(T&& prop_container) const { - if constexpr (is_optional) { + if constexpr (detail::is_optional) { if (prop_container.has_value()) return (*this)(*prop_container); return props_val< diff --git a/include/async_mqtt5/impl/run_op.hpp b/include/async_mqtt5/impl/run_op.hpp index fb19196..b8d7eab 100644 --- a/include/async_mqtt5/impl/run_op.hpp +++ b/include/async_mqtt5/impl/run_op.hpp @@ -54,10 +54,10 @@ public: }); } - run_op(run_op&&) noexcept = default; + run_op(run_op&&) = default; run_op(const run_op&) = delete; - run_op& operator=(run_op&&) noexcept = default; + run_op& operator=(run_op&&) = default; run_op& operator=(const run_op&) = delete; using allocator_type = asio::associated_allocator_t; diff --git a/include/async_mqtt5/logger.hpp b/include/async_mqtt5/logger.hpp index b390a14..2744eb0 100644 --- a/include/async_mqtt5/logger.hpp +++ b/include/async_mqtt5/logger.hpp @@ -8,17 +8,19 @@ #ifndef ASYNC_MQTT5_LOGGER_HPP #define ASYNC_MQTT5_LOGGER_HPP +#include #include #include -#include #include +#include +#include +#include #include #include #include - -#include +#include namespace async_mqtt5 { @@ -83,7 +85,7 @@ public: if (!ec && _level < log_level::info) return; - write_prefix(); + output_prefix(); std::clog << "resolve: " << host << ":" << port; @@ -110,7 +112,7 @@ public: if (!ec && _level < log_level::info) return; - write_prefix(); + output_prefix(); std::clog << "connect: " << ep.address().to_string() << ":" << ep.port() @@ -128,7 +130,7 @@ public: if (!ec && _level < log_level::info) return; - write_prefix(); + output_prefix(); std::clog << "TLS handshake: " << ep.address().to_string() << ":" << ep.port() @@ -146,7 +148,7 @@ public: if (!ec && _level < log_level::info) return; - write_prefix(); + output_prefix(); std::clog << "WebSocket handshake: " << ep.address().to_string() << ":" << ep.port() @@ -165,13 +167,17 @@ public: */ void at_connack( reason_code rc, - bool /* session_present */, const connack_props& /* ca_props */ + bool session_present, const connack_props& ca_props ) { if (!rc && _level < log_level::info) return; - write_prefix(); + output_prefix(); std::clog << "connack: " << rc.message() << "."; + if (_level == log_level::debug) { + std::clog << " session_present:" << session_present << " "; + output_props(ca_props); + } std::clog << std::endl; } @@ -183,19 +189,67 @@ public: * \param dc_props \__DISCONNECT_PROPS\__ received in the \__DISCONNECT\__ packet. */ void at_disconnect(reason_code rc, const disconnect_props& dc_props) { - write_prefix(); + output_prefix(); std::clog << "disconnect: " << rc.message() << "."; - if (dc_props[prop::reason_string].has_value()) - std::clog << " Reason string: " << * dc_props[prop::reason_string]; + if (_level == log_level::debug) + output_props(dc_props); std::clog << std::endl; } private: - void write_prefix() { + void output_prefix() { std::clog << prefix << " "; } + + template + void output_props(const Props& props) { + props.visit( + [](const auto& prop, const auto& val) -> bool { + if constexpr (detail::is_optional) { + if (val.has_value()) { + std::clog << property_name(prop) << ":"; + using value_type = boost::remove_cv_ref_t; + if constexpr (std::is_same_v) + std::clog << std::to_string(*val) << " "; + else + std::clog << *val << " "; + } + } else { // is vector + if (val.empty()) + return true; + + std::clog << property_name(prop) << ":"; + std::clog << "["; + for (auto i = 0; i < val.size(); i++) { + if constexpr (detail::is_pair) + std::clog << "(" << val[i].first << "," << val[i].second << ")"; + else + std::clog << std::to_string(val[i]); + if (i + 1 < val.size()) + std::clog << ", "; + } + std::clog << "]"; + } + return true; + } + ); + } + + template + static std::string_view property_name(std::integral_constant) { + return prop::name_v

; + } + }; +// Verify that the logger class satisfies the LoggerType concept +static_assert(has_at_resolve); +static_assert(has_at_tcp_connect); +static_assert(has_at_tls_handshake); +static_assert(has_at_ws_handshake); +static_assert(has_at_connack); +static_assert(has_at_disconnect); + } // end namespace async_mqtt5 diff --git a/include/async_mqtt5/logger_traits.hpp b/include/async_mqtt5/logger_traits.hpp new file mode 100644 index 0000000..94554b3 --- /dev/null +++ b/include/async_mqtt5/logger_traits.hpp @@ -0,0 +1,103 @@ +// +// Copyright (c) 2023-2024 Ivica Siladic, Bruno Iljazovic, Korina Simicevic +// +// 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_LOGGER_TRAITS_HPP +#define ASYNC_MQTT5_LOGGER_TRAITS_HPP + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace async_mqtt5 { + +namespace asio = boost::asio; +using boost::system::error_code; + +// NOOP Logger +class noop_logger {}; + +// at_resolve + +template +using at_resolve_sig = decltype( + std::declval().at_resolve( + std::declval(), + std::declval(), std::declval(), + std::declval() + ) +); +template +constexpr bool has_at_resolve = boost::is_detected::value; + +// at_tcp_connect + +template +using at_tcp_connect_sig = decltype( + std::declval().at_tcp_connect( + std::declval(), std::declval() + ) +); +template +constexpr bool has_at_tcp_connect = boost::is_detected::value; + +// at_tls_handshake + +template +using at_tls_handshake_sig = decltype( + std::declval().at_tls_handshake( + std::declval(), std::declval() + ) +); +template +constexpr bool has_at_tls_handshake = boost::is_detected::value; + +// at_ws_handshake + +template +using at_ws_handshake_sig = decltype( + std::declval().at_ws_handshake( + std::declval(), std::declval() + ) +); +template +constexpr bool has_at_ws_handshake = boost::is_detected::value; + +// at_connack + +template +using at_connack_sig = decltype( + std::declval().at_connack( + std::declval(), + std::declval(), std::declval() + ) +); +template +constexpr bool has_at_connack = boost::is_detected::value; + +// at_disconnect + +template +using at_disconnect_sig = decltype( + std::declval().at_disconnect( + std::declval(), std::declval() + ) +); +template +constexpr bool has_at_disconnect = boost::is_detected::value; + +} // end namespace async_mqtt5 + + +#endif // !ASYNC_MQTT5_LOGGER_TRAITS_HPP diff --git a/include/async_mqtt5/mqtt_client.hpp b/include/async_mqtt5/mqtt_client.hpp index 8f6a36a..f83942e 100644 --- a/include/async_mqtt5/mqtt_client.hpp +++ b/include/async_mqtt5/mqtt_client.hpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -52,7 +53,7 @@ namespace asio = boost::asio; template < typename StreamType, typename TlsContext = std::monostate, - typename LoggerType = detail::noop_logger + typename LoggerType = noop_logger > class mqtt_client { public: @@ -134,7 +135,6 @@ public: */ mqtt_client(mqtt_client&&) noexcept = default; - /** * \brief Move assignment operator. * @@ -163,7 +163,6 @@ public: return _impl->get_executor(); } - /** * \brief Get the context object used in TLS/SSL connection. * diff --git a/include/async_mqtt5/property_types.hpp b/include/async_mqtt5/property_types.hpp index aca5a09..6ee87a1 100644 --- a/include/async_mqtt5/property_types.hpp +++ b/include/async_mqtt5/property_types.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/test/include/test_common/packet_util.hpp b/test/include/test_common/packet_util.hpp index f7eb7cc..3408a23 100644 --- a/test/include/test_common/packet_util.hpp +++ b/test/include/test_common/packet_util.hpp @@ -20,9 +20,9 @@ #include #include +#include #include #include -#include namespace async_mqtt5::test { @@ -89,6 +89,7 @@ template 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) if (v.has_value()) stream << *v << " "; diff --git a/test/include/test_common/test_autoconnect_stream.hpp b/test/include/test_common/test_autoconnect_stream.hpp index 9b8a1a5..c4aa107 100644 --- a/test/include/test_common/test_autoconnect_stream.hpp +++ b/test/include/test_common/test_autoconnect_stream.hpp @@ -30,7 +30,7 @@ using error_code = boost::system::error_code; template < typename StreamType, typename StreamContext = std::monostate, - typename LoggerType = async_mqtt5::detail::noop_logger + typename LoggerType = async_mqtt5::noop_logger > class test_autoconnect_stream { public: diff --git a/test/integration/client_functions.cpp b/test/integration/client_functions.cpp index f7214b0..5319a68 100644 --- a/test/integration/client_functions.cpp +++ b/test/integration/client_functions.cpp @@ -23,6 +23,7 @@ #include #include +#include #include "test_common/message_exchange.hpp" #include "test_common/packet_util.hpp" @@ -406,7 +407,7 @@ BOOST_FIXTURE_TEST_CASE(connack_properties, shared_connack_prop_test_data) { connack_props cprops_ = c.connack_properties(); cprops_.visit([&](const auto& p, const auto& val) -> bool { BOOST_TEST_REQUIRE(p); - if constexpr (is_vector) + if constexpr (detail::is_vector) BOOST_TEST(val == cprops[p]); else { BOOST_TEST_REQUIRE(val.has_value()); @@ -429,7 +430,7 @@ BOOST_FIXTURE_TEST_CASE(connack_property, shared_connack_prop_test_data) { std::move(broker_side), [&](client_type& c) { cprops.visit([&](const auto& p, const auto& val) -> bool{ - if constexpr (is_vector) + if constexpr (detail::is_vector) BOOST_TEST(val == c.connack_property(p)); else { BOOST_TEST_REQUIRE(val.has_value()); diff --git a/test/integration/executors.cpp b/test/integration/executors.cpp index 88870d5..04bb93c 100644 --- a/test/integration/executors.cpp +++ b/test/integration/executors.cpp @@ -34,7 +34,11 @@ using strand_type = asio::strand; BOOST_AUTO_TEST_SUITE(executors) -void run_test(asio::io_context& ioc, strand_type io_ex, auto bind_async_run, auto bind_async_op) { +template +void run_test( + asio::io_context& ioc, strand_type io_ex, + AsyncRunOp&& bind_async_run, AsyncOp&& bind_async_op +) { using test::after; using namespace std::chrono_literals; diff --git a/test/integration/receive_publish.cpp b/test/integration/receive_publish.cpp index bac0a6e..df9f0d4 100644 --- a/test/integration/receive_publish.cpp +++ b/test/integration/receive_publish.cpp @@ -473,7 +473,7 @@ BOOST_FIXTURE_TEST_CASE(receive_buffer_overflow, shared_test_data) { .async_run(asio::detached); asio::steady_timer timer(executor); - timer.expires_after(7s); + timer.expires_after(10s); timer.async_wait( [&](error_code) { c.async_receive([&]( diff --git a/test/unit/connect_op.cpp b/test/unit/connect_op.cpp index 239e03c..1554a03 100644 --- a/test/unit/connect_op.cpp +++ b/test/unit/connect_op.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -65,8 +66,8 @@ void run_unit_test( std::move(h)(ec); }; - detail::log_invoke d; - detail::connect_op( + detail::log_invoke d; + detail::connect_op( stream, mqtt_ctx, d, std::move(handler) ).perform(*std::begin(eps), std::move(ap)); diff --git a/test/unit/logger.cpp b/test/unit/logger.cpp index 14cedbd..2f063b5 100644 --- a/test/unit/logger.cpp +++ b/test/unit/logger.cpp @@ -25,6 +25,7 @@ #include #include +#include #include "test_common/message_exchange.hpp" #include "test_common/test_service.hpp" @@ -54,12 +55,12 @@ void assign_tls_sni( void logger_test() { - BOOST_STATIC_ASSERT(detail::has_at_resolve); - BOOST_STATIC_ASSERT(detail::has_at_tcp_connect); - BOOST_STATIC_ASSERT(detail::has_at_tls_handshake); - BOOST_STATIC_ASSERT(detail::has_at_ws_handshake); - BOOST_STATIC_ASSERT(detail::has_at_connack); - BOOST_STATIC_ASSERT(detail::has_at_disconnect); + BOOST_STATIC_ASSERT(has_at_resolve); + BOOST_STATIC_ASSERT(has_at_tcp_connect); + BOOST_STATIC_ASSERT(has_at_tls_handshake); + BOOST_STATIC_ASSERT(has_at_ws_handshake); + BOOST_STATIC_ASSERT(has_at_connack); + BOOST_STATIC_ASSERT(has_at_disconnect); } using stream_type = boost::beast::websocket::stream< diff --git a/test/unit/reconnect_op.cpp b/test/unit/reconnect_op.cpp index b072307..3cf63a5 100644 --- a/test/unit/reconnect_op.cpp +++ b/test/unit/reconnect_op.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -113,7 +114,7 @@ void run_connect_to_localhost_test(int succeed_after) { ); auto stream_ctx = stream_context(std::monostate {}); - auto log = detail::log_invoke(); + auto log = detail::log_invoke(); auto auto_stream = astream(ioc.get_executor(), stream_ctx, log); auto_stream.brokers("localhost", 1883); @@ -147,7 +148,7 @@ BOOST_AUTO_TEST_CASE(no_servers) { asio::io_context ioc; auto stream_ctx = stream_context(std::monostate{}); - auto log = detail::log_invoke(); + auto log = detail::log_invoke(); auto auto_stream = astream(ioc.get_executor(), stream_ctx, log); auto_stream.brokers("", 1883); diff --git a/test/unit/serialization.cpp b/test/unit/serialization.cpp index 9fb63e9..30317df 100644 --- a/test/unit/serialization.cpp +++ b/test/unit/serialization.cpp @@ -692,7 +692,7 @@ BOOST_AUTO_TEST_CASE(test_pingresp) { BOOST_AUTO_TEST_CASE(subscription_identifiers) { // check boost::container::small_vector interface - BOOST_TEST_REQUIRE(is_small_vector); + BOOST_TEST_REQUIRE(detail::is_small_vector); // check optional interface prop::subscription_identifiers sub_ids;