mqtt_client.hpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  1. //
  2. // Copyright (c) 2023-2025 Ivica Siladic, Bruno Iljazovic, Korina Simicevic
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MQTT5_MQTT_CLIENT_HPP
  8. #define BOOST_MQTT5_MQTT_CLIENT_HPP
  9. #include <boost/mqtt5/error.hpp>
  10. #include <boost/mqtt5/logger_traits.hpp>
  11. #include <boost/mqtt5/types.hpp>
  12. #include <boost/mqtt5/detail/log_invoke.hpp>
  13. #include <boost/mqtt5/detail/rebind_executor.hpp>
  14. #include <boost/mqtt5/impl/client_service.hpp>
  15. #include <boost/mqtt5/impl/publish_send_op.hpp>
  16. #include <boost/mqtt5/impl/re_auth_op.hpp>
  17. #include <boost/mqtt5/impl/run_op.hpp>
  18. #include <boost/mqtt5/impl/subscribe_op.hpp>
  19. #include <boost/mqtt5/impl/unsubscribe_op.hpp>
  20. #include <boost/asio/async_result.hpp>
  21. #include <boost/system/error_code.hpp>
  22. #include <memory>
  23. #include <string>
  24. #include <type_traits>
  25. #include <variant> // std::monostate
  26. #include <vector>
  27. namespace boost::mqtt5 {
  28. namespace asio = boost::asio;
  29. /**
  30. * \brief \__MQTT\__ client used to connect and communicate with a Broker.
  31. *
  32. * \tparam \__StreamType\__ Type of the underlying transport protocol used to transfer
  33. * the stream of bytes between the Client and the Broker. The transport must be
  34. * ordered and lossless.
  35. * \tparam \__TlsContext\__ Type of the context object used in TLS/SSL connections.
  36. * \tparam \__LoggerType\__ Type of object used to log events within the Client.
  37. *
  38. * \par Thread safety
  39. * Distinct objects: safe. \n
  40. * Shared objects: unsafe. \n
  41. * This class is <b>not thread-safe</b>.
  42. * The application must also ensure that all asynchronous operations are performed within the same implicit or explicit strand.
  43. */
  44. template <
  45. typename StreamType,
  46. typename TlsContext = std::monostate,
  47. typename LoggerType = noop_logger
  48. >
  49. class mqtt_client {
  50. public:
  51. /// The executor type associated with the client.
  52. using executor_type = typename StreamType::executor_type;
  53. /// Rebinds the client type to another executor.
  54. template <typename Executor>
  55. struct rebind_executor {
  56. /// The client type when rebound to the specified executor.
  57. using other = mqtt_client<
  58. typename detail::rebind_executor<StreamType, Executor>::other,
  59. TlsContext,
  60. LoggerType
  61. >;
  62. };
  63. private:
  64. using stream_type = StreamType;
  65. using tls_context_type = TlsContext;
  66. using logger_type = LoggerType;
  67. using client_service_type = detail::client_service<
  68. stream_type, tls_context_type, logger_type
  69. >;
  70. using impl_type = std::shared_ptr<client_service_type>;
  71. impl_type _impl;
  72. public:
  73. /**
  74. * \brief Constructs a Client with given parameters.
  75. *
  76. * \param ex An executor that will be associated with the Client.
  77. * \param tls_context A context object used in TLS/SSL connection.
  78. * \param logger An object satisfying the \__LoggerType\__ concept used to log events within the Client.
  79. */
  80. explicit mqtt_client(
  81. const executor_type& ex,
  82. tls_context_type tls_context = {}, logger_type logger = {}
  83. ) :
  84. _impl(std::make_shared<client_service_type>(
  85. ex, std::move(tls_context), std::move(logger)
  86. ))
  87. {}
  88. /**
  89. * \brief Constructs a Client with given parameters.
  90. *
  91. * \tparam \__ExecutionContext\__ Type of a concrete execution context.
  92. * \param context Execution context whose executor will be associated with the Client.
  93. * \param tls_context A context object used in TLS/SSL connection.
  94. * \param logger An object satisfying the \__LoggerType\__ concept used to log events within the Client.
  95. *
  96. * \par Precondition
  97. * \code
  98. * std::is_convertible_v<ExecutionContext&, asio::execution_context&>
  99. * \endcode
  100. */
  101. template <
  102. typename ExecutionContext,
  103. std::enable_if_t<
  104. std::is_convertible_v<ExecutionContext&, asio::execution_context&>,
  105. bool
  106. > = true
  107. >
  108. explicit mqtt_client(
  109. ExecutionContext& context,
  110. tls_context_type tls_context = {}, logger_type logger = {}
  111. ) :
  112. mqtt_client(
  113. context.get_executor(),
  114. std::move(tls_context), std::move(logger)
  115. )
  116. {}
  117. /**
  118. * \brief Move-construct an mqtt_client from another.
  119. */
  120. mqtt_client(mqtt_client&&) noexcept = default;
  121. /**
  122. * \brief Move assignment operator.
  123. *
  124. * \details Cancels this client first.
  125. */
  126. mqtt_client& operator=(mqtt_client&& other) noexcept {
  127. _impl->cancel();
  128. _impl = std::move(other._impl);
  129. return *this;
  130. }
  131. /**
  132. * \brief Destructor.
  133. *
  134. * \details Automatically calls \ref cancel.
  135. */
  136. ~mqtt_client() {
  137. if (_impl)
  138. _impl->cancel();
  139. }
  140. /**
  141. * \brief Get the executor associated with the object.
  142. */
  143. executor_type get_executor() const noexcept {
  144. return _impl->get_executor();
  145. }
  146. /**
  147. * \brief Get the context object used in TLS/SSL connection.
  148. *
  149. * \note This function may only be invoked
  150. * when the template parameter \__TlsContext\__ was configured
  151. * with non-default type during the creation of a \ref mqtt_client.
  152. *
  153. * \par Precondition
  154. * \code
  155. * !std::is_same_v<TlsContext, std::monostate>
  156. * \endcode
  157. */
  158. template <
  159. typename Ctx = TlsContext,
  160. std::enable_if_t<!std::is_same_v<Ctx, std::monostate>, bool> = true
  161. >
  162. decltype(auto) tls_context() {
  163. return _impl->tls_context();
  164. }
  165. /**
  166. * \brief Start the Client.
  167. *
  168. * \param token Completion token that will be used to produce a
  169. * completion handler. The handler will be invoked when the operation completes.
  170. *
  171. * \par Handler signature
  172. * The handler signature for this operation:
  173. * \code
  174. * void (__ERROR_CODE__)
  175. * \endcode
  176. *
  177. * \par Completion condition
  178. * The asynchronous operation will complete with
  179. * `boost::asio::error::operation_aborted` when the client is cancelled by calling
  180. * \ref async_disconnect, \ref cancel, destruction or
  181. * if a non-recoverable error happens during a connection attempt (e.g. access denied).
  182. *
  183. * \par Error codes
  184. * The list of all possible error codes that this operation can finish with:\n
  185. * - `boost::asio::error::operation_aborted`\n
  186. *
  187. * \par Per-Operation Cancellation
  188. * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
  189. * - `cancellation_type::terminal` - invokes \ref cancel \n
  190. */
  191. template <
  192. typename CompletionToken =
  193. typename asio::default_completion_token<executor_type>::type
  194. >
  195. decltype(auto) async_run(CompletionToken&& token = {}) {
  196. using Signature = void (error_code);
  197. return asio::async_initiate<CompletionToken, Signature>(
  198. detail::initiate_async_run(_impl), token
  199. );
  200. }
  201. /**
  202. * \brief Cancel all asynchronous operations. This function has terminal effects.
  203. *
  204. * \details All outstanding operations will complete
  205. * with `boost::asio::error::operation_aborted`.
  206. *
  207. * \attention This function has terminal effects and will close the Client.
  208. * The Client cannot be used before calling \ref async_run again.
  209. */
  210. void cancel() {
  211. auto impl = _impl;
  212. _impl = impl->dup();
  213. impl->cancel();
  214. }
  215. /**
  216. * \brief Assign a \ref will Message.
  217. *
  218. * \details The \ref will Message that the Broker should publish
  219. * after the Network Connection is closed and it is not
  220. * closed normally.
  221. *
  222. * \attention This function takes action when the client is in a non-operational state,
  223. * meaning the \ref async_run function has not been invoked.
  224. * Furthermore, you can use this function after the \ref cancel function has been called,
  225. * before the \ref async_run function is invoked again.
  226. */
  227. mqtt_client& will(will will) {
  228. _impl->will(std::move(will));
  229. return *this;
  230. }
  231. /**
  232. * \brief Assign credentials that will be used to connect to a Broker.
  233. *
  234. * \details Credentials consist of a unique Client Identifier and, optionally,
  235. * a User Name and Password.
  236. *
  237. * \attention This function takes action when the client is in a non-operational state,
  238. * meaning the \ref async_run function has not been invoked.
  239. * Furthermore, you can use this function after the \ref cancel function has been called,
  240. * before the \ref async_run function is invoked again.
  241. */
  242. mqtt_client& credentials(
  243. std::string client_id,
  244. std::string username = "", std::string password = ""
  245. ) {
  246. _impl->credentials(
  247. std::move(client_id),
  248. std::move(username), std::move(password)
  249. );
  250. return *this;
  251. }
  252. /**
  253. * \brief Assign a list of Brokers that the Client will attempt to connect to.
  254. *
  255. * \details The Client will cycle through the list of hosts,
  256. * attempting to establish a connection with each
  257. * until it successfully establishes a connection.
  258. *
  259. * \param hosts List of Broker addresses and ports.
  260. * Address and ports are separated with a colon `:` while
  261. * pairs of addresses and ports are separated with a comma `,`.
  262. * \param default_port The default port to connect to in case the port is not
  263. * explicitly specified in the `hosts` list.
  264. *
  265. * \attention This function takes action when the client is in a non-operational state,
  266. * meaning the \ref async_run function has not been invoked.
  267. * Furthermore, you can use this function after the \ref cancel function has been called,
  268. * before the \ref async_run function is invoked again.
  269. *
  270. * \par Example
  271. * Some valid `hosts` string:
  272. *
  273. * \code
  274. * std::string valid_hosts_1 = "broker1:1883, broker2, broker3:1883";
  275. * std::string valid_hosts_2 = "broker1";
  276. * \endcode
  277. *
  278. */
  279. mqtt_client& brokers(std::string hosts, uint16_t default_port = 1883) {
  280. _impl->brokers(std::move(hosts), default_port);
  281. return *this;
  282. }
  283. /**
  284. * \brief Assign an authenticator that the Client will use for
  285. * \__ENHANCED_AUTH\__ on every connect to a Broker.
  286. * Re-authentication can be initiated by calling \ref re_authenticate.
  287. *
  288. * \param authenticator Object that will be stored (move-constructed or by reference)
  289. * and used for authentication. It needs to satisfy \__Authenticator\__ concept.
  290. *
  291. * \attention This function takes action when the client is in a non-operational state,
  292. * meaning the \ref async_run function has not been invoked.
  293. * Furthermore, you can use this function after the \ref cancel function has been called,
  294. * before the \ref async_run function is invoked again.
  295. *
  296. */
  297. template <typename Authenticator>
  298. mqtt_client& authenticator(Authenticator&& authenticator) {
  299. static_assert(
  300. detail::is_authenticator<Authenticator>,
  301. "The type does not satisfy the Authenticator concept"
  302. );
  303. _impl->authenticator(std::forward<Authenticator>(authenticator));
  304. return *this;
  305. }
  306. /**
  307. * \brief Assign the maximum time interval that is permitted to elapse between
  308. * two transmissions from the Client.
  309. *
  310. * \details A non-zero value initiates a process of sending a \__PINGREQ\__
  311. * packet every `seconds`. If this function is not invoked, the Client assumes
  312. * a \__KEEP_ALIVE\__ interval of 60 seconds.
  313. *
  314. * \param seconds Time interval in seconds.
  315. *
  316. * \note If the Server sends a \__SERVER_KEEP_ALIVE\__,
  317. * the Client will send a \__PINGREQ\__ packet every \__SERVER_KEEP_ALIVE\__ seconds.
  318. *
  319. * \attention This function takes action when the client is in a non-operational state,
  320. * meaning the \ref async_run function has not been invoked.
  321. * Furthermore, you can use this function after the \ref cancel function has been called,
  322. * before the \ref async_run function is invoked again.
  323. *
  324. */
  325. mqtt_client& keep_alive(uint16_t seconds) {
  326. _impl->keep_alive(seconds);
  327. return *this;
  328. }
  329. /**
  330. * \brief Assign \__CONNECT_PROPS\__ that will be sent in a \__CONNECT\__ packet.
  331. * \param props \__CONNECT_PROPS\__ sent in a \__CONNECT\__ packet.
  332. * \see See \__CONNECT_PROPS\__ for all eligible properties.
  333. */
  334. mqtt_client& connect_properties(connect_props props) {
  335. _impl->connect_properties(std::move(props));
  336. return *this;
  337. }
  338. /**
  339. * \brief Assign a property that will be sent in a \__CONNECT\__ packet.
  340. * \param prop The \__CONNECT_PROPS\__ property to set.
  341. * \param value Value that will be assigned to the property.
  342. *
  343. * \par Example
  344. * \code
  345. * client.connect_property(prop::session_expiry_interval, 40); // ok
  346. * client.connect_property(prop::reason_string, "reason"); // does not compile, not a CONNECT prop!
  347. * \endcode
  348. *
  349. * \see See \__CONNECT_PROPS\__ for all eligible properties.
  350. */
  351. template <prop::property_type p>
  352. mqtt_client& connect_property(
  353. std::integral_constant<prop::property_type, p> prop,
  354. prop::value_type_t<p> value
  355. ) {
  356. _impl->connect_property(prop, std::move(value));
  357. return *this;
  358. }
  359. /**
  360. * \brief Initiates \__RE_AUTHENTICATION\__
  361. * using the authenticator given in the \ref authenticator method.
  362. *
  363. * \note If \ref authenticator was not called, this method does nothing.
  364. */
  365. void re_authenticate() {
  366. detail::re_auth_op { _impl }.perform();
  367. }
  368. /**
  369. * \brief Retrieves the value of a specific property from the last \__CONNACK\__ packet received.
  370. *
  371. * \details The return type varies according to the property requested.
  372. * For all properties, the return type will be `std::optional` of their respective value type.
  373. * For `boost::mqtt5::prop::user_property`, the return type is
  374. * `std::vector<std::pair<std::string, std::string>>`.
  375. *
  376. * \param prop The \__CONNACK_PROPS\__ property value to retrieve.
  377. *
  378. * \par Example
  379. * \code
  380. * std::optional<std::string> auth_method = client.connack_property(boost::mqtt5::prop::authentication_method); // ok
  381. * std::optional<std::string> c_type = client.connack_property(boost::mqtt5::prop::content_type); // does not compile, not a CONNACK prop!
  382. * \endcode
  383. *
  384. * \see See \__CONNACK_PROPS\__ for all eligible properties.
  385. */
  386. template <prop::property_type p>
  387. const auto& connack_property(
  388. std::integral_constant<prop::property_type, p> prop
  389. ) const {
  390. return _impl->connack_property(prop);
  391. }
  392. /**
  393. * \brief Retrieves the \__CONNACK_PROPS\__ from the last \__CONNACK\__ packet received.
  394. *
  395. * \see See \__CONNACK_PROPS\__ for all eligible properties.
  396. */
  397. const connack_props& connack_properties() const {
  398. return _impl->connack_properties();
  399. }
  400. /**
  401. * \brief Send a \__PUBLISH\__ packet to Broker to transport an
  402. * Application Message.
  403. *
  404. * \tparam qos_type The \ref qos_e level of assurance for delivery.
  405. * \param topic Identification of the information channel to which
  406. * Payload data is published.
  407. * \param payload The Application Message that is being published.
  408. * \param retain The \ref retain_e flag.
  409. * \param props An instance of \__PUBLISH_PROPS\__.
  410. * \param token Completion token that will be used to produce a
  411. * completion handler. The handler will be invoked when the operation completes.
  412. * On immediate completion, invocation of the handler will be performed in a manner
  413. * equivalent to using \__ASYNC_IMMEDIATE\__.
  414. *
  415. * \par Handler signature
  416. * The handler signature for this operation depends on the \ref qos_e specified:\n
  417. *
  418. * `qos` == `qos_e::at_most_once`:
  419. * \code
  420. * void (
  421. * __ERROR_CODE__ // Result of operation
  422. * )
  423. * \endcode
  424. *
  425. * `qos` == `qos_e::at_least_once`:
  426. * \code
  427. * void (
  428. * __ERROR_CODE__, // Result of operation.
  429. * __REASON_CODE__, // Reason Code received from Broker.
  430. * __PUBACK_PROPS__ // Properties received in the PUBACK packet.
  431. * )
  432. * \endcode
  433. *
  434. * `qos` == `qos_e::exactly_once`:
  435. * \code
  436. * void (
  437. * __ERROR_CODE__, // Result of operation.
  438. * __REASON_CODE__, // Reason Code received from Broker.
  439. * __PUBCOMP_PROPS__ // Properties received in the PUBCOMP packet.
  440. * )
  441. * \endcode
  442. *
  443. * \par Completion condition
  444. * Depending on the \ref qos_e specified, the asynchronous operation will complete
  445. * when one of the following conditions is true:\n
  446. * - If `qos` == `qos_e::at_most_once` and the Client
  447. * has successfully written the packet to the transport. \n
  448. * - If `qos` == `qos_e::at_least_once` and the packet has
  449. * been sent and acknowledged through the reception of a \__PUBACK\__ packet.
  450. * - If `qos` == `qos_e::exactly_once` and the packet has
  451. * been sent and fully acknowledged through the reception of a \__PUBCOMP\__ packet.
  452. * - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
  453. *
  454. * \par Error codes
  455. * The list of all possible error codes that this operation can finish with:\n
  456. * - `boost::system::errc::errc_t::success` \n
  457. * - `boost::asio::error::operation_aborted` \n
  458. * - `boost::asio::error::no_recovery` \n
  459. * - \ref boost::mqtt5::client::error::malformed_packet
  460. * - \ref boost::mqtt5::client::error::packet_too_large
  461. * - \ref boost::mqtt5::client::error::pid_overrun
  462. * - \ref boost::mqtt5::client::error::qos_not_supported
  463. * - \ref boost::mqtt5::client::error::retain_not_available
  464. * - \ref boost::mqtt5::client::error::topic_alias_maximum_reached
  465. * - \ref boost::mqtt5::client::error::invalid_topic
  466. *
  467. * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
  468. *
  469. * \par Per-Operation Cancellation
  470. * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
  471. * - `cancellation_type::terminal` - invokes \ref cancel \n
  472. * - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__PUBLISH\__ packet \n
  473. *
  474. */
  475. template <qos_e qos_type,
  476. typename CompletionToken =
  477. typename asio::default_completion_token<executor_type>::type
  478. >
  479. decltype(auto) async_publish(
  480. std::string topic, std::string payload,
  481. retain_e retain, const publish_props& props,
  482. CompletionToken&& token = {}
  483. ) {
  484. using Signature = detail::on_publish_signature<qos_type>;
  485. return asio::async_initiate<CompletionToken, Signature>(
  486. detail::initiate_async_publish<client_service_type, qos_type>(_impl),
  487. token,
  488. std::move(topic), std::move(payload), retain, props
  489. );
  490. }
  491. /**
  492. * \brief Send a \__SUBSCRIBE\__ packet to Broker to create a Subscription
  493. * to one or more Topics of interest.
  494. *
  495. * \details After the Subscription has been established, the Broker will send
  496. * PUBLISH packets to the Client to forward Application Messages that were published
  497. * to Topics that the Client subscribed to. The Application Messages can be received
  498. * with \ref mqtt_client::async_receive function.
  499. *
  500. * \param topics A list of \ref subscribe_topic of interest.
  501. * \param props An instance of \__SUBSCRIBE_PROPS\__.
  502. * \param token Completion token that will be used to produce a
  503. * completion handler. The handler will be invoked when the operation completes.
  504. * On immediate completion, invocation of the handler will be performed in a manner
  505. * equivalent to using \__ASYNC_IMMEDIATE\__.
  506. *
  507. * \par Handler signature
  508. * The handler signature for this operation:
  509. * \code
  510. * void (
  511. * __ERROR_CODE__, // Result of operation.
  512. * std::vector<__REASON_CODE__>, // Vector of Reason Codes indicating
  513. * // the Subscription result for each Topic
  514. * // in the SUBSCRIBE packet.
  515. * __SUBACK_PROPS__, // Properties received in the SUBACK packet.
  516. * )
  517. * \endcode
  518. *
  519. * \par Completion condition
  520. * The asynchronous operation will complete when one of the following conditions is true:\n
  521. * - The Client has successfully sent a \__SUBSCRIBE\__ packet
  522. * and has received a \__SUBACK\__ response from the Broker.\n
  523. * - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
  524. *
  525. * \par Error codes
  526. * The list of all possible error codes that this operation can finish with:\n
  527. * - `boost::system::errc::errc_t::success` \n
  528. * - `boost::asio::error::no_recovery` \n
  529. * - `boost::asio::error::operation_aborted` \n
  530. * - \ref boost::mqtt5::client::error::malformed_packet
  531. * - \ref boost::mqtt5::client::error::packet_too_large
  532. * - \ref boost::mqtt5::client::error::pid_overrun
  533. * - \ref boost::mqtt5::client::error::invalid_topic
  534. * - \ref boost::mqtt5::client::error::wildcard_subscription_not_available
  535. * - \ref boost::mqtt5::client::error::subscription_identifier_not_available
  536. * - \ref boost::mqtt5::client::error::shared_subscription_not_available
  537. *
  538. * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
  539. *
  540. * \par Per-Operation Cancellation
  541. * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
  542. * - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
  543. * - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__SUBSCRIBE\__ packet \n
  544. *
  545. */
  546. template <
  547. typename CompletionToken =
  548. typename asio::default_completion_token<executor_type>::type
  549. >
  550. decltype(auto) async_subscribe(
  551. const std::vector<subscribe_topic>& topics,
  552. const subscribe_props& props,
  553. CompletionToken&& token = {}
  554. ) {
  555. using Signature = void (
  556. error_code, std::vector<reason_code>, suback_props
  557. );
  558. return asio::async_initiate<CompletionToken, Signature>(
  559. detail::initiate_async_subscribe(_impl), token,
  560. topics, props
  561. );
  562. }
  563. /**
  564. * \brief Send a \__SUBSCRIBE\__ packet to Broker to create a Subscription
  565. * to one Topic of interest.
  566. *
  567. * \details After the Subscription has been established, the Broker will send
  568. * \__PUBLISH\__ packets to the Client to forward Application Messages that were published
  569. * to Topics that the Client subscribed to. The Application Messages can be received
  570. * with \ref mqtt_client::async_receive function.
  571. *
  572. * \param topic A \ref subscribe_topic of interest.
  573. * \param props An instance of \__SUBSCRIBE_PROPS\__.
  574. * \param token Completion token that will be used to produce a
  575. * completion handler. The handler will be invoked when the operation completes.
  576. * On immediate completion, invocation of the handler will be performed in a manner
  577. * equivalent to using \__ASYNC_IMMEDIATE\__.
  578. *
  579. * \par Handler signature
  580. * The handler signature for this operation:
  581. * \code
  582. * void (
  583. * __ERROR_CODE__, // Result of operation.
  584. * std::vector<__REASON_CODE__>, // Vector of Reason Codes containing the
  585. * // single Subscription result for the Topic
  586. * // in the SUBSCRIBE packet.
  587. * __SUBACK_PROPS__, // Properties received in the SUBACK packet.
  588. * )
  589. * \endcode
  590. *
  591. * \par Completion condition
  592. * The asynchronous operation will complete when one of the following conditions is true:\n
  593. * - The Client has successfully sent a \__SUBSCRIBE\__ packet
  594. * and has received a \__SUBACK\__ response from the Broker.\n
  595. * - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
  596. *
  597. * \par Error codes
  598. * The list of all possible error codes that this operation can finish with:\n
  599. * - `boost::system::errc::errc_t::success` \n
  600. * - `boost::asio::error::no_recovery` \n
  601. * - `boost::asio::error::operation_aborted` \n
  602. * - \ref boost::mqtt5::client::error::malformed_packet
  603. * - \ref boost::mqtt5::client::error::packet_too_large
  604. * - \ref boost::mqtt5::client::error::pid_overrun
  605. * - \ref boost::mqtt5::client::error::invalid_topic
  606. * - \ref boost::mqtt5::client::error::wildcard_subscription_not_available
  607. * - \ref boost::mqtt5::client::error::subscription_identifier_not_available
  608. * - \ref boost::mqtt5::client::error::shared_subscription_not_available
  609. *
  610. * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
  611. *
  612. * \par Per-Operation Cancellation
  613. * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
  614. * - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
  615. * - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__SUBSCRIBE\__ packet \n
  616. *
  617. */
  618. template <
  619. typename CompletionToken =
  620. typename asio::default_completion_token<executor_type>::type
  621. >
  622. decltype(auto) async_subscribe(
  623. const subscribe_topic& topic, const subscribe_props& props,
  624. CompletionToken&& token = {}
  625. ) {
  626. return async_subscribe(
  627. std::vector<subscribe_topic> { topic }, props,
  628. std::forward<CompletionToken>(token)
  629. );
  630. }
  631. /**
  632. * \brief Send an \__UNSUBSCRIBE\__ packet to Broker to unsubscribe from one
  633. * or more Topics.
  634. *
  635. * \note The Client may still receive residual Application Messages
  636. * through the \ref mqtt_client::async_receive function
  637. * from Topics the Client just unsubscribed to.
  638. *
  639. * \param topics List of Topics to unsubscribe from.
  640. * \param props An instance of \__UNSUBSCRIBE_PROPS\__.
  641. * \param token Completion token that will be used to produce a
  642. * completion handler. The handler will be invoked when the operation completes.
  643. * On immediate completion, invocation of the handler will be performed in a manner
  644. * equivalent to using \__ASYNC_IMMEDIATE\__.
  645. *
  646. * \par Handler signature
  647. * The handler signature for this operation:
  648. * \code
  649. * void (
  650. * __ERROR_CODE__, // Result of operation.
  651. * std::vector<__REASON_CODE__>, // Vector of Reason Codes indicating
  652. * // the result of unsubscribe operation
  653. * // for each Topic in the UNSUBSCRIBE packet.
  654. * __UNSUBACK_PROPS__, // Properties received in the UNSUBACK packet.
  655. * )
  656. * \endcode
  657. *
  658. * \par Completion condition
  659. * The asynchronous operation will complete when one of the following conditions is true:\n
  660. * - The Client has successfully sent an \__UNSUBSCRIBE\__ packet
  661. * and has received an \__UNSUBACK\__ response from the Broker.\n
  662. * - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
  663. *
  664. * \par Error codes
  665. * The list of all possible error codes that this operation can finish with:\n
  666. * - `boost::system::errc::errc_t::success` \n
  667. * - `boost::asio::error::no_recovery` \n
  668. * - `boost::asio::error::operation_aborted` \n
  669. * - \ref boost::mqtt5::client::error::malformed_packet
  670. * - \ref boost::mqtt5::client::error::packet_too_large
  671. * - \ref boost::mqtt5::client::error::pid_overrun
  672. * - \ref boost::mqtt5::client::error::invalid_topic
  673. *
  674. * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
  675. *
  676. * \par Per-Operation Cancellation
  677. * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
  678. * - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
  679. * - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__UNSUBSCRIBE\__ packet \n
  680. *
  681. */
  682. template <
  683. typename CompletionToken =
  684. typename asio::default_completion_token<executor_type>::type
  685. >
  686. decltype(auto) async_unsubscribe(
  687. const std::vector<std::string>& topics, const unsubscribe_props& props,
  688. CompletionToken&& token = {}
  689. ) {
  690. using Signature = void (
  691. error_code, std::vector<reason_code>, unsuback_props
  692. );
  693. return asio::async_initiate<CompletionToken, Signature>(
  694. detail::initiate_async_unsubscribe(_impl), token,
  695. topics, props
  696. );
  697. }
  698. /**
  699. * \brief Send an \__UNSUBSCRIBE\__ packet to Broker to unsubscribe
  700. * from one Topic.
  701. *
  702. * \note The Client may still receive residual Application Messages
  703. * through the \ref mqtt_client::async_receive function
  704. * from Topics the Client just unsubscribed to.
  705. *
  706. * \param topic Topic to unsubscribe from.
  707. * \param props An instance of \__UNSUBSCRIBE_PROPS\__.
  708. * \param token Completion token that will be used to produce a
  709. * completion handler. The handler will be invoked when the operation completes.
  710. * On immediate completion, invocation of the handler will be performed in a manner
  711. * equivalent to using \__ASYNC_IMMEDIATE\__.
  712. *
  713. * \par Handler signature
  714. * The handler signature for this operation:
  715. * \code
  716. * void (
  717. * __ERROR_CODE__, // Result of operation.
  718. * std::vector<__REASON_CODE__>, // Vector of Reason Codes containing
  719. * // the result of unsubscribe operation
  720. * // for the Topic in the UNSUBSCRIBE packet.
  721. * __UNSUBACK_PROPS__, // Properties received in the UNSUBACK packet.
  722. * )
  723. * \endcode
  724. *
  725. * \par Completion condition
  726. * The asynchronous operation will complete when one of the following conditions is true:\n
  727. * - The Client has successfully sent an \__UNSUBSCRIBE\__ packet
  728. * and has received an \__UNSUBACK\__ response from the Broker.\n
  729. * - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
  730. *
  731. * \par Error codes
  732. * The list of all possible error codes that this operation can finish with:\n
  733. * - `boost::system::errc::errc_t::success` \n
  734. * - `boost::asio::error::no_recovery` \n
  735. * - `boost::asio::error::operation_aborted` \n
  736. * - \ref boost::mqtt5::client::error::malformed_packet
  737. * - \ref boost::mqtt5::client::error::packet_too_large
  738. * - \ref boost::mqtt5::client::error::pid_overrun
  739. * - \ref boost::mqtt5::client::error::invalid_topic
  740. *
  741. * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
  742. *
  743. * \par Per-Operation Cancellation
  744. * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
  745. * - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
  746. * - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__UNSUBSCRIBE\__ packet \n
  747. *
  748. */
  749. template <
  750. typename CompletionToken =
  751. typename asio::default_completion_token<executor_type>::type
  752. >
  753. decltype(auto) async_unsubscribe(
  754. const std::string& topic, const unsubscribe_props& props,
  755. CompletionToken&& token = {}
  756. ) {
  757. return async_unsubscribe(
  758. std::vector<std::string> { topic }, props,
  759. std::forward<CompletionToken>(token)
  760. );
  761. }
  762. /**
  763. * \brief Asynchronously receive an Application Message.
  764. *
  765. * \details The Client will receive and complete deliveries for all the
  766. * \__PUBLISH\__ packets received from the Broker throughout its lifetime.
  767. * The Client will store them internally in the order they were delivered.
  768. * Calling this function will attempt to receive an Application Message
  769. * from internal storage.
  770. *
  771. * \note It is only recommended to call this function if you have established
  772. * a successful Subscription to a Topic using the \ref async_subscribe function.
  773. *
  774. * \param token Completion token that will be used to produce a
  775. * completion handler. The handler will be invoked when the operation completes.
  776. * On immediate completion, invocation of the handler will be performed in a manner
  777. * equivalent to using \__POST\__.
  778. *
  779. * \par Handler signature
  780. * The handler signature for this operation:
  781. * \code
  782. * void (
  783. * __ERROR_CODE__, // Result of operation.
  784. * std::string, // Topic, the origin of the Application Message.
  785. * std::string, // Payload, the content of the Application Message.
  786. * __PUBLISH_PROPS__, // Properties received in the PUBLISH packet.
  787. * )
  788. * \endcode
  789. *
  790. * \par Completion condition
  791. * The asynchronous operation will complete when one of the following conditions is true:\n
  792. * - The Client has a pending Application Message in its internal storage
  793. * ready to be received.
  794. * - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
  795. *
  796. * \par Error codes
  797. * The list of all possible error codes that this operation can finish with:\n
  798. * - `boost::system::errc::errc_t::success`\n
  799. * - `boost::asio::error::operation_aborted`\n
  800. * - \ref boost::mqtt5::client::error::session_expired
  801. *
  802. * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
  803. *
  804. * \par Per-Operation Cancellation
  805. * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
  806. * - `cancellation_type::terminal` \n
  807. * - `cancellation_type::partial` \n
  808. * - `cancellation_type::total` \n
  809. */
  810. template <
  811. typename CompletionToken =
  812. typename asio::default_completion_token<executor_type>::type
  813. >
  814. decltype(auto) async_receive(CompletionToken&& token = {}) {
  815. return _impl->async_channel_receive(std::forward<CompletionToken>(token));
  816. }
  817. /**
  818. * \brief Disconnect the Client by sending a \__DISCONNECT\__ packet
  819. * with a specified Reason Code. This function has terminal effects.
  820. *
  821. * \details The Client will attempt to send a \__DISCONNECT\__ packet to the Broker
  822. * with a Reason Code describing the reason for disconnection.
  823. * If the \__DISCONNECT\__ packet is successfully transmitted,
  824. * or if `5 seconds` elapsed without a successful send, the Client will terminate the connection.
  825. *
  826. * \attention This function has terminal effects and will close the Client.
  827. * See \ref mqtt_client::cancel.
  828. *
  829. * \param reason_code Reason Code to notify
  830. * the Broker of the reason for the disconnection.
  831. * \param props An instance of \__DISCONNECT_PROPS\__.
  832. * \param token Completion token that will be used to produce a
  833. * completion handler. The handler will be invoked when the operation completes.
  834. *
  835. * \par Handler signature
  836. * The handler signature for this operation:
  837. * \code
  838. * void (
  839. * __ERROR_CODE__ // Result of operation.
  840. * )
  841. * \endcode
  842. *
  843. * \par Completion condition
  844. * The asynchronous operation will complete when one of the following conditions is true:\n
  845. * - The Client has sent a \__DISCONNECT\__ packet.\n
  846. * - 5 seconds have elapsed without a successful send.\n
  847. * - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
  848. *
  849. * \par Error codes
  850. * The list of all possible error codes that this operation can finish with:\n
  851. * - `boost::system::errc::errc_t::success`\n
  852. * - `boost::asio::error::operation_aborted`[footnote
  853. This error code can appear if the Client fails to send the \__DISCONNECT\__ packet to the Server.
  854. Regardless, the connection to the Server is terminated, and the Client is cancelled.
  855. ]\n
  856. * - \ref boost::mqtt5::client::error::malformed_packet
  857. *
  858. * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
  859. *
  860. * \par Per-Operation Cancellation
  861. * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
  862. * - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
  863. *
  864. */
  865. template <
  866. typename CompletionToken =
  867. typename asio::default_completion_token<executor_type>::type
  868. >
  869. decltype(auto) async_disconnect(
  870. disconnect_rc_e reason_code, const disconnect_props& props,
  871. CompletionToken&& token = {}
  872. ) {
  873. auto impl = _impl;
  874. _impl = impl->dup();
  875. return detail::async_terminal_disconnect(
  876. detail::disconnect_rc_e(static_cast<uint8_t>(reason_code)),
  877. props, impl, std::forward<CompletionToken>(token)
  878. );
  879. }
  880. /**
  881. * \brief Disconnect the Client by sending a \__DISCONNECT\__ packet
  882. * with a Reason Code of reason_codes.normal_disconnection.
  883. * This function has terminal effects.
  884. *
  885. * \details The Client will attempt to send a \__DISCONNECT\__ packet to the Broker
  886. * with a Reason Code describing the reason for disconnection.
  887. * If the \__DISCONNECT\__ packet is successfully transmitted,
  888. * or if `5 seconds` elapsed without a successful send, the Client will terminate the connection.
  889. *
  890. * \attention This function has terminal effects and will close the Client.
  891. * See \ref mqtt_client::cancel.
  892. *
  893. * \param token Completion token that will be used to produce a
  894. * completion handler. The handler will be invoked when the operation completes.
  895. *
  896. * \par Handler signature
  897. * The handler signature for this operation:
  898. * \code
  899. * void (
  900. * __ERROR_CODE__ // Result of operation.
  901. * )
  902. * \endcode
  903. *
  904. * \par Completion condition
  905. * The asynchronous operation will complete when one of the following conditions is true:\n
  906. * - The Client has attempted to send a \__DISCONNECT\__ packet, regardless of whether
  907. * the sending was successful or not.\n
  908. * - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n
  909. *
  910. * \par Error codes
  911. * The list of all possible error codes that this operation can finish with:\n
  912. * - `boost::system::errc::errc_t::success`\n
  913. * - `boost::asio::error::operation_aborted`[footnote
  914. This error code can appear if the Client fails to send the \__DISCONNECT\__ packet to the Server.
  915. Regardless, the connection to the Server is terminated, and the Client is cancelled.
  916. ]\n
  917. * - \ref boost::mqtt5::client::error::malformed_packet
  918. *
  919. * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code.
  920. *
  921. * \par Per-Operation Cancellation
  922. * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n
  923. * - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n
  924. */
  925. template <
  926. typename CompletionToken =
  927. typename asio::default_completion_token<executor_type>::type
  928. >
  929. decltype(auto) async_disconnect(CompletionToken&& token = {}) {
  930. return async_disconnect(
  931. disconnect_rc_e::normal_disconnection,
  932. disconnect_props {}, std::forward<CompletionToken>(token)
  933. );
  934. }
  935. };
  936. } // end namespace boost::mqtt5
  937. #endif // !BOOST_MQTT5_MQTT_CLIENT_HPP