Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:38:23

0001 //
0002 // Copyright (c) 2023-2025 Ivica Siladic, Bruno Iljazovic, Korina Simicevic
0003 //
0004 // Distributed under the Boost Software License, Version 1.0.
0005 // (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #ifndef BOOST_MQTT5_MQTT_CLIENT_HPP
0009 #define BOOST_MQTT5_MQTT_CLIENT_HPP
0010 
0011 #include <boost/mqtt5/error.hpp>
0012 #include <boost/mqtt5/logger_traits.hpp>
0013 #include <boost/mqtt5/types.hpp>
0014 
0015 #include <boost/mqtt5/detail/log_invoke.hpp>
0016 #include <boost/mqtt5/detail/rebind_executor.hpp>
0017 
0018 #include <boost/mqtt5/impl/client_service.hpp>
0019 #include <boost/mqtt5/impl/publish_send_op.hpp>
0020 #include <boost/mqtt5/impl/re_auth_op.hpp>
0021 #include <boost/mqtt5/impl/run_op.hpp>
0022 #include <boost/mqtt5/impl/subscribe_op.hpp>
0023 #include <boost/mqtt5/impl/unsubscribe_op.hpp>
0024 
0025 #include <boost/asio/async_result.hpp>
0026 #include <boost/system/error_code.hpp>
0027 
0028 #include <memory>
0029 #include <string>
0030 #include <type_traits>
0031 #include <variant> // std::monostate
0032 #include <vector>
0033 
0034 namespace boost::mqtt5 {
0035 
0036 namespace asio = boost::asio;
0037 
0038 /**
0039  * \brief \__MQTT\__ client used to connect and communicate with a Broker.
0040  *
0041  * \tparam \__StreamType\__ Type of the underlying transport protocol used to transfer
0042  * the stream of bytes between the Client and the Broker. The transport must be
0043  * ordered and lossless.
0044  * \tparam \__TlsContext\__ Type of the context object used in TLS/SSL connections.
0045  * \tparam \__LoggerType\__ Type of object used to log events within the Client.
0046  *
0047  * \par Thread safety
0048  * Distinct objects: safe. \n
0049  * Shared objects: unsafe. \n
0050  * This class is <b>not thread-safe</b>.
0051  * The application must also ensure that all asynchronous operations are performed within the same implicit or explicit strand.
0052  */
0053 template <
0054     typename StreamType,
0055     typename TlsContext = std::monostate,
0056     typename LoggerType = noop_logger
0057 >
0058 class mqtt_client {
0059 public:
0060     /// The executor type associated with the client.
0061     using executor_type = typename StreamType::executor_type;
0062 
0063     /// Rebinds the client type to another executor.
0064     template <typename Executor>
0065     struct rebind_executor {
0066         /// The client type when rebound to the specified executor.
0067         using other = mqtt_client<
0068             typename detail::rebind_executor<StreamType, Executor>::other,
0069             TlsContext,
0070             LoggerType
0071         >;
0072     };
0073 
0074 private:
0075     using stream_type = StreamType;
0076     using tls_context_type = TlsContext;
0077     using logger_type = LoggerType;
0078 
0079     using client_service_type = detail::client_service<
0080         stream_type, tls_context_type, logger_type
0081     >;
0082     using impl_type = std::shared_ptr<client_service_type>;
0083     impl_type _impl;
0084 
0085 public:
0086     /**
0087      * \brief Constructs a Client with given parameters.
0088      *
0089      * \param ex An executor that will be associated with the Client.
0090      * \param tls_context A context object used in TLS/SSL connection.
0091      * \param logger An object satisfying the \__LoggerType\__ concept used to log events within the Client.
0092      */
0093     explicit mqtt_client(
0094         const executor_type& ex,
0095         tls_context_type tls_context = {}, logger_type logger = {}
0096     ) :
0097         _impl(std::make_shared<client_service_type>(
0098             ex, std::move(tls_context), std::move(logger)
0099         ))
0100     {}
0101 
0102     /**
0103      * \brief Constructs a Client with given parameters.
0104      *
0105      * \tparam \__ExecutionContext\__ Type of a concrete execution context.
0106      * \param context Execution context whose executor will be associated with the Client.
0107      * \param tls_context A context object used in TLS/SSL connection.
0108      * \param logger An object satisfying the \__LoggerType\__ concept used to log events within the Client.
0109      *
0110      * \par Precondition
0111      * \code
0112      * std::is_convertible_v<ExecutionContext&, asio::execution_context&>
0113      * \endcode
0114      */
0115     template <
0116         typename ExecutionContext,
0117         std::enable_if_t<
0118             std::is_convertible_v<ExecutionContext&, asio::execution_context&>, 
0119             bool
0120         > = true
0121     >
0122     explicit mqtt_client(
0123         ExecutionContext& context,
0124         tls_context_type tls_context = {}, logger_type logger = {}
0125     ) :
0126         mqtt_client(
0127             context.get_executor(),
0128             std::move(tls_context), std::move(logger)
0129         )
0130     {}
0131 
0132     /**
0133      * \brief Move-construct an mqtt_client from another.
0134      */
0135     mqtt_client(mqtt_client&&) noexcept = default;
0136 
0137     /**
0138      * \brief Move assignment operator.
0139      *
0140      * \details Cancels this client first.
0141      */
0142     mqtt_client& operator=(mqtt_client&& other) noexcept {
0143         _impl->cancel();
0144         _impl = std::move(other._impl);
0145         return *this;
0146     }
0147 
0148     /**
0149      * \brief Destructor.
0150      *
0151      * \details Automatically calls \ref cancel.
0152      */
0153     ~mqtt_client() {
0154         if (_impl)
0155             _impl->cancel();
0156     }
0157 
0158     /**
0159      * \brief Get the executor associated with the object.
0160      */
0161     executor_type get_executor() const noexcept {
0162         return _impl->get_executor();
0163     }
0164 
0165     /**
0166      * \brief Get the context object used in TLS/SSL connection.
0167      *
0168      * \note This function may only be invoked
0169      * when the template parameter \__TlsContext\__ was configured
0170      * with non-default type during the creation of a \ref mqtt_client.
0171      *
0172      * \par Precondition
0173      * \code
0174      * !std::is_same_v<TlsContext, std::monostate>
0175      * \endcode
0176      */
0177     template <
0178         typename Ctx = TlsContext,
0179         std::enable_if_t<!std::is_same_v<Ctx, std::monostate>, bool> = true
0180     >
0181     decltype(auto) tls_context() {
0182         return _impl->tls_context();
0183     }
0184 
0185     /**
0186      * \brief Start the Client.
0187      *
0188      * \param token Completion token that will be used to produce a
0189      * completion handler. The handler will be invoked when the operation completes.
0190      *
0191      * \par Handler signature
0192      * The handler signature for this operation:
0193      *    \code
0194      *        void (__ERROR_CODE__)
0195      *    \endcode
0196      *
0197      * \par Completion condition
0198      * The asynchronous operation will complete with
0199      * `boost::asio::error::operation_aborted` when the client is cancelled by calling
0200      * \ref async_disconnect, \ref cancel, destruction or
0201      * if a non-recoverable error happens during a connection attempt (e.g. access denied).
0202      *
0203      *    \par Error codes
0204      *    The list of all possible error codes that this operation can finish with:\n
0205      *        - `boost::asio::error::operation_aborted`\n
0206      *
0207      *    \par Per-Operation Cancellation
0208      *    This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
0209      *        - `cancellation_type::terminal` - invokes \ref cancel \n
0210      */
0211     template <
0212         typename CompletionToken =
0213             typename asio::default_completion_token<executor_type>::type
0214     >
0215     decltype(auto) async_run(CompletionToken&& token = {}) {
0216         using Signature = void (error_code);
0217         return asio::async_initiate<CompletionToken, Signature>(
0218             detail::initiate_async_run(_impl), token
0219         );
0220     }
0221 
0222     /**
0223      * \brief Cancel all asynchronous operations. This function has terminal effects.
0224      *
0225      * \details All outstanding operations will complete
0226      * with `boost::asio::error::operation_aborted`.
0227      *
0228      * \attention This function has terminal effects and will close the Client.
0229      * The Client cannot be used before calling \ref async_run again.
0230      */
0231     void cancel() {
0232         auto impl = _impl;
0233         _impl = impl->dup();
0234         impl->cancel();
0235     }
0236 
0237     /**
0238      * \brief Assign a \ref will Message.
0239      *
0240      * \details The \ref will Message that the Broker should publish
0241      * after the Network Connection is closed and it is not
0242      * closed normally.
0243      *
0244      * \attention This function takes action when the client is in a non-operational state,
0245      * meaning the \ref async_run function has not been invoked.
0246      * Furthermore, you can use this function after the \ref cancel function has been called,
0247      * before the \ref async_run function is invoked again.
0248      */
0249     mqtt_client& will(will will) {
0250         _impl->will(std::move(will));
0251         return *this;
0252     }
0253 
0254     /**
0255      * \brief Assign credentials that will be used to connect to a Broker.
0256      *
0257      * \details Credentials consist of a unique Client Identifier and, optionally,
0258      * a User Name and Password.
0259      *
0260      * \attention This function takes action when the client is in a non-operational state,
0261      * meaning the \ref async_run function has not been invoked.
0262      * Furthermore, you can use this function after the \ref cancel function has been called,
0263      * before the \ref async_run function is invoked again.
0264      */
0265     mqtt_client& credentials(
0266         std::string client_id,
0267         std::string username = "", std::string password = ""
0268     ) {
0269         _impl->credentials(
0270             std::move(client_id),
0271             std::move(username), std::move(password)
0272         );
0273         return *this;
0274     }
0275 
0276     /**
0277      * \brief Assign a list of Brokers that the Client will attempt to connect to.
0278      *
0279      * \details The Client will cycle through the list of hosts,
0280      * attempting to establish a connection with each
0281      * until it successfully establishes a connection.
0282      *
0283      * \param hosts List of Broker addresses and ports.
0284      * Address and ports are separated with a colon `:` while
0285      * pairs of addresses and ports are separated with a comma `,`.
0286      * \param default_port The default port to connect to in case the port is not
0287      * explicitly specified in the `hosts` list.
0288      *
0289      * \attention This function takes action when the client is in a non-operational state,
0290      * meaning the \ref async_run function has not been invoked.
0291      * Furthermore, you can use this function after the \ref cancel function has been called,
0292      * before the \ref async_run function is invoked again.
0293      *
0294      * \par Example
0295      * Some valid `hosts` string:
0296      *
0297      * \code
0298      *        std::string valid_hosts_1 = "broker1:1883, broker2, broker3:1883";
0299      *        std::string valid_hosts_2 = "broker1";
0300      * \endcode
0301      *
0302      */
0303     mqtt_client& brokers(std::string hosts, uint16_t default_port = 1883) {
0304         _impl->brokers(std::move(hosts), default_port);
0305         return *this;
0306     }
0307 
0308     /**
0309      * \brief Assign an authenticator that the Client will use for
0310      * \__ENHANCED_AUTH\__ on every connect to a Broker.
0311      * Re-authentication can be initiated by calling \ref re_authenticate.
0312      *
0313      * \param authenticator Object that will be stored (move-constructed or by reference)
0314      * and used for authentication. It needs to satisfy \__Authenticator\__ concept.
0315      *
0316      * \attention This function takes action when the client is in a non-operational state,
0317      * meaning the \ref async_run function has not been invoked.
0318      * Furthermore, you can use this function after the \ref cancel function has been called,
0319      * before the \ref async_run function is invoked again.
0320      *
0321      */
0322     template <typename Authenticator>
0323     mqtt_client& authenticator(Authenticator&& authenticator) {
0324         static_assert(
0325             detail::is_authenticator<Authenticator>,
0326             "The type does not satisfy the Authenticator concept"
0327         );
0328         _impl->authenticator(std::forward<Authenticator>(authenticator));
0329         return *this;
0330     }
0331 
0332     /**
0333      * \brief Assign the maximum time interval that is permitted to elapse between
0334      * two transmissions from the Client.
0335      *
0336      * \details A non-zero value initiates a process of sending a \__PINGREQ\__
0337      * packet every `seconds`. If this function is not invoked, the Client assumes
0338      * a \__KEEP_ALIVE\__ interval of 60 seconds.
0339      *
0340      * \param seconds Time interval in seconds.
0341      *
0342      * \note If the Server sends a \__SERVER_KEEP_ALIVE\__,
0343      * the Client will send a \__PINGREQ\__ packet every \__SERVER_KEEP_ALIVE\__ seconds.
0344      *
0345      * \attention This function takes action when the client is in a non-operational state,
0346      * meaning the \ref async_run function has not been invoked.
0347      * Furthermore, you can use this function after the \ref cancel function has been called,
0348      * before the \ref async_run function is invoked again.
0349      *
0350      */
0351     mqtt_client& keep_alive(uint16_t seconds) {
0352         _impl->keep_alive(seconds);
0353         return *this;
0354     }
0355 
0356     /**
0357      * \brief Assign \__CONNECT_PROPS\__ that will be sent in a \__CONNECT\__ packet.
0358      * \param props \__CONNECT_PROPS\__ sent in a \__CONNECT\__ packet.
0359      * \see See \__CONNECT_PROPS\__ for all eligible properties.
0360      */
0361     mqtt_client& connect_properties(connect_props props) {
0362         _impl->connect_properties(std::move(props));
0363         return *this;
0364     }
0365 
0366     /**
0367      * \brief Assign a property that will be sent in a \__CONNECT\__ packet.
0368      * \param prop The \__CONNECT_PROPS\__ property to set.
0369      * \param value Value that will be assigned to the property.
0370      *
0371      * \par Example
0372      * \code
0373      * client.connect_property(prop::session_expiry_interval, 40); // ok
0374      * client.connect_property(prop::reason_string, "reason"); // does not compile, not a CONNECT prop!
0375      * \endcode
0376      *
0377      * \see See \__CONNECT_PROPS\__ for all eligible properties.
0378      */
0379     template <prop::property_type p>
0380     mqtt_client& connect_property(
0381         std::integral_constant<prop::property_type, p> prop,
0382         prop::value_type_t<p> value
0383     ) {
0384         _impl->connect_property(prop, std::move(value));
0385         return *this;
0386     }
0387 
0388     /**
0389      * \brief Initiates \__RE_AUTHENTICATION\__
0390      * using the authenticator given in the \ref authenticator method.
0391      *
0392      * \note If \ref authenticator was not called, this method does nothing.
0393      */
0394     void re_authenticate() {
0395         detail::re_auth_op { _impl }.perform();
0396     }
0397 
0398     /**
0399      * \brief Retrieves the value of a specific property from the last \__CONNACK\__ packet received.
0400      *
0401      * \details The return type varies according to the property requested.
0402      * For all properties, the return type will be `std::optional` of their respective value type.
0403      * For `boost::mqtt5::prop::user_property`, the return type is
0404      * `std::vector<std::pair<std::string, std::string>>`.
0405      *
0406      * \param prop The \__CONNACK_PROPS\__ property value to retrieve.
0407      *
0408      * \par Example
0409      * \code
0410      *    std::optional<std::string> auth_method = client.connack_property(boost::mqtt5::prop::authentication_method); // ok
0411      *    std::optional<std::string> c_type = client.connack_property(boost::mqtt5::prop::content_type); // does not compile, not a CONNACK prop!
0412      * \endcode
0413      *
0414      * \see See \__CONNACK_PROPS\__ for all eligible properties.
0415      */
0416     template <prop::property_type p>
0417     const auto& connack_property(
0418         std::integral_constant<prop::property_type, p> prop
0419     ) const {
0420         return _impl->connack_property(prop);
0421     }
0422 
0423     /**
0424      * \brief Retrieves the \__CONNACK_PROPS\__ from the last \__CONNACK\__ packet received.
0425      *
0426      * \see See \__CONNACK_PROPS\__ for all eligible properties.
0427      */
0428     const connack_props& connack_properties() const {
0429         return _impl->connack_properties();
0430     }
0431 
0432     /**
0433      * \brief Send a \__PUBLISH\__ packet to Broker to transport an
0434      * Application Message.
0435      *
0436      * \tparam qos_type The \ref qos_e level of assurance for delivery.
0437      * \param topic Identification of the information channel to which
0438      * Payload data is published.
0439      * \param payload The Application Message that is being published.
0440      * \param retain The \ref retain_e flag.
0441      * \param props An instance of \__PUBLISH_PROPS\__. 
0442      * \param token Completion token that will be used to produce a
0443      * completion handler. The handler will be invoked when the operation completes.
0444      * On immediate completion, invocation of the handler will be performed in a manner
0445      * equivalent to using \__ASYNC_IMMEDIATE\__.
0446      *
0447      * \par Handler signature
0448      * The handler signature for this operation depends on the \ref qos_e specified:\n
0449      *
0450      *    `qos` == `qos_e::at_most_once`:
0451      *        \code
0452      *            void (
0453      *                __ERROR_CODE__    // Result of operation
0454      *            )
0455      *        \endcode
0456      *
0457      *    `qos` == `qos_e::at_least_once`:
0458      *        \code
0459      *            void (
0460      *                __ERROR_CODE__,    // Result of operation.
0461      *                __REASON_CODE__,   // Reason Code received from Broker.
0462      *                __PUBACK_PROPS__   // Properties received in the PUBACK packet.
0463      *            )
0464      *        \endcode
0465      *
0466      *    `qos` == `qos_e::exactly_once`:
0467      *        \code
0468      *            void (
0469      *                __ERROR_CODE__,    // Result of operation.
0470      *                __REASON_CODE__,   // Reason Code received from Broker.
0471      *                __PUBCOMP_PROPS__  // Properties received in the PUBCOMP packet.
0472      *            )
0473      *        \endcode
0474      *
0475      *    \par Completion condition
0476      *    Depending on the \ref qos_e specified, the asynchronous operation will complete
0477      *    when one of the following conditions is true:\n
0478      *        - If `qos` == `qos_e::at_most_once` and the Client
0479      *        has successfully written the packet to the transport. \n
0480      *        - If `qos` == `qos_e::at_least_once` and the packet has
0481      *        been sent and acknowledged through the reception of a \__PUBACK\__ packet.
0482      *        - If `qos` == `qos_e::exactly_once` and the packet has
0483      *        been sent and fully acknowledged through the reception of a \__PUBCOMP\__ packet.
0484      *        - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
0485      *
0486      *    \par Error codes
0487      *    The list of all possible error codes that this operation can finish with:\n
0488      *        - `boost::system::errc::errc_t::success` \n
0489      *        - `boost::asio::error::operation_aborted` \n
0490      *        - `boost::asio::error::no_recovery` \n
0491      *        - \ref boost::mqtt5::client::error::malformed_packet
0492      *        - \ref boost::mqtt5::client::error::packet_too_large
0493      *        - \ref boost::mqtt5::client::error::pid_overrun
0494      *        - \ref boost::mqtt5::client::error::qos_not_supported
0495      *        - \ref boost::mqtt5::client::error::retain_not_available
0496      *        - \ref boost::mqtt5::client::error::topic_alias_maximum_reached
0497      *        - \ref boost::mqtt5::client::error::invalid_topic
0498      *
0499      * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
0500      * 
0501      *    \par Per-Operation Cancellation
0502      *    This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
0503      *        - `cancellation_type::terminal` - invokes \ref cancel \n
0504      *        - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__PUBLISH\__ packet \n
0505      *
0506      */
0507     template <qos_e qos_type, 
0508         typename CompletionToken =
0509             typename asio::default_completion_token<executor_type>::type
0510     >
0511     decltype(auto) async_publish(
0512         std::string topic, std::string payload,
0513         retain_e retain, const publish_props& props,
0514         CompletionToken&& token = {}
0515     ) {
0516         using Signature = detail::on_publish_signature<qos_type>;
0517         return asio::async_initiate<CompletionToken, Signature>(
0518             detail::initiate_async_publish<client_service_type, qos_type>(_impl),
0519             token,
0520             std::move(topic), std::move(payload), retain, props
0521         );
0522     }
0523 
0524     /**
0525      * \brief Send a \__SUBSCRIBE\__ packet to Broker to create a Subscription
0526      * to one or more Topics of interest.
0527      *
0528      * \details After the Subscription has been established, the Broker will send
0529      * PUBLISH packets to the Client to forward Application Messages that were published
0530      * to Topics that the Client subscribed to. The Application Messages can be received
0531      * with \ref mqtt_client::async_receive function.
0532      *
0533      * \param topics A list of \ref subscribe_topic of interest.
0534      * \param props An instance of \__SUBSCRIBE_PROPS\__.
0535      * \param token Completion token that will be used to produce a
0536      * completion handler. The handler will be invoked when the operation completes.
0537      * On immediate completion, invocation of the handler will be performed in a manner
0538      * equivalent to using \__ASYNC_IMMEDIATE\__.
0539      *
0540      * \par Handler signature
0541      * The handler signature for this operation:
0542      *    \code
0543      *        void (
0544      *            __ERROR_CODE__,    // Result of operation.
0545      *            std::vector<__REASON_CODE__>,  // Vector of Reason Codes indicating
0546      *                                           // the Subscription result for each Topic
0547      *                                           // in the SUBSCRIBE packet.
0548      *            __SUBACK_PROPS__,  // Properties received in the SUBACK packet.
0549      *        )
0550      *    \endcode
0551      *
0552      *    \par Completion condition
0553      *    The asynchronous operation will complete when one of the following conditions is true:\n
0554      *        - The Client has successfully sent a \__SUBSCRIBE\__ packet
0555      *        and has received a \__SUBACK\__ response from the Broker.\n
0556      *        - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
0557      *
0558      *    \par Error codes
0559      *    The list of all possible error codes that this operation can finish with:\n
0560      *        - `boost::system::errc::errc_t::success` \n
0561      *        - `boost::asio::error::no_recovery` \n
0562      *        - `boost::asio::error::operation_aborted` \n
0563      *        - \ref boost::mqtt5::client::error::malformed_packet
0564      *        - \ref boost::mqtt5::client::error::packet_too_large
0565      *        - \ref boost::mqtt5::client::error::pid_overrun
0566      *        - \ref boost::mqtt5::client::error::invalid_topic
0567      *        - \ref boost::mqtt5::client::error::wildcard_subscription_not_available
0568      *        - \ref boost::mqtt5::client::error::subscription_identifier_not_available
0569      *        - \ref boost::mqtt5::client::error::shared_subscription_not_available
0570      *
0571      * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
0572      * 
0573      *    \par Per-Operation Cancellation
0574      *    This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
0575      *        - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
0576      *        - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__SUBSCRIBE\__ packet \n
0577      *
0578      */
0579     template <
0580         typename CompletionToken =
0581             typename asio::default_completion_token<executor_type>::type
0582     >
0583     decltype(auto) async_subscribe(
0584         const std::vector<subscribe_topic>& topics,
0585         const subscribe_props& props,
0586         CompletionToken&& token = {}
0587     ) {
0588         using Signature = void (
0589             error_code, std::vector<reason_code>, suback_props
0590         );
0591         return asio::async_initiate<CompletionToken, Signature>(
0592             detail::initiate_async_subscribe(_impl), token,
0593             topics, props
0594         );
0595     }
0596 
0597     /**
0598      * \brief Send a \__SUBSCRIBE\__ packet to Broker to create a Subscription
0599      * to one Topic of interest.
0600      *
0601      * \details After the Subscription has been established, the Broker will send
0602      * \__PUBLISH\__ packets to the Client to forward Application Messages that were published
0603      * to Topics that the Client subscribed to. The Application Messages can be received
0604      * with \ref mqtt_client::async_receive function.
0605      *
0606      * \param topic A \ref subscribe_topic of interest.
0607      * \param props An instance of \__SUBSCRIBE_PROPS\__.
0608      * \param token Completion token that will be used to produce a
0609      * completion handler. The handler will be invoked when the operation completes.
0610      * On immediate completion, invocation of the handler will be performed in a manner
0611      * equivalent to using \__ASYNC_IMMEDIATE\__.
0612      *
0613      * \par Handler signature
0614      * The handler signature for this operation:
0615      *    \code
0616      *        void (
0617      *            __ERROR_CODE__,    // Result of operation.
0618      *            std::vector<__REASON_CODE__>,  // Vector of Reason Codes containing the
0619      *                                           // single Subscription result for the Topic
0620      *                                           // in the SUBSCRIBE packet.
0621      *            __SUBACK_PROPS__,  // Properties received in the SUBACK packet.
0622      *        )
0623      *    \endcode
0624      *
0625      *    \par Completion condition
0626      *    The asynchronous operation will complete when one of the following conditions is true:\n
0627      *        - The Client has successfully sent a \__SUBSCRIBE\__ packet
0628      *        and has received a \__SUBACK\__ response from the Broker.\n
0629      *        - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
0630      *
0631      *    \par Error codes
0632      *    The list of all possible error codes that this operation can finish with:\n
0633      *        - `boost::system::errc::errc_t::success` \n
0634      *        - `boost::asio::error::no_recovery` \n
0635      *        - `boost::asio::error::operation_aborted` \n
0636      *        - \ref boost::mqtt5::client::error::malformed_packet
0637      *        - \ref boost::mqtt5::client::error::packet_too_large
0638      *        - \ref boost::mqtt5::client::error::pid_overrun
0639      *        - \ref boost::mqtt5::client::error::invalid_topic
0640      *        - \ref boost::mqtt5::client::error::wildcard_subscription_not_available
0641      *        - \ref boost::mqtt5::client::error::subscription_identifier_not_available
0642      *        - \ref boost::mqtt5::client::error::shared_subscription_not_available
0643      *
0644      * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
0645      * 
0646      *    \par Per-Operation Cancellation
0647      *    This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
0648      *        - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
0649      *        - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__SUBSCRIBE\__ packet \n
0650      *
0651      */
0652     template <
0653         typename CompletionToken =
0654             typename asio::default_completion_token<executor_type>::type
0655     >
0656     decltype(auto) async_subscribe(
0657         const subscribe_topic& topic, const subscribe_props& props,
0658         CompletionToken&& token = {}
0659     ) {
0660         return async_subscribe(
0661             std::vector<subscribe_topic> { topic }, props,
0662             std::forward<CompletionToken>(token)
0663         );
0664     }
0665 
0666 
0667     /**
0668      * \brief Send an \__UNSUBSCRIBE\__ packet to Broker to unsubscribe from one
0669      * or more Topics.
0670      *
0671      * \note The Client may still receive residual Application Messages
0672      * through the \ref mqtt_client::async_receive function
0673      * from Topics the Client just unsubscribed to.
0674      *
0675      * \param topics List of Topics to unsubscribe from.
0676      * \param props An instance of \__UNSUBSCRIBE_PROPS\__.
0677      * \param token Completion token that will be used to produce a
0678      * completion handler. The handler will be invoked when the operation completes.
0679      * On immediate completion, invocation of the handler will be performed in a manner
0680      * equivalent to using \__ASYNC_IMMEDIATE\__.
0681      *
0682      * \par Handler signature
0683      * The handler signature for this operation:
0684      *    \code
0685      *        void (
0686      *            __ERROR_CODE__, // Result of operation.
0687      *            std::vector<__REASON_CODE__>,  // Vector of Reason Codes indicating
0688      *                                           // the result of unsubscribe operation
0689      *                                           // for each Topic in the UNSUBSCRIBE packet.
0690      *            __UNSUBACK_PROPS__, // Properties received in the UNSUBACK packet.
0691      *        )
0692      *    \endcode
0693      *
0694      *    \par Completion condition
0695      *    The asynchronous operation will complete when one of the following conditions is true:\n
0696      *        - The Client has successfully sent an \__UNSUBSCRIBE\__ packet
0697      *        and has received an \__UNSUBACK\__ response from the Broker.\n
0698      *        - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
0699      *
0700      *    \par Error codes
0701      *    The list of all possible error codes that this operation can finish with:\n
0702      *        - `boost::system::errc::errc_t::success` \n
0703      *        - `boost::asio::error::no_recovery` \n
0704      *        - `boost::asio::error::operation_aborted` \n
0705      *        - \ref boost::mqtt5::client::error::malformed_packet
0706      *        - \ref boost::mqtt5::client::error::packet_too_large
0707      *        - \ref boost::mqtt5::client::error::pid_overrun
0708      *        - \ref boost::mqtt5::client::error::invalid_topic
0709      *
0710      * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
0711      * 
0712      *    \par Per-Operation Cancellation
0713      *    This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
0714      *        - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
0715      *        - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__UNSUBSCRIBE\__ packet \n
0716      *
0717      */
0718     template <
0719         typename CompletionToken =
0720             typename asio::default_completion_token<executor_type>::type
0721     >
0722     decltype(auto) async_unsubscribe(
0723         const std::vector<std::string>& topics,    const unsubscribe_props& props,
0724         CompletionToken&& token = {}
0725     ) {
0726         using Signature = void (
0727             error_code, std::vector<reason_code>, unsuback_props
0728         );
0729         return asio::async_initiate<CompletionToken, Signature>(
0730             detail::initiate_async_unsubscribe(_impl), token,
0731             topics, props
0732         );
0733     }
0734 
0735     /**
0736      * \brief Send an \__UNSUBSCRIBE\__ packet to Broker to unsubscribe
0737      * from one Topic.
0738      *
0739      * \note The Client may still receive residual Application Messages
0740      * through the \ref mqtt_client::async_receive function
0741      * from Topics the Client just unsubscribed to.
0742      *
0743      * \param topic Topic to unsubscribe from.
0744      * \param props An instance of \__UNSUBSCRIBE_PROPS\__.
0745      * \param token Completion token that will be used to produce a
0746      * completion handler. The handler will be invoked when the operation completes.
0747      * On immediate completion, invocation of the handler will be performed in a manner
0748      * equivalent to using \__ASYNC_IMMEDIATE\__.
0749      *
0750      * \par Handler signature
0751      * The handler signature for this operation:
0752      *    \code
0753      *        void (
0754      *            __ERROR_CODE__, // Result of operation.
0755      *            std::vector<__REASON_CODE__>,  // Vector of Reason Codes containing
0756      *                                           // the result of unsubscribe operation
0757      *                                           // for the Topic in the UNSUBSCRIBE packet.
0758      *            __UNSUBACK_PROPS__, // Properties received in the UNSUBACK packet.
0759      *        )
0760      *    \endcode
0761      *
0762      *    \par Completion condition
0763      *    The asynchronous operation will complete when one of the following conditions is true:\n
0764      *        - The Client has successfully sent an \__UNSUBSCRIBE\__ packet
0765      *        and has received an \__UNSUBACK\__ response from the Broker.\n
0766      *        - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
0767      *
0768      *    \par Error codes
0769      *    The list of all possible error codes that this operation can finish with:\n
0770      *        - `boost::system::errc::errc_t::success` \n
0771      *        - `boost::asio::error::no_recovery` \n
0772      *        - `boost::asio::error::operation_aborted` \n
0773      *        - \ref boost::mqtt5::client::error::malformed_packet
0774      *        - \ref boost::mqtt5::client::error::packet_too_large
0775      *        - \ref boost::mqtt5::client::error::pid_overrun
0776      *        - \ref boost::mqtt5::client::error::invalid_topic
0777      *
0778      * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
0779      * 
0780      *    \par Per-Operation Cancellation
0781      *    This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
0782      *        - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
0783      *        - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__UNSUBSCRIBE\__ packet \n
0784      *
0785      */
0786     template <
0787         typename CompletionToken =
0788             typename asio::default_completion_token<executor_type>::type
0789     >
0790     decltype(auto) async_unsubscribe(
0791         const std::string& topic, const unsubscribe_props& props,
0792         CompletionToken&& token = {}
0793     ) {
0794         return async_unsubscribe(
0795             std::vector<std::string> { topic }, props,
0796             std::forward<CompletionToken>(token)
0797         );
0798     }
0799 
0800 
0801     /**
0802      * \brief Asynchronously receive an Application Message.
0803      *
0804      * \details The Client will receive and complete deliveries for all the
0805      * \__PUBLISH\__ packets received from the Broker throughout its lifetime.
0806      * The Client will store them internally in the order they were delivered.
0807      * Calling this function will attempt to receive an Application Message
0808      * from internal storage.
0809      *
0810      * \note It is only recommended to call this function if you have established
0811      * a successful Subscription to a Topic using the \ref async_subscribe function.
0812      *
0813      * \param token Completion token that will be used to produce a
0814      * completion handler. The handler will be invoked when the operation completes.
0815      * On immediate completion, invocation of the handler will be performed in a manner
0816      * equivalent to using \__POST\__.
0817      *
0818      * \par Handler signature
0819      * The handler signature for this operation:
0820      *    \code
0821      *        void (
0822      *            __ERROR_CODE__, // Result of operation.
0823      *            std::string,    // Topic, the origin of the Application Message.
0824      *            std::string,    // Payload, the content of the Application Message.
0825      *            __PUBLISH_PROPS__, // Properties received in the PUBLISH packet.
0826      *        )
0827      *    \endcode
0828      *
0829      * \par Completion condition
0830      *    The asynchronous operation will complete when one of the following conditions is true:\n
0831      *        - The Client has a pending Application Message in its internal storage
0832      *        ready to be received.
0833      *        - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
0834      *
0835      *    \par Error codes
0836      *    The list of all possible error codes that this operation can finish with:\n
0837      *        - `boost::system::errc::errc_t::success`\n
0838      *        - `boost::asio::error::operation_aborted`\n
0839      *        - \ref boost::mqtt5::client::error::session_expired
0840      *
0841      * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
0842      * 
0843      *    \par Per-Operation Cancellation
0844      *    This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
0845      *        - `cancellation_type::terminal` \n
0846      *        - `cancellation_type::partial` \n
0847      *        - `cancellation_type::total` \n
0848      */
0849     template <
0850         typename CompletionToken =
0851             typename asio::default_completion_token<executor_type>::type
0852     >
0853     decltype(auto) async_receive(CompletionToken&& token = {}) {
0854         return _impl->async_channel_receive(std::forward<CompletionToken>(token));
0855     }
0856 
0857     /**
0858      * \brief Disconnect the Client by sending a \__DISCONNECT\__ packet
0859      * with a specified Reason Code. This function has terminal effects.
0860      *
0861      * \details The Client will attempt to send a \__DISCONNECT\__ packet to the Broker
0862      * with a Reason Code describing the reason for disconnection.
0863      * If the \__DISCONNECT\__ packet is successfully transmitted,
0864      * or if `5 seconds` elapsed without a successful send, the Client will terminate the connection.
0865      *
0866      * \attention This function has terminal effects and will close the Client.
0867      * See \ref mqtt_client::cancel.
0868      *
0869      * \param reason_code Reason Code to notify
0870      * the Broker of the reason for the disconnection.
0871      * \param props An instance of \__DISCONNECT_PROPS\__.
0872      * \param token Completion token that will be used to produce a
0873      * completion handler. The handler will be invoked when the operation completes.
0874      *
0875      * \par Handler signature
0876      * The handler signature for this operation:
0877      *    \code
0878      *        void (
0879      *            __ERROR_CODE__ // Result of operation.
0880      *        )
0881      *    \endcode
0882      *
0883      *    \par Completion condition
0884      *    The asynchronous operation will complete when one of the following conditions is true:\n
0885      *        - The Client has sent a \__DISCONNECT\__ packet.\n
0886      *        - 5 seconds have elapsed without a successful send.\n
0887      *        - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
0888      *
0889      *    \par Error codes
0890      *    The list of all possible error codes that this operation can finish with:\n
0891      *        - `boost::system::errc::errc_t::success`\n
0892      *        - `boost::asio::error::operation_aborted`[footnote
0893                 This error code can appear if the Client fails to send the \__DISCONNECT\__ packet to the Server.
0894                 Regardless, the connection to the Server is terminated, and the Client is cancelled.
0895             ]\n
0896      *        - \ref boost::mqtt5::client::error::malformed_packet
0897      *
0898      * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
0899      * 
0900      *    \par Per-Operation Cancellation
0901      *    This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
0902      *        - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
0903      * 
0904      */
0905     template <
0906         typename CompletionToken =
0907             typename asio::default_completion_token<executor_type>::type
0908     >
0909     decltype(auto) async_disconnect(
0910         disconnect_rc_e reason_code, const disconnect_props& props,
0911         CompletionToken&& token = {}
0912     ) {
0913         auto impl = _impl;
0914         _impl = impl->dup();
0915         return detail::async_terminal_disconnect(
0916             detail::disconnect_rc_e(static_cast<uint8_t>(reason_code)),
0917             props, impl, std::forward<CompletionToken>(token)
0918         );
0919     }
0920 
0921     /**
0922      * \brief Disconnect the Client by sending a \__DISCONNECT\__ packet
0923      * with a Reason Code of reason_codes.normal_disconnection.
0924      * This function has terminal effects.
0925      * 
0926      * \details The Client will attempt to send a \__DISCONNECT\__ packet to the Broker
0927      * with a Reason Code describing the reason for disconnection.
0928      * If the \__DISCONNECT\__ packet is successfully transmitted,
0929      * or if `5 seconds` elapsed without a successful send, the Client will terminate the connection.
0930      *
0931      * \attention This function has terminal effects and will close the Client.
0932      * See \ref mqtt_client::cancel.
0933      *
0934      * \param token Completion token that will be used to produce a
0935      * completion handler. The handler will be invoked when the operation completes.
0936      *
0937      * \par Handler signature
0938      * The handler signature for this operation:
0939      *    \code
0940      *        void (
0941      *            __ERROR_CODE__ // Result of operation.
0942      *        )
0943      *    \endcode
0944      *
0945      *    \par Completion condition
0946      *    The asynchronous operation will complete when one of the following conditions is true:\n
0947      *        - The Client has attempted to send a \__DISCONNECT\__ packet, regardless of whether
0948      *        the sending was successful or not.\n
0949      *        - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
0950      *
0951      *    \par Error codes
0952      *    The list of all possible error codes that this operation can finish with:\n
0953      *        - `boost::system::errc::errc_t::success`\n
0954      *        - `boost::asio::error::operation_aborted`[footnote
0955                 This error code can appear if the Client fails to send the \__DISCONNECT\__ packet to the Server.
0956                 Regardless, the connection to the Server is terminated, and the Client is cancelled.
0957             ]\n
0958      *        - \ref boost::mqtt5::client::error::malformed_packet
0959      *
0960      * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
0961      *
0962      *    \par Per-Operation Cancellation
0963      *    This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
0964      *        - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
0965      */
0966     template <
0967         typename CompletionToken =
0968             typename asio::default_completion_token<executor_type>::type
0969     >
0970     decltype(auto) async_disconnect(CompletionToken&& token = {}) {
0971         return async_disconnect(
0972             disconnect_rc_e::normal_disconnection,
0973             disconnect_props {}, std::forward<CompletionToken>(token)
0974         );
0975     }
0976 
0977 };
0978 
0979 } // end namespace boost::mqtt5
0980 
0981 #endif // !BOOST_MQTT5_MQTT_CLIENT_HPP